From 9e8574f44c22615de09c133eeb9f60cccfc5aed3 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 13:51:12 -0500 Subject: Bump date on version tag --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index d1ca7769..c2254a7b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Version History -## v2025-12-06 +## v2025-12-21 - You can now discard empty struct values. - For an enum `Foo(A,B,C)`, the syntax `f!` now desugars to `f.A!` using the -- cgit v1.2.3 From 60a9e99425b3cfe12cf9e4fdfd74430c638e16e0 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 14:22:56 -0500 Subject: Add github workflow --- .github/workflows/release.yml | 105 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..234d0685 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,105 @@ +name: Release binaries + +on: + [push] + +jobs: + linux-x86_64: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install deps + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + libgmp-dev \ + libunistring-dev \ + libgc-dev \ + xxd \ + binutils + + - name: Build + run: | + make clean + make + + - name: Package + run: | + mkdir -p tomo + cp tomo tomo/tomo + tar -czf tomo-linux-x86_64.tar.gz tomo + sha256sum tomo-linux-x86_64.tar.gz > tomo-linux-x86_64.tar.gz.sha256 + + - name: Upload + uses: softprops/action-gh-release@v2 + with: + files: | + tomo-linux-x86_64.tar.gz + tomo-linux-x86_64.tar.gz.sha256 + + linux-aarch64: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install deps + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + libgmp-dev \ + libunistring-dev \ + libgc-dev \ + xxd \ + binutils + + - name: Build + run: | + make clean + make + + - name: Package + run: | + mkdir -p tomo + cp tomo tomo/tomo + tar -czf tomo-linux-aarch64.tar.gz tomo + sha256sum tomo-linux-aarch64.tar.gz > tomo-linux-aarch64.tar.gz.sha256 + + - name: Upload + uses: softprops/action-gh-release@v2 + with: + files: | + tomo-linux-aarch64.tar.gz + tomo-linux-aarch64.tar.gz.sha256 + + macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + + - name: Install deps + run: | + brew update + brew install gmp libunistring bdw-gc xxd llvm binutils + + - name: Build + run: | + make clean + make + + - name: Package + run: | + mkdir -p tomo + cp tomo tomo/tomo + tar -czf tomo-macos-universal.tar.gz tomo + shasum -a 256 tomo-macos-universal.tar.gz > tomo-macos-universal.tar.gz.sha256 + + - name: Upload + uses: softprops/action-gh-release@v2 + with: + files: | + tomo-macos-universal.tar.gz + tomo-macos-universal.tar.gz.sha256 + -- cgit v1.2.3 From 1fe355e1e14f099c594573db709555429464b204 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 14:23:10 -0500 Subject: Only build on tag pushing --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 234d0685..45b02e08 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,9 @@ name: Release binaries on: - [push] + push: + tags: + - 'v*' jobs: linux-x86_64: -- cgit v1.2.3 From 1836d095f88ff83d9499c1e979362352a1fa400b Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 14:27:16 -0500 Subject: Minor fix for checking return values --- .github/workflows/release.yml | 4 +--- src/stdlib/intX.c.h | 10 ++++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 45b02e08..234d0685 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,7 @@ name: Release binaries on: - push: - tags: - - 'v*' + [push] jobs: linux-x86_64: diff --git a/src/stdlib/intX.c.h b/src/stdlib/intX.c.h index 0910c7f1..72dfe6ed 100644 --- a/src/stdlib/intX.c.h +++ b/src/stdlib/intX.c.h @@ -55,12 +55,12 @@ public void NAMESPACED(serialize)(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) { (void)info, (void)pointers; #if INTX_C_H__INT_BITS < 32 - fwrite(obj, sizeof(INT_T), 1, out); + if (fwrite(obj, sizeof(INT_T), 1, out) != sizeof(INT_T)) fail("Failed to write whole integer"); #else INT_T i = *(INT_T *)obj; UINT_T z = (UINT_T)((i << 1L) ^ (i >> (INTX_C_H__INT_BITS - 1L))); // Zigzag encode while (z >= 0x80L) { - fputc((uint8_t)(z | 0x80L), out); + if (fputc((uint8_t)(z | 0x80L), out) == EOF) fail("Failed to write full integer"); z >>= 7L; } fputc((uint8_t)z, out); @@ -71,11 +71,13 @@ public void NAMESPACED(deserialize)(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *info) { (void)info, (void)pointers; #if INTX_C_H__INT_BITS < 32 - fread(outval, sizeof(INT_T), 1, in); + if (fread(outval, sizeof(INT_T), 1, in) != sizeof(INT_T)) fail("Failed to read full integer"); #else UINT_T z = 0; for (size_t shift = 0;; shift += 7) { - uint8_t byte = (uint8_t)fgetc(in); + int i = fgetc(in); + if (i == EOF) fail("Failed to read whole integer"); + uint8_t byte = (uint8_t)i; z |= ((UINT_T)(byte & 0x7F)) << shift; if ((byte & 0x80) == 0) break; } -- cgit v1.2.3 From 86a08a38a8a60b6a0de0da62a5d3fa843f6db71f Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 14:30:11 -0500 Subject: Fix empty allocation --- .github/workflows/release.yml | 6 +++--- src/stdlib/lists.h | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 234d0685..d153774f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,7 @@ jobs: - name: Build run: | make clean - make + make -j - name: Package run: | @@ -58,7 +58,7 @@ jobs: - name: Build run: | make clean - make + make -j - name: Package run: | @@ -87,7 +87,7 @@ jobs: - name: Build run: | make clean - make + make -j - name: Package run: | diff --git a/src/stdlib/lists.h b/src/stdlib/lists.h index 457fed52..6a0a1d43 100644 --- a/src/stdlib/lists.h +++ b/src/stdlib/lists.h @@ -61,7 +61,8 @@ extern char _EMPTY_LIST_SENTINEL; t items[] = {__VA_ARGS__}; \ (List_t){.length = sizeof(items) / sizeof(items[0]), \ .stride = (int64_t)&items[1] - (int64_t)&items[0], \ - .data = memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ + .data = sizeof(items) == 0 ? &_EMPTY_LIST_SENTINEL \ + : memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ .atomic = 0, \ .data_refcount = 0}; \ }) @@ -70,7 +71,7 @@ extern char _EMPTY_LIST_SENTINEL; t items[N] = {__VA_ARGS__}; \ (List_t){.length = N, \ .stride = (int64_t)&items[1] - (int64_t)&items[0], \ - .data = memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ + .data = N == 0 ? &_EMPTY_LIST_SENTINEL : memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ .atomic = 0, \ .data_refcount = 0}; \ }) -- cgit v1.2.3 From 63e6d52f1e1ad9ba3e5dd453115abfc2f8418fc6 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 15:27:05 -0500 Subject: Rework makefile to simplify things --- Makefile | 101 ++++++++++++++++++++++++++++++++--------------------- build/include/tomo | 1 - link_versions.sh | 5 --- local-tomo | 6 ++-- 4 files changed, 64 insertions(+), 49 deletions(-) delete mode 120000 build/include/tomo delete mode 100644 link_versions.sh diff --git a/Makefile b/Makefile index 32192554..342fec8e 100644 --- a/Makefile +++ b/Makefile @@ -107,49 +107,81 @@ else LIBTOMO_FLAGS += -Wl,-soname,libtomo@$(TOMO_VERSION).so endif EXE_FILE=tomo@$(TOMO_VERSION) -MODULES_FILE=build/lib/tomo@$(TOMO_VERSION)/modules.ini COMPILER_OBJS=$(patsubst %.c,%.o,$(wildcard src/*.c src/compile/*.c src/parse/*.c src/formatter/*.c)) STDLIB_OBJS=$(patsubst %.c,%.o,$(wildcard src/stdlib/*.c)) TESTS=$(patsubst test/%.tm,test/results/%.tm.testresult,$(wildcard test/[!_]*.tm)) API_YAML=$(wildcard api/*.yaml) API_MD=$(patsubst %.yaml,%.md,$(API_YAML)) -INCLUDE_SYMLINK=build/include/tomo@$(TOMO_VERSION) -all: config.mk check-c-compiler check-libs $(INCLUDE_SYMLINK) build/lib/$(LIB_FILE) build/lib/$(AR_FILE) $(MODULES_FILE) build/bin/$(EXE_FILE) +all: config.mk check-c-compiler check-libs build @$(ECHO) "All done!" -$(INCLUDE_SYMLINK): - ln -s ../../src/stdlib $@ +headers := $(wildcard src/stdlib/*.h) +build_headers := $(patsubst src/stdlib/%.h, build/$(TOMO_VERSION)/include/tomo@$(TOMO_VERSION)/%.h, $(headers)) -version: - @echo $(TOMO_VERSION) +# find all man pages +manpages := $(wildcard man/*/*) -check-c-compiler: - @$(DEFAULT_C_COMPILER) -v 2>/dev/null >/dev/null \ - || { printf '\033[31;1m%s\033[m\n' "You have set your DEFAULT_C_COMPILER to $(DEFAULT_C_COMPILER) in your config.mk, but I can't run it!"; exit 1; } +# generate corresponding build paths with .gz +build_manpages := $(foreach f,$(manpages),build/$(TOMO_VERSION)/$(f).gz) -check-libs: check-c-compiler - @echo 'int main() { return 0; }' | $(DEFAULT_C_COMPILER) $(LDFLAGS) $(LDLIBS) -x c - -o /dev/null 2>/dev/null >/dev/null \ - || { printf '\033[31;1m%s\033[m\n' "I expected to find the following libraries on your system, but I can't find them: $(LDLIBS)"; exit 1; } +# Ensure directories exist +dirs := build/$(TOMO_VERSION)/include/tomo@$(TOMO_VERSION) \ + build/$(TOMO_VERSION)/lib \ + build/$(TOMO_VERSION)/lib/tomo@$(TOMO_VERSION) \ + build/$(TOMO_VERSION)/bin \ + build/$(TOMO_VERSION)/man/man1 \ + build/$(TOMO_VERSION)/man/man3 \ + build/$(TOMO_VERSION)/share/licenses/tomo@$(TOMO_VERSION) + +$(dirs): + mkdir -p $@ + +# Rule for copying headers +build/$(TOMO_VERSION)/include/tomo@$(TOMO_VERSION)%.h: src/stdlib/%.h | build/$(TOMO_VERSION)/include/tomo@$(TOMO_VERSION) + cp $< $@ + +# Rule for gzipping man pages +build/$(TOMO_VERSION)/man/man1/%.gz: man/man1/% | build/$(TOMO_VERSION)/man/man1 + gzip -c $< > $@ +build/$(TOMO_VERSION)/man/man3/%.gz: man/man3/% | build/$(TOMO_VERSION)/man/man3 + gzip -c $< > $@ -build/bin/$(EXE_FILE): $(STDLIB_OBJS) $(COMPILER_OBJS) - @mkdir -p build/bin +build/$(TOMO_VERSION)/bin/tomo: build/$(TOMO_VERSION)/bin/tomo@$(TOMO_VERSION) | build/$(TOMO_VERSION)/bin + ln -sf tomo@$(TOMO_VERSION) $@ + +build/$(TOMO_VERSION)/bin/$(EXE_FILE): $(STDLIB_OBJS) $(COMPILER_OBJS) | build/$(TOMO_VERSION)/bin @$(ECHO) $(CC) $(CFLAGS_PLACEHOLDER) $(LDFLAGS) $^ $(LDLIBS) -o $@ @$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@ -build/lib/$(LIB_FILE): $(STDLIB_OBJS) - @mkdir -p build/lib +build/$(TOMO_VERSION)/lib/$(LIB_FILE): $(STDLIB_OBJS) | build/$(TOMO_VERSION)/lib @$(ECHO) $(CC) $^ $(CFLAGS_PLACEHOLDER) $(OSFLAGS) $(LDFLAGS) $(LDLIBS) $(LIBTOMO_FLAGS) -o $@ @$(CC) $^ $(CFLAGS) $(OSFLAGS) $(LDFLAGS) $(LDLIBS) $(LIBTOMO_FLAGS) -o $@ -$(MODULES_FILE): modules/core.ini modules/examples.ini - @mkdir -p build/lib/tomo@$(TOMO_VERSION) +build/$(TOMO_VERSION)/lib/$(AR_FILE): $(STDLIB_OBJS) | build/$(TOMO_VERSION)/lib + ar -rcs $@ $^ + +build/$(TOMO_VERSION)/lib/tomo@$(TOMO_VERSION)/modules.ini: modules/core.ini modules/examples.ini | build/$(TOMO_VERSION)/lib/tomo@$(TOMO_VERSION) @cat $^ > $@ -build/lib/$(AR_FILE): $(STDLIB_OBJS) - @mkdir -p build/lib - ar -rcs $@ $^ +build/$(TOMO_VERSION)/share/licenses/tomo@$(TOMO_VERSION)/LICENSE.md: LICENSE.md | build/$(TOMO_VERSION)/share/licenses/tomo@$(TOMO_VERSION) + cp $< $@ + +build: build/$(TOMO_VERSION)/bin/tomo build/$(TOMO_VERSION)/bin/tomo@$(TOMO_VERSION) build/$(TOMO_VERSION)/lib/$(LIB_FILE) \ + build/$(TOMO_VERSION)/lib/$(AR_FILE) build/$(TOMO_VERSION)/lib/tomo@$(TOMO_VERSION)/modules.ini \ + build/$(TOMO_VERSION)/share/licenses/tomo@$(TOMO_VERSION)/LICENSE.md $(build_headers) $(build_manpages) + +version: + @echo $(TOMO_VERSION) + +check-c-compiler: + @$(DEFAULT_C_COMPILER) -v 2>/dev/null >/dev/null \ + || { printf '\033[31;1m%s\033[m\n' "You have set your DEFAULT_C_COMPILER to $(DEFAULT_C_COMPILER) in your config.mk, but I can't run it!"; exit 1; } + +check-libs: check-c-compiler + @echo 'int main() { return 0; }' | $(DEFAULT_C_COMPILER) $(LDFLAGS) $(LDLIBS) -x c - -o /dev/null 2>/dev/null >/dev/null \ + || { printf '\033[31;1m%s\033[m\n' "I expected to find the following libraries on your system, but I can't find them: $(LDLIBS)"; exit 1; } tags: ctags src/*.{c,h} src/stdlib/*.{c,h} src/compile/*.{c,h} src/parse/*.{c,h} src/formatter/*.{c,h} @@ -181,7 +213,7 @@ src/tomo.o: src/changes.md.h %: %.tm ./local-tomo -e $< -test/results/%.tm.testresult: test/%.tm build/bin/$(EXE_FILE) +test/results/%.tm.testresult: test/%.tm build @mkdir -p test/results @printf '\033[33;1;4m%s\033[m\n' $< @if ! COLOR=1 LC_ALL=C ./local-tomo -O 1 $< 2>&1 | tee $@; then \ @@ -193,7 +225,7 @@ test: $(TESTS) @printf '\033[32;7m ALL TESTS PASSED! \033[m\n' clean: - rm -rf build/{lib,bin}/* $(COMPILER_OBJS) $(STDLIB_OBJS) test/*.tm.testresult test/.build lib/*/.build examples/.build examples/*/.build + rm -rf build/* $(COMPILER_OBJS) $(STDLIB_OBJS) test/*.tm.testresult test/.build lib/*/.build examples/.build examples/*/.build %: %.md pandoc --lua-filter=docs/.pandoc/bold-code.lua -s $< -t man -o $@ @@ -228,7 +260,7 @@ check-utilities: check-c-compiler @which debugedit 2>/dev/null >/dev/null \ || printf '\033[33;1m%s\033[m\n' "I couldn't find 'debugedit' on your system! Try installing the package 'debugedit' with your package manager. (It's not required though)" -install-files: $(INCLUDE_SYMLINK) build/bin/$(EXE_FILE) build/lib/$(LIB_FILE) build/lib/$(AR_FILE) $(MODULES_FILE) check-utilities +install-files: build check-utilities @if ! echo "$$PATH" | tr ':' '\n' | grep -qx "$(PREFIX)/bin"; then \ echo $$PATH; \ printf "\033[31;1mError: '$(PREFIX)/bin' is not in your \$$PATH variable!\033[m\n" >&2; \ @@ -241,17 +273,7 @@ install-files: $(INCLUDE_SYMLINK) build/bin/$(EXE_FILE) build/lib/$(LIB_FILE) bu $(SUDO) -u $(OWNER) $(MAKE) install-files; \ exit 0; \ fi; \ - mkdir -p -m 755 "$(PREFIX)/man/man1" "$(PREFIX)/man/man3" "$(PREFIX)/bin" \ - "$(PREFIX)/include/tomo@$(TOMO_VERSION)" "$(PREFIX)/lib" "$(PREFIX)/lib/tomo@$(TOMO_VERSION)" "$(PREFIX)/share/licenses/tomo@$(TOMO_VERSION)"; \ - cp src/stdlib/*.h "$(PREFIX)/include/tomo@$(TOMO_VERSION)/"; \ - cp build/lib/$(LIB_FILE) build/lib/$(AR_FILE) "$(PREFIX)/lib/"; \ - cp $(MODULES_FILE) "$(PREFIX)/lib/tomo@$(TOMO_VERSION)"; \ - cp LICENSE.md "$(PREFIX)/share/licenses/tomo@$(TOMO_VERSION)"; \ - rm -f "$(PREFIX)/bin/$(EXE_FILE)"; \ - cp build/bin/$(EXE_FILE) "$(PREFIX)/bin/"; \ - cp man/man1/* "$(PREFIX)/man/man1/"; \ - cp man/man3/* "$(PREFIX)/man/man3/"; \ - sh link_versions.sh + cp -r build/$(TOMO_VERSION)/* $(PREFIX)/ install: install-files @@ -260,11 +282,10 @@ uninstall: $(SUDO) -u $(OWNER) $(MAKE) uninstall; \ exit 0; \ fi; \ - rm -rvf "$(PREFIX)/bin/tomo" "$(PREFIX)/bin/tomo"[0-9]* "$(PREFIX)/bin/tomo_v"* "$(PREFIX)/include/tomo_v"* \ - "$(PREFIX)/lib/libtomo_v*" "$(PREFIX)/lib/tomo@$(TOMO_VERSION)" "$(PREFIX)/share/licenses/tomo@$(TOMO_VERSION)"; \ - sh link_versions.sh + rm -rvf "$(PREFIX)/bin/tomo" "$(PREFIX)/bin/tomo"* "$(PREFIX)/include/tomo"* \ + "$(PREFIX)/lib/libtomo@"* "$(PREFIX)/lib/tomo@"* "$(PREFIX)/share/licenses/tomo@"*; \ endif .SUFFIXES: -.PHONY: all clean install install-files uninstall test tags core-libs examples deps check-utilities check-c-compiler check-libs version +.PHONY: all build clean install install-files uninstall test tags core-libs examples deps check-utilities check-c-compiler check-libs version diff --git a/build/include/tomo b/build/include/tomo deleted file mode 120000 index 82be04c8..00000000 --- a/build/include/tomo +++ /dev/null @@ -1 +0,0 @@ -../../src/stdlib \ No newline at end of file diff --git a/link_versions.sh b/link_versions.sh deleted file mode 100644 index 8dadbb52..00000000 --- a/link_versions.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -TOMO_PREFIX="$(awk -F= '/PREFIX/{print $2}' config.mk)" -cd "$TOMO_PREFIX/bin" -top_version="$(printf '%s\n' 'tomo@'* | sort -r | head -1)" -ln -fs "$top_version" tomo diff --git a/local-tomo b/local-tomo index 76864837..74143604 100755 --- a/local-tomo +++ b/local-tomo @@ -1,11 +1,11 @@ #!/bin/sh version=$(awk '/^## / {print $2; exit}' CHANGES.md) here="$(realpath "$(dirname "$0")")" -if [ ! -e "$here/build/bin/tomo@$version" ]; then +if [ ! -e "$here/dist/$version/bin/tomo@$version" ]; then echo "Tomo hasn't been compiled yet! Run \`make\` to compile it!" exit 1; fi -PATH="$here/build/bin${PATH:+:$PATH}" \ -TOMO_PATH="$here/build" \ +PATH="$here/dist/$version/bin${PATH:+:$PATH}" \ +TOMO_PATH="$here/dist/$version" \ tomo@"$version" "$@" -- cgit v1.2.3 From 434ffd71c9a7eebd46ec0cba1d97b0827b874901 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 15:54:55 -0500 Subject: Don't embed tomo version/path information at compile time, instead infer it at runtime --- Makefile | 49 +++++++++++++++++++------------------- local-tomo | 11 +++------ src/compile/files.c | 4 ++-- src/compile/headers.c | 4 ++-- src/compile/statements.c | 2 +- src/config.h | 9 +------ src/modules.c | 6 ++--- src/stdlib/stacktrace.c | 2 +- src/stdlib/stdlib.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++-- src/stdlib/stdlib.h | 1 + src/tomo.c | 28 ++++++++++++---------- src/typecheck.c | 3 ++- 12 files changed, 116 insertions(+), 64 deletions(-) diff --git a/Makefile b/Makefile index 342fec8e..11b3282f 100644 --- a/Makefile +++ b/Makefile @@ -84,8 +84,8 @@ O=-O3 TOMO_VERSION=$(shell awk 'BEGIN{hashes=sprintf("%c%c",35,35)} $$1==hashes {print $$2; exit}' CHANGES.md) GIT_VERSION=$(shell git log -1 --pretty=format:"%as_%h") CFLAGS=$(CCONFIG) $(INCLUDE_DIRS) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS) $(LTO) \ - -DTOMO_INSTALL='"$(PREFIX)"' -DSUDO='"$(SUDO)"' -DDEFAULT_C_COMPILER='"$(DEFAULT_C_COMPILER)"' \ - -DTOMO_VERSION='"$(TOMO_VERSION)"' -DGIT_VERSION='"$(GIT_VERSION)"' + -DSUDO='"$(SUDO)"' -DDEFAULT_C_COMPILER='"$(DEFAULT_C_COMPILER)"' \ + -DGIT_VERSION='"$(GIT_VERSION)"' CFLAGS_PLACEHOLDER="$$(printf '\033[2m\033[m\n')" LDLIBS=-lgc -lm -lunistring -lgmp LIBTOMO_FLAGS=-shared @@ -117,60 +117,61 @@ API_MD=$(patsubst %.yaml,%.md,$(API_YAML)) all: config.mk check-c-compiler check-libs build @$(ECHO) "All done!" +BUILD_DIR=build/$(TOMO_VERSION) headers := $(wildcard src/stdlib/*.h) -build_headers := $(patsubst src/stdlib/%.h, build/$(TOMO_VERSION)/include/tomo@$(TOMO_VERSION)/%.h, $(headers)) +build_headers := $(patsubst src/stdlib/%.h, $(BUILD_DIR)/include/tomo@$(TOMO_VERSION)/%.h, $(headers)) # find all man pages manpages := $(wildcard man/*/*) # generate corresponding build paths with .gz -build_manpages := $(foreach f,$(manpages),build/$(TOMO_VERSION)/$(f).gz) +build_manpages := $(foreach f,$(manpages),$(BUILD_DIR)/$(f).gz) # Ensure directories exist -dirs := build/$(TOMO_VERSION)/include/tomo@$(TOMO_VERSION) \ - build/$(TOMO_VERSION)/lib \ - build/$(TOMO_VERSION)/lib/tomo@$(TOMO_VERSION) \ - build/$(TOMO_VERSION)/bin \ - build/$(TOMO_VERSION)/man/man1 \ - build/$(TOMO_VERSION)/man/man3 \ - build/$(TOMO_VERSION)/share/licenses/tomo@$(TOMO_VERSION) +dirs := $(BUILD_DIR)/include/tomo@$(TOMO_VERSION) \ + $(BUILD_DIR)/lib \ + $(BUILD_DIR)/lib/tomo@$(TOMO_VERSION) \ + $(BUILD_DIR)/bin \ + $(BUILD_DIR)/man/man1 \ + $(BUILD_DIR)/man/man3 \ + $(BUILD_DIR)/share/licenses/tomo@$(TOMO_VERSION) $(dirs): mkdir -p $@ # Rule for copying headers -build/$(TOMO_VERSION)/include/tomo@$(TOMO_VERSION)%.h: src/stdlib/%.h | build/$(TOMO_VERSION)/include/tomo@$(TOMO_VERSION) +$(BUILD_DIR)/include/tomo@$(TOMO_VERSION)%.h: src/stdlib/%.h | $(BUILD_DIR)/include/tomo@$(TOMO_VERSION) cp $< $@ # Rule for gzipping man pages -build/$(TOMO_VERSION)/man/man1/%.gz: man/man1/% | build/$(TOMO_VERSION)/man/man1 +$(BUILD_DIR)/man/man1/%.gz: man/man1/% | $(BUILD_DIR)/man/man1 gzip -c $< > $@ -build/$(TOMO_VERSION)/man/man3/%.gz: man/man3/% | build/$(TOMO_VERSION)/man/man3 +$(BUILD_DIR)/man/man3/%.gz: man/man3/% | $(BUILD_DIR)/man/man3 gzip -c $< > $@ -build/$(TOMO_VERSION)/bin/tomo: build/$(TOMO_VERSION)/bin/tomo@$(TOMO_VERSION) | build/$(TOMO_VERSION)/bin +$(BUILD_DIR)/bin/tomo: $(BUILD_DIR)/bin/tomo@$(TOMO_VERSION) | $(BUILD_DIR)/bin ln -sf tomo@$(TOMO_VERSION) $@ -build/$(TOMO_VERSION)/bin/$(EXE_FILE): $(STDLIB_OBJS) $(COMPILER_OBJS) | build/$(TOMO_VERSION)/bin +$(BUILD_DIR)/bin/$(EXE_FILE): $(STDLIB_OBJS) $(COMPILER_OBJS) | $(BUILD_DIR)/bin @$(ECHO) $(CC) $(CFLAGS_PLACEHOLDER) $(LDFLAGS) $^ $(LDLIBS) -o $@ @$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@ -build/$(TOMO_VERSION)/lib/$(LIB_FILE): $(STDLIB_OBJS) | build/$(TOMO_VERSION)/lib +$(BUILD_DIR)/lib/$(LIB_FILE): $(STDLIB_OBJS) | $(BUILD_DIR)/lib @$(ECHO) $(CC) $^ $(CFLAGS_PLACEHOLDER) $(OSFLAGS) $(LDFLAGS) $(LDLIBS) $(LIBTOMO_FLAGS) -o $@ @$(CC) $^ $(CFLAGS) $(OSFLAGS) $(LDFLAGS) $(LDLIBS) $(LIBTOMO_FLAGS) -o $@ -build/$(TOMO_VERSION)/lib/$(AR_FILE): $(STDLIB_OBJS) | build/$(TOMO_VERSION)/lib +$(BUILD_DIR)/lib/$(AR_FILE): $(STDLIB_OBJS) | $(BUILD_DIR)/lib ar -rcs $@ $^ -build/$(TOMO_VERSION)/lib/tomo@$(TOMO_VERSION)/modules.ini: modules/core.ini modules/examples.ini | build/$(TOMO_VERSION)/lib/tomo@$(TOMO_VERSION) +$(BUILD_DIR)/lib/tomo@$(TOMO_VERSION)/modules.ini: modules/core.ini modules/examples.ini | $(BUILD_DIR)/lib/tomo@$(TOMO_VERSION) @cat $^ > $@ -build/$(TOMO_VERSION)/share/licenses/tomo@$(TOMO_VERSION)/LICENSE.md: LICENSE.md | build/$(TOMO_VERSION)/share/licenses/tomo@$(TOMO_VERSION) +$(BUILD_DIR)/share/licenses/tomo@$(TOMO_VERSION)/LICENSE.md: LICENSE.md | $(BUILD_DIR)/share/licenses/tomo@$(TOMO_VERSION) cp $< $@ -build: build/$(TOMO_VERSION)/bin/tomo build/$(TOMO_VERSION)/bin/tomo@$(TOMO_VERSION) build/$(TOMO_VERSION)/lib/$(LIB_FILE) \ - build/$(TOMO_VERSION)/lib/$(AR_FILE) build/$(TOMO_VERSION)/lib/tomo@$(TOMO_VERSION)/modules.ini \ - build/$(TOMO_VERSION)/share/licenses/tomo@$(TOMO_VERSION)/LICENSE.md $(build_headers) $(build_manpages) +build: $(BUILD_DIR)/bin/tomo $(BUILD_DIR)/bin/tomo@$(TOMO_VERSION) $(BUILD_DIR)/lib/$(LIB_FILE) \ + $(BUILD_DIR)/lib/$(AR_FILE) $(BUILD_DIR)/lib/tomo@$(TOMO_VERSION)/modules.ini \ + $(BUILD_DIR)/share/licenses/tomo@$(TOMO_VERSION)/LICENSE.md $(build_headers) $(build_manpages) version: @echo $(TOMO_VERSION) @@ -273,7 +274,7 @@ install-files: build check-utilities $(SUDO) -u $(OWNER) $(MAKE) install-files; \ exit 0; \ fi; \ - cp -r build/$(TOMO_VERSION)/* $(PREFIX)/ + cp -r $(BUILD_DIR)/* $(PREFIX)/ install: install-files diff --git a/local-tomo b/local-tomo index 74143604..d16e090e 100755 --- a/local-tomo +++ b/local-tomo @@ -1,11 +1,6 @@ #!/bin/sh version=$(awk '/^## / {print $2; exit}' CHANGES.md) -here="$(realpath "$(dirname "$0")")" -if [ ! -e "$here/dist/$version/bin/tomo@$version" ]; then - echo "Tomo hasn't been compiled yet! Run \`make\` to compile it!" - exit 1; +if ! [ -e ./build/$version/bin/tomo ]; then + make -j fi - -PATH="$here/dist/$version/bin${PATH:+:$PATH}" \ -TOMO_PATH="$here/dist/$version" \ -tomo@"$version" "$@" +exec ./build/$version/bin/tomo "$@" diff --git a/src/compile/files.c b/src/compile/files.c index 27c2e041..555f848c 100644 --- a/src/compile/files.c +++ b/src/compile/files.c @@ -194,8 +194,8 @@ Text_t compile_file(env_t *env, ast_t *ast) { const char *name = file_base_name(ast->file->filename); return Texts(env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT, - "#define __SOURCE_FILE__ ", quoted_str(ast->file->filename), "\n", - "#include \n" + "#define __SOURCE_FILE__ ", quoted_str(ast->file->filename), "\n", "#include \n" "#include \"", name, ".tm.h\"\n\n", includes, env->code->local_typedefs, "\n", env->code->lambdas, "\n", env->code->staticdefs, "\n", top_level_code, "public void ", diff --git a/src/compile/headers.c b/src/compile/headers.c index e90556a1..df7142d1 100644 --- a/src/compile/headers.c +++ b/src/compile/headers.c @@ -135,7 +135,7 @@ Text_t compile_file_header(env_t *env, Path_t header_path, ast_t *ast) { Text_t header = Texts("#pragma once\n", env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT, - "#include \n"); + "#include \n"); compile_typedef_info_t info = {.env = env, .header = &header, .header_path = header_path}; visit_topologically(Match(ast, Block)->statements, (Closure_t){.fn = (void *)_make_typedefs, &info}); @@ -161,7 +161,7 @@ Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast) module_info_t mod = get_used_module_info(ast); glob_t tm_files; const char *folder = mod.version ? String(mod.name, "@", mod.version) : mod.name; - if (glob(String(TOMO_PATH, "/lib/tomo@" TOMO_VERSION "/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, + if (glob(String(TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0) { if (!try_install_module(mod, true)) code_err(ast, "Could not find library"); diff --git a/src/compile/statements.c b/src/compile/statements.c index 01fb1a0b..13dcc064 100644 --- a/src/compile/statements.c +++ b/src/compile/statements.c @@ -197,7 +197,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) { module_info_t mod = get_used_module_info(ast); glob_t tm_files; const char *folder = mod.version ? String(mod.name, "@", mod.version) : mod.name; - if (glob(String(TOMO_PATH, "/lib/tomo@" TOMO_VERSION "/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, + if (glob(String(TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0) { if (!try_install_module(mod, true)) code_err(ast, "Could not find library"); diff --git a/src/config.h b/src/config.h index 8ee44200..e703f95d 100644 --- a/src/config.h +++ b/src/config.h @@ -1,18 +1,11 @@ // Configuration of values that will be baked into the executable: -#ifndef TOMO_VERSION -#define TOMO_VERSION "v???" -#endif - #ifndef GIT_VERSION #define GIT_VERSION "???" #endif -#ifndef TOMO_INSTALL -#define TOMO_INSTALL "/usr/local" -#endif - extern const char *TOMO_PATH; +extern const char *TOMO_VERSION; #ifndef DEFAULT_C_COMPILER #define DEFAULT_C_COMPILER "cc" diff --git a/src/modules.c b/src/modules.c index 36952ec8..056cd5cc 100644 --- a/src/modules.c +++ b/src/modules.c @@ -102,7 +102,7 @@ module_info_t get_used_module_info(ast_t *use) { const char *name = Match(use, Use)->path; module_info_t *info = new (module_info_t, .name = name); Path_t tomo_default_modules = - Path$from_text(Texts(Text$from_str(TOMO_PATH), "/lib/tomo@" TOMO_VERSION "/modules.ini")); + Path$from_text(Texts(Text$from_str(TOMO_PATH), "/lib/tomo@", TOMO_VERSION, "/modules.ini")); read_modules_ini(tomo_default_modules, info); read_modules_ini(Path$sibling(Path$from_str(use->file->filename), Text("modules.ini")), info); read_modules_ini(Path$with_extension(Path$from_str(use->file->filename), Text(":modules.ini"), false), info); @@ -111,8 +111,8 @@ module_info_t get_used_module_info(ast_t *use) { } bool try_install_module(module_info_t mod, bool ask_confirmation) { - Path_t dest = Path$from_text(Texts(Text$from_str(TOMO_PATH), "/lib/tomo@" TOMO_VERSION "/", Text$from_str(mod.name), - "@", Text$from_str(mod.version))); + Path_t dest = Path$from_text(Texts(Text$from_str(TOMO_PATH), "/lib/tomo@", TOMO_VERSION, "/", + Text$from_str(mod.name), "@", Text$from_str(mod.version))); if (Path$exists(dest)) return true; print("No such path: ", dest); diff --git a/src/stdlib/stacktrace.c b/src/stdlib/stacktrace.c index ea939f62..1eba8188 100644 --- a/src/stdlib/stacktrace.c +++ b/src/stdlib/stacktrace.c @@ -98,7 +98,7 @@ void print_stacktrace(FILE *out, int offset) { cwd[cwd_len++] = '/'; cwd[cwd_len] = '\0'; - const char *install_dir = String(TOMO_PATH, "/lib/tomo@" TOMO_VERSION "/"); + const char *install_dir = String(TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/"); static void *stack[1024]; int64_t size = (int64_t)backtrace(stack, sizeof(stack) / sizeof(stack[0])); diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c index f4e6d678..688f2eb4 100644 --- a/src/stdlib/stdlib.c +++ b/src/stdlib/stdlib.c @@ -17,6 +17,7 @@ #include "files.h" #include "metamethods.h" #include "optionals.h" +#include "paths.h" #include "print.h" #include "siphash.h" #include "stacktrace.h" @@ -39,11 +40,66 @@ static ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) { public bool USE_COLOR; + +public +const char *TOMO_PATH = "/usr/local"; + public -Text_t TOMO_VERSION_TEXT = Text(TOMO_VERSION); +const char *TOMO_VERSION = "v0"; + +public +Text_t TOMO_VERSION_TEXT = Text("v0"); + +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD) +#include + +static inline const char *get_library_path(void *func) { + static Dl_info info; + if (dladdr(func, &info)) { + return info.dli_fname; // full path of the library + } + return NULL; +} + +#elif defined(_WIN32) +#include + +static inline const char *get_library_path(void *func) { + static char path[MAX_PATH]; + HMODULE hm = NULL; + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCSTR)func, &hm)) { + if (GetModuleFileName(hm, path, MAX_PATH)) { + return path; + } + } + return NULL; +} + +#else +#error "Unsupported platform" +#endif + +const char *resolve_symlinks(const char *path) { + static char resolved[PATH_MAX]; + if (realpath(path, resolved) != NULL) { + return resolved; + } else { + perror("realpath"); + return NULL; + } +} public -const char *TOMO_PATH = TOMO_INSTALL; +void tomo_configure(void) { + const char *lib_path = resolve_symlinks(get_library_path(get_library_path)); + Path_t path = Path$from_str(lib_path); + TOMO_PATH = Path$as_c_string(Path$parent(Path$parent(path))); + Text_t base_name = Path$base_name(path); + TOMO_VERSION_TEXT = Text$without_suffix( + Text$without_prefix(Text$without_prefix(base_name, Text("lib")), Text("tomo@")), Text(".so")); + TOMO_VERSION = Text$as_c_string(TOMO_VERSION_TEXT); +} static _Noreturn void signal_handler(int sig, siginfo_t *info, void *userdata) { (void)info, (void)userdata; @@ -60,6 +116,7 @@ static _Noreturn void signal_handler(int sig, siginfo_t *info, void *userdata) { public void tomo_init(void) { GC_INIT(); + tomo_configure(); const char *color_env = getenv("COLOR"); USE_COLOR = color_env ? strcmp(color_env, "1") == 0 : isatty(STDOUT_FILENO); const char *no_color_env = getenv("NO_COLOR"); diff --git a/src/stdlib/stdlib.h b/src/stdlib/stdlib.h index 3afe3529..234048e9 100644 --- a/src/stdlib/stdlib.h +++ b/src/stdlib/stdlib.h @@ -15,6 +15,7 @@ extern bool USE_COLOR; extern Text_t TOMO_VERSION_TEXT; +void tomo_configure(void); void tomo_init(void); void tomo_at_cleanup(Closure_t fn); void tomo_cleanup(void); diff --git a/src/tomo.c b/src/tomo.c index 04f0289a..f829951c 100644 --- a/src/tomo.c +++ b/src/tomo.c @@ -92,8 +92,8 @@ static OptionalText_t show_codegen = NONE_TEXT, " -D_BSD_SOURCE" #endif " -DGC_THREADS"), - ldlibs = Text("-lgc -lm -lgmp -lunistring -ltomo@" TOMO_VERSION), ldflags = Text(""), - optimization = Text("2"), cc = Text(DEFAULT_C_COMPILER); + ldlibs = Text("-lgc -lm -lgmp -lunistring"), ldflags = Text(""), optimization = Text("2"), + cc = Text(DEFAULT_C_COMPILER); static Text_t config_summary, // This will be either "" or "sudo -u " or "doas -u " @@ -139,6 +139,9 @@ static List_t normalize_tm_paths(List_t paths) { } int main(int argc, char *argv[]) { + GC_INIT(); + tomo_configure(); + #ifdef __linux__ // Get the file modification time of the compiler, so we // can recompile files after changing the compiler: @@ -149,6 +152,7 @@ int main(int argc, char *argv[]) { if (stat(compiler_path, &compiler_stat) != 0) err(1, "Could not find age of compiler"); #endif + ldlibs = Texts(ldlibs, " -ltomo@", TOMO_VERSION); #ifdef __OpenBSD__ ldlibs = Texts(ldlibs, Text(" -lexecinfo")); #endif @@ -168,7 +172,7 @@ int main(int argc, char *argv[]) { if (getenv("TOMO_PATH")) TOMO_PATH = getenv("TOMO_PATH"); - cflags = Texts("-I'", TOMO_PATH, "/include' -I'", TOMO_PATH, "/lib/tomo@" TOMO_VERSION "' ", cflags); + cflags = Texts("-I'", TOMO_PATH, "/include' -I'", TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "' ", cflags); // Set up environment variables: const char *PATH = getenv("PATH"); @@ -187,7 +191,7 @@ int main(int argc, char *argv[]) { // Run a tool: if ((streq(argv[1], "-r") || streq(argv[1], "--run")) && argc >= 3) { if (strcspn(argv[2], "/;$") == strlen(argv[2])) { - const char *program = String("'", TOMO_PATH, "'/lib/tomo@" TOMO_VERSION "/", argv[2], "/", argv[2]); + const char *program = String("'", TOMO_PATH, "'/lib/tomo@", TOMO_VERSION, "/", argv[2], "/", argv[2]); execv(program, &argv[2]); } print_err("This is not an installed tomo program: ", argv[2]); @@ -219,7 +223,7 @@ int main(int argc, char *argv[]) { " --source-mapping|-m : toggle source mapping in generated code\n" " --changelog: show the Tomo changelog\n" " --run|-r: run a program from ", - TOMO_PATH, "/share/tomo@" TOMO_VERSION "/installed\n"); + TOMO_PATH, "/share/tomo@", TOMO_VERSION, "/installed\n"); Text_t help = Texts(Text("\x1b[1mtomo\x1b[m: a compiler for the Tomo programming language"), Text("\n\n"), usage); cli_arg_t tomo_args[] = { {"run", &run_files, List$info(&Path$info), .short_flag = 'r'}, // @@ -293,7 +297,7 @@ int main(int argc, char *argv[]) { // Uninstall libraries: for (int64_t i = 0; i < (int64_t)uninstall_libraries.length; i++) { Text_t *u = (Text_t *)(uninstall_libraries.data + i * uninstall_libraries.stride); - xsystem(as_owner, "rm -rvf '", TOMO_PATH, "'/lib/tomo@" TOMO_VERSION "/", *u, " '", TOMO_PATH, "'/bin/", *u, + xsystem(as_owner, "rm -rvf '", TOMO_PATH, "'/lib/tomo@", TOMO_VERSION, "/", *u, " '", TOMO_PATH, "'/bin/", *u, " '", TOMO_PATH, "'/man/man1/", *u, ".1"); print("Uninstalled ", *u); } @@ -510,7 +514,7 @@ void build_library(Path_t lib_dir) { void install_library(Path_t lib_dir) { Text_t lib_name = get_library_name(lib_dir); - Path_t dest = Path$child(Path$from_str(String(TOMO_PATH, "/lib/tomo@" TOMO_VERSION)), lib_name); + Path_t dest = Path$child(Path$from_str(String(TOMO_PATH, "/lib/tomo@", TOMO_VERSION)), lib_name); print("Installing ", lib_dir, " into ", dest); if (!Enum$equal(&lib_dir, &dest, &Path$info)) { if (verbose) whisper("Clearing out any pre-existing version of ", lib_name); @@ -530,7 +534,7 @@ void install_library(Path_t lib_dir) { "' " ">/dev/null 2>/dev/null")); (void)result; - print("Installed \033[1m", lib_dir, "\033[m to ", TOMO_PATH, "/lib/tomo@" TOMO_VERSION "/", lib_name); + print("Installed \033[1m", lib_dir, "\033[m to ", TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/", lib_name); } void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t *extra_ldlibs, compile_mode_t mode) { @@ -692,13 +696,13 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l case USE_MODULE: { module_info_t mod = get_used_module_info(stmt_ast); const char *full_name = mod.version ? String(mod.name, "@", mod.version) : mod.name; - Text_t lib = Texts("-Wl,-rpath,'", TOMO_PATH, "/lib/tomo@" TOMO_VERSION "/", Text$from_str(full_name), - "' '", TOMO_PATH, "/lib/tomo@" TOMO_VERSION "/", Text$from_str(full_name), "/lib", + Text_t lib = Texts("-Wl,-rpath,'", TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/", Text$from_str(full_name), + "' '", TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/", Text$from_str(full_name), "/lib", Text$from_str(full_name), SHARED_SUFFIX "'"); Table$set(to_link, &lib, NULL, Table$info(&Text$info, &Void$info)); - List_t children = - Path$glob(Path$from_str(String(TOMO_PATH, "/lib/tomo@" TOMO_VERSION "/", full_name, "/[!._0-9]*.tm"))); + List_t children = Path$glob( + Path$from_str(String(TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/", full_name, "/[!._0-9]*.tm"))); for (int64_t i = 0; i < (int64_t)children.length; i++) { Path_t *child = (Path_t *)(children.data + i * children.stride); Table_t discarded = {.entries = EMPTY_LIST, .fallback = to_compile}; diff --git a/src/typecheck.c b/src/typecheck.c index 139f0655..1ac3943c 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -225,7 +225,8 @@ static env_t *load_module(env_t *env, ast_t *use_ast) { module_info_t mod = get_used_module_info(use_ast); glob_t tm_files; const char *folder = mod.version ? String(mod.name, "@", mod.version) : mod.name; - if (glob(String(TOMO_PATH, "/lib/tomo@" TOMO_VERSION "/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) + if (glob(String(TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, + &tm_files) != 0) { if (!try_install_module(mod, true)) code_err(use_ast, "Couldn't find or install library: ", folder); } -- cgit v1.2.3 From bd244c69f5edd6b84753a4bc6c2bd1ca011e90f7 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 16:02:08 -0500 Subject: Tweak build setup --- .github/workflows/release.yml | 12 +++--------- Makefile | 2 +- local-tomo | 4 ++-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d153774f..57bfc0e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,9 +27,7 @@ jobs: - name: Package run: | - mkdir -p tomo - cp tomo tomo/tomo - tar -czf tomo-linux-x86_64.tar.gz tomo + tar -czf tomo-linux-x86_64.tar.gz build/* sha256sum tomo-linux-x86_64.tar.gz > tomo-linux-x86_64.tar.gz.sha256 - name: Upload @@ -62,9 +60,7 @@ jobs: - name: Package run: | - mkdir -p tomo - cp tomo tomo/tomo - tar -czf tomo-linux-aarch64.tar.gz tomo + tar -czf tomo-linux-aarch64.tar.gz build/* sha256sum tomo-linux-aarch64.tar.gz > tomo-linux-aarch64.tar.gz.sha256 - name: Upload @@ -91,9 +87,7 @@ jobs: - name: Package run: | - mkdir -p tomo - cp tomo tomo/tomo - tar -czf tomo-macos-universal.tar.gz tomo + tar -czf tomo-macos-universal.tar.gz build/* shasum -a 256 tomo-macos-universal.tar.gz > tomo-macos-universal.tar.gz.sha256 - name: Upload diff --git a/Makefile b/Makefile index 11b3282f..e7e689d0 100644 --- a/Makefile +++ b/Makefile @@ -117,7 +117,7 @@ API_MD=$(patsubst %.yaml,%.md,$(API_YAML)) all: config.mk check-c-compiler check-libs build @$(ECHO) "All done!" -BUILD_DIR=build/$(TOMO_VERSION) +BUILD_DIR=build/tomo@$(TOMO_VERSION) headers := $(wildcard src/stdlib/*.h) build_headers := $(patsubst src/stdlib/%.h, $(BUILD_DIR)/include/tomo@$(TOMO_VERSION)/%.h, $(headers)) diff --git a/local-tomo b/local-tomo index d16e090e..2bab6e2b 100755 --- a/local-tomo +++ b/local-tomo @@ -1,6 +1,6 @@ #!/bin/sh version=$(awk '/^## / {print $2; exit}' CHANGES.md) -if ! [ -e ./build/$version/bin/tomo ]; then +if ! [ -e ./build/tomo@$version/bin/tomo ]; then make -j fi -exec ./build/$version/bin/tomo "$@" +exec ./build/tomo@$version/bin/tomo "$@" -- cgit v1.2.3 From e9ec2558b7493064d0c353007b79791e3946eb4a Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 16:03:30 -0500 Subject: Put workflow back --- .github/workflows/release.yml | 6 ++++-- CHANGES.md | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 57bfc0e7..e9fa03a7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,9 @@ name: Release binaries on: - [push] + push: + tags: + - 'v*' jobs: linux-x86_64: @@ -78,7 +80,7 @@ jobs: - name: Install deps run: | brew update - brew install gmp libunistring bdw-gc xxd llvm binutils + brew install gmp libunistring bdw-gc llvm binutils - name: Build run: | diff --git a/CHANGES.md b/CHANGES.md index c2254a7b..a8a4a354 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Version History +## v2025-12-21.2 + +- Update build process + ## v2025-12-21 - You can now discard empty struct values. -- cgit v1.2.3 From ec0a043dee7a5465a09fc2723680e743a5d18213 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 16:07:02 -0500 Subject: Mac doesn't need to install xxd --- install_dependencies.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install_dependencies.sh b/install_dependencies.sh index 5ce6bd74..be5810d0 100755 --- a/install_dependencies.sh +++ b/install_dependencies.sh @@ -55,8 +55,8 @@ case "$PKG_MGR" in xbps) $SUDO xbps-install -S gc libunistring binutils gmp xxd ;; pkg_add) $SUDO pkg_add boehm-gc libunistring binutils gmp xxd ;; freebsd-pkg) $SUDO pkg install boehm-gc libunistring binutils gmp xxd ;; - brew) brew install bdw-gc libunistring binutils llvm gmp xxd ;; - macports) $SUDO port install boehm-gc libunistring binutils gmp xxd ;; + brew) brew install bdw-gc libunistring binutils llvm gmp ;; + macports) $SUDO port install boehm-gc libunistring binutils gmp ;; zypper) $SUDO zypper install gc-devel libunistring-devel binutils gmp-devel xxd ;; nix) nix-env -iA nixpkgs.boehmgc.dev nixpkgs.libunistring nixpkgs.binutils nixpkgs.nixpkgs.gmp xxd ;; spack) spack install boehm-gc libunistring binutils gmp xxd ;; -- cgit v1.2.3 From 07cc08924a9a15b4f743a68e0eabf8337b0b446a Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 16:11:46 -0500 Subject: Add workflow permissions --- .github/workflows/release.yml | 3 +++ CHANGES.md | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e9fa03a7..80d5bb67 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -99,3 +99,6 @@ jobs: tomo-macos-universal.tar.gz tomo-macos-universal.tar.gz.sha256 +permissions: + contents: write + packages: write diff --git a/CHANGES.md b/CHANGES.md index a8a4a354..12796657 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Version History +## v2025-12-21.3 + +- Version bump + ## v2025-12-21.2 - Update build process -- cgit v1.2.3 From 7b3a651da64e3e1951ab01e5650c5ceaa1dd5d84 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 17:04:08 -0500 Subject: Remove xxd dependency (no more building with changelog) --- .github/workflows/release.yml | 9 ++- .gitignore | 2 - CHANGES.md | 4 ++ Makefile | 11 ---- install_dependencies.sh | 22 +++---- install_script.sh | 143 ++++++++++++++++++++++++++++++++++++++++++ src/tomo.c | 9 +-- 7 files changed, 165 insertions(+), 35 deletions(-) create mode 100755 install_script.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 80d5bb67..cdbf541d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,8 @@ jobs: - name: Package run: | - tar -czf tomo-linux-x86_64.tar.gz build/* + TAG=${GITHUB_REF#refs/tags/} + tar -C build -czf tomo-linux-x86_64.tar.gz tomo@${TAG} sha256sum tomo-linux-x86_64.tar.gz > tomo-linux-x86_64.tar.gz.sha256 - name: Upload @@ -62,7 +63,8 @@ jobs: - name: Package run: | - tar -czf tomo-linux-aarch64.tar.gz build/* + TAG=${GITHUB_REF#refs/tags/} + tar -C build -czf tomo-linux-aarch64.tar.gz tomo@{TAG} sha256sum tomo-linux-aarch64.tar.gz > tomo-linux-aarch64.tar.gz.sha256 - name: Upload @@ -89,7 +91,8 @@ jobs: - name: Package run: | - tar -czf tomo-macos-universal.tar.gz build/* + TAG=${GITHUB_REF#refs/tags/} + tar -C build -czf tomo-macos-universal.tar.gz tomo@${TAG} shasum -a 256 tomo-macos-universal.tar.gz > tomo-macos-universal.tar.gz.sha256 - name: Upload diff --git a/.gitignore b/.gitignore index 78087392..5f521074 100644 --- a/.gitignore +++ b/.gitignore @@ -11,8 +11,6 @@ /lib/*/*.so /lib/*/*.a -/src/changes.md.h - .build /build *.o diff --git a/CHANGES.md b/CHANGES.md index 12796657..14e0dc5f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Version History +## v2025-12-21.4 + +- Version bump and deprecated `--changelog` flag + ## v2025-12-21.3 - Version bump diff --git a/Makefile b/Makefile index e7e689d0..95384d50 100644 --- a/Makefile +++ b/Makefile @@ -200,17 +200,6 @@ src/stdlib/int64.o src/stdlib/int32.o src/stdlib/int16.o src/stdlib/int8.o: src/ # Num implementations depend on the shared header: src/stdlib/num32.o src/stdlib/num64.o: src/stdlib/numX.c.h -# These files all depend on the current tomo version: -src/compile/files.o src/compile/headers.o src/compile/statements.o src/config.o src/environment.o \ - src/modules.o src/stdlib/stacktrace.o src/stdlib/stdlib.o src/tomo.o src/typecheck.o: CHANGES.md - -src/changes.md.h: CHANGES.md - @$(ECHO) "Embedding changes.md" - xxd -i $< > $@ - -# The main Tomo executable embeds the changelog: -src/tomo.o: src/changes.md.h - %: %.tm ./local-tomo -e $< diff --git a/install_dependencies.sh b/install_dependencies.sh index be5810d0..9b9026e9 100755 --- a/install_dependencies.sh +++ b/install_dependencies.sh @@ -48,19 +48,19 @@ fi # Install packages case "$PKG_MGR" in - apt) $SUDO apt install libgc-dev libunistring-dev binutils libgmp-dev xxd ;; - dnf) $SUDO dnf install gc-devel libunistring-devel binutils gmp-devel xxd ;; - pacman) $SUDO pacman -S gc libunistring binutils gmp xxd ;; - yay|paru) $PKG_MGR -S gc libunistring binutils gmp xxd ;; - xbps) $SUDO xbps-install -S gc libunistring binutils gmp xxd ;; - pkg_add) $SUDO pkg_add boehm-gc libunistring binutils gmp xxd ;; - freebsd-pkg) $SUDO pkg install boehm-gc libunistring binutils gmp xxd ;; + apt) $SUDO apt install libgc-dev libunistring-dev binutils libgmp-dev ;; + dnf) $SUDO dnf install gc-devel libunistring-devel binutils gmp-devel ;; + pacman) $SUDO pacman -S gc libunistring binutils gmp ;; + yay|paru) $PKG_MGR -S gc libunistring binutils gmp ;; + xbps) $SUDO xbps-install -S gc libunistring binutils gmp ;; + pkg_add) $SUDO pkg_add boehm-gc libunistring binutils gmp ;; + freebsd-pkg) $SUDO pkg install boehm-gc libunistring binutils gmp ;; brew) brew install bdw-gc libunistring binutils llvm gmp ;; macports) $SUDO port install boehm-gc libunistring binutils gmp ;; - zypper) $SUDO zypper install gc-devel libunistring-devel binutils gmp-devel xxd ;; - nix) nix-env -iA nixpkgs.boehmgc.dev nixpkgs.libunistring nixpkgs.binutils nixpkgs.nixpkgs.gmp xxd ;; - spack) spack install boehm-gc libunistring binutils gmp xxd ;; - conda) conda install boehm-gc libunistring binutils gmp xxd ;; + zypper) $SUDO zypper install gc-devel libunistring-devel binutils gmp-devel ;; + nix) nix-env -iA nixpkgs.boehmgc.dev nixpkgs.libunistring nixpkgs.binutils nixpkgs.nixpkgs.gmp ;; + spack) spack install boehm-gc libunistring binutils gmp ;; + conda) conda install boehm-gc libunistring binutils gmp ;; *) echo "Unknown package manager: $PKG_MGR" >&2 exit 1 diff --git a/install_script.sh b/install_script.sh new file mode 100755 index 00000000..1b3d92ae --- /dev/null +++ b/install_script.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash +set -euo pipefail + +OWNER="bruce-hill" +REPO="tomo" + +# Fetch latest release tag +TAG=$(curl -s "https://api.github.com/repos/$OWNER/$REPO/releases/latest" \ + | grep -Po '"tag_name": "\K.*?(?=")') + +if [[ -z "$TAG" ]]; then + echo "Failed to get latest release tag" + exit 1 +fi + +# Detect platform +OS="$(uname -s)" +ARCH="$(uname -m)" +case "$OS" in + Linux) + case "$ARCH" in + x86_64) FILE="tomo-linux-x86_64.tar.gz" ;; + aarch64|arm64) FILE="tomo-linux-aarch64.tar.gz" ;; + *) echo "Unsupported architecture: $ARCH"; exit 1 ;; + esac + ;; + Darwin) + FILE="tomo-macos-universal.tar.gz" + ;; + *) + echo "Unsupported OS: $OS" + exit 1 + ;; +esac + +# Download the artifact (if not present) +if ! [ -e "$FILE" ]; then + URL="https://github.com/$OWNER/$REPO/releases/download/$TAG/$FILE" + echo "Downloading $URL ..." + curl -L -o "$FILE" "$URL" + echo "Downloaded $FILE" +fi + +# Download checksum (if not present) +if ! [ -e "$FILE.sha256" ]; then + CHECKSUM_URL="$URL.sha256" + echo "Downloading checksum $CHECKSUM_URL ..." + curl -L -o "$FILE.sha256" "$CHECKSUM_URL" +fi + +# Verify checksum +shasum --check "$FILE.sha256" +echo "Verified checksum" + +# Configure `doas` vs `sudo` +if command -v doas >/dev/null 2>&1; then + SUDO="doas" +elif command -v sudo >/dev/null 2>&1; then + SUDO="sudo" +else + echo "Neither doas nor sudo found." >&2 + exit 1 +fi + +# Autodetect package manager: +if [ -z "${PACKAGE_MANAGER:-}" ]; then + if command -v dnf >/dev/null 2>&1; then + PACKAGE_MANAGER="dnf" + elif command -v yay >/dev/null 2>&1; then + PACKAGE_MANAGER="yay" + elif command -v paru >/dev/null 2>&1; then + PACKAGE_MANAGER="paru" + elif command -v pacman >/dev/null 2>&1; then + PACKAGE_MANAGER="pacman" + elif command -v xbps-install >/dev/null 2>&1; then + PACKAGE_MANAGER="xbps" + elif command -v pkg_add >/dev/null 2>&1; then + PACKAGE_MANAGER="pkg_add" + elif command -v pkg >/dev/null 2>&1; then + PACKAGE_MANAGER="freebsd-pkg" + elif command -v brew >/dev/null 2>&1; then + PACKAGE_MANAGER="brew" + elif command -v port >/dev/null 2>&1; then + PACKAGE_MANAGER="macports" + elif command -v zypper >/dev/null 2>&1; then + PACKAGE_MANAGER="zypper" + elif command -v nix-env >/dev/null 2>&1; then + PACKAGE_MANAGER="nix" + elif command -v spack >/dev/null 2>&1; then + PACKAGE_MANAGER="spack" + elif command -v conda >/dev/null 2>&1; then + PACKAGE_MANAGER="conda" + elif command -v apt >/dev/null 2>&1; then + PACKAGE_MANAGER="apt" + elif command -v apt-get >/dev/null 2>&1; then + PACKAGE_MANAGER="apt-get" + else + echo "Unsupported package manager" >&2 + exit 1 + fi +fi + +# Install packages +echo 'Installing dependencies...' +case "$PACKAGE_MANAGER" in + apt) $SUDO apt install libgc-dev libunistring-dev binutils libgmp-dev ;; + apt-get) $SUDO apt-get install libgc-dev libunistring-dev binutils libgmp-dev ;; + dnf) $SUDO dnf install gc-devel libunistring-devel binutils gmp-devel ;; + pacman) $SUDO pacman -S gc libunistring binutils gmp ;; + yay|paru) $PACKAGE_MANAGER -S gc libunistring binutils gmp ;; + xbps) $SUDO xbps-install -S gc libunistring binutils gmp ;; + pkg_add) $SUDO pkg_add boehm-gc libunistring binutils gmp ;; + freebsd-pkg) $SUDO pkg install boehm-gc libunistring binutils gmp ;; + brew) brew install bdw-gc libunistring binutils llvm gmp ;; + macports) $SUDO port install boehm-gc libunistring binutils gmp ;; + zypper) $SUDO zypper install gc-devel libunistring-devel binutils gmp-devel ;; + nix) nix-env -iA nixpkgs.boehmgc.dev nixpkgs.libunistring nixpkgs.binutils nixpkgs.nixpkgs.gmp ;; + spack) spack install boehm-gc libunistring binutils gmp ;; + conda) conda install boehm-gc libunistring binutils gmp ;; + *) + echo "Unknown package manager: $PACKAGE_MANAGER" >&2 + exit 1 + ;; +esac + +# Choose installation location +default_prefix='/usr/local' +if echo "$PATH" | tr ':' '\n' | grep -qx "$HOME/.local/bin"; then + default_prefix="~/.local" +fi +printf '\033[1mChoose where to install Tomo (default: %s):\033[m ' "$default_prefix" +read DEST +if [ -z "$DEST" ]; then DEST="$default_prefix"; fi +DEST="${DEST/#\~/$HOME}" + +# Install +if ! [ -w "$DEST" ]; then + USER="$(ls -ld "$DEST" | awk '{print $$3}')" + $(SUDO) -u "$USER" tar -xzf "$FILE" -C "$DEST" --strip-components=1 "tomo@$TAG" +else + tar -xzf "$FILE" -C "$DEST" --strip-components=1 "tomo@$TAG" +fi +echo "Installed to $DEST" diff --git a/src/tomo.c b/src/tomo.c index f829951c..1517259e 100644 --- a/src/tomo.c +++ b/src/tomo.c @@ -14,7 +14,6 @@ #endif #include "ast.h" -#include "changes.md.h" #include "compile/cli.h" #include "compile/files.h" #include "compile/headers.h" @@ -77,7 +76,7 @@ static const char *paths_str(List_t paths) { #endif static OptionalBool_t verbose = false, quiet = false, show_version = false, show_prefix = false, clean_build = false, - source_mapping = true, show_changelog = false, should_install = false; + source_mapping = true, should_install = false; static List_t format_files = EMPTY_LIST, format_files_inplace = EMPTY_LIST, parse_files = EMPTY_LIST, transpile_files = EMPTY_LIST, compile_objects = EMPTY_LIST, compile_executables = EMPTY_LIST, @@ -244,7 +243,6 @@ int main(int argc, char *argv[]) { {"optimization", &optimization, &Text$info, .short_flag = 'O'}, // {"force-rebuild", &clean_build, &Bool$info, .short_flag = 'f'}, // {"source-mapping", &source_mapping, &Bool$info, .short_flag = 'm'}, - {"changelog", &show_changelog, &Bool$info}, // }; tomo_parse_args(argc, argv, usage, help, TOMO_VERSION, sizeof(tomo_args) / sizeof(tomo_args[0]), tomo_args); @@ -253,11 +251,6 @@ int main(int argc, char *argv[]) { return 0; } - if (show_changelog) { - print_inline(string_slice((const char *)CHANGES_md, (size_t)CHANGES_md_len)); - return 0; - } - if (show_version) { if (verbose) print(TOMO_VERSION, " ", GIT_VERSION); else print(TOMO_VERSION); -- cgit v1.2.3 From fff696978ff1036b4faf864276cbc55864775bd8 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 17:07:05 -0500 Subject: Add missing file --- embed_file.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100755 embed_file.sh diff --git a/embed_file.sh b/embed_file.sh new file mode 100755 index 00000000..503fee80 --- /dev/null +++ b/embed_file.sh @@ -0,0 +1,10 @@ +#/bin/sh +awk ' + BEGIN { printf "const char *'$1' = " } + { + gsub(/\\/,"\\\\"); + gsub(/"/,"\\\""); + printf "\"%s\\n\"\n", $0 + } + END { print ";" } +' -- cgit v1.2.3 From 0a5fc171c54bdedce0dad34701562831a497fccc Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 17:08:59 -0500 Subject: Fix workflow --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cdbf541d..1d6c5dba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,7 +64,7 @@ jobs: - name: Package run: | TAG=${GITHUB_REF#refs/tags/} - tar -C build -czf tomo-linux-aarch64.tar.gz tomo@{TAG} + tar -C build -czf tomo-linux-aarch64.tar.gz tomo@${TAG} sha256sum tomo-linux-aarch64.tar.gz > tomo-linux-aarch64.tar.gz.sha256 - name: Upload -- cgit v1.2.3 From fb77bd546ce42e5703cfb12ab1ebbe9772b83cef Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 17:26:37 -0500 Subject: Fixes for builds and accessing version and install script --- CHANGES.md | 4 ++++ README.md | 8 ++++++++ install_script.sh | 4 +++- src/stdlib/stdlib.c | 43 ++++++++++++++++++++++++++++++++++--------- 4 files changed, 49 insertions(+), 10 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 14e0dc5f..0393cecf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Version History +## v2025-12-21.5 + +- Various fixes for versioning and builds. + ## v2025-12-21.4 - Version bump and deprecated `--changelog` flag diff --git a/README.md b/README.md index ac7e91ba..456b9e46 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,14 @@ of many language features or the other example programs/modules in [examples/](examples/). You can also look at the [core libraries](lib/) which are implemented in Tomo. +## Quick Installation + +If you don't want to build from source but just want to install, run this script: + +``` +curl https://raw.githubusercontent.com/bruce-hill/tomo/refs/heads/main/install_script.sh | bash` +``` + ## Features ### Performance diff --git a/install_script.sh b/install_script.sh index 1b3d92ae..21535f3d 100755 --- a/install_script.sh +++ b/install_script.sh @@ -129,7 +129,7 @@ if echo "$PATH" | tr ':' '\n' | grep -qx "$HOME/.local/bin"; then default_prefix="~/.local" fi printf '\033[1mChoose where to install Tomo (default: %s):\033[m ' "$default_prefix" -read DEST +read DEST Date: Sun, 21 Dec 2025 17:28:03 -0500 Subject: Fix typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 456b9e46..8b888f51 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ are implemented in Tomo. If you don't want to build from source but just want to install, run this script: ``` -curl https://raw.githubusercontent.com/bruce-hill/tomo/refs/heads/main/install_script.sh | bash` +curl https://raw.githubusercontent.com/bruce-hill/tomo/refs/heads/main/install_script.sh | bash ``` ## Features -- cgit v1.2.3 From 7bc1f4d9b974f7baa2df7b9f2aa62351165e18e1 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 17:33:09 -0500 Subject: Update quick install script --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b888f51..f2b4bc36 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,9 @@ are implemented in Tomo. If you don't want to build from source but just want to install, run this script: ``` -curl https://raw.githubusercontent.com/bruce-hill/tomo/refs/heads/main/install_script.sh | bash +curl -o /tmp/install_tomo.sh https://raw.githubusercontent.com/bruce-hill/tomo/refs/heads/main/install_script.sh \ + && bash /tmp/install_tomo.sh +rm -f /tmp/install_tomo.sh ``` ## Features -- cgit v1.2.3 From 4b976138dce395af734ad4f6a310191ac690e922 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 19:19:56 -0500 Subject: Update readme --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index f2b4bc36..61062bfc 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,14 @@ are implemented in Tomo. ## Quick Installation +### Arch User Repository (AUR) + +``` +yay -Sy tomo-bin +``` + +### Install Script + If you don't want to build from source but just want to install, run this script: ``` -- cgit v1.2.3 From 082786bf79b2672114c5d14dc22d91ba5a90923b Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 19:37:15 -0500 Subject: More sensible REPL-like behavior if run without any args --- CHANGES.md | 4 ++++ src/tomo.c | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 0393cecf..e609509b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Version History +## v2025-12-21.6 + +- Add smarter default behavior if run without any args (REPL-like script runner) + ## v2025-12-21.5 - Various fixes for versioning and builds. diff --git a/src/tomo.c b/src/tomo.c index 1517259e..5ebd6663 100644 --- a/src/tomo.c +++ b/src/tomo.c @@ -397,6 +397,32 @@ int main(int argc, char *argv[]) { run_files = normalize_tm_paths(run_files); + if (run_files.length == 0 && format_files.length == 0 && format_files_inplace.length == 0 && parse_files.length == 0 + && transpile_files.length == 0 && compile_objects.length == 0 && compile_executables.length == 0 + && run_files.length == 0 && uninstall_libraries.length == 0 && libraries.length == 0) { + Path_t path = Path$from_str(String("~/.local/tomo/state/tomo@", TOMO_VERSION, "/run.tm")); + path = Path$expand_home(path); + Path$create_directory(Path$parent(path), 0755, true); + if (!Path$exists(path)) { + Path$write(path, + Text("# This is a handy Tomo REPL-like runner\n" // + "# Normally you would run `tomo ./file.tm` to run a script\n" // + "# See `tomo --help` for full usage\n" // + "\n" // + "func main()\n" // + " # Put your code here:\n" // + " pass\n" // + "\n" // + "# Save and exit to run\n"), + 0644); + } + List$insert(&run_files, &path, I(0), sizeof(path)); + const char *editor = getenv("EDITOR"); + if (!editor || editor[0] == '\0') editor = "vim"; + int status = system(String(editor, " ", path)); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) return 1; + } + // Compile runnable files in parallel, then execute in serial: for (int64_t i = 0; i < (int64_t)run_files.length; i++) { Path_t path = *(Path_t *)(run_files.data + i * run_files.stride); -- cgit v1.2.3 From 9deadfa357eaf5313621fca54c633a190f4a788d Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 19:49:28 -0500 Subject: Fix performance regression on makefile --- Makefile | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 95384d50..5eba8e4e 100644 --- a/Makefile +++ b/Makefile @@ -18,14 +18,24 @@ else include config.mk # Modified progress counter based on: https://stackoverflow.com/a/35320895 +# Only run counter if we're actually building (not for phony targets or no-ops) ifndef NO_PROGRESS ifndef ECHO -T := $(shell $(MAKE) ECHO="COUNTTHIS" $(MAKECMDGOALS) --no-print-directory \ - -n | grep -c "COUNTTHIS") +# Only count if building actual files, not just checking +ifneq ($(filter build all,$(MAKECMDGOALS)),) +T := $(shell $(MAKE) ECHO="COUNTTHIS" $(filter-out check-c-compiler check-libs,$(MAKECMDGOALS)) --no-print-directory \ + -nq 2>/dev/null | grep -c "COUNTTHIS") +ifeq ($(T),0) +ECHO = echo +else N := x C = $(words $N)$(eval N := x $N) ECHO = echo -e "[`expr $C '*' 100 / $T`%]" endif +else +ECHO = echo +endif +endif endif ifndef ECHO ECHO = echo @@ -82,7 +92,7 @@ G=-ggdb O=-O3 # Note: older versions of Make have buggy behavior with hash marks inside strings, so this ugly code is necessary: TOMO_VERSION=$(shell awk 'BEGIN{hashes=sprintf("%c%c",35,35)} $$1==hashes {print $$2; exit}' CHANGES.md) -GIT_VERSION=$(shell git log -1 --pretty=format:"%as_%h") +GIT_VERSION=$(shell git log -1 --pretty=format:"%as_%h" 2>/dev/null || echo "unknown") CFLAGS=$(CCONFIG) $(INCLUDE_DIRS) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS) $(LTO) \ -DSUDO='"$(SUDO)"' -DDEFAULT_C_COMPILER='"$(DEFAULT_C_COMPILER)"' \ -DGIT_VERSION='"$(GIT_VERSION)"' @@ -145,8 +155,10 @@ $(BUILD_DIR)/include/tomo@$(TOMO_VERSION)%.h: src/stdlib/%.h | $(BUILD_DIR)/incl # Rule for gzipping man pages $(BUILD_DIR)/man/man1/%.gz: man/man1/% | $(BUILD_DIR)/man/man1 + @$(ECHO) Gzipping manpage $< gzip -c $< > $@ $(BUILD_DIR)/man/man3/%.gz: man/man3/% | $(BUILD_DIR)/man/man3 + @$(ECHO) Gzipping manpage $< gzip -c $< > $@ $(BUILD_DIR)/bin/tomo: $(BUILD_DIR)/bin/tomo@$(TOMO_VERSION) | $(BUILD_DIR)/bin -- cgit v1.2.3 From 123e84696354be18bfd881b939d642b0a94f756f Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 20:10:12 -0500 Subject: Fix slow manpage gzipping rules --- Makefile | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 5eba8e4e..7f247b49 100644 --- a/Makefile +++ b/Makefile @@ -131,11 +131,8 @@ BUILD_DIR=build/tomo@$(TOMO_VERSION) headers := $(wildcard src/stdlib/*.h) build_headers := $(patsubst src/stdlib/%.h, $(BUILD_DIR)/include/tomo@$(TOMO_VERSION)/%.h, $(headers)) -# find all man pages -manpages := $(wildcard man/*/*) - # generate corresponding build paths with .gz -build_manpages := $(foreach f,$(manpages),$(BUILD_DIR)/$(f).gz) +build_manpages := $(patsubst %,$(BUILD_DIR)/%.gz,$(wildcard man/man*/*)) # Ensure directories exist dirs := $(BUILD_DIR)/include/tomo@$(TOMO_VERSION) \ @@ -154,11 +151,7 @@ $(BUILD_DIR)/include/tomo@$(TOMO_VERSION)%.h: src/stdlib/%.h | $(BUILD_DIR)/incl cp $< $@ # Rule for gzipping man pages -$(BUILD_DIR)/man/man1/%.gz: man/man1/% | $(BUILD_DIR)/man/man1 - @$(ECHO) Gzipping manpage $< - gzip -c $< > $@ -$(BUILD_DIR)/man/man3/%.gz: man/man3/% | $(BUILD_DIR)/man/man3 - @$(ECHO) Gzipping manpage $< +$(BUILD_DIR)/man/%.gz: man/% | $(BUILD_DIR)/man/man1 $(BUILD_DIR)/man/man3 gzip -c $< > $@ $(BUILD_DIR)/bin/tomo: $(BUILD_DIR)/bin/tomo@$(TOMO_VERSION) | $(BUILD_DIR)/bin -- cgit v1.2.3 From 47b6a6c5d7cb667b84b758faa8fe7017b25d3443 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 19:49:28 -0500 Subject: Fix performance regression on makefile --- Makefile | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 95384d50..5eba8e4e 100644 --- a/Makefile +++ b/Makefile @@ -18,14 +18,24 @@ else include config.mk # Modified progress counter based on: https://stackoverflow.com/a/35320895 +# Only run counter if we're actually building (not for phony targets or no-ops) ifndef NO_PROGRESS ifndef ECHO -T := $(shell $(MAKE) ECHO="COUNTTHIS" $(MAKECMDGOALS) --no-print-directory \ - -n | grep -c "COUNTTHIS") +# Only count if building actual files, not just checking +ifneq ($(filter build all,$(MAKECMDGOALS)),) +T := $(shell $(MAKE) ECHO="COUNTTHIS" $(filter-out check-c-compiler check-libs,$(MAKECMDGOALS)) --no-print-directory \ + -nq 2>/dev/null | grep -c "COUNTTHIS") +ifeq ($(T),0) +ECHO = echo +else N := x C = $(words $N)$(eval N := x $N) ECHO = echo -e "[`expr $C '*' 100 / $T`%]" endif +else +ECHO = echo +endif +endif endif ifndef ECHO ECHO = echo @@ -82,7 +92,7 @@ G=-ggdb O=-O3 # Note: older versions of Make have buggy behavior with hash marks inside strings, so this ugly code is necessary: TOMO_VERSION=$(shell awk 'BEGIN{hashes=sprintf("%c%c",35,35)} $$1==hashes {print $$2; exit}' CHANGES.md) -GIT_VERSION=$(shell git log -1 --pretty=format:"%as_%h") +GIT_VERSION=$(shell git log -1 --pretty=format:"%as_%h" 2>/dev/null || echo "unknown") CFLAGS=$(CCONFIG) $(INCLUDE_DIRS) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS) $(LTO) \ -DSUDO='"$(SUDO)"' -DDEFAULT_C_COMPILER='"$(DEFAULT_C_COMPILER)"' \ -DGIT_VERSION='"$(GIT_VERSION)"' @@ -145,8 +155,10 @@ $(BUILD_DIR)/include/tomo@$(TOMO_VERSION)%.h: src/stdlib/%.h | $(BUILD_DIR)/incl # Rule for gzipping man pages $(BUILD_DIR)/man/man1/%.gz: man/man1/% | $(BUILD_DIR)/man/man1 + @$(ECHO) Gzipping manpage $< gzip -c $< > $@ $(BUILD_DIR)/man/man3/%.gz: man/man3/% | $(BUILD_DIR)/man/man3 + @$(ECHO) Gzipping manpage $< gzip -c $< > $@ $(BUILD_DIR)/bin/tomo: $(BUILD_DIR)/bin/tomo@$(TOMO_VERSION) | $(BUILD_DIR)/bin -- cgit v1.2.3 From 0ea6cdf216ca765039f3c01f5b32dc1103265b58 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 21 Dec 2025 20:10:12 -0500 Subject: Fix slow manpage gzipping rules --- Makefile | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 5eba8e4e..7f247b49 100644 --- a/Makefile +++ b/Makefile @@ -131,11 +131,8 @@ BUILD_DIR=build/tomo@$(TOMO_VERSION) headers := $(wildcard src/stdlib/*.h) build_headers := $(patsubst src/stdlib/%.h, $(BUILD_DIR)/include/tomo@$(TOMO_VERSION)/%.h, $(headers)) -# find all man pages -manpages := $(wildcard man/*/*) - # generate corresponding build paths with .gz -build_manpages := $(foreach f,$(manpages),$(BUILD_DIR)/$(f).gz) +build_manpages := $(patsubst %,$(BUILD_DIR)/%.gz,$(wildcard man/man*/*)) # Ensure directories exist dirs := $(BUILD_DIR)/include/tomo@$(TOMO_VERSION) \ @@ -154,11 +151,7 @@ $(BUILD_DIR)/include/tomo@$(TOMO_VERSION)%.h: src/stdlib/%.h | $(BUILD_DIR)/incl cp $< $@ # Rule for gzipping man pages -$(BUILD_DIR)/man/man1/%.gz: man/man1/% | $(BUILD_DIR)/man/man1 - @$(ECHO) Gzipping manpage $< - gzip -c $< > $@ -$(BUILD_DIR)/man/man3/%.gz: man/man3/% | $(BUILD_DIR)/man/man3 - @$(ECHO) Gzipping manpage $< +$(BUILD_DIR)/man/%.gz: man/% | $(BUILD_DIR)/man/man1 $(BUILD_DIR)/man/man3 gzip -c $< > $@ $(BUILD_DIR)/bin/tomo: $(BUILD_DIR)/bin/tomo@$(TOMO_VERSION) | $(BUILD_DIR)/bin -- cgit v1.2.3 From 01cbec2d67399b7e8d18d409d1b1eccf00aba733 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 22 Dec 2025 14:23:04 -0500 Subject: Fix makefile progress indicator --- Makefile | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 7f247b49..965ccbe3 100644 --- a/Makefile +++ b/Makefile @@ -18,24 +18,14 @@ else include config.mk # Modified progress counter based on: https://stackoverflow.com/a/35320895 -# Only run counter if we're actually building (not for phony targets or no-ops) ifndef NO_PROGRESS ifndef ECHO -# Only count if building actual files, not just checking -ifneq ($(filter build all,$(MAKECMDGOALS)),) -T := $(shell $(MAKE) ECHO="COUNTTHIS" $(filter-out check-c-compiler check-libs,$(MAKECMDGOALS)) --no-print-directory \ - -nq 2>/dev/null | grep -c "COUNTTHIS") -ifeq ($(T),0) -ECHO = echo -else +T := $(shell $(MAKE) ECHO="COUNTTHIS" $(MAKECMDGOALS) --no-print-directory \ + -n | grep -c "COUNTTHIS") N := x C = $(words $N)$(eval N := x $N) ECHO = echo -e "[`expr $C '*' 100 / $T`%]" endif -else -ECHO = echo -endif -endif endif ifndef ECHO ECHO = echo -- cgit v1.2.3 From 0ee53cd5a79d41b124413d5da3e4279d06b17bfc Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 22 Dec 2025 14:34:42 -0500 Subject: Attempted improvements for building on mac --- .github/workflows/release.yml | 32 +++++++++++++++++++++++++++----- Makefile | 2 +- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1d6c5dba..2be7084e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,10 @@ on: tags: - 'v*' +permissions: + contents: write + packages: write + jobs: linux-x86_64: runs-on: ubuntu-latest @@ -84,10 +88,31 @@ jobs: brew update brew install gmp libunistring bdw-gc llvm binutils - - name: Build + - name: Build arm64 run: | make clean - make -j + make -j \ + CC=clang \ + CFLAGS="-arch arm64" \ + LDFLAGS="-arch arm64" + mv build/tomo build/tomo-arm64 + + - name: Build x86_64 + run: | + make clean + make -j \ + CC=clang \ + CFLAGS="-arch x86_64" \ + LDFLAGS="-arch x86_64" + mv build/tomo build/tomo-x86_64 + + - name: Create universal binary + run: | + lipo -create \ + build/tomo-arm64 \ + build/tomo-x86_64 \ + -output build/tomo + lipo -info build/tomo - name: Package run: | @@ -102,6 +127,3 @@ jobs: tomo-macos-universal.tar.gz tomo-macos-universal.tar.gz.sha256 -permissions: - contents: write - packages: write diff --git a/Makefile b/Makefile index 965ccbe3..855c8b09 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,7 @@ O=-O3 # Note: older versions of Make have buggy behavior with hash marks inside strings, so this ugly code is necessary: TOMO_VERSION=$(shell awk 'BEGIN{hashes=sprintf("%c%c",35,35)} $$1==hashes {print $$2; exit}' CHANGES.md) GIT_VERSION=$(shell git log -1 --pretty=format:"%as_%h" 2>/dev/null || echo "unknown") -CFLAGS=$(CCONFIG) $(INCLUDE_DIRS) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS) $(LTO) \ +CFLAGS += $(CCONFIG) $(INCLUDE_DIRS) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS) $(LTO) \ -DSUDO='"$(SUDO)"' -DDEFAULT_C_COMPILER='"$(DEFAULT_C_COMPILER)"' \ -DGIT_VERSION='"$(GIT_VERSION)"' CFLAGS_PLACEHOLDER="$$(printf '\033[2m\033[m\n')" -- cgit v1.2.3 From 83e6cc9197bd8e7a19834d291fe4c5e62639db38 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 22 Dec 2025 16:32:40 -0500 Subject: Add Path.writer() and Path.byte_writer() --- CHANGES.md | 4 ++ api/api.md | 52 +++++++++++++++++++++++++ api/paths.md | 52 +++++++++++++++++++++++++ api/paths.yaml | 76 ++++++++++++++++++++++++++++++++++++ man/man3/tomo-Path.3 | 18 ++++++++- man/man3/tomo-Path.byte_writer.3 | 42 ++++++++++++++++++++ man/man3/tomo-Path.writer.3 | 42 ++++++++++++++++++++ src/environment.c | 4 ++ src/stdlib/paths.c | 83 +++++++++++++++++++++++++++++++++++++--- src/stdlib/paths.h | 2 + 10 files changed, 368 insertions(+), 7 deletions(-) create mode 100644 man/man3/tomo-Path.byte_writer.3 create mode 100644 man/man3/tomo-Path.writer.3 diff --git a/CHANGES.md b/CHANGES.md index e609509b..72c4cef5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Version History +## v2025-12-22 + +- Added `Path.writer()` and `Path.byte_writer()` for multiple successive writes + ## v2025-12-21.6 - Add smarter default behavior if run without any args (REPL-like script runner) diff --git a/api/api.md b/api/api.md index 2be5e4e9..d035a3fe 100644 --- a/api/api.md +++ b/api/api.md @@ -2645,6 +2645,32 @@ else for line in (/dev/stdin).by_line()! say(line.upper()) +``` +## Path.byte_writer + +```tomo +Path.byte_writer : func(path: Path, append: Bool = no, permissions: Int32 = Int32(0o644) -> func(bytes:[Byte], close:Bool=no -> Result)) +``` + +Returns a function that can be used to repeatedly write bytes to the same file. + +The file writer will keep its file descriptor open after each write (unless the `close` argument is set to `yes`). If the file writer is never closed, it will be automatically closed when the file writer is garbage collected. + +Argument | Type | Description | Default +---------|------|-------------|--------- +path | `Path` | The path of the file to write to. | - +append | `Bool` | If set to `yes`, writes to the file will append. If set to `no`, then the first write to the file will overwrite its contents and subsequent calls will append. | `no` +permissions | `Int32` | The permissions to set on the file if it is created. | `Int32(0o644)` + +**Return:** Returns a function that can repeatedly write bytes to the same file. If `close` is set to `yes`, then the file will be closed after writing. If this function is called again after closing, the file will be reopened for appending. + + +**Example:** +```tomo +write := (./file.txt).byte_writer() +write("Hello\n".utf8())! +write("world\n".utf8(), close=yes)! + ``` ## Path.can_execute @@ -3463,6 +3489,32 @@ assert created == (./file-27QHtq.txt) assert created.read() == [1, 2, 3] created.remove() +``` +## Path.writer + +```tomo +Path.writer : func(path: Path, append: Bool = no, permissions: Int32 = Int32(0o644) -> func(text:Text, close:Bool=no -> Result)) +``` + +Returns a function that can be used to repeatedly write to the same file. + +The file writer will keep its file descriptor open after each write (unless the `close` argument is set to `yes`). If the file writer is never closed, it will be automatically closed when the file writer is garbage collected. + +Argument | Type | Description | Default +---------|------|-------------|--------- +path | `Path` | The path of the file to write to. | - +append | `Bool` | If set to `yes`, writes to the file will append. If set to `no`, then the first write to the file will overwrite its contents and subsequent calls will append. | `no` +permissions | `Int32` | The permissions to set on the file if it is created. | `Int32(0o644)` + +**Return:** Returns a function that can repeatedly write to the same file. If `close` is set to `yes`, then the file will be closed after writing. If this function is called again after closing, the file will be reopened for appending. + + +**Example:** +```tomo +write := (./file.txt).writer() +write("Hello\n")! +write("world\n", close=yes)! + ``` # Table diff --git a/api/paths.md b/api/paths.md index 435932e3..8c08b45b 100644 --- a/api/paths.md +++ b/api/paths.md @@ -117,6 +117,32 @@ else for line in (/dev/stdin).by_line()! say(line.upper()) +``` +## Path.byte_writer + +```tomo +Path.byte_writer : func(path: Path, append: Bool = no, permissions: Int32 = Int32(0o644) -> func(bytes:[Byte], close:Bool=no -> Result)) +``` + +Returns a function that can be used to repeatedly write bytes to the same file. + +The file writer will keep its file descriptor open after each write (unless the `close` argument is set to `yes`). If the file writer is never closed, it will be automatically closed when the file writer is garbage collected. + +Argument | Type | Description | Default +---------|------|-------------|--------- +path | `Path` | The path of the file to write to. | - +append | `Bool` | If set to `yes`, writes to the file will append. If set to `no`, then the first write to the file will overwrite its contents and subsequent calls will append. | `no` +permissions | `Int32` | The permissions to set on the file if it is created. | `Int32(0o644)` + +**Return:** Returns a function that can repeatedly write bytes to the same file. If `close` is set to `yes`, then the file will be closed after writing. If this function is called again after closing, the file will be reopened for appending. + + +**Example:** +```tomo +write := (./file.txt).byte_writer() +write("Hello\n".utf8())! +write("world\n".utf8(), close=yes)! + ``` ## Path.can_execute @@ -936,3 +962,29 @@ assert created.read() == [1, 2, 3] created.remove() ``` +## Path.writer + +```tomo +Path.writer : func(path: Path, append: Bool = no, permissions: Int32 = Int32(0o644) -> func(text:Text, close:Bool=no -> Result)) +``` + +Returns a function that can be used to repeatedly write to the same file. + +The file writer will keep its file descriptor open after each write (unless the `close` argument is set to `yes`). If the file writer is never closed, it will be automatically closed when the file writer is garbage collected. + +Argument | Type | Description | Default +---------|------|-------------|--------- +path | `Path` | The path of the file to write to. | - +append | `Bool` | If set to `yes`, writes to the file will append. If set to `no`, then the first write to the file will overwrite its contents and subsequent calls will append. | `no` +permissions | `Int32` | The permissions to set on the file if it is created. | `Int32(0o644)` + +**Return:** Returns a function that can repeatedly write to the same file. If `close` is set to `yes`, then the file will be closed after writing. If this function is called again after closing, the file will be reopened for appending. + + +**Example:** +```tomo +write := (./file.txt).writer() +write("Hello\n")! +write("world\n", close=yes)! + +``` diff --git a/api/paths.yaml b/api/paths.yaml index 02b8fbe8..a659ffbc 100644 --- a/api/paths.yaml +++ b/api/paths.yaml @@ -838,6 +838,82 @@ Path.write: example: | (./file.txt).write("Hello, world!") +Path.writer: + short: create a file writer + description: > + Returns a function that can be used to repeatedly write to the same file. + note: > + The file writer will keep its file descriptor open after each write (unless + the `close` argument is set to `yes`). If the file writer is never closed, + it will be automatically closed when the file writer is garbage collected. + return: + type: 'func(text:Text, close:Bool=no -> Result)' + description: > + Returns a function that can repeatedly write to the same file. If `close` + is set to `yes`, then the file will be closed after writing. If this + function is called again after closing, the file will be reopened for + appending. + args: + path: + type: 'Path' + description: > + The path of the file to write to. + append: + type: 'Bool' + default: 'no' + description: > + If set to `yes`, writes to the file will append. If set to `no`, then + the first write to the file will overwrite its contents and subsequent + calls will append. + permissions: + type: 'Int32' + default: 'Int32(0o644)' + description: > + The permissions to set on the file if it is created. + example: | + write := (./file.txt).writer() + write("Hello\n")! + write("world\n", close=yes)! + +Path.byte_writer: + short: create a byte-based file writer + description: > + Returns a function that can be used to repeatedly write bytes to the same + file. + note: > + The file writer will keep its file descriptor open after each write (unless + the `close` argument is set to `yes`). If the file writer is never closed, + it will be automatically closed when the file writer is garbage collected. + return: + type: 'func(bytes:[Byte], close:Bool=no -> Result)' + description: > + Returns a function that can repeatedly write bytes to the same file. If + `close` is set to `yes`, then the file will be closed after writing. If + this function is called again after closing, the file will be reopened + for appending. + args: + path: + type: 'Path' + description: > + The path of the file to write to. + append: + type: 'Bool' + default: 'no' + description: > + If set to `yes`, writes to the file will append. If set to `no`, then + the first write to the file will overwrite its contents and subsequent + calls will append. + permissions: + type: 'Int32' + default: 'Int32(0o644)' + description: > + The permissions to set on the file if it is created. + example: | + write := (./file.txt).byte_writer() + write("Hello\n".utf8())! + write("world\n".utf8(), close=yes)! + + Path.write_bytes: short: write bytes to a file description: > diff --git a/man/man3/tomo-Path.3 b/man/man3/tomo-Path.3 index ae9b6d51..b916005a 100644 --- a/man/man3/tomo-Path.3 +++ b/man/man3/tomo-Path.3 @@ -2,7 +2,7 @@ .\" Copyright (c) 2025 Bruce Hill .\" All rights reserved. .\" -.TH Path 3 2025-12-07 "Tomo man-pages" +.TH Path 3 2025-12-22 "Tomo man-pages" .SH NAME Path \- a Tomo type .SH LIBRARY @@ -50,6 +50,14 @@ For more, see: .BR Tomo-Path.by_line (3) +.TP +.BI Path.byte_writer\ :\ func(path:\ Path,\ append:\ Bool\ =\ no,\ permissions:\ Int32\ =\ Int32(0o644)\ ->\ func(bytes:[Byte],\ close:Bool=no\ ->\ Result)) +Returns a function that can be used to repeatedly write bytes to the same file. + +For more, see: +.BR Tomo-Path.byte_writer (3) + + .TP .BI Path.can_execute\ :\ func(path:\ Path\ ->\ Bool) Returns whether or not a file can be executed by the current user/group. @@ -345,3 +353,11 @@ Writes the given bytes to a unique file path based on the specified path. The fi For more, see: .BR Tomo-Path.write_unique_bytes (3) + +.TP +.BI Path.writer\ :\ func(path:\ Path,\ append:\ Bool\ =\ no,\ permissions:\ Int32\ =\ Int32(0o644)\ ->\ func(text:Text,\ close:Bool=no\ ->\ Result)) +Returns a function that can be used to repeatedly write to the same file. + +For more, see: +.BR Tomo-Path.writer (3) + diff --git a/man/man3/tomo-Path.byte_writer.3 b/man/man3/tomo-Path.byte_writer.3 new file mode 100644 index 00000000..595f0156 --- /dev/null +++ b/man/man3/tomo-Path.byte_writer.3 @@ -0,0 +1,42 @@ +'\" t +.\" Copyright (c) 2025 Bruce Hill +.\" All rights reserved. +.\" +.TH Path.byte_writer 3 2025-12-22 "Tomo man-pages" +.SH NAME +Path.byte_writer \- create a byte-based file writer +.SH LIBRARY +Tomo Standard Library +.SH SYNOPSIS +.nf +.BI Path.byte_writer\ :\ func(path:\ Path,\ append:\ Bool\ =\ no,\ permissions:\ Int32\ =\ Int32(0o644)\ ->\ func(bytes:[Byte],\ close:Bool=no\ ->\ Result)) +.fi +.SH DESCRIPTION +Returns a function that can be used to repeatedly write bytes to the same file. + + +.SH ARGUMENTS + +.TS +allbox; +lb lb lbx lb +l l l l. +Name Type Description Default +path Path The path of the file to write to. - +append Bool If set to \fByes\fR, writes to the file will append. If set to \fBno\fR, then the first write to the file will overwrite its contents and subsequent calls will append. no +permissions Int32 The permissions to set on the file if it is created. Int32(0o644) +.TE +.SH RETURN +Returns a function that can repeatedly write bytes to the same file. If `close` is set to `yes`, then the file will be closed after writing. If this function is called again after closing, the file will be reopened for appending. + +.SH NOTES +The file writer will keep its file descriptor open after each write (unless the `close` argument is set to `yes`). If the file writer is never closed, it will be automatically closed when the file writer is garbage collected. + +.SH EXAMPLES +.EX +write := (./file.txt).byte_writer() +write("Hello\[rs]n".utf8())! +write("world\[rs]n".utf8(), close=yes)! +.EE +.SH SEE ALSO +.BR Tomo-Path (3) diff --git a/man/man3/tomo-Path.writer.3 b/man/man3/tomo-Path.writer.3 new file mode 100644 index 00000000..8b3d53d8 --- /dev/null +++ b/man/man3/tomo-Path.writer.3 @@ -0,0 +1,42 @@ +'\" t +.\" Copyright (c) 2025 Bruce Hill +.\" All rights reserved. +.\" +.TH Path.writer 3 2025-12-22 "Tomo man-pages" +.SH NAME +Path.writer \- create a file writer +.SH LIBRARY +Tomo Standard Library +.SH SYNOPSIS +.nf +.BI Path.writer\ :\ func(path:\ Path,\ append:\ Bool\ =\ no,\ permissions:\ Int32\ =\ Int32(0o644)\ ->\ func(text:Text,\ close:Bool=no\ ->\ Result)) +.fi +.SH DESCRIPTION +Returns a function that can be used to repeatedly write to the same file. + + +.SH ARGUMENTS + +.TS +allbox; +lb lb lbx lb +l l l l. +Name Type Description Default +path Path The path of the file to write to. - +append Bool If set to \fByes\fR, writes to the file will append. If set to \fBno\fR, then the first write to the file will overwrite its contents and subsequent calls will append. no +permissions Int32 The permissions to set on the file if it is created. Int32(0o644) +.TE +.SH RETURN +Returns a function that can repeatedly write to the same file. If `close` is set to `yes`, then the file will be closed after writing. If this function is called again after closing, the file will be reopened for appending. + +.SH NOTES +The file writer will keep its file descriptor open after each write (unless the `close` argument is set to `yes`). If the file writer is never closed, it will be automatically closed when the file writer is garbage collected. + +.SH EXAMPLES +.EX +write := (./file.txt).writer() +write("Hello\[rs]n")! +write("world\[rs]n", close=yes)! +.EE +.SH SEE ALSO +.BR Tomo-Path (3) diff --git a/src/environment.c b/src/environment.c index cf662749..a82274e7 100644 --- a/src/environment.c +++ b/src/environment.c @@ -341,6 +341,10 @@ env_t *global_env(bool source_mapping) { {"subdirectories", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, // {"unique_directory", "Path$unique_directory", "func(path:Path -> Path)"}, // {"write", "Path$write", "func(path:Path, text:Text, permissions=Int32(0o644) -> Result)"}, // + {"writer", "Path$writer", + "func(path:Path, append=no, permissions=Int32(0o644) -> func(text:Text, close=no -> Result))"}, // + {"byte_writer", "Path$byte_writer", + "func(path:Path, append=no, permissions=Int32(0o644) -> func(bytes:[Byte], close=no -> Result))"}, // {"write_bytes", "Path$write_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644) -> Result)"}, // {"write_unique", "Path$write_unique", "func(path:Path, text:Text -> Path?)"}, // {"write_unique_bytes", "Path$write_unique_bytes", "func(path:Path, bytes:[Byte] -> Path?)"}), diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c index ed8383fd..9c74f4c1 100644 --- a/src/stdlib/paths.c +++ b/src/stdlib/paths.c @@ -324,6 +324,77 @@ Result_t Path$append_bytes(Path_t path, List_t bytes, int permissions) { return _write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions); } +typedef struct { + const char *path_str; + int fd; + int mode; + int permissions; +} writer_data_t; + +static Result_t _write_bytes_to_fd(List_t bytes, bool close_file, void *userdata) { + writer_data_t *data = userdata; + if (bytes.length > 0) { + int fd = open(data->path_str, data->mode, data->permissions); + if (fd == -1) { + if (errno == EMFILE || errno == ENFILE) { + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that + // will be closed by GC finalizers. + GC_gcollect(); + fd = open(data->path_str, data->mode, data->permissions); + } + if (fd == -1) return FailureResult("Could not write to file: ", data->path_str, " (", strerror(errno), ")"); + } + data->fd = fd; + + if (bytes.stride != 1) List$compact(&bytes, 1); + ssize_t written = write(data->fd, bytes.data, (size_t)bytes.length); + if (written != (ssize_t)bytes.length) + return FailureResult("Could not write to file: ", data->path_str, " (", strerror(errno), ")"); + } + // After first successful write, all writes are appends + data->mode = (O_WRONLY | O_CREAT | O_APPEND); + + if (close_file && data->fd != -1) { + if (close(data->fd) == -1) + return FailureResult("Failed to close file: ", data->path_str, " (", strerror(errno), ")"); + data->fd = -1; + } + return SuccessResult; +} + +static Result_t _write_text_to_fd(Text_t text, bool close_file, void *userdata) { + return _write_bytes_to_fd(Text$utf8(text), close_file, userdata); +} + +static void _writer_cleanup(writer_data_t *data) { + if (data && data->fd != -1) { + close(data->fd); + data->fd = -1; + } +} + +public +Closure_t Path$byte_writer(Path_t path, bool append, int permissions) { + path = Path$expand_home(path); + const char *path_str = Path$as_c_string(path); + int mode = append ? (O_WRONLY | O_CREAT | O_APPEND) : (O_WRONLY | O_CREAT | O_TRUNC); + writer_data_t *userdata = + new (writer_data_t, .fd = -1, .path_str = path_str, .mode = mode, .permissions = permissions); + GC_register_finalizer(userdata, (void *)_writer_cleanup, NULL, NULL, NULL); + return (Closure_t){.fn = _write_bytes_to_fd, .userdata = userdata}; +} + +public +Closure_t Path$writer(Path_t path, bool append, int permissions) { + path = Path$expand_home(path); + const char *path_str = Path$as_c_string(path); + int mode = append ? (O_WRONLY | O_CREAT | O_APPEND) : (O_WRONLY | O_CREAT | O_TRUNC); + writer_data_t *userdata = + new (writer_data_t, .fd = -1, .path_str = path_str, .mode = mode, .permissions = permissions); + GC_register_finalizer(userdata, (void *)_writer_cleanup, NULL, NULL, NULL); + return (Closure_t){.fn = _write_text_to_fd, .userdata = userdata}; +} + public OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) { path = Path$expand_home(path); @@ -331,8 +402,8 @@ OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) { int fd = open(path_str, O_RDONLY); if (fd == -1) { if (errno == EMFILE || errno == ENFILE) { - // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will - // be closed by GC finalizers. + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that + // will be closed by GC finalizers. GC_gcollect(); fd = open(path_str, O_RDONLY); } @@ -705,8 +776,8 @@ OptionalClosure_t Path$by_line(Path_t path) { FILE *f = fopen(path_str, "r"); if (f == NULL) { if (errno == EMFILE || errno == ENFILE) { - // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will - // be closed by GC finalizers. + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that + // will be closed by GC finalizers. GC_gcollect(); f = fopen(path_str, "r"); } @@ -726,8 +797,8 @@ OptionalList_t Path$lines(Path_t path) { FILE *f = fopen(path_str, "r"); if (f == NULL) { if (errno == EMFILE || errno == ENFILE) { - // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will - // be closed by GC finalizers. + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that + // will be closed by GC finalizers. GC_gcollect(); f = fopen(path_str, "r"); } diff --git a/src/stdlib/paths.h b/src/stdlib/paths.h index 881a3c78..c272314c 100644 --- a/src/stdlib/paths.h +++ b/src/stdlib/paths.h @@ -39,6 +39,8 @@ Result_t Path$write(Path_t path, Text_t text, int permissions); Result_t Path$write_bytes(Path_t path, List_t bytes, int permissions); Result_t Path$append(Path_t path, Text_t text, int permissions); Result_t Path$append_bytes(Path_t path, List_t bytes, int permissions); +Closure_t Path$byte_writer(Path_t path, bool append, int permissions); +Closure_t Path$writer(Path_t path, bool append, int permissions); OptionalText_t Path$read(Path_t path); OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t limit); Result_t Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t group, bool follow_symlinks); -- cgit v1.2.3 From 04a8c524dbc4f13204b075dcb49b2f9b924a6f1c Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 22 Dec 2025 17:44:31 -0500 Subject: Use static linking to produce executables. --- CHANGES.md | 1 + Makefile | 2 +- src/tomo.c | 83 +++++++++++++++++++++++++++++++++++++++++++++----------------- 3 files changed, 62 insertions(+), 24 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 72c4cef5..976d7318 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ ## v2025-12-22 +- Use static linking for compiled executables - Added `Path.writer()` and `Path.byte_writer()` for multiple successive writes ## v2025-12-21.6 diff --git a/Makefile b/Makefile index 855c8b09..bb807187 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,7 @@ TOMO_VERSION=$(shell awk 'BEGIN{hashes=sprintf("%c%c",35,35)} $$1==hashes {print GIT_VERSION=$(shell git log -1 --pretty=format:"%as_%h" 2>/dev/null || echo "unknown") CFLAGS += $(CCONFIG) $(INCLUDE_DIRS) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS) $(LTO) \ -DSUDO='"$(SUDO)"' -DDEFAULT_C_COMPILER='"$(DEFAULT_C_COMPILER)"' \ - -DGIT_VERSION='"$(GIT_VERSION)"' + -DGIT_VERSION='"$(GIT_VERSION)"' -ffunction-sections -fdata-sections CFLAGS_PLACEHOLDER="$$(printf '\033[2m\033[m\n')" LDLIBS=-lgc -lm -lunistring -lgmp LIBTOMO_FLAGS=-shared diff --git a/src/tomo.c b/src/tomo.c index 5ebd6663..4b817e87 100644 --- a/src/tomo.c +++ b/src/tomo.c @@ -151,7 +151,6 @@ int main(int argc, char *argv[]) { if (stat(compiler_path, &compiler_stat) != 0) err(1, "Could not find age of compiler"); #endif - ldlibs = Texts(ldlibs, " -ltomo@", TOMO_VERSION); #ifdef __OpenBSD__ ldlibs = Texts(ldlibs, Text(" -lexecinfo")); #endif @@ -268,7 +267,8 @@ int main(int argc, char *argv[]) { cflags = Texts(cflags, Text(" -Wno-parentheses-equality")); } - ldflags = Texts("-Wl,-rpath,'", TOMO_PATH, "/lib' ", ldflags); + ldflags = + Texts("-Wl,-rpath,'", TOMO_PATH, "/lib' ", ldflags, " -ffunction-sections -fdata-sections -Wl,--gc-sections"); #ifdef __APPLE__ cflags = Texts(cflags, Text(" -I/opt/homebrew/include")); @@ -508,27 +508,38 @@ void build_library(Path_t lib_dir) { Text_t lib_name = get_library_name(lib_dir); Path_t shared_lib = Path$child(lib_dir, Texts(Text("lib"), lib_name, Text(SHARED_SUFFIX))); - if (!is_stale_for_any(shared_lib, object_files, false)) { - if (verbose) whisper("Unchanged: ", shared_lib); - return; - } - - FILE *prog = run_cmd(cc, " -O", optimization, " ", cflags, " ", ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), + if (is_stale_for_any(shared_lib, object_files, false)) { + FILE *prog = + run_cmd(cc, " -O", optimization, " ", cflags, " ", ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), #ifdef __APPLE__ - " -Wl,-install_name,@rpath/'lib", lib_name, SHARED_SUFFIX, - "'" + " -Wl,-install_name,@rpath/'lib", lib_name, SHARED_SUFFIX, + "'" #else - " -Wl,-soname,'lib", lib_name, SHARED_SUFFIX, - "'" + " -Wl,-soname,'lib", lib_name, SHARED_SUFFIX, + "'" #endif - " -shared ", - paths_str(object_files), " -o '", shared_lib, "'"); + " -shared ", + paths_str(object_files), " -o '", shared_lib, "'"); - if (!prog) print_err("Failed to run C compiler: ", cc); - int status = pclose(prog); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE); + if (!prog) print_err("Failed to run C compiler: ", cc); + int status = pclose(prog); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE); + + if (!quiet) print("Compiled shared library:\t", Path$relative_to(shared_lib, Path$current_dir())); + } else { + if (verbose) whisper("Unchanged: ", shared_lib); + } - if (!quiet) print("Compiled library:\t", Path$relative_to(shared_lib, Path$current_dir())); + Path_t archive = Path$child(lib_dir, Texts(Text("lib"), lib_name, ".a")); + if (is_stale_for_any(archive, object_files, false)) { + FILE *prog = run_cmd("ar -rcs '", archive, "' ", paths_str(object_files)); + if (!prog) print_err("Failed to run `ar`"); + int status = pclose(prog); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE); + if (!quiet) print("Compiled static library:\t", Path$relative_to(archive, Path$current_dir())); + } else { + if (verbose) whisper("Unchanged: ", archive); + } } void install_library(Path_t lib_dir) { @@ -715,9 +726,8 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l case USE_MODULE: { module_info_t mod = get_used_module_info(stmt_ast); const char *full_name = mod.version ? String(mod.name, "@", mod.version) : mod.name; - Text_t lib = Texts("-Wl,-rpath,'", TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/", Text$from_str(full_name), - "' '", TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/", Text$from_str(full_name), "/lib", - Text$from_str(full_name), SHARED_SUFFIX "'"); + Text_t lib = Texts(TOMO_PATH, "/lib/tomo@", TOMO_VERSION, "/", Text$from_str(full_name), "/lib", + Text$from_str(full_name), ".a"); Table$set(to_link, &lib, NULL, Table$info(&Text$info, &Void$info)); List_t children = Path$glob( @@ -943,8 +953,35 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t Path_t runner_file = build_file(path, ".runner.c"); Path$write(runner_file, program, 0644); - FILE *runner = run_cmd(cc, " ", cflags, " -O", optimization, " ", ldflags, " ", ldlibs, " ", - list_text(extra_ldlibs), " ", paths_str(object_files), " ", runner_file, " -o ", exe_path); + // .a archive files need to go later in the positional order: + List_t archives = EMPTY_LIST; + for (int64_t i = 0; i < (int64_t)extra_ldlibs.length;) { + Text_t *lib = (Text_t *)(extra_ldlibs.data + i * extra_ldlibs.stride); + if (Text$ends_with(*lib, Text(".a"), NULL)) { + List$insert(&archives, lib, I(0), sizeof(Text_t)); + List$remove_at(&extra_ldlibs, I(i + 1), I(1), sizeof(Text_t)); + } else { + i += 1; + } + } + + FILE *runner = run_cmd(cc, " ", + // C flags: + cflags, " -O", optimization, " ", + // Linker flags and dynamically linked shared libraries: + ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), " ", + // Object files: + paths_str(object_files), " ", + // Input file: + runner_file, + // Statically linked archive files (must come after runner): + // Libraries are grouped to allow for circular dependencies among + // the libraries that are used. + " -Wl,--start-group ", list_text(archives), " -Wl,--end-group ", + // Tomo static library: + TOMO_PATH, "/lib/libtomo@", TOMO_VERSION, ".a", + // Output file: + " -o ", exe_path); if (show_codegen.length > 0) { FILE *out = run_cmd(show_codegen); -- cgit v1.2.3 From 7653cc84cff0b9168eeaccd056631e70a16930a0 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 22 Dec 2025 17:51:26 -0500 Subject: Rip out shared library code --- Makefile | 12 +----------- src/tomo.c | 33 ++------------------------------- 2 files changed, 3 insertions(+), 42 deletions(-) diff --git a/Makefile b/Makefile index bb807187..0356fa59 100644 --- a/Makefile +++ b/Makefile @@ -88,7 +88,6 @@ CFLAGS += $(CCONFIG) $(INCLUDE_DIRS) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS) $(LT -DGIT_VERSION='"$(GIT_VERSION)"' -ffunction-sections -fdata-sections CFLAGS_PLACEHOLDER="$$(printf '\033[2m\033[m\n')" LDLIBS=-lgc -lm -lunistring -lgmp -LIBTOMO_FLAGS=-shared ifeq ($(OS),OpenBSD) LDLIBS += -lexecinfo @@ -100,11 +99,6 @@ AR_FILE=libtomo@$(TOMO_VERSION).a ifeq ($(OS),Darwin) INCLUDE_DIRS += -I/opt/homebrew/include LDFLAGS += -L/opt/homebrew/lib - LIB_FILE=libtomo@$(TOMO_VERSION).dylib - LIBTOMO_FLAGS += -Wl,-install_name,@rpath/libtomo@$(TOMO_VERSION).dylib -else - LIB_FILE=libtomo@$(TOMO_VERSION).so - LIBTOMO_FLAGS += -Wl,-soname,libtomo@$(TOMO_VERSION).so endif EXE_FILE=tomo@$(TOMO_VERSION) @@ -151,10 +145,6 @@ $(BUILD_DIR)/bin/$(EXE_FILE): $(STDLIB_OBJS) $(COMPILER_OBJS) | $(BUILD_DIR)/bin @$(ECHO) $(CC) $(CFLAGS_PLACEHOLDER) $(LDFLAGS) $^ $(LDLIBS) -o $@ @$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@ -$(BUILD_DIR)/lib/$(LIB_FILE): $(STDLIB_OBJS) | $(BUILD_DIR)/lib - @$(ECHO) $(CC) $^ $(CFLAGS_PLACEHOLDER) $(OSFLAGS) $(LDFLAGS) $(LDLIBS) $(LIBTOMO_FLAGS) -o $@ - @$(CC) $^ $(CFLAGS) $(OSFLAGS) $(LDFLAGS) $(LDLIBS) $(LIBTOMO_FLAGS) -o $@ - $(BUILD_DIR)/lib/$(AR_FILE): $(STDLIB_OBJS) | $(BUILD_DIR)/lib ar -rcs $@ $^ @@ -164,7 +154,7 @@ $(BUILD_DIR)/lib/tomo@$(TOMO_VERSION)/modules.ini: modules/core.ini modules/exam $(BUILD_DIR)/share/licenses/tomo@$(TOMO_VERSION)/LICENSE.md: LICENSE.md | $(BUILD_DIR)/share/licenses/tomo@$(TOMO_VERSION) cp $< $@ -build: $(BUILD_DIR)/bin/tomo $(BUILD_DIR)/bin/tomo@$(TOMO_VERSION) $(BUILD_DIR)/lib/$(LIB_FILE) \ +build: $(BUILD_DIR)/bin/tomo $(BUILD_DIR)/bin/tomo@$(TOMO_VERSION) \ $(BUILD_DIR)/lib/$(AR_FILE) $(BUILD_DIR)/lib/tomo@$(TOMO_VERSION)/modules.ini \ $(BUILD_DIR)/share/licenses/tomo@$(TOMO_VERSION)/LICENSE.md $(build_headers) $(build_manpages) diff --git a/src/tomo.c b/src/tomo.c index 4b817e87..b8588566 100644 --- a/src/tomo.c +++ b/src/tomo.c @@ -69,12 +69,6 @@ static const char *paths_str(List_t paths) { return Text$as_c_string(result); } -#ifdef __APPLE__ -#define SHARED_SUFFIX ".dylib" -#else -#define SHARED_SUFFIX ".so" -#endif - static OptionalBool_t verbose = false, quiet = false, show_version = false, show_prefix = false, clean_build = false, source_mapping = true, should_install = false; @@ -507,29 +501,6 @@ void build_library(Path_t lib_dir) { compile_files(env, tm_files, &object_files, &extra_ldlibs, COMPILE_OBJ); Text_t lib_name = get_library_name(lib_dir); - Path_t shared_lib = Path$child(lib_dir, Texts(Text("lib"), lib_name, Text(SHARED_SUFFIX))); - if (is_stale_for_any(shared_lib, object_files, false)) { - FILE *prog = - run_cmd(cc, " -O", optimization, " ", cflags, " ", ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), -#ifdef __APPLE__ - " -Wl,-install_name,@rpath/'lib", lib_name, SHARED_SUFFIX, - "'" -#else - " -Wl,-soname,'lib", lib_name, SHARED_SUFFIX, - "'" -#endif - " -shared ", - paths_str(object_files), " -o '", shared_lib, "'"); - - if (!prog) print_err("Failed to run C compiler: ", cc); - int status = pclose(prog); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE); - - if (!quiet) print("Compiled shared library:\t", Path$relative_to(shared_lib, Path$current_dir())); - } else { - if (verbose) whisper("Unchanged: ", shared_lib); - } - Path_t archive = Path$child(lib_dir, Texts(Text("lib"), lib_name, ".a")); if (is_stale_for_any(archive, object_files, false)) { FILE *prog = run_cmd("ar -rcs '", archive, "' ", paths_str(object_files)); @@ -556,11 +527,11 @@ void install_library(Path_t lib_dir) { } // If we have `debugedit` on this system, use it to remap the debugging source information // to point to the installed version of the source file. Otherwise, fail silently. - if (verbose) whisper("Updating debug symbols for ", dest, "/lib", lib_name, SHARED_SUFFIX); + if (verbose) whisper("Updating debug symbols for ", dest, "/lib", lib_name, ".a"); int result = system(String(as_owner, "debugedit -b ", lib_dir, " -d '", dest, "'" " '", - dest, "/lib", lib_name, SHARED_SUFFIX, + dest, "/lib", lib_name, ".a", "' " ">/dev/null 2>/dev/null")); (void)result; -- cgit v1.2.3 From cd6923bf137caa24c3cc2d05965c7cfa2428fb4d Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 22 Dec 2025 17:53:02 -0500 Subject: Update changelog --- CHANGES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 976d7318..9690a745 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,9 @@ ## v2025-12-22 -- Use static linking for compiled executables +- Use static linking instead of dynamic linking for the Tomo standard library + as well as for user libraries. This produces binaries that do not depend on + having Tomo and the library installed at runtime. - Added `Path.writer()` and `Path.byte_writer()` for multiple successive writes ## v2025-12-21.6 -- cgit v1.2.3 From 32cbf32c91688b93c40e457e61b29decb416a6e7 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 22 Dec 2025 18:02:47 -0500 Subject: Clean up .git/ when installing libraries --- src/modules.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules.c b/src/modules.c index 056cd5cc..c7c29d24 100644 --- a/src/modules.c +++ b/src/modules.c @@ -131,6 +131,9 @@ bool try_install_module(module_info_t mod, bool ask_confirmation) { if (mod.revision) xsystem("git clone --depth=1 --revision ", mod.revision, " ", mod.git, " ", dest); else if (mod.version) xsystem("git clone --depth=1 --branch ", mod.version, " ", mod.git, " ", dest); else xsystem("git clone --depth=1 ", mod.git, " ", dest); + // Clean up .git/ folder after cloning: + xsystem("rm -rf ", dest, "/.git"); + // Build library: xsystem("tomo -L ", dest); return true; } else if (mod.url) { -- cgit v1.2.3 From 903c957c7e686401e12bf52ad0ac00d0efa6fd30 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 22 Dec 2025 18:07:12 -0500 Subject: Bugfix for `Success || Void` typechecking --- src/typecheck.c | 6 +----- src/types.c | 9 +++++++++ src/types.h | 1 + 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/typecheck.c b/src/typecheck.c index 1ac3943c..a073cb2e 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -1531,11 +1531,7 @@ PUREFUNC bool is_discardable(env_t *env, ast_t *ast) { case Metadata: return true; default: break; } - type_t *t = get_type(env, ast); - if (t->tag == StructType) { - return (Match(t, StructType)->fields == NULL); - } - return (t->tag == VoidType || t->tag == AbortType || t->tag == ReturnType); + return is_discardable_type(get_type(env, ast)); } type_t *get_arg_ast_type(env_t *env, arg_ast_t *arg) { diff --git a/src/types.c b/src/types.c index 46df5c64..1ccb7952 100644 --- a/src/types.c +++ b/src/types.c @@ -144,6 +144,13 @@ PUREFUNC type_t *value_type(type_t *t) { return t; } +PUREFUNC bool is_discardable_type(type_t *t) { + if (t->tag == StructType) { + return (Match(t, StructType)->fields == NULL); + } + return (t->tag == VoidType || t->tag == AbortType || t->tag == ReturnType); +} + type_t *type_or_type(type_t *a, type_t *b) { if (!a) return b; if (!b) return a; @@ -153,6 +160,8 @@ type_t *type_or_type(type_t *a, type_t *b) { return a->tag == OptionalType ? a : Type(OptionalType, a); if (a->tag == ReturnType && b->tag == ReturnType) return Type(ReturnType, .ret = type_or_type(Match(a, ReturnType)->ret, Match(b, ReturnType)->ret)); + if ((a->tag == VoidType && is_discardable_type(b)) || (is_discardable_type(a) && b->tag == VoidType)) + return Type(VoidType); if (is_incomplete_type(a) && type_eq(b, most_complete_type(a, b))) return b; if (is_incomplete_type(b) && type_eq(a, most_complete_type(a, b))) return a; diff --git a/src/types.h b/src/types.h index 66e6ba12..df5729ca 100644 --- a/src/types.h +++ b/src/types.h @@ -142,6 +142,7 @@ PUREFUNC bool type_eq(type_t *a, type_t *b); PUREFUNC bool type_is_a(type_t *t, type_t *req); type_t *type_or_type(type_t *a, type_t *b); type_t *value_type(type_t *a); +PUREFUNC bool is_discardable_type(type_t *t); typedef enum { NUM_PRECISION_EQUAL, NUM_PRECISION_LESS, -- cgit v1.2.3 From 354605f56937e6f51695084d208521f7f2df890c Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 22 Dec 2025 18:17:03 -0500 Subject: Fix for CLI usage showing flag type instead of flag name for positional args --- src/compile/cli.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/compile/cli.c b/src/compile/cli.c index 63a467ca..ade6caa7 100644 --- a/src/compile/cli.c +++ b/src/compile/cli.c @@ -83,10 +83,8 @@ static Text_t generate_usage(env_t *env, type_t *fn_type) { else if (t->tag == ListType) usage = Texts(usage, "[", flags, " ", get_flag_options(t, Text("|")), "]"); else if (t->tag == EnumType) usage = Texts(usage, "[", flags, " val]"); else usage = Texts(usage, "[", flags, " ", get_flag_options(t, Text("|")), "]"); - } else if (t->tag == EnumType) { - usage = Texts(usage, "\x1b[1m", flag, "\x1b[m"); } else { - usage = Texts(usage, "\x1b[1m", get_flag_options(t, Text("|")), "\x1b[m"); + usage = Texts(usage, "\x1b[1m", flag, "\x1b[m"); } } return usage; -- cgit v1.2.3 From f10273d2ef7d5bdd3592c4c19476bd62a9b4a164 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 22 Dec 2025 18:32:31 -0500 Subject: Allow writes without reads to global vars --- src/compile/functions.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compile/functions.c b/src/compile/functions.c index ec37c0ad..01377a89 100644 --- a/src/compile/functions.c +++ b/src/compile/functions.c @@ -630,6 +630,8 @@ static void check_unused_vars(env_t *env, arg_ast_t *args, ast_t *body) { const char *name; } *entry = unused.entries.data + i * unused.entries.stride; if (streq(entry->name, "_")) continue; + // Global/file scoped vars are okay to mutate without reading + if (get_binding(env, entry->name) != NULL) continue; ast_t *var = Table$str_get(assigned_vars, entry->name); code_err(var, "This variable was assigned to, but never read from."); } -- cgit v1.2.3 From 8b255d8f23c121fbec478a714e580b32641853d7 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 22 Dec 2025 19:48:56 -0500 Subject: Update learnxiny code --- examples/learnxiny.tm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/learnxiny.tm b/examples/learnxiny.tm index 748f8957..6971f2a9 100644 --- a/examples/learnxiny.tm +++ b/examples/learnxiny.tm @@ -19,7 +19,7 @@ func main() my_num := 2.0 # Strings can use interpolation with the dollar sign $: - say("My variable is $my_variable and this is a sum: $(1 + 2)") + say("My variable is $my_variable, my num is $my_num, and this is a sum: $(1 + 2)") say(" Multiline strings begin with a " at the end of a line and continue in @@ -115,6 +115,7 @@ func main() # Empty tables require specifying the key and value types: empty_table : {Text:Int} + assert empty_table == {} # Tables can be iterated over either by key or key,value: for key in table -- cgit v1.2.3 From a89500afd9a34f2af94ab6fcd7a0469b0450732a Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 22 Dec 2025 19:52:24 -0500 Subject: Update example versions --- modules/examples.ini | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/examples.ini b/modules/examples.ini index 20a04639..8e53d431 100644 --- a/modules/examples.ini +++ b/modules/examples.ini @@ -1,27 +1,27 @@ [log] -version=v1.0 +version=v2025-12-22 git=https://github.com/bruce-hill/tomo-log [ini] -version=v1.0 +version=v2025-12-22 git=https://github.com/bruce-hill/tomo-ini [vectors] -version=v1.0 +version=v1.1 git=https://github.com/bruce-hill/tomo-vectors [http] -version=v1.1 +version=v1.2 git=https://github.com/bruce-hill/tomo-http [http-server] -version=v1.0 +version=v2025-12-22 git=https://github.com/bruce-hill/tomo-http-server [wrap] -version=v1.0 +version=v2025-12-22 git=https://github.com/bruce-hill/tomo-wrap [colorful] -version=v1.0 +version=v2025-11-29 git=https://github.com/bruce-hill/tomo-colorful -- cgit v1.2.3 From 4a6c0438f9a2c82e834116e3e1bc110b8cae8432 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 23 Dec 2025 13:58:33 -0500 Subject: Big speedup my trimming down MAP_LIST macro and inlining some applications of it. --- CHANGES.md | 4 ++++ src/compile/assertions.c | 14 +++++--------- src/compile/indexing.c | 2 +- src/compile/optionals.c | 8 ++++---- src/compile/promotions.c | 12 ++++++------ src/compile/text.c | 15 +++------------ src/stdlib/bytes.c | 2 +- src/stdlib/c_strings.c | 4 ++-- src/stdlib/enums.c | 2 +- src/stdlib/intX.c.h | 2 +- src/stdlib/lists.h | 10 ++++++---- src/stdlib/mapmacro.h | 4 +--- src/stdlib/memory.c | 2 +- src/stdlib/numX.c.h | 6 +++--- src/stdlib/paths.c | 4 ++-- src/stdlib/pointers.c | 8 ++++---- src/stdlib/stdlib.h | 4 ++-- src/stdlib/structs.c | 4 ++-- src/stdlib/tables.c | 6 +++--- src/stdlib/tables.h | 4 ++-- src/stdlib/text.c | 16 ++++++++-------- src/stdlib/text.h | 5 +++-- 22 files changed, 65 insertions(+), 73 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 9690a745..6f8f5e16 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Version History +## v2025-12-23 + +- Improved C preprocessing performance by eliminating expensive macro calls. + ## v2025-12-22 - Use static linking instead of dynamic linking for the Tomo standard library diff --git a/src/compile/assertions.c b/src/compile/assertions.c index 34055998..18531fd9 100644 --- a/src/compile/assertions.c +++ b/src/compile/assertions.c @@ -60,13 +60,10 @@ Text_t compile_assertion(env_t *env, ast_t *ast) { compile_declaration(operand_t, Text("_rhs")), " = ", compile_to_type(env, cmp.rhs, operand_t), ";\n", "\n#line ", line, "\n", "if (!(", compile_condition(env, var_comparison), "))\n", "#line ", line, "\n", Texts("fail_source(", quoted_str(ast->file->filename), ", ", (int64_t)(expr->start - expr->file->text), - ", ", (int64_t)(expr->end - expr->file->text), ", ", - message ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") - : Text("\"This assertion failed!\""), - ", ", "\" (\", ", expr_as_text(Text("_lhs"), operand_t, Text("no")), - ", " - "\" ", - failure, " \", ", expr_as_text(Text("_rhs"), operand_t, Text("no")), ", \")\");\n"), + ", ", (int64_t)(expr->end - expr->file->text), ", Text$concat(", + message ? compile_to_type(env, message, Type(TextType)) : Text("Text(\"This assertion failed!\")"), + ", Text(\" (\"), ", expr_as_text(Text("_lhs"), operand_t, Text("no")), ", Text(\" ", failure, + " \"), ", expr_as_text(Text("_rhs"), operand_t, Text("no")), ", Text(\")\")));\n"), "}\n"); } default: { @@ -74,8 +71,7 @@ Text_t compile_assertion(env_t *env, ast_t *ast) { return Texts("if (!(", compile_condition(env, expr), "))\n", "#line ", line, "\n", "fail_source(", quoted_str(ast->file->filename), ", ", (int64_t)(expr->start - expr->file->text), ", ", (int64_t)(expr->end - expr->file->text), ", ", - message ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") - : Text("\"This assertion failed!\""), + message ? compile_to_type(env, message, Type(TextType)) : Text("Text(\"This assertion failed!\")"), ");\n"); } } diff --git a/src/compile/indexing.c b/src/compile/indexing.c index 13062641..031ef9a0 100644 --- a/src/compile/indexing.c +++ b/src/compile/indexing.c @@ -50,7 +50,7 @@ Text_t compile_indexing(env_t *env, ast_t *ast, bool checked) { return Texts("({ ", compile_declaration(item_type, Text("opt")), " = ", code, "; ", "if unlikely (", check_none(item_type, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(", quoted_str(ast->file->filename), ", ", start, ", ", end, ", ", - "\"This was expected to be a value, but it's `none`\\n\");\n", + "Text(\"This was expected to be a value, but it's `none`\\n\"));\n", optional_into_nonnone(item_type, Text("opt")), "; })"); } return code; diff --git a/src/compile/optionals.c b/src/compile/optionals.c index 83f387e2..75dff935 100644 --- a/src/compile/optionals.c +++ b/src/compile/optionals.c @@ -134,9 +134,9 @@ Text_t compile_non_optional(env_t *env, ast_t *ast) { compile_to_pointer_depth(env, f->fielded, 0, true), ";", "if unlikely (_test_enum.$tag != ", tag_name, ") {\n", "#line ", line, "\n", "fail_source(", quoted_str(f->fielded->file->filename), ", ", (int64_t)(f->fielded->start - f->fielded->file->text), - ", ", (int64_t)(f->fielded->end - f->fielded->file->text), ", ", "\"This was expected to be ", - tag->name, ", but it was: \", ", expr_as_text(Text("_test_enum"), enum_t, Text("false")), - ", \"\\n\");\n}\n", + ", ", (int64_t)(f->fielded->end - f->fielded->file->text), ", ", + "Text$concat(Text(\"This was expected to be ", tag->name, ", but it was: \"), ", + expr_as_text(Text("_test_enum"), enum_t, Text("false")), ", Text(\"\\n\")));\n}\n", compile_maybe_incref( env, WrapLiteralCode(value, Texts("_test_enum.", tag->name), .type = tag->type), tag->type), "; })"); @@ -149,7 +149,7 @@ Text_t compile_non_optional(env_t *env, ast_t *ast) { check_none(value_t, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(", quoted_str(value->file->filename), ", ", (int64_t)(value->start - value->file->text), ", ", (int64_t)(value->end - value->file->text), ", ", - "\"This was expected to be a value, but it's `none`\\n\");\n", + "Text(\"This was expected to be a value, but it's `none`\\n\"));\n", optional_into_nonnone(value_t, Text("opt")), "; })"); } } diff --git a/src/compile/promotions.c b/src/compile/promotions.c index 5b0ccb95..482e5ed0 100644 --- a/src/compile/promotions.c +++ b/src/compile/promotions.c @@ -61,12 +61,12 @@ bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t *neede // Automatic optional checking for nums: if (needed->tag == NumType && actual->tag == OptionalType && Match(actual, OptionalType)->type->tag == NumType) { int64_t line = get_line_number(ast->file, ast->start); - *code = - Texts("({ ", compile_declaration(actual, Text("opt")), " = ", *code, "; ", "if unlikely (", - check_none(actual, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(", - quoted_str(ast->file->filename), ", ", (int64_t)(ast->start - ast->file->text), ", ", - (int64_t)(ast->end - ast->file->text), ", ", "\"This was expected to be a value, but it's none\");\n", - optional_into_nonnone(actual, Text("opt")), "; })"); + *code = Texts("({ ", compile_declaration(actual, Text("opt")), " = ", *code, "; ", "if unlikely (", + check_none(actual, Text("opt")), ")\n", "#line ", line, "\n", "fail_source(", + quoted_str(ast->file->filename), ", ", (int64_t)(ast->start - ast->file->text), ", ", + (int64_t)(ast->end - ast->file->text), ", ", + "Text(\"This was expected to be a value, but it's none\"));\n", + optional_into_nonnone(actual, Text("opt")), "; })"); return true; } diff --git a/src/compile/text.c b/src/compile/text.c index 3a8a227c..2a64ca45 100644 --- a/src/compile/text.c +++ b/src/compile/text.c @@ -4,7 +4,6 @@ #include "../ast.h" #include "../environment.h" -#include "../naming.h" #include "../stdlib/datatypes.h" #include "../stdlib/tables.h" #include "../stdlib/text.h" @@ -102,19 +101,12 @@ Text_t compile_text_ast(env_t *env, ast_t *ast) { type_t *text_t = lang ? Table$str_get(*env->types, lang) : TEXT_TYPE; if (!text_t || text_t->tag != TextType) code_err(ast, quoted(lang), " is not a valid text language name"); - Text_t lang_constructor; - if (!lang || streq(lang, "Text")) lang_constructor = Text("Text"); - else - lang_constructor = namespace_name(Match(text_t, TextType)->env, Match(text_t, TextType)->env->namespace->parent, - Text$from_str(lang)); - ast_list_t *chunks = Match(ast, TextJoin)->children; if (!chunks) { - return Texts(lang_constructor, "(\"\")"); + return Text("EMPTY_TEXT"); } else if (!chunks->next && chunks->ast->tag == TextLiteral) { Text_t literal = Match(chunks->ast, TextLiteral)->text; - if (string_literal_is_all_ascii(literal)) - return Texts(lang_constructor, "(", compile_text_literal(literal), ")"); + if (string_literal_is_all_ascii(literal)) return Texts("Text(", compile_text_literal(literal), ")"); return Texts("((", compile_type(text_t), ")", compile(env, chunks->ast), ")"); } else { Text_t code = EMPTY_TEXT; @@ -142,7 +134,6 @@ Text_t compile_text_ast(env_t *env, ast_t *ast) { code = Texts(code, chunk_code); if (chunk->next) code = Texts(code, ", "); } - if (chunks->next) return Texts(lang_constructor, "s(", code, ")"); - else return code; + return Texts("Text$concat(", code, ")"); } } diff --git a/src/stdlib/bytes.c b/src/stdlib/bytes.c index 4416d804..a1e953a0 100644 --- a/src/stdlib/bytes.c +++ b/src/stdlib/bytes.c @@ -25,7 +25,7 @@ PUREFUNC public Text_t Byte$as_text(const void *b, bool colorize, const TypeInfo '\0', }; Text_t text = Text$from_str(digits); - if (colorize) text = Texts(Text("\x1b[35m"), text, Text("\x1b[m")); + if (colorize) text = Text$concat(Text("\x1b[35m"), text, Text("\x1b[m")); return text; } diff --git a/src/stdlib/c_strings.c b/src/stdlib/c_strings.c index 57960577..cbe46b68 100644 --- a/src/stdlib/c_strings.c +++ b/src/stdlib/c_strings.c @@ -70,8 +70,8 @@ const char *CString$join(const char *glue, List_t strings) { Text_t ret = EMPTY_TEXT; Text_t glue_text = Text$from_str(glue); for (int64_t i = 0; i < (int64_t)strings.length; i++) { - if (i > 0) ret = Texts(ret, glue_text); - ret = Texts(ret, Text$from_str(*(const char **)(strings.data + i * strings.stride))); + if (i > 0) ret = Text$concat(ret, glue_text); + ret = Text$concat(ret, Text$from_str(*(const char **)(strings.data + i * strings.stride))); } return Text$as_c_string(ret); } diff --git a/src/stdlib/enums.c b/src/stdlib/enums.c index b9b970fa..9cc16c5d 100644 --- a/src/stdlib/enums.c +++ b/src/stdlib/enums.c @@ -65,7 +65,7 @@ Text_t Enum$as_text(const void *obj, bool colorize, const TypeInfo_t *type) { NamedType_t value = type->EnumInfo.tags[tag - 1]; if (!value.type || value.type->size == 0) { Text_t text = Text$from_str(value.name); - return colorize ? Texts(Text("\x1b[1m"), text, Text("\x1b[m")) : text; + return colorize ? Text$concat(Text("\x1b[1m"), text, Text("\x1b[m")) : text; } return generic_as_text(obj + value_offset(type), colorize, value.type); diff --git a/src/stdlib/intX.c.h b/src/stdlib/intX.c.h index 72dfe6ed..03322e5b 100644 --- a/src/stdlib/intX.c.h +++ b/src/stdlib/intX.c.h @@ -98,7 +98,7 @@ Text_t NAMESPACED(as_text)(const void *i, bool colorize, const TypeInfo_t *info) (void)info; if (!i) return Text(NAME_STR); Text_t text = _int64_to_text((int64_t)(*(INT_T *)i)); - return colorize ? Texts(Text("\033[35m"), text, Text("\033[m")) : text; + return colorize ? Text$concat(Text("\033[35m"), text, Text("\033[m")) : text; } public Text_t NAMESPACED(value_as_text)(INT_T i) { return _int64_to_text((int64_t)i); } diff --git a/src/stdlib/lists.h b/src/stdlib/lists.h index 6a0a1d43..9ac8bf1b 100644 --- a/src/stdlib/lists.h +++ b/src/stdlib/lists.h @@ -20,8 +20,9 @@ extern char _EMPTY_LIST_SENTINEL; int64_t index = index_expr; \ int64_t off = index + (index < 0) * (list.length + 1) - 1; \ if (unlikely(off < 0 || off >= list.length)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid list index: ", index, " (list has length ", \ - (int64_t)list.length, ")\n"); \ + fail_source(__SOURCE_FILE__, start, end, \ + Text$concat(Text("Invalid list index: "), convert_to_text(index), Text(" (list has length "), \ + convert_to_text((int64_t)list.length), Text(")\n"))); \ *(item_type *)(list.data + list.stride * off); \ }) #define List_get(list_expr, index_expr, item_type, var, optional_expr, none_expr) \ @@ -40,8 +41,9 @@ extern char _EMPTY_LIST_SENTINEL; int64_t index = index_expr; \ int64_t off = index + (index < 0) * (list->length + 1) - 1; \ if (unlikely(off < 0 || off >= list->length)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid list index: ", index, " (list has length ", \ - (int64_t)list->length, ")\n"); \ + fail_source(__SOURCE_FILE__, start, end, \ + Text$concat(Text("Invalid list index: "), convert_to_text(index), Text(" (list has length "), \ + convert_to_text((int64_t)list->length), Text(")\n"))); \ if (list->data_refcount > 0) List$compact(list, sizeof(item_type)); \ (item_type *)(list->data + list->stride * off); \ }) diff --git a/src/stdlib/mapmacro.h b/src/stdlib/mapmacro.h index 7b0e3c4e..5e9eaa36 100644 --- a/src/stdlib/mapmacro.h +++ b/src/stdlib/mapmacro.h @@ -7,9 +7,7 @@ #define EVAL0(...) __VA_ARGS__ #define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__))) #define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) -#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) -#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) -#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) +#define EVAL(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) #define MAP_END(...) #define MAP_OUT diff --git a/src/stdlib/memory.c b/src/stdlib/memory.c index fd396463..53a180fb 100644 --- a/src/stdlib/memory.c +++ b/src/stdlib/memory.c @@ -18,7 +18,7 @@ Text_t Memory$as_text(const void *p, bool colorize, const TypeInfo_t *info) { (void)info; if (!p) return Text("Memory"); Text_t text = Text$from_str(String("Memory<", (void *)p, ">")); - return colorize ? Texts(Text("\x1b[0;34;1m"), text, Text("\x1b[m")) : text; + return colorize ? Text$concat(Text("\x1b[0;34;1m"), text, Text("\x1b[m")) : text; } public diff --git a/src/stdlib/numX.c.h b/src/stdlib/numX.c.h index 0e84708f..2fde8c45 100644 --- a/src/stdlib/numX.c.h +++ b/src/stdlib/numX.c.h @@ -50,7 +50,7 @@ PUREFUNC Text_t NAMESPACED(as_text)(const void *x, bool colorize, const TypeInfo if (!x) return Text(TYPE_STR); static const Text_t color_prefix = Text("\x1b[35m"), color_suffix = Text("\x1b[m"); Text_t text = NAMESPACED(value_as_text)(*(NUM_T *)x); - return colorize ? Texts(color_prefix, text, color_suffix) : text; + return colorize ? Text$concat(color_prefix, text, color_suffix) : text; } public PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *info) { @@ -73,7 +73,7 @@ PUREFUNC Text_t NAMESPACED(as_text)(const void *x, bool colorize, const TypeInfo if (!x) return Text(TYPE_STR); static const Text_t color_prefix = Text("\x1b[35m"), color_suffix = Text("\x1b[m"); Text_t text = Num$value_as_text((double)*(NUM_T *)x); - return colorize ? Texts(color_prefix, text, color_suffix) : text; + return colorize ? Text$concat(color_prefix, text, color_suffix) : text; } public PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *info) { @@ -115,7 +115,7 @@ public Text_t NAMESPACED(percent)(NUM_T x, NUM_T precision) { NUM_T d = SUFFIXED(100.) * x; d = NAMESPACED(with_precision)(d, precision); - return Texts(NAMESPACED(value_as_text)(d), Text("%")); + return Text$concat(NAMESPACED(value_as_text)(d), Text("%")); } public diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c index 9c74f4c1..e3028cce 100644 --- a/src/stdlib/paths.c +++ b/src/stdlib/paths.c @@ -690,7 +690,7 @@ bool Path$has_extension(Path_t path, Text_t extension) { if (extension.length == 0) return !Text$has(Text$from(last, I(2)), Text(".")) || Text$equal_values(last, Text("..")); - if (!Text$starts_with(extension, Text("."), NULL)) extension = Texts(Text("."), extension); + if (!Text$starts_with(extension, Text("."), NULL)) extension = Text$concat(Text("."), extension); return Text$ends_with(Text$from(last, I(2)), extension, NULL); } @@ -879,7 +879,7 @@ Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type) { && (path->components.length == 0 || !Text$equal_values(*(Text_t *)(path->components.data), Text("..")))) text = Text$concat(path->components.length > 0 ? Text("./") : Text("."), text); - if (color) text = Texts(Text("\033[32;1m"), text, Text("\033[m")); + if (color) text = Text$concat(Text("\033[32;1m"), text, Text("\033[m")); return text; } diff --git a/src/stdlib/pointers.c b/src/stdlib/pointers.c index 74037613..0bf9a274 100644 --- a/src/stdlib/pointers.c +++ b/src/stdlib/pointers.c @@ -38,14 +38,14 @@ Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *type) { if (top_level) { root = ptr; } else if (ptr == root) { - Text_t text = Texts(Text$from_str(ptr_info.sigil), Text("~1")); - return colorize ? Texts(Text("\x1b[34;1m"), text, Text("\x1b[m")) : text; + Text_t text = Text$concat(Text$from_str(ptr_info.sigil), Text("~1")); + return colorize ? Text$concat(Text("\x1b[34;1m"), text, Text("\x1b[m")) : text; } else { TypeInfo_t rec_table = *Table$info(type, &Int64$info); int64_t *id = Table$get(pending, x, &rec_table); if (id) { - Text_t text = Texts(Text$from_str(ptr_info.sigil), Int64$value_as_text(*id)); - return colorize ? Texts(Text("\x1b[34;1m"), text, Text("\x1b[m")) : text; + Text_t text = Text$concat(Text$from_str(ptr_info.sigil), Int64$value_as_text(*id)); + return colorize ? Text$concat(Text("\x1b[34;1m"), text, Text("\x1b[m")) : text; } int64_t next_id = (int64_t)pending.entries.length + 2; Table$set(&pending, x, &next_id, &rec_table); diff --git a/src/stdlib/stdlib.h b/src/stdlib/stdlib.h index 234048e9..087ed4bf 100644 --- a/src/stdlib/stdlib.h +++ b/src/stdlib/stdlib.h @@ -37,7 +37,7 @@ void tomo_cleanup(void); exit(1); \ }) -#define fail_source(filename, start, end, ...) \ +#define fail_source(filename, start, end, message) \ ({ \ tomo_cleanup(); \ fflush(stdout); \ @@ -46,7 +46,7 @@ void tomo_cleanup(void); print_stacktrace(stderr, 0); \ fputs("\n", stderr); \ if (USE_COLOR) fputs("\x1b[31;1m", stderr); \ - fprint_inline(stderr, __VA_ARGS__); \ + Text$print(stderr, message); \ file_t *_file = (filename) ? load_file(filename) : NULL; \ if ((filename) && _file) { \ fputs("\n", stderr); \ diff --git a/src/stdlib/structs.c b/src/stdlib/structs.c index da8f1461..d1b9f824 100644 --- a/src/stdlib/structs.c +++ b/src/stdlib/structs.c @@ -126,10 +126,10 @@ PUREFUNC public Text_t Struct$as_text(const void *obj, bool colorize, const Type Text_t name = Text$from_str(type->StructInfo.name); if (type->StructInfo.is_secret || type->StructInfo.is_opaque) { - return colorize ? Texts(Text("\x1b[0;1m"), name, Text("\x1b[m(...)")) : Texts(name, Text("(...)")); + return colorize ? Text$concat(Text("\x1b[0;1m"), name, Text("\x1b[m(...)")) : Text$concat(name, Text("(...)")); } - Text_t text = colorize ? Texts(Text("\x1b[0;1m"), name, Text("\x1b[m(")) : Texts(name, Text("(")); + Text_t text = colorize ? Text$concat(Text("\x1b[0;1m"), name, Text("\x1b[m(")) : Text$concat(name, Text("(")); ptrdiff_t byte_offset = 0; ptrdiff_t bit_offset = 0; for (int i = 0; i < type->StructInfo.num_fields; i++) { diff --git a/src/stdlib/tables.c b/src/stdlib/tables.c index a801957f..400cc1b8 100644 --- a/src/stdlib/tables.c +++ b/src/stdlib/tables.c @@ -537,9 +537,9 @@ Text_t Table$as_text(const void *obj, bool colorize, const TypeInfo_t *type) { __typeof(type->TableInfo) table = type->TableInfo; if (!t) { - return table.value->size > 0 ? Texts("{", generic_as_text(NULL, false, table.key), ":", - generic_as_text(NULL, false, table.value), "}") - : Texts("{", generic_as_text(NULL, false, table.key), "}"); + return table.value->size > 0 ? Text$concat(Text("{"), generic_as_text(NULL, false, table.key), Text(":"), + generic_as_text(NULL, false, table.value), Text("}")) + : Text$concat(Text("{"), generic_as_text(NULL, false, table.key), Text("}")); } int64_t val_off = (int64_t)value_offset(type); diff --git a/src/stdlib/tables.h b/src/stdlib/tables.h index cf1c3625..93da465e 100644 --- a/src/stdlib/tables.h +++ b/src/stdlib/tables.h @@ -47,8 +47,8 @@ void *Table$get(Table_t t, const void *key, const TypeInfo_t *type); val_t *value = Table$get(t, &key, info); \ if (unlikely(value == NULL)) \ fail_source(__SOURCE_FILE__, start, end, \ - "This key was not found in the table: ", generic_as_text(&key, false, info->TableInfo.key), \ - "\n"); \ + Text$concat(Text("This key was not found in the table: "), \ + generic_as_text(&key, false, info->TableInfo.key), Text("\n"))); \ *value; \ }) #define Table$get_or_setdefault(table_expr, key_t, val_t, key_expr, default_expr, info_expr) \ diff --git a/src/stdlib/text.c b/src/stdlib/text.c index b4b27fed..411f3546 100644 --- a/src/stdlib/text.c +++ b/src/stdlib/text.c @@ -606,8 +606,8 @@ Text_t Text$middle_pad(Text_t text, Int_t width, Text_t padding, Text_t language if (padding.length == 0) fail("Cannot pad with an empty text!"); int64_t needed = Int64$from_int(width, false) - Int64$from_int(Text$width(text, language), false); - return Texts(Text$repeat_to_width(padding, needed / 2, language), text, - Text$repeat_to_width(padding, (needed + 1) / 2, language)); + return Text$concat(Text$repeat_to_width(padding, needed / 2, language), text, + Text$repeat_to_width(padding, (needed + 1) / 2, language)); } public @@ -1506,8 +1506,8 @@ Text_t Text$quoted(Text_t text, bool colorize, Text_t quotation_mark) { Text_t ret = Text$escaped(text, colorize, quotation_mark); if (!(Text$equal_values(quotation_mark, Text("\"")) || Text$equal_values(quotation_mark, Text("'")) || Text$equal_values(quotation_mark, Text("`")))) - ret = Texts("$", quotation_mark, ret, quotation_mark); - else ret = Texts(quotation_mark, ret, quotation_mark); + ret = Text$concat(Text("$"), quotation_mark, ret, quotation_mark); + else ret = Text$concat(quotation_mark, ret, quotation_mark); return ret; } @@ -1803,11 +1803,11 @@ Int_t Text$memory_size(Text_t text) { public Text_t Text$layout(Text_t text) { switch (text.tag) { - case TEXT_ASCII: return Texts(Text("ASCII("), Int64$value_as_text(text.length), Text(")")); - case TEXT_GRAPHEMES: return Texts(Text("Graphemes("), Int64$value_as_text(text.length), Text(")")); - case TEXT_BLOB: return Texts(Text("Blob("), Int64$value_as_text(text.length), Text(")")); + case TEXT_ASCII: return Text$concat(Text("ASCII("), Int64$value_as_text(text.length), Text(")")); + case TEXT_GRAPHEMES: return Text$concat(Text("Graphemes("), Int64$value_as_text(text.length), Text(")")); + case TEXT_BLOB: return Text$concat(Text("Blob("), Int64$value_as_text(text.length), Text(")")); case TEXT_CONCAT: - return Texts(Text("Concat("), Text$layout(*text.left), Text(", "), Text$layout(*text.right), Text(")")); + return Text$concat(Text("Concat("), Text$layout(*text.left), Text(", "), Text$layout(*text.right), Text(")")); default: errx(1, "Invalid text tag: %d", text.tag); } } diff --git a/src/stdlib/text.h b/src/stdlib/text.h index 9ad7441c..776ea8ec 100644 --- a/src/stdlib/text.h +++ b/src/stdlib/text.h @@ -63,8 +63,9 @@ OptionalText_t Text$cluster(Text_t text, Int_t index_int); Int_t index = index_expr; \ OptionalText_t cluster = Text$cluster(text, index); \ if (unlikely(cluster.tag == TEXT_NONE)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid text index: ", index, " (text has length ", \ - (int64_t)text.length, ")\n"); \ + fail_source(__SOURCE_FILE__, start, end, \ + Text$concat(Text("Invalid text index: "), convert_to_text(index), Text(" (text has length "), \ + convert_to_text((int64_t)text.length), Text(")\n"))); \ cluster; \ }) OptionalText_t Text$from_str(const char *str); -- cgit v1.2.3 From 63a3d9d91b3998f5558e2e22023a2bdd4caf950c Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 23 Dec 2025 14:19:26 -0500 Subject: Remove dead-ish code for debugging tables --- src/stdlib/tables.c | 49 ------------------------------------------------- 1 file changed, 49 deletions(-) diff --git a/src/stdlib/tables.c b/src/stdlib/tables.c index 400cc1b8..d417545d 100644 --- a/src/stdlib/tables.c +++ b/src/stdlib/tables.c @@ -24,14 +24,6 @@ #include "types.h" #include "util.h" -// #define DEBUG_TABLES - -#ifdef DEBUG_TABLES -#define hdebug(...) print_inline("\x1b[2m", __VA_ARGS__, "\x1b[m") -#else -#define hdebug(...) (void)0 -#endif - // Helper accessors for type functions/values: #define HASH_KEY(t, k) (generic_hash((k), type->TableInfo.key) % ((t).bucket_info->count)) #define EQUAL_KEYS(x, y) (generic_equal((x), (y), type->TableInfo.key)) @@ -76,18 +68,6 @@ PUREFUNC static INLINE size_t value_offset(const TypeInfo_t *info) { return offset; } -static INLINE void hshow(const Table_t *t) { - hdebug("{"); - for (uint32_t i = 0; t->bucket_info && i < t->bucket_info->count; i++) { - if (i > 0) hdebug(" "); - if (t->bucket_info->buckets[i].occupied) - hdebug("[", i, "]=", (uint32_t)t->bucket_info->buckets[i].index, "(", - t->bucket_info->buckets[i].next_bucket, ")"); - else hdebug("[", i, "]=_"); - } - hdebug("}\n"); -} - static void maybe_copy_on_write(Table_t *t, const TypeInfo_t *type) { if (t->entries.data_refcount != 0) List$compact(&t->entries, (int64_t)entry_size(type)); @@ -104,14 +84,10 @@ PUREFUNC public void *Table$get_raw(Table_t t, const void *key, const TypeInfo_t if (!key || !t.bucket_info) return NULL; uint64_t hash = HASH_KEY(t, key); - hshow(&t); - hdebug("Getting value with initial probe at ", hash, "\n"); bucket_t *buckets = t.bucket_info->buckets; for (uint64_t i = hash; buckets[i].occupied; i = buckets[i].next_bucket) { - hdebug("Checking against key in bucket ", i, "\n"); void *entry = GET_ENTRY(t, buckets[i].index); if (EQUAL_KEYS(entry, key)) { - hdebug("Found key!\n"); return entry + value_offset(type); } if (buckets[i].next_bucket == END_OF_CHAIN) break; @@ -130,24 +106,18 @@ PUREFUNC public void *Table$get(Table_t t, const void *key, const TypeInfo_t *ty static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const TypeInfo_t *type) { assert(t->bucket_info); - hshow(t); const void *key = entry; bucket_t *buckets = t->bucket_info->buckets; uint64_t hash = HASH_KEY(*t, key); - hdebug("Hash value (mod ", (int32_t)t->bucket_info->count, ") = ", hash, "\n"); bucket_t *bucket = &buckets[hash]; if (!bucket->occupied) { - hdebug("Got an empty space\n"); // Empty space: bucket->occupied = 1; bucket->index = index; bucket->next_bucket = END_OF_CHAIN; - hshow(t); return; } - hdebug("Collision detected in bucket ", hash, " (entry ", (uint32_t)bucket->index, ")\n"); - while (buckets[t->bucket_info->last_free].occupied) { assert(t->bucket_info->last_free > 0); --t->bucket_info->last_free; @@ -155,7 +125,6 @@ static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const uint64_t collided_hash = HASH_KEY(*t, GET_ENTRY(*t, bucket->index)); if (collided_hash != hash) { // Collided with a mid-chain entry - hdebug("Hit a mid-chain entry at bucket ", hash, " (chain starting at ", collided_hash, ")\n"); // Find chain predecessor uint64_t predecessor = collided_hash; while (buckets[predecessor].next_bucket != hash) @@ -169,19 +138,15 @@ static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const bucket->index = index; bucket->next_bucket = END_OF_CHAIN; } else { // Collided with the start of a chain, put the new entry in chain position #2 - hdebug("Hit start of a chain\n"); buckets[t->bucket_info->last_free] = (bucket_t){.occupied = 1, .index = index, .next_bucket = bucket->next_bucket}; bucket->next_bucket = t->bucket_info->last_free; } - hshow(t); } static void hashmap_resize_buckets(Table_t *t, uint32_t new_capacity, const TypeInfo_t *type) { if (unlikely(new_capacity > TABLE_MAX_BUCKETS)) fail("Table has exceeded the maximum table size (2^31) and cannot grow further!"); - hdebug("About to resize from ", t->bucket_info ? (int32_t)t->bucket_info->count : 0, " to ", new_capacity, "\n"); - hshow(t); size_t alloc_size = sizeof(bucket_info_t) + sizeof(bucket_t[new_capacity]); t->bucket_info = GC_MALLOC_ATOMIC(alloc_size); memset(t->bucket_info->buckets, 0, sizeof(bucket_t[new_capacity])); @@ -189,12 +154,8 @@ static void hashmap_resize_buckets(Table_t *t, uint32_t new_capacity, const Type t->bucket_info->last_free = new_capacity - 1; // Rehash: for (int64_t i = 0; i < (int64_t)Table$length(*t); i++) { - hdebug("Rehashing ", i, "\n"); Table$set_bucket(t, GET_ENTRY(*t, i), i, type); } - - hshow(t); - hdebug("Finished resizing\n"); } // Return address of value @@ -206,7 +167,6 @@ public void *Table$reserve(Table_t *t, const void *key, const void *value, const TypeInfo_t *type) { assert(type->tag == TableInfo); if (!t || !key) return NULL; - hshow(t); t->hash = 0; @@ -295,12 +255,10 @@ void Table$remove(Table_t *t, const void *key, const TypeInfo_t *type) { // maybe update lastfree_index1 to removed bucket's index uint64_t hash = HASH_KEY(*t, key); - hdebug("Removing key with hash ", hash, "\n"); bucket_t *bucket, *prev = NULL; for (uint64_t i = hash; t->bucket_info->buckets[i].occupied; i = t->bucket_info->buckets[i].next_bucket) { if (EQUAL_KEYS(GET_ENTRY(*t, t->bucket_info->buckets[i].index), key)) { bucket = &t->bucket_info->buckets[i]; - hdebug("Found key to delete in bucket ", i, "\n"); goto found_it; } if (t->bucket_info->buckets[i].next_bucket == END_OF_CHAIN) return; @@ -319,8 +277,6 @@ found_it:; // instead of O(N) int64_t last_entry = (int64_t)t->entries.length - 1; if (bucket->index != last_entry) { - hdebug("Removing key/value from the middle of the entries list\n"); - // Find the bucket that points to the last entry's index: uint64_t i = HASH_KEY(*t, GET_ENTRY(*t, last_entry)); while (t->bucket_info->buckets[i].index != last_entry) @@ -341,22 +297,17 @@ found_it:; int64_t bucket_to_clear; if (prev) { // Middle (or end) of a chain - hdebug("Removing from middle of a chain\n"); bucket_to_clear = (bucket - t->bucket_info->buckets); prev->next_bucket = bucket->next_bucket; } else if (bucket->next_bucket != END_OF_CHAIN) { // Start of a chain - hdebug("Removing from start of a chain\n"); bucket_to_clear = bucket->next_bucket; *bucket = t->bucket_info->buckets[bucket_to_clear]; } else { // Empty chain - hdebug("Removing from empty chain\n"); bucket_to_clear = (bucket - t->bucket_info->buckets); } t->bucket_info->buckets[bucket_to_clear] = (bucket_t){0}; if (bucket_to_clear > t->bucket_info->last_free) t->bucket_info->last_free = bucket_to_clear; - - hshow(t); } CONSTFUNC public void *Table$entry(Table_t t, int64_t n) { -- cgit v1.2.3 From 1a62de25c448d2661864ba25a98686ed506e66af Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 23 Dec 2025 15:28:05 -0500 Subject: Overhaul workflow to add automatic pushing to AUR and fix up mac builds --- .github/workflows/release.yml | 171 +++++++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 76 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2be7084e..0e4f08d0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,123 +7,142 @@ on: permissions: contents: write - packages: write + +env: + PKGNAME: tomo jobs: - linux-x86_64: - runs-on: ubuntu-latest + build-linux: + strategy: + matrix: + arch: + - x86_64 + - aarch64 + + include: + - arch: x86_64 + runner: ubuntu-latest + - arch: aarch64 + runner: ubuntu-24.04-arm64 + + runs-on: ${{ matrix.runner }} + steps: - uses: actions/checkout@v4 - name: Install deps run: | sudo apt-get update - sudo apt-get install -y \ - build-essential \ - libgmp-dev \ - libunistring-dev \ - libgc-dev \ - xxd \ - binutils + sudo apt-get install -y build-essential libgmp-dev libunistring-dev libgc-dev binutils - name: Build run: | - make clean make -j - name: Package run: | - TAG=${GITHUB_REF#refs/tags/} - tar -C build -czf tomo-linux-x86_64.tar.gz tomo@${TAG} - sha256sum tomo-linux-x86_64.tar.gz > tomo-linux-x86_64.tar.gz.sha256 + TAG=${GITHUB_REF_NAME} + FILE=${PKGNAME}-linux-${{ matrix.arch }}.tar.gz + tar -C build -czf "$FILE" ${PKGNAME}@${TAG} + sha256sum "$FILE" > "$FILE.sha256" - - name: Upload - uses: softprops/action-gh-release@v2 + - name: Upload artifacts + uses: actions/upload-artifact@v3 with: - files: | - tomo-linux-x86_64.tar.gz - tomo-linux-x86_64.tar.gz.sha256 + name: linux-${{ matrix.arch }} + path: | + ${PKGNAME}-linux-${{ matrix.arch }}.tar.gz + ${PKGNAME}-linux-${{ matrix.arch }}.tar.gz.sha256 - linux-aarch64: - runs-on: ubuntu-latest + build-macos: + runs-on: macos-latest steps: - uses: actions/checkout@v4 - name: Install deps run: | - sudo apt-get update - sudo apt-get install -y \ - build-essential \ - libgmp-dev \ - libunistring-dev \ - libgc-dev \ - xxd \ - binutils + brew update + brew install gmp libunistring bdw-gc llvm binutils - - name: Build + - name: Build arm64 run: | - make clean make -j - name: Package run: | - TAG=${GITHUB_REF#refs/tags/} - tar -C build -czf tomo-linux-aarch64.tar.gz tomo@${TAG} - sha256sum tomo-linux-aarch64.tar.gz > tomo-linux-aarch64.tar.gz.sha256 + TAG=${GITHUB_REF_NAME} + tar -C build -czf ${PKGNAME}-macos-arm64.tar.gz ${PKGNAME}@${TAG} + shasum -a 256 ${PKGNAME}-macos-arm64.tar.gz > ${PKGNAME}-macos-arm64.tar.gz.sha256 - - name: Upload + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: macos-arm64 + path: | + ${PKGNAME}-macos-arm64.tar.gz + ${PKGNAME}-macos-arm64.tar.gz.sha256 + + upload-release: + needs: [build-linux, build-macos] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Download all artifacts + uses: actions/download-artifact@v3 + with: + path: release/ + + - name: List artifacts + run: ls -l release/ + + - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: - files: | - tomo-linux-aarch64.tar.gz - tomo-linux-aarch64.tar.gz.sha256 + files: release/* - macos: - runs-on: macos-latest + aur: + needs: upload-release + runs-on: ubuntu-latest + env: + AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }} steps: - uses: actions/checkout@v4 - name: Install deps run: | - brew update - brew install gmp libunistring bdw-gc llvm binutils + sudo apt-get install -y pacman-contrib jq gh - - name: Build arm64 + - name: Set up SSH run: | - make clean - make -j \ - CC=clang \ - CFLAGS="-arch arm64" \ - LDFLAGS="-arch arm64" - mv build/tomo build/tomo-arm64 - - - name: Build x86_64 + mkdir -p ~/.ssh + echo "$AUR_SSH_KEY" > ~/.ssh/id_ed25519 + chmod 600 ~/.ssh/id_ed25519 + ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts + + - name: Wait for release assets run: | - make clean - make -j \ - CC=clang \ - CFLAGS="-arch x86_64" \ - LDFLAGS="-arch x86_64" - mv build/tomo build/tomo-x86_64 - - - name: Create universal binary + TAG=${GITHUB_REF_NAME} + for i in $(seq 60); do + if gh release view "$TAG" --json assets \ + | jq -e '[ .assets[].name ] | index("tomo-linux-x86_64.tar.gz") and index("tomo-linux-aarch64.tar.gz")' >/dev/null + then exit 0; fi + sleep 10 + done + echo "Timed out waiting for release assets" + exit 1 + + - name: Update PKGBUILD run: | - lipo -create \ - build/tomo-arm64 \ - build/tomo-x86_64 \ - -output build/tomo - lipo -info build/tomo - - - name: Package + TAG=${GITHUB_REF_NAME#v} + sed -i "s/^_tomo_version=.*/_tomo_version=${TAG}/" PKGBUILD + updpkgsums + makepkg --printsrcinfo > .SRCINFO + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + git diff --quiet || git commit -am "Release v${TAG}" + + - name: Push to AUR run: | - TAG=${GITHUB_REF#refs/tags/} - tar -C build -czf tomo-macos-universal.tar.gz tomo@${TAG} - shasum -a 256 tomo-macos-universal.tar.gz > tomo-macos-universal.tar.gz.sha256 - - - name: Upload - uses: softprops/action-gh-release@v2 - with: - files: | - tomo-macos-universal.tar.gz - tomo-macos-universal.tar.gz.sha256 + git push aur HEAD:master -- cgit v1.2.3 From b6366896db2c42ad5db92c16ea669bc0653f4878 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 23 Dec 2025 15:30:01 -0500 Subject: Fix makefile after merge --- Makefile | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 3bd9179f..a2d3480f 100644 --- a/Makefile +++ b/Makefile @@ -18,24 +18,14 @@ else include config.mk # Modified progress counter based on: https://stackoverflow.com/a/35320895 -# Only run counter if we're actually building (not for phony targets or no-ops) ifndef NO_PROGRESS ifndef ECHO -# Only count if building actual files, not just checking -ifneq ($(filter build all,$(MAKECMDGOALS)),) -T := $(shell $(MAKE) ECHO="COUNTTHIS" $(filter-out check-c-compiler check-libs,$(MAKECMDGOALS)) --no-print-directory \ - -nq 2>/dev/null | grep -c "COUNTTHIS") -ifeq ($(T),0) -ECHO = echo -else +T := $(shell $(MAKE) ECHO="COUNTTHIS" $(MAKECMDGOALS) --no-print-directory \ + -n | grep -c "COUNTTHIS") N := x C = $(words $N)$(eval N := x $N) ECHO = echo -e "[`expr $C '*' 100 / $T`%]" endif -else -ECHO = echo -endif -endif endif ifndef ECHO ECHO = echo -- cgit v1.2.3 From 44f822a3d52051986df6d7b0fbd5c2a2a1bf6cf5 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 23 Dec 2025 15:33:25 -0500 Subject: Add support for convert_to_text(Int_t) --- src/stdlib/text.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stdlib/text.h b/src/stdlib/text.h index 776ea8ec..08423cfe 100644 --- a/src/stdlib/text.h +++ b/src/stdlib/text.h @@ -45,7 +45,8 @@ static inline Text_t Text_from_text(Text_t t) { return t; } int32_t: Int32$value_as_text, \ int64_t: Int64$value_as_text, \ double: Num$value_as_text, \ - float: Num32$value_as_text)(x) + float: Num32$value_as_text, \ + Int_t: Int$value_as_text)(x) Text_t Text$_concat(int n, Text_t items[n]); #define Text$concat(...) Text$_concat(sizeof((Text_t[]){__VA_ARGS__}) / sizeof(Text_t), (Text_t[]){__VA_ARGS__}) -- cgit v1.2.3 From 51462a1cf52a9ad955bd35a85add955ef312dff2 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 23 Dec 2025 18:26:40 -0500 Subject: Support expanding CFLAGS --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a2d3480f..86bf1a1b 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,7 @@ O=-O3 # Note: older versions of Make have buggy behavior with hash marks inside strings, so this ugly code is necessary: TOMO_VERSION=$(shell awk 'BEGIN{hashes=sprintf("%c%c",35,35)} $$1==hashes {print $$2; exit}' CHANGES.md) GIT_VERSION=$(shell git log -1 --pretty=format:"%as_%h" 2>/dev/null || echo "unknown") -CFLAGS=$(CCONFIG) $(INCLUDE_DIRS) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS) $(LTO) \ +CFLAGS+=$(CCONFIG) $(INCLUDE_DIRS) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS) $(LTO) \ -DSUDO='"$(SUDO)"' -DDEFAULT_C_COMPILER='"$(DEFAULT_C_COMPILER)"' \ -DGIT_VERSION='"$(GIT_VERSION)"' -ffunction-sections -fdata-sections CFLAGS_PLACEHOLDER="$$(printf '\033[2m\033[m\n')" -- cgit v1.2.3 From fccd03d7e79fcca9c63fcb122da6a0844d0f9d58 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 23 Dec 2025 18:27:03 -0500 Subject: Add badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 61062bfc..bfb72e18 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Release binaries](https://github.com/bruce-hill/tomo/actions/workflows/release.yml/badge.svg)](https://github.com/bruce-hill/tomo/releases) + # Tomo - Tomorrow's Language Tomo is a statically typed, safe, simple, lightweight, efficient programming -- cgit v1.2.3 From 1c13267ae3ca293d634e34074f2c8c5197062a86 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 23 Dec 2025 18:27:25 -0500 Subject: Fix for openbsd ifdef --- src/stdlib/stdlib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c index 023791a2..7b038dac 100644 --- a/src/stdlib/stdlib.c +++ b/src/stdlib/stdlib.c @@ -50,7 +50,7 @@ const char *TOMO_VERSION = "v0"; public Text_t TOMO_VERSION_TEXT = Text("v0"); -#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #include static inline const char *get_library_path(void *func) { -- cgit v1.2.3 From f0bc32eb252d6f04d7aaa8a7f34e9e00d27d070f Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 23 Dec 2025 18:32:49 -0500 Subject: Fix `cp` behavior for copying symlinks --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 86bf1a1b..bd4bc1dc 100644 --- a/Makefile +++ b/Makefile @@ -248,7 +248,7 @@ install-files: build check-utilities $(SUDO) -u $(OWNER) $(MAKE) install-files; \ exit 0; \ fi; \ - cp -r $(BUILD_DIR)/* $(PREFIX)/ + cp -R $(BUILD_DIR)/* $(PREFIX)/ install: install-files -- cgit v1.2.3 From bd53e3b52f7599900a11c1766fe806c50c267bc2 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 23 Dec 2025 18:44:18 -0500 Subject: Fix some GCC-specific logic that works differently on mac/clang --- src/tomo.c | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/tomo.c b/src/tomo.c index b8588566..ceee2518 100644 --- a/src/tomo.c +++ b/src/tomo.c @@ -71,6 +71,7 @@ static const char *paths_str(List_t paths) { static OptionalBool_t verbose = false, quiet = false, show_version = false, show_prefix = false, clean_build = false, source_mapping = true, should_install = false; +static bool is_gcc = false, is_clang = false; static List_t format_files = EMPTY_LIST, format_files_inplace = EMPTY_LIST, parse_files = EMPTY_LIST, transpile_files = EMPTY_LIST, compile_objects = EMPTY_LIST, compile_executables = EMPTY_LIST, @@ -250,19 +251,20 @@ int main(int argc, char *argv[]) { return 0; } - bool is_gcc = (system(String(cc, " -v 2>&1 | grep -q 'gcc version'")) == 0); + is_gcc = (system(String(cc, " -v 2>&1 | grep -q 'gcc version'")) == 0); if (is_gcc) { cflags = Texts(cflags, Text(" -fsanitize=signed-integer-overflow -fno-sanitize-recover" " -fno-signaling-nans -fno-trapping-math -fno-finite-math-only")); } - bool is_clang = (system(String(cc, " -v 2>&1 | grep -q 'clang version'")) == 0); + is_clang = (system(String(cc, " -v 2>&1 | grep -q 'clang version'")) == 0); if (is_clang) { cflags = Texts(cflags, Text(" -Wno-parentheses-equality")); } - ldflags = - Texts("-Wl,-rpath,'", TOMO_PATH, "/lib' ", ldflags, " -ffunction-sections -fdata-sections -Wl,--gc-sections"); + ldflags = Texts("-Wl,-rpath,'", TOMO_PATH, "/lib' ", ldflags, " -ffunction-sections -fdata-sections"); + if (is_gcc) ldflags = Texts(ldflags, " -Wl,--gc-sections"); + else if (is_clang) ldflags = Texts(ldflags, " -Wl,-dead_strip"); #ifdef __APPLE__ cflags = Texts(cflags, Text(" -I/opt/homebrew/include")); @@ -936,23 +938,24 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t } } - FILE *runner = run_cmd(cc, " ", - // C flags: - cflags, " -O", optimization, " ", - // Linker flags and dynamically linked shared libraries: - ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), " ", - // Object files: - paths_str(object_files), " ", - // Input file: - runner_file, - // Statically linked archive files (must come after runner): - // Libraries are grouped to allow for circular dependencies among - // the libraries that are used. - " -Wl,--start-group ", list_text(archives), " -Wl,--end-group ", - // Tomo static library: - TOMO_PATH, "/lib/libtomo@", TOMO_VERSION, ".a", - // Output file: - " -o ", exe_path); + FILE *runner = run_cmd( + cc, + // C flags: + " ", cflags, " -O", optimization, + // Linker flags and dynamically linked shared libraries: + " ", ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), " ", + // Object files: + paths_str(object_files), + // Input file: + " ", runner_file, + // Statically linked archive files (must come after runner): + // Libraries are grouped to allow for circular dependencies among + // the libraries that are used. + " ", is_gcc ? Texts("-Wl,--start-group ", list_text(archives), " -Wl,--end-group") : list_text(archives), + // Tomo static library: + " ", TOMO_PATH, "/lib/libtomo@", TOMO_VERSION, ".a", + // Output file: + " -o ", exe_path); if (show_codegen.length > 0) { FILE *out = run_cmd(show_codegen); -- cgit v1.2.3 From 7d09323e379c3ce1ac5c476a09b4409f90d3a33d Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 23 Dec 2025 18:45:28 -0500 Subject: Update changelog --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 6f8f5e16..529711a3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Version History +## v2025-12-23.2 + +- Fixes for OpenBSD and Mac. + ## v2025-12-23 - Improved C preprocessing performance by eliminating expensive macro calls. -- cgit v1.2.3 From 1f35ffa018f9e870fa21d0ffbca15d99e23233de Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Wed, 24 Dec 2025 12:02:58 -0500 Subject: Remove unused script --- embed_file.sh | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100755 embed_file.sh diff --git a/embed_file.sh b/embed_file.sh deleted file mode 100755 index 503fee80..00000000 --- a/embed_file.sh +++ /dev/null @@ -1,10 +0,0 @@ -#/bin/sh -awk ' - BEGIN { printf "const char *'$1' = " } - { - gsub(/\\/,"\\\\"); - gsub(/"/,"\\\""); - printf "\"%s\\n\"\n", $0 - } - END { print ";" } -' -- cgit v1.2.3 From b80e21ce3d673d981e44a725c62b62563a77db9b Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 28 Dec 2025 17:20:58 -0500 Subject: Bugfix for Table.clear() --- src/stdlib/tables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stdlib/tables.c b/src/stdlib/tables.c index d417545d..adbdd6bd 100644 --- a/src/stdlib/tables.c +++ b/src/stdlib/tables.c @@ -316,7 +316,7 @@ CONSTFUNC public void *Table$entry(Table_t t, int64_t n) { } public -void Table$clear(Table_t *t) { memset(t, 0, sizeof(Table_t)); } +void Table$clear(Table_t *t) { *t = EMPTY_TABLE; } public Table_t Table$sorted(Table_t t, const TypeInfo_t *type) { -- cgit v1.2.3 From cfce376f585e0cd0231e95843617f75bd65b6c07 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 28 Dec 2025 17:27:05 -0500 Subject: Change autoformatter to no longer allow single-line functions --- .clang-format | 2 +- src/ast.c | 8 ++++++-- src/compile/blocks.c | 4 +++- src/compile/promotions.c | 4 +++- src/compile/text.h | 8 ++++++-- src/environment.c | 4 +++- src/parse/expressions.c | 4 +++- src/parse/utils.c | 8 ++++++-- src/stdlib/bigint.c | 4 +++- src/stdlib/bigint.h | 24 ++++++++++++++++++------ src/stdlib/bools.h | 24 ++++++++++++++++++------ src/stdlib/bytes.c | 8 ++++++-- src/stdlib/bytes.h | 8 ++++++-- src/stdlib/intX.c.h | 8 ++++++-- src/stdlib/intX.h | 44 +++++++++++++++++++++++++++++++++----------- src/stdlib/lists.c | 12 +++++++++--- src/stdlib/numX.c.h | 20 +++++++++++++++----- src/stdlib/numX.h | 24 ++++++++++++++++++------ src/stdlib/paths.c | 16 ++++++++++++---- src/stdlib/print.h | 16 ++++++++++++---- src/stdlib/stdlib.c | 12 +++++++++--- src/stdlib/tables.c | 32 ++++++++++++++++++++++---------- src/stdlib/tables.h | 2 +- src/stdlib/text.c | 12 +++++++++--- src/stdlib/text.h | 4 +++- src/types.c | 8 ++++++-- 26 files changed, 237 insertions(+), 83 deletions(-) diff --git a/.clang-format b/.clang-format index 82e3ce3b..1fabd157 100644 --- a/.clang-format +++ b/.clang-format @@ -71,7 +71,7 @@ AllowShortCaseExpressionOnASingleLine: true AllowShortCaseLabelsOnASingleLine: true AllowShortCompoundRequirementOnASingleLine: true AllowShortEnumsOnASingleLine: true -AllowShortFunctionsOnASingleLine: All +AllowShortFunctionsOnASingleLine: false AllowShortIfStatementsOnASingleLine: AllIfsAndElse AllowShortLambdasOnASingleLine: All AllowShortLoopsOnASingleLine: false diff --git a/src/ast.c b/src/ast.c index e87ca005..69c55327 100644 --- a/src/ast.c +++ b/src/ast.c @@ -85,7 +85,9 @@ static Text_t tags_to_sexp(tag_ast_t *tags); static Text_t optional_sexp(const char *tag, ast_t *ast); static Text_t optional_type_sexp(const char *tag, type_ast_t *ast); -static Text_t quoted_text(const char *text) { return Text$quoted(Text$from_str(text), false, Text("\"")); } +static Text_t quoted_text(const char *text) { + return Text$quoted(Text$from_str(text), false, Text("\"")); +} Text_t ast_list_to_sexp(ast_list_t *asts) { Text_t c = EMPTY_TEXT; @@ -281,7 +283,9 @@ Text_t ast_to_sexp(ast_t *ast) { } } -const char *ast_to_sexp_str(ast_t *ast) { return Text$as_c_string(ast_to_sexp(ast)); } +const char *ast_to_sexp_str(ast_t *ast) { + return Text$as_c_string(ast_to_sexp(ast)); +} OptionalText_t ast_source(ast_t *ast) { if (ast == NULL || ast->start == NULL || ast->end == NULL) return NONE_TEXT; diff --git a/src/compile/blocks.c b/src/compile/blocks.c index 1059fd34..66869ecc 100644 --- a/src/compile/blocks.c +++ b/src/compile/blocks.c @@ -9,7 +9,9 @@ #include "compilation.h" public -Text_t compile_block(env_t *env, ast_t *ast) { return Texts("{\n", compile_inline_block(env, ast), "}\n"); } +Text_t compile_block(env_t *env, ast_t *ast) { + return Texts("{\n", compile_inline_block(env, ast), "}\n"); +} Text_t compile_block_expression(env_t *env, ast_t *ast) { ast_list_t *stmts = Match(ast, Block)->statements; diff --git a/src/compile/promotions.c b/src/compile/promotions.c index 482e5ed0..7a169821 100644 --- a/src/compile/promotions.c +++ b/src/compile/promotions.c @@ -8,7 +8,9 @@ #include "../types.h" #include "compilation.h" -static Text_t quoted_str(const char *str) { return Text$quoted(Text$from_str(str), false, Text("\"")); } +static Text_t quoted_str(const char *str) { + return Text$quoted(Text$from_str(str), false, Text("\"")); +} public bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t *needed) { diff --git a/src/compile/text.h b/src/compile/text.h index c160c7a9..ae3cc5c3 100644 --- a/src/compile/text.h +++ b/src/compile/text.h @@ -14,5 +14,9 @@ Text_t compile_text(env_t *env, ast_t *ast, Text_t color); Text_t compile_text_literal(Text_t literal); Text_t expr_as_text(Text_t expr, type_t *t, Text_t color); -MACROLIKE Text_t quoted_str(const char *str) { return Text$quoted(Text$from_str(str), false, Text("\"")); } -MACROLIKE Text_t quoted_text(Text_t text) { return Text$quoted(text, false, Text("\"")); } +MACROLIKE Text_t quoted_str(const char *str) { + return Text$quoted(Text$from_str(str), false, Text("\"")); +} +MACROLIKE Text_t quoted_text(Text_t text) { + return Text$quoted(text, false, Text("\"")); +} diff --git a/src/environment.c b/src/environment.c index a82274e7..b3009a5e 100644 --- a/src/environment.c +++ b/src/environment.c @@ -715,7 +715,9 @@ env_t *namespace_env(env_t *env, const char *namespace_name) { return ns_env; } -PUREFUNC binding_t *get_binding(env_t *env, const char *name) { return Table$str_get(*env->locals, name); } +PUREFUNC binding_t *get_binding(env_t *env, const char *name) { + return Table$str_get(*env->locals, name); +} binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name) { type_t *self_type = get_type(env, self); diff --git a/src/parse/expressions.c b/src/parse/expressions.c index d031c49f..27e44129 100644 --- a/src/parse/expressions.c +++ b/src/parse/expressions.c @@ -195,7 +195,9 @@ ast_t *parse_term(parse_ctx_t *ctx, const char *pos) { return term; } -ast_t *parse_expr(parse_ctx_t *ctx, const char *pos) { return parse_infix_expr(ctx, pos, 0); } +ast_t *parse_expr(parse_ctx_t *ctx, const char *pos) { + return parse_infix_expr(ctx, pos, 0); +} ast_t *parse_extended_expr(parse_ctx_t *ctx, const char *pos) { ast_t *expr = NULL; diff --git a/src/parse/utils.c b/src/parse/utils.c index f1b518ca..03e0ebcd 100644 --- a/src/parse/utils.c +++ b/src/parse/utils.c @@ -41,7 +41,9 @@ size_t some_not(const char **pos, const char *forbid) { return len; } -size_t spaces(const char **pos) { return some_of(pos, " \t"); } +size_t spaces(const char **pos) { + return some_of(pos, " \t"); +} void whitespace(parse_ctx_t *ctx, const char **pos) { while (some_of(pos, " \t\r\n") || comment(ctx, pos)) @@ -93,7 +95,9 @@ const char *get_id(const char **inout) { return word; } -PUREFUNC const char *eol(const char *str) { return str + strcspn(str, "\r\n"); } +PUREFUNC const char *eol(const char *str) { + return str + strcspn(str, "\r\n"); +} bool comment(parse_ctx_t *ctx, const char **pos) { if ((*pos)[0] == '#') { diff --git a/src/stdlib/bigint.c b/src/stdlib/bigint.c index 2d145bd5..c9bfb68e 100644 --- a/src/stdlib/bigint.c +++ b/src/stdlib/bigint.c @@ -393,7 +393,9 @@ PUREFUNC Closure_t Int$onward(Int_t first, Int_t step) { } public -Int_t Int$from_str(const char *str) { return Int$parse(Text$from_str(str), NONE_INT, NULL); } +Int_t Int$from_str(const char *str) { + return Int$parse(Text$from_str(str), NONE_INT, NULL); +} public OptionalInt_t Int$parse(Text_t text, OptionalInt_t base, Text_t *remainder) { diff --git a/src/stdlib/bigint.h b/src/stdlib/bigint.h index 9ce4c800..8c3502bf 100644 --- a/src/stdlib/bigint.h +++ b/src/stdlib/bigint.h @@ -195,18 +195,30 @@ MACROLIKE PUREFUNC Int_t Int$from_num64(double n, bool truncate) { if (!truncate && unlikely(mpz_get_d(result) != n)) fail("Could not convert to an integer without truncation: ", n); return Int$from_mpz(result); } -MACROLIKE PUREFUNC Int_t Int$from_num32(float n, bool truncate) { return Int$from_num64((double)n, truncate); } +MACROLIKE PUREFUNC Int_t Int$from_num32(float n, bool truncate) { + return Int$from_num64((double)n, truncate); +} MACROLIKE Int_t Int$from_int64(int64_t i) { if likely (i >= SMALLEST_SMALL_INT && i <= BIGGEST_SMALL_INT) return (Int_t){.small = (i << 2L) | 1L}; mpz_t result; mpz_init_set_si(result, i); return Int$from_mpz(result); } -MACROLIKE CONSTFUNC Int_t Int$from_int32(Int32_t i) { return Int$from_int64((Int32_t)i); } -MACROLIKE CONSTFUNC Int_t Int$from_int16(Int16_t i) { return I_small(i); } -MACROLIKE CONSTFUNC Int_t Int$from_int8(Int8_t i) { return I_small(i); } -MACROLIKE CONSTFUNC Int_t Int$from_byte(Byte_t b) { return I_small(b); } -MACROLIKE CONSTFUNC Int_t Int$from_bool(Bool_t b) { return I_small(b); } +MACROLIKE CONSTFUNC Int_t Int$from_int32(Int32_t i) { + return Int$from_int64((Int32_t)i); +} +MACROLIKE CONSTFUNC Int_t Int$from_int16(Int16_t i) { + return I_small(i); +} +MACROLIKE CONSTFUNC Int_t Int$from_int8(Int8_t i) { + return I_small(i); +} +MACROLIKE CONSTFUNC Int_t Int$from_byte(Byte_t b) { + return I_small(b); +} +MACROLIKE CONSTFUNC Int_t Int$from_bool(Bool_t b) { + return I_small(b); +} #ifdef __GNUC__ #pragma GCC diagnostic pop diff --git a/src/stdlib/bools.h b/src/stdlib/bools.h index 52bd45a8..17ff3681 100644 --- a/src/stdlib/bools.h +++ b/src/stdlib/bools.h @@ -13,11 +13,23 @@ PUREFUNC Text_t Bool$as_text(const void *b, bool colorize, const TypeInfo_t *type); OptionalBool_t Bool$parse(Text_t text, Text_t *remainder); -MACROLIKE Bool_t Bool$from_int(Int_t i) { return (i.small != 0); } -MACROLIKE Bool_t Bool$from_int64(Int64_t i) { return (i != 0); } -MACROLIKE Bool_t Bool$from_int32(Int32_t i) { return (i != 0); } -MACROLIKE Bool_t Bool$from_int16(Int16_t i) { return (i != 0); } -MACROLIKE Bool_t Bool$from_int8(Int8_t i) { return (i != 0); } -MACROLIKE Bool_t Bool$from_byte(uint8_t b) { return (b != 0); } +MACROLIKE Bool_t Bool$from_int(Int_t i) { + return (i.small != 0); +} +MACROLIKE Bool_t Bool$from_int64(Int64_t i) { + return (i != 0); +} +MACROLIKE Bool_t Bool$from_int32(Int32_t i) { + return (i != 0); +} +MACROLIKE Bool_t Bool$from_int16(Int16_t i) { + return (i != 0); +} +MACROLIKE Bool_t Bool$from_int8(Int8_t i) { + return (i != 0); +} +MACROLIKE Bool_t Bool$from_byte(uint8_t b) { + return (b != 0); +} extern const TypeInfo_t Bool$info; diff --git a/src/stdlib/bytes.c b/src/stdlib/bytes.c index a1e953a0..2b8dab4d 100644 --- a/src/stdlib/bytes.c +++ b/src/stdlib/bytes.c @@ -30,7 +30,9 @@ PUREFUNC public Text_t Byte$as_text(const void *b, bool colorize, const TypeInfo } public -CONSTFUNC bool Byte$is_between(const Byte_t x, const Byte_t low, const Byte_t high) { return low <= x && x <= high; } +CONSTFUNC bool Byte$is_between(const Byte_t x, const Byte_t low, const Byte_t high) { + return low <= x && x <= high; +} public OptionalByte_t Byte$parse(Text_t text, OptionalInt_t base, Text_t *remainder) { @@ -67,7 +69,9 @@ public bool Byte$get_bit(Byte_t x, Int_t bit_index) { if (Int$compare_value(bit_index, I(1)) < 0) fail("Invalid bit index (expected 1 or higher): ", bit_index); if (Int$compare_value(bit_index, I(8)) > 0) - fail("Bit index is too large! There are only 8 bits in a byte, but index is: ", bit_index); + fail("Bit index is too large! There are only 8 bits in a byte, but index " + "is: ", + bit_index); return ((x & (Byte_t)(1L << (Int64$from_int(bit_index, true) - 1L))) != 0); } diff --git a/src/stdlib/bytes.h b/src/stdlib/bytes.h index 6581f300..b0a1c213 100644 --- a/src/stdlib/bytes.h +++ b/src/stdlib/bytes.h @@ -21,8 +21,12 @@ Byte_t Byte$from_int16(int16_t i, bool truncate); OptionalByte_t Byte$parse(Text_t text, OptionalInt_t base, Text_t *remainder); Closure_t Byte$to(Byte_t first, Byte_t last, OptionalInt8_t step); -MACROLIKE Byte_t Byte$from_int8(int8_t i) { return (Byte_t)i; } -MACROLIKE Byte_t Byte$from_bool(bool b) { return (Byte_t)b; } +MACROLIKE Byte_t Byte$from_int8(int8_t i) { + return (Byte_t)i; +} +MACROLIKE Byte_t Byte$from_bool(bool b) { + return (Byte_t)b; +} CONSTFUNC bool Byte$is_between(const Byte_t x, const Byte_t low, const Byte_t high); extern const Byte_t Byte$min; diff --git a/src/stdlib/intX.c.h b/src/stdlib/intX.c.h index 03322e5b..3e6648f7 100644 --- a/src/stdlib/intX.c.h +++ b/src/stdlib/intX.c.h @@ -101,7 +101,9 @@ Text_t NAMESPACED(as_text)(const void *i, bool colorize, const TypeInfo_t *info) return colorize ? Text$concat(Text("\033[35m"), text, Text("\033[m")) : text; } public -Text_t NAMESPACED(value_as_text)(INT_T i) { return _int64_to_text((int64_t)i); } +Text_t NAMESPACED(value_as_text)(INT_T i) { + return _int64_to_text((int64_t)i); +} public PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *info) { (void)info; @@ -117,7 +119,9 @@ CONSTFUNC bool NAMESPACED(is_between)(const INT_T x, const INT_T low, const INT_ return low <= x && x <= high; } public -CONSTFUNC INT_T NAMESPACED(clamped)(INT_T x, INT_T min, INT_T max) { return x < min ? min : (x > max ? max : x); } +CONSTFUNC INT_T NAMESPACED(clamped)(INT_T x, INT_T min, INT_T max) { + return x < min ? min : (x > max ? max : x); +} public Text_t NAMESPACED(hex)(INT_T i, Int_t digits_int, bool uppercase, bool prefix) { Int_t as_int = Int$from_int64((int64_t)i); diff --git a/src/stdlib/intX.h b/src/stdlib/intX.h index 1c8b4a05..3c4fa976 100644 --- a/src/stdlib/intX.h +++ b/src/stdlib/intX.h @@ -49,8 +49,12 @@ Closure_t NAMESPACED(onward)(INTX_T first, INTX_T step); PUREFUNC OPT_T NAMESPACED(parse)(Text_t text, OptionalInt_t base, Text_t *remainder); CONSTFUNC bool NAMESPACED(is_between)(const INTX_T x, const INTX_T low, const INTX_T high); CONSTFUNC INTX_T NAMESPACED(clamped)(INTX_T x, INTX_T min, INTX_T max); -MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_byte)(Byte_t b) { return (INTX_T)b; } -MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_bool)(Bool_t b) { return (INTX_T)b; } +MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_byte)(Byte_t b) { + return (INTX_T)b; +} +MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_bool)(Bool_t b) { + return (INTX_T)b; +} CONSTFUNC INTX_T NAMESPACED(gcd)(INTX_T x, INTX_T y); extern const INTX_T NAMESPACED(min), NAMESPACED(max); extern const TypeInfo_t NAMESPACED(info); @@ -75,15 +79,25 @@ MACROLIKE INTX_T NAMESPACED(modulo)(INTX_T D, INTX_T d) { return r; } -MACROLIKE INTX_T NAMESPACED(modulo1)(INTX_T D, INTX_T d) { return NAMESPACED(modulo)(D - 1, d) + 1; } +MACROLIKE INTX_T NAMESPACED(modulo1)(INTX_T D, INTX_T d) { + return NAMESPACED(modulo)(D - 1, d) + 1; +} -MACROLIKE PUREFUNC INTX_T NAMESPACED(wrapping_plus)(INTX_T x, INTX_T y) { return (INTX_T)((UINTX_T)x + (UINTX_T)y); } +MACROLIKE PUREFUNC INTX_T NAMESPACED(wrapping_plus)(INTX_T x, INTX_T y) { + return (INTX_T)((UINTX_T)x + (UINTX_T)y); +} -MACROLIKE PUREFUNC INTX_T NAMESPACED(wrapping_minus)(INTX_T x, INTX_T y) { return (INTX_T)((UINTX_T)x + (UINTX_T)y); } +MACROLIKE PUREFUNC INTX_T NAMESPACED(wrapping_minus)(INTX_T x, INTX_T y) { + return (INTX_T)((UINTX_T)x + (UINTX_T)y); +} -MACROLIKE PUREFUNC INTX_T NAMESPACED(unsigned_left_shifted)(INTX_T x, INTX_T y) { return (INTX_T)((UINTX_T)x << y); } +MACROLIKE PUREFUNC INTX_T NAMESPACED(unsigned_left_shifted)(INTX_T x, INTX_T y) { + return (INTX_T)((UINTX_T)x << y); +} -MACROLIKE PUREFUNC INTX_T NAMESPACED(unsigned_right_shifted)(INTX_T x, INTX_T y) { return (INTX_T)((UINTX_T)x >> y); } +MACROLIKE PUREFUNC INTX_T NAMESPACED(unsigned_right_shifted)(INTX_T x, INTX_T y) { + return (INTX_T)((UINTX_T)x >> y); +} void NAMESPACED(serialize)(const void *obj, FILE *out, Table_t *, const TypeInfo_t *); void NAMESPACED(deserialize)(FILE *in, void *outval, List_t *, const TypeInfo_t *); @@ -121,7 +135,9 @@ MACROLIKE PUREFUNC INTX_T NAMESPACED(from_int64)(Int64_t i64, bool truncate) { return i; } #elif INTX_H__INT_BITS > 64 -MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int64)(Int64_t i) { return (INTX_T)i; } +MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int64)(Int64_t i) { + return (INTX_T)i; +} #endif #if INTX_H__INT_BITS < 32 @@ -131,7 +147,9 @@ MACROLIKE PUREFUNC INTX_T NAMESPACED(from_int32)(Int32_t i32, bool truncate) { return i; } #elif INTX_H__INT_BITS > 32 -MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int32)(Int32_t i) { return (INTX_T)i; } +MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int32)(Int32_t i) { + return (INTX_T)i; +} #endif #if INTX_H__INT_BITS < 16 @@ -141,11 +159,15 @@ MACROLIKE PUREFUNC INTX_T NAMESPACED(from_int16)(Int16_t i16, bool truncate) { return i; } #elif INTX_H__INT_BITS > 16 -MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int16)(Int16_t i) { return (INTX_T)i; } +MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int16)(Int16_t i) { + return (INTX_T)i; +} #endif #if INTX_H__INT_BITS > 8 -MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int8)(Int8_t i) { return (INTX_T)i; } +MACROLIKE CONSTFUNC INTX_T NAMESPACED(from_int8)(Int8_t i) { + return (INTX_T)i; +} #endif #undef PASTE3_ diff --git a/src/stdlib/lists.c b/src/stdlib/lists.c index db514671..9753cd86 100644 --- a/src/stdlib/lists.c +++ b/src/stdlib/lists.c @@ -440,10 +440,14 @@ List_t List$sample(List_t list, Int_t int_n, List_t weights, OptionalClosure_t r } public -List_t List$from(List_t list, Int_t first) { return List$slice(list, first, I_small(-1)); } +List_t List$from(List_t list, Int_t first) { + return List$slice(list, first, I_small(-1)); +} public -List_t List$to(List_t list, Int_t last) { return List$slice(list, I_small(1), last); } +List_t List$to(List_t list, Int_t last) { + return List$slice(list, I_small(1), last); +} public List_t List$by(List_t list, Int_t int_stride, int64_t padded_item_size) { @@ -554,7 +558,9 @@ bool List$has(List_t list, void *item, const TypeInfo_t *type) { } public -void List$clear(List_t *list) { *list = list->atomic ? EMPTY_ATOMIC_LIST : EMPTY_LIST; } +void List$clear(List_t *list) { + *list = list->atomic ? EMPTY_ATOMIC_LIST : EMPTY_LIST; +} public int32_t List$compare(const void *vx, const void *vy, const TypeInfo_t *type) { diff --git a/src/stdlib/numX.c.h b/src/stdlib/numX.c.h index 2fde8c45..ed91fbf5 100644 --- a/src/stdlib/numX.c.h +++ b/src/stdlib/numX.c.h @@ -66,7 +66,9 @@ PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInf } #elif NUMX_C_H__BITS == 32 public -PUREFUNC Text_t NAMESPACED(value_as_text)(NUM_T x) { return Num$value_as_text((double)x); } +PUREFUNC Text_t NAMESPACED(value_as_text)(NUM_T x) { + return Num$value_as_text((double)x); +} public PUREFUNC Text_t NAMESPACED(as_text)(const void *x, bool colorize, const TypeInfo_t *info) { (void)info; @@ -147,7 +149,9 @@ CONSTFUNC NUM_T NAMESPACED(mod1)(NUM_T num, NUM_T modulus) { } public -CONSTFUNC NUM_T NAMESPACED(mix)(NUM_T amount, NUM_T x, NUM_T y) { return (SUFFIXED(1.0) - amount) * x + amount * y; } +CONSTFUNC NUM_T NAMESPACED(mix)(NUM_T amount, NUM_T x, NUM_T y) { + return (SUFFIXED(1.0) - amount) * x + amount * y; +} public CONSTFUNC bool NAMESPACED(is_between)(const NUM_T x, const NUM_T low, const NUM_T high) { @@ -184,11 +188,17 @@ CONSTFUNC bool NAMESPACED(is_none)(const void *n, const TypeInfo_t *info) { } public -CONSTFUNC bool NAMESPACED(isinf)(NUM_T n) { return (fpclassify(n) == FP_INFINITE); } +CONSTFUNC bool NAMESPACED(isinf)(NUM_T n) { + return (fpclassify(n) == FP_INFINITE); +} public -CONSTFUNC bool NAMESPACED(finite)(NUM_T n) { return (fpclassify(n) != FP_INFINITE); } +CONSTFUNC bool NAMESPACED(finite)(NUM_T n) { + return (fpclassify(n) != FP_INFINITE); +} public -CONSTFUNC bool NAMESPACED(isnan)(NUM_T n) { return (fpclassify(n) == FP_NAN); } +CONSTFUNC bool NAMESPACED(isnan)(NUM_T n) { + return (fpclassify(n) == FP_NAN); +} public const TypeInfo_t NAMESPACED(info) = { diff --git a/src/stdlib/numX.h b/src/stdlib/numX.h index 3d65cb59..87794762 100644 --- a/src/stdlib/numX.h +++ b/src/stdlib/numX.h @@ -53,9 +53,13 @@ CONSTFUNC bool NAMESPACED(is_between)(const NUM_T x, const NUM_T low, const NUM_ CONSTFUNC NUM_T NAMESPACED(clamped)(NUM_T x, NUM_T low, NUM_T high); #if NUMX_H__BITS == 64 -MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_num32)(float n) { return (NUM_T)n; } +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_num32)(float n) { + return (NUM_T)n; +} #elif NUMX_H__BITS == 32 -MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_num64)(double n) { return (NUM_T)n; } +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_num64)(double n) { + return (NUM_T)n; +} #endif #ifdef __GNUC__ @@ -88,10 +92,18 @@ MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int64)(Int64_t i, bool truncate) { fail("Could not convert integer to " TYPE_STR " without losing precision: ", i); return n; } -MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int32)(Int32_t i) { return (NUM_T)i; } -MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int16)(Int16_t i) { return (NUM_T)i; } -MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int8)(Int8_t i) { return (NUM_T)i; } -MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_byte)(Byte_t i) { return (NUM_T)i; } +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int32)(Int32_t i) { + return (NUM_T)i; +} +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int16)(Int16_t i) { + return (NUM_T)i; +} +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_int8)(Int8_t i) { + return (NUM_T)i; +} +MACROLIKE CONSTFUNC NUM_T NAMESPACED(from_byte)(Byte_t i) { + return (NUM_T)i; +} extern const TypeInfo_t NAMESPACED(info); diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c index e3028cce..cbe52f5f 100644 --- a/src/stdlib/paths.c +++ b/src/stdlib/paths.c @@ -98,7 +98,9 @@ Path_t Path$from_str(const char *str) { } public -Path_t Path$from_text(Text_t text) { return Path$from_str(Text$as_c_string(text)); } +Path_t Path$from_text(Text_t text) { + return Path$from_str(Text$as_c_string(text)); +} public Path_t Path$expand_home(Path_t path) { @@ -645,7 +647,9 @@ OptionalPath_t Path$write_unique_bytes(Path_t path, List_t bytes) { } public -OptionalPath_t Path$write_unique(Path_t path, Text_t text) { return Path$write_unique_bytes(path, Text$utf8(text)); } +OptionalPath_t Path$write_unique(Path_t path, Text_t text) { + return Path$write_unique_bytes(path, Text$utf8(text)); +} public OptionalPath_t Path$parent(Path_t path) { @@ -709,7 +713,9 @@ Path_t Path$child(Path_t path, Text_t name) { } public -Path_t Path$sibling(Path_t path, Text_t name) { return Path$child(Path$parent(path), name); } +Path_t Path$sibling(Path_t path, Text_t name) { + return Path$child(Path$parent(path), name); +} public OptionalPath_t Path$with_extension(Path_t path, Text_t extension, bool replace) { @@ -865,7 +871,9 @@ int Path$print(FILE *f, Path_t path) { } public -const char *Path$as_c_string(Path_t path) { return String(path); } +const char *Path$as_c_string(Path_t path) { + return String(path); +} public Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type) { diff --git a/src/stdlib/print.h b/src/stdlib/print.h index 7106d561..943a084a 100644 --- a/src/stdlib/print.h +++ b/src/stdlib/print.h @@ -80,10 +80,18 @@ int _print_double(FILE *f, double x); int _print_hex(FILE *f, hex_format_t hex); int _print_hex_double(FILE *f, hex_double_t hex); int _print_oct(FILE *f, oct_format_t oct); -PRINT_FN _print_float(FILE *f, float x) { return _print_double(f, (double)x); } -PRINT_FN _print_pointer(FILE *f, void *p) { return _print_hex(f, hex((uint64_t)p)); } -PRINT_FN _print_bool(FILE *f, bool b) { return fputs(b ? "yes" : "no", f); } -PRINT_FN _print_str(FILE *f, const char *s) { return fputs(s ? s : "(null)", f); } +PRINT_FN _print_float(FILE *f, float x) { + return _print_double(f, (double)x); +} +PRINT_FN _print_pointer(FILE *f, void *p) { + return _print_hex(f, hex((uint64_t)p)); +} +PRINT_FN _print_bool(FILE *f, bool b) { + return fputs(b ? "yes" : "no", f); +} +PRINT_FN _print_str(FILE *f, const char *s) { + return fputs(s ? s : "(null)", f); +} int _print_char(FILE *f, char c); int _print_quoted(FILE *f, quoted_t quoted); PRINT_FN _print_string_slice(FILE *f, string_slice_t slice) { diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c index 7b038dac..a05b8753 100644 --- a/src/stdlib/stdlib.c +++ b/src/stdlib/stdlib.c @@ -159,10 +159,14 @@ void tomo_init(void) { } public -_Noreturn void fail_text(Text_t message) { fail(message); } +_Noreturn void fail_text(Text_t message) { + fail(message); +} public -Text_t builtin_last_err() { return Text$from_str(strerror(errno)); } +Text_t builtin_last_err() { + return Text$from_str(strerror(errno)); +} static int _inspect_depth = 0; static file_t *file = NULL; @@ -328,7 +332,9 @@ typedef struct cleanup_s { static cleanup_t *cleanups = NULL; public -void tomo_at_cleanup(Closure_t fn) { cleanups = new (cleanup_t, .cleanup_fn = fn, .next = cleanups); } +void tomo_at_cleanup(Closure_t fn) { + cleanups = new (cleanup_t, .cleanup_fn = fn, .next = cleanups); +} public void tomo_cleanup(void) { diff --git a/src/stdlib/tables.c b/src/stdlib/tables.c index adbdd6bd..753059c8 100644 --- a/src/stdlib/tables.c +++ b/src/stdlib/tables.c @@ -137,7 +137,8 @@ static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const bucket->occupied = 1; bucket->index = index; bucket->next_bucket = END_OF_CHAIN; - } else { // Collided with the start of a chain, put the new entry in chain position #2 + } else { // Collided with the start of a chain, put the new entry in chain + // position #2 buckets[t->bucket_info->last_free] = (bucket_t){.occupied = 1, .index = index, .next_bucket = bucket->next_bucket}; bucket->next_bucket = t->bucket_info->last_free; @@ -146,7 +147,8 @@ static void Table$set_bucket(Table_t *t, const void *entry, int32_t index, const static void hashmap_resize_buckets(Table_t *t, uint32_t new_capacity, const TypeInfo_t *type) { if (unlikely(new_capacity > TABLE_MAX_BUCKETS)) - fail("Table has exceeded the maximum table size (2^31) and cannot grow further!"); + fail("Table has exceeded the maximum table size (2^31) and cannot grow " + "further!"); size_t alloc_size = sizeof(bucket_info_t) + sizeof(bucket_t[new_capacity]); t->bucket_info = GC_MALLOC_ATOMIC(alloc_size); memset(t->bucket_info->buckets, 0, sizeof(bucket_t[new_capacity])); @@ -316,7 +318,9 @@ CONSTFUNC public void *Table$entry(Table_t t, int64_t n) { } public -void Table$clear(Table_t *t) { *t = EMPTY_TABLE; } +void Table$clear(Table_t *t) { + *t = EMPTY_TABLE; +} public Table_t Table$sorted(Table_t t, const TypeInfo_t *type) { @@ -535,7 +539,8 @@ Table_t Table$from_entries(List_t entries, const TypeInfo_t *type) { // "set intersection" in formal terms public Table_t Table$intersection(Table_t a, Table_t b, const TypeInfo_t *type) { - // Return a table such that t[k]==a[k] for all k such that a.has(k), b.has(k), and a[k]==b[k] + // Return a table such that t[k]==a[k] for all k such that a.has(k), b.has(k), + // and a[k]==b[k] Table_t result = EMPTY_TABLE; const size_t offset = value_offset(type); for (Table_t *t = &a; t; t = t->fallback) { @@ -553,8 +558,8 @@ Table_t Table$intersection(Table_t a, Table_t b, const TypeInfo_t *type) { // "set union" in formal terms public Table_t Table$with(Table_t a, Table_t b, const TypeInfo_t *type) { - // return a table such that t[k]==b[k] for all k such that b.has(k), and t[k]==a[k] for all k such that a.has(k) and - // not b.has(k) + // return a table such that t[k]==b[k] for all k such that b.has(k), and + // t[k]==a[k] for all k such that a.has(k) and not b.has(k) Table_t result = EMPTY_TABLE; const size_t offset = value_offset(type); for (Table_t *t = &a; t; t = t->fallback) { @@ -596,7 +601,8 @@ Table_t Table$difference(Table_t a, Table_t b, const TypeInfo_t *type) { // "without" is "set difference" in formal terms public Table_t Table$without(Table_t a, Table_t b, const TypeInfo_t *type) { - // Return a table such that t[k]==a[k] for all k such that not b.has(k) or b[k] != a[k] + // Return a table such that t[k]==a[k] for all k such that not b.has(k) or + // b[k] != a[k] Table_t result = EMPTY_TABLE; const size_t offset = value_offset(type); for (Table_t *t = &a; t; t = t->fallback) { @@ -655,12 +661,18 @@ void *Table$str_reserve(Table_t *t, const char *key, const void *value) { } public -void Table$str_set(Table_t *t, const char *key, const void *value) { Table$set(t, &key, &value, &CStrToVoidStarTable); } +void Table$str_set(Table_t *t, const char *key, const void *value) { + Table$set(t, &key, &value, &CStrToVoidStarTable); +} public -void Table$str_remove(Table_t *t, const char *key) { return Table$remove(t, &key, &CStrToVoidStarTable); } +void Table$str_remove(Table_t *t, const char *key) { + return Table$remove(t, &key, &CStrToVoidStarTable); +} -CONSTFUNC public void *Table$str_entry(Table_t t, int64_t n) { return Table$entry(t, n); } +CONSTFUNC public void *Table$str_entry(Table_t t, int64_t n) { + return Table$entry(t, n); +} PUREFUNC public bool Table$is_none(const void *obj, const TypeInfo_t *info) { (void)info; diff --git a/src/stdlib/tables.h b/src/stdlib/tables.h index 93da465e..f3f9d2c7 100644 --- a/src/stdlib/tables.h +++ b/src/stdlib/tables.h @@ -4,7 +4,7 @@ #include #include -#include +#include // IWYU pragma: export #include "datatypes.h" #include "lists.h" diff --git a/src/stdlib/text.c b/src/stdlib/text.c index 411f3546..4bf6d999 100644 --- a/src/stdlib/text.c +++ b/src/stdlib/text.c @@ -669,10 +669,14 @@ Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int) { } public -Text_t Text$from(Text_t text, Int_t first) { return Text$slice(text, first, I_small(-1)); } +Text_t Text$from(Text_t text, Int_t first) { + return Text$slice(text, first, I_small(-1)); +} public -Text_t Text$to(Text_t text, Int_t last) { return Text$slice(text, I_small(1), last); } +Text_t Text$to(Text_t text, Int_t last) { + return Text$slice(text, I_small(1), last); +} public Text_t Text$reversed(Text_t text) { @@ -814,7 +818,9 @@ OptionalText_t Text$from_strn(const char *str, size_t len) { } public -OptionalText_t Text$from_str(const char *str) { return str ? Text$from_strn(str, strlen(str)) : Text(""); } +OptionalText_t Text$from_str(const char *str) { + return str ? Text$from_strn(str, strlen(str)) : Text(""); +} static void u8_buf_append(Text_t text, Byte_t **buf, int64_t *capacity, int64_t *i) { switch (text.tag) { diff --git a/src/stdlib/text.h b/src/stdlib/text.h index 08423cfe..856b173a 100644 --- a/src/stdlib/text.h +++ b/src/stdlib/text.h @@ -33,7 +33,9 @@ static inline Text_t Text_from_str_literal(const char *str) { return (Text_t){.length = strlen(str), .tag = TEXT_ASCII, .ascii = str}; } -static inline Text_t Text_from_text(Text_t t) { return t; } +static inline Text_t Text_from_text(Text_t t) { + return t; +} #define convert_to_text(x) \ _Generic(x, \ diff --git a/src/types.c b/src/types.c index 1ccb7952..24453150 100644 --- a/src/types.c +++ b/src/types.c @@ -136,7 +136,9 @@ bool type_is_a(type_t *t, type_t *req) { return false; } -type_t *non_optional(type_t *t) { return t->tag == OptionalType ? Match(t, OptionalType)->type : t; } +type_t *non_optional(type_t *t) { + return t->tag == OptionalType ? Match(t, OptionalType)->type : t; +} PUREFUNC type_t *value_type(type_t *t) { while (t->tag == PointerType) @@ -457,7 +459,9 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed) { return false; } -PUREFUNC bool is_int_type(type_t *t) { return t->tag == IntType || t->tag == BigIntType || t->tag == ByteType; } +PUREFUNC bool is_int_type(type_t *t) { + return t->tag == IntType || t->tag == BigIntType || t->tag == ByteType; +} PUREFUNC bool is_numeric_type(type_t *t) { return t->tag == IntType || t->tag == BigIntType || t->tag == NumType || t->tag == ByteType; -- cgit v1.2.3 From 0f9af5f44bd2735f34a48ceb177837a5a6ef25b0 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 29 Dec 2025 00:07:53 -0500 Subject: Fix for file descriptors being reopened unintentionally --- src/stdlib/paths.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c index cbe52f5f..dcedcd03 100644 --- a/src/stdlib/paths.c +++ b/src/stdlib/paths.c @@ -336,17 +336,19 @@ typedef struct { static Result_t _write_bytes_to_fd(List_t bytes, bool close_file, void *userdata) { writer_data_t *data = userdata; if (bytes.length > 0) { - int fd = open(data->path_str, data->mode, data->permissions); - if (fd == -1) { - if (errno == EMFILE || errno == ENFILE) { - // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that - // will be closed by GC finalizers. - GC_gcollect(); - fd = open(data->path_str, data->mode, data->permissions); + if (data->fd == -1) { + data->fd = open(data->path_str, data->mode, data->permissions); + if (data->fd == -1) { + if (errno == EMFILE || errno == ENFILE) { + // If we hit file handle limits, run GC collection to try to clean up any lingering file handles + // that will be closed by GC finalizers. + GC_gcollect(); + data->fd = open(data->path_str, data->mode, data->permissions); + } + if (data->fd == -1) + return FailureResult("Could not write to file: ", data->path_str, " (", strerror(errno), ")"); } - if (fd == -1) return FailureResult("Could not write to file: ", data->path_str, " (", strerror(errno), ")"); } - data->fd = fd; if (bytes.stride != 1) List$compact(&bytes, 1); ssize_t written = write(data->fd, bytes.data, (size_t)bytes.length); -- cgit v1.2.3 From dbae987f1fb54da795185a03f4c00d56a639f8cd Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Wed, 31 Dec 2025 15:13:32 -0500 Subject: Changed is_between() to be bidirectional --- CHANGES.md | 4 ++++ api/api.md | 23 +++++++++++++---------- api/bytes.md | 7 ++++--- api/bytes.yaml | 7 ++++--- api/integers.md | 9 +++++---- api/integers.yaml | 11 ++++++----- api/nums.md | 7 ++++--- api/nums.yaml | 7 ++++--- man/man3/tomo-Byte.is_between.3 | 9 +++++---- man/man3/tomo-Int.3 | 4 ++-- man/man3/tomo-Int.is_between.3 | 11 ++++++----- man/man3/tomo-Num.is_between.3 | 9 +++++---- src/compile/comparisons.c | 1 + src/compile/functions.c | 1 + src/environment.c | 16 ++++++++-------- src/stdlib/bigint.c | 4 +++- src/stdlib/bytes.c | 2 +- src/stdlib/intX.c.h | 2 +- src/stdlib/numX.c.h | 2 +- test/integers.tm | 1 + 20 files changed, 79 insertions(+), 58 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 529711a3..9b6b2cb0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Version History +## v2025-12-31 + +- Changed `is_between()` to be bidirectional so `(5).is_between(10, 1) == yes` + ## v2025-12-23.2 - Fixes for OpenBSD and Mac. diff --git a/api/api.md b/api/api.md index d035a3fe..df8386ff 100644 --- a/api/api.md +++ b/api/api.md @@ -295,15 +295,16 @@ Determines if an integer is between two numbers (inclusive). Argument | Type | Description | Default ---------|------|-------------|--------- x | `Byte` | The integer to be checked. | - -low | `Byte` | The lower bound to check (inclusive). | - -high | `Byte` | The upper bound to check (inclusive). | - +low | `Byte` | One end of the range to check (inclusive); | - +high | `Byte` | The other end of the range to check (inclusive); | - -**Return:** `yes` if `low <= x and x <= high`, otherwise `no` +**Return:** `yes` if `a <= x and x <= b` or `b <= x and x <= a`, otherwise `no` **Example:** ```tomo assert Byte(7).is_between(1, 10) == yes +assert Byte(7).is_between(10, 1) == yes assert Byte(7).is_between(100, 200) == no assert Byte(7).is_between(1, 7) == yes @@ -545,7 +546,7 @@ assert (255).hex(digits=4, uppercase=yes, prefix=yes) == "0x00FF" ## Int.is_between ```tomo -Int.is_between : func(x: Int, low: Int, high: Int -> Bool) +Int.is_between : func(x: Int, a: Int, b: Int -> Bool) ``` Determines if an integer is between two numbers (inclusive). @@ -553,15 +554,16 @@ Determines if an integer is between two numbers (inclusive). Argument | Type | Description | Default ---------|------|-------------|--------- x | `Int` | The integer to be checked. | - -low | `Int` | The lower bound to check (inclusive). | - -high | `Int` | The upper bound to check (inclusive). | - +a | `Int` | One end of the range to check (inclusive). | - +b | `Int` | The other end of the range to check (inclusive). | - -**Return:** `yes` if `low <= x and x <= high`, otherwise `no` +**Return:** `yes` if `a <= x and x <= b` or `a >= x and x >= b`, otherwise `no` **Example:** ```tomo assert (7).is_between(1, 10) == yes +assert (7).is_between(10, 1) == yes assert (7).is_between(100, 200) == no assert (7).is_between(1, 7) == yes @@ -1949,15 +1951,16 @@ Determines if a number is between two numbers (inclusive). Argument | Type | Description | Default ---------|------|-------------|--------- x | `Num` | The integer to be checked. | - -low | `Num` | The lower bound to check (inclusive). | - -high | `Num` | The upper bound to check (inclusive). | - +low | `Num` | One end of the range to check (inclusive). | - +high | `Num` | The other end of the range to check (inclusive). | - -**Return:** `yes` if `low <= x and x <= high`, otherwise `no` +**Return:** `yes` if `a <= x and x <= b` or `b <= x and x <= a`, otherwise `no` **Example:** ```tomo assert (7.5).is_between(1, 10) == yes +assert (7.5).is_between(10, 1) == yes assert (7.5).is_between(100, 200) == no assert (7.5).is_between(1, 7.5) == yes diff --git a/api/bytes.md b/api/bytes.md index bb54d92c..99055523 100644 --- a/api/bytes.md +++ b/api/bytes.md @@ -62,15 +62,16 @@ Determines if an integer is between two numbers (inclusive). Argument | Type | Description | Default ---------|------|-------------|--------- x | `Byte` | The integer to be checked. | - -low | `Byte` | The lower bound to check (inclusive). | - -high | `Byte` | The upper bound to check (inclusive). | - +low | `Byte` | One end of the range to check (inclusive); | - +high | `Byte` | The other end of the range to check (inclusive); | - -**Return:** `yes` if `low <= x and x <= high`, otherwise `no` +**Return:** `yes` if `a <= x and x <= b` or `b <= x and x <= a`, otherwise `no` **Example:** ```tomo assert Byte(7).is_between(1, 10) == yes +assert Byte(7).is_between(10, 1) == yes assert Byte(7).is_between(100, 200) == no assert Byte(7).is_between(1, 7) == yes diff --git a/api/bytes.yaml b/api/bytes.yaml index dea650e2..adf7103b 100644 --- a/api/bytes.yaml +++ b/api/bytes.yaml @@ -57,7 +57,7 @@ Byte.is_between: return: type: 'Bool' description: > - `yes` if `low <= x and x <= high`, otherwise `no` + `yes` if `a <= x and x <= b` or `b <= x and x <= a`, otherwise `no` args: x: type: 'Byte' @@ -66,13 +66,14 @@ Byte.is_between: low: type: 'Byte' description: > - The lower bound to check (inclusive). + One end of the range to check (inclusive); high: type: 'Byte' description: > - The upper bound to check (inclusive). + The other end of the range to check (inclusive); example: | assert Byte(7).is_between(1, 10) == yes + assert Byte(7).is_between(10, 1) == yes assert Byte(7).is_between(100, 200) == no assert Byte(7).is_between(1, 7) == yes diff --git a/api/integers.md b/api/integers.md index ef3a6a60..73779021 100644 --- a/api/integers.md +++ b/api/integers.md @@ -138,7 +138,7 @@ assert (255).hex(digits=4, uppercase=yes, prefix=yes) == "0x00FF" ## Int.is_between ```tomo -Int.is_between : func(x: Int, low: Int, high: Int -> Bool) +Int.is_between : func(x: Int, a: Int, b: Int -> Bool) ``` Determines if an integer is between two numbers (inclusive). @@ -146,15 +146,16 @@ Determines if an integer is between two numbers (inclusive). Argument | Type | Description | Default ---------|------|-------------|--------- x | `Int` | The integer to be checked. | - -low | `Int` | The lower bound to check (inclusive). | - -high | `Int` | The upper bound to check (inclusive). | - +a | `Int` | One end of the range to check (inclusive). | - +b | `Int` | The other end of the range to check (inclusive). | - -**Return:** `yes` if `low <= x and x <= high`, otherwise `no` +**Return:** `yes` if `a <= x and x <= b` or `a >= x and x >= b`, otherwise `no` **Example:** ```tomo assert (7).is_between(1, 10) == yes +assert (7).is_between(10, 1) == yes assert (7).is_between(100, 200) == no assert (7).is_between(1, 7) == yes diff --git a/api/integers.yaml b/api/integers.yaml index b3c6b579..bbbd395d 100644 --- a/api/integers.yaml +++ b/api/integers.yaml @@ -146,22 +146,23 @@ Int.is_between: return: type: 'Bool' description: > - `yes` if `low <= x and x <= high`, otherwise `no` + `yes` if `a <= x and x <= b` or `a >= x and x >= b`, otherwise `no` args: x: type: 'Int' description: > The integer to be checked. - low: + a: type: 'Int' description: > - The lower bound to check (inclusive). - high: + One end of the range to check (inclusive). + b: type: 'Int' description: > - The upper bound to check (inclusive). + The other end of the range to check (inclusive). example: | assert (7).is_between(1, 10) == yes + assert (7).is_between(10, 1) == yes assert (7).is_between(100, 200) == no assert (7).is_between(1, 7) == yes diff --git a/api/nums.md b/api/nums.md index dac5967f..1bad194d 100644 --- a/api/nums.md +++ b/api/nums.md @@ -574,15 +574,16 @@ Determines if a number is between two numbers (inclusive). Argument | Type | Description | Default ---------|------|-------------|--------- x | `Num` | The integer to be checked. | - -low | `Num` | The lower bound to check (inclusive). | - -high | `Num` | The upper bound to check (inclusive). | - +low | `Num` | One end of the range to check (inclusive). | - +high | `Num` | The other end of the range to check (inclusive). | - -**Return:** `yes` if `low <= x and x <= high`, otherwise `no` +**Return:** `yes` if `a <= x and x <= b` or `b <= x and x <= a`, otherwise `no` **Example:** ```tomo assert (7.5).is_between(1, 10) == yes +assert (7.5).is_between(10, 1) == yes assert (7.5).is_between(100, 200) == no assert (7.5).is_between(1, 7.5) == yes diff --git a/api/nums.yaml b/api/nums.yaml index 4561bb91..28714ab9 100644 --- a/api/nums.yaml +++ b/api/nums.yaml @@ -401,7 +401,7 @@ Num.is_between: return: type: 'Bool' description: > - `yes` if `low <= x and x <= high`, otherwise `no` + `yes` if `a <= x and x <= b` or `b <= x and x <= a`, otherwise `no` args: x: type: 'Num' @@ -410,13 +410,14 @@ Num.is_between: low: type: 'Num' description: > - The lower bound to check (inclusive). + One end of the range to check (inclusive). high: type: 'Num' description: > - The upper bound to check (inclusive). + The other end of the range to check (inclusive). example: | assert (7.5).is_between(1, 10) == yes + assert (7.5).is_between(10, 1) == yes assert (7.5).is_between(100, 200) == no assert (7.5).is_between(1, 7.5) == yes diff --git a/man/man3/tomo-Byte.is_between.3 b/man/man3/tomo-Byte.is_between.3 index 06e53fb0..3b539ea1 100644 --- a/man/man3/tomo-Byte.is_between.3 +++ b/man/man3/tomo-Byte.is_between.3 @@ -2,7 +2,7 @@ .\" Copyright (c) 2025 Bruce Hill .\" All rights reserved. .\" -.TH Byte.is_between 3 2025-11-29 "Tomo man-pages" +.TH Byte.is_between 3 2025-12-31 "Tomo man-pages" .SH NAME Byte.is_between \- test if inside a range .SH LIBRARY @@ -23,15 +23,16 @@ lb lb lbx l l l. Name Type Description x Byte The integer to be checked. -low Byte The lower bound to check (inclusive). -high Byte The upper bound to check (inclusive). +low Byte One end of the range to check (inclusive); +high Byte The other end of the range to check (inclusive); .TE .SH RETURN -`yes` if `low <= x and x <= high`, otherwise `no` +`yes` if `a <= x and x <= b` or `b <= x and x <= a`, otherwise `no` .SH EXAMPLES .EX assert Byte(7).is_between(1, 10) == yes +assert Byte(7).is_between(10, 1) == yes assert Byte(7).is_between(100, 200) == no assert Byte(7).is_between(1, 7) == yes .EE diff --git a/man/man3/tomo-Int.3 b/man/man3/tomo-Int.3 index 186c0aae..64ae7bf6 100644 --- a/man/man3/tomo-Int.3 +++ b/man/man3/tomo-Int.3 @@ -2,7 +2,7 @@ .\" Copyright (c) 2025 Bruce Hill .\" All rights reserved. .\" -.TH Int 3 2025-11-30 "Tomo man-pages" +.TH Int 3 2025-12-31 "Tomo man-pages" .SH NAME Int \- a Tomo type .SH LIBRARY @@ -59,7 +59,7 @@ For more, see: .TP -.BI Int.is_between\ :\ func(x:\ Int,\ low:\ Int,\ high:\ Int\ ->\ Bool) +.BI Int.is_between\ :\ func(x:\ Int,\ a:\ Int,\ b:\ Int\ ->\ Bool) Determines if an integer is between two numbers (inclusive). For more, see: diff --git a/man/man3/tomo-Int.is_between.3 b/man/man3/tomo-Int.is_between.3 index 8087e0d0..fd54eb41 100644 --- a/man/man3/tomo-Int.is_between.3 +++ b/man/man3/tomo-Int.is_between.3 @@ -2,14 +2,14 @@ .\" Copyright (c) 2025 Bruce Hill .\" All rights reserved. .\" -.TH Int.is_between 3 2025-11-29 "Tomo man-pages" +.TH Int.is_between 3 2025-12-31 "Tomo man-pages" .SH NAME Int.is_between \- test if an int is in a range .SH LIBRARY Tomo Standard Library .SH SYNOPSIS .nf -.BI Int.is_between\ :\ func(x:\ Int,\ low:\ Int,\ high:\ Int\ ->\ Bool) +.BI Int.is_between\ :\ func(x:\ Int,\ a:\ Int,\ b:\ Int\ ->\ Bool) .fi .SH DESCRIPTION Determines if an integer is between two numbers (inclusive). @@ -23,15 +23,16 @@ lb lb lbx l l l. Name Type Description x Int The integer to be checked. -low Int The lower bound to check (inclusive). -high Int The upper bound to check (inclusive). +a Int One end of the range to check (inclusive). +b Int The other end of the range to check (inclusive). .TE .SH RETURN -`yes` if `low <= x and x <= high`, otherwise `no` +`yes` if `a <= x and x <= b` or `a >= x and x >= b`, otherwise `no` .SH EXAMPLES .EX assert (7).is_between(1, 10) == yes +assert (7).is_between(10, 1) == yes assert (7).is_between(100, 200) == no assert (7).is_between(1, 7) == yes .EE diff --git a/man/man3/tomo-Num.is_between.3 b/man/man3/tomo-Num.is_between.3 index 4276c8e3..ea78d895 100644 --- a/man/man3/tomo-Num.is_between.3 +++ b/man/man3/tomo-Num.is_between.3 @@ -2,7 +2,7 @@ .\" Copyright (c) 2025 Bruce Hill .\" All rights reserved. .\" -.TH Num.is_between 3 2025-11-29 "Tomo man-pages" +.TH Num.is_between 3 2025-12-31 "Tomo man-pages" .SH NAME Num.is_between \- check if a number is in a range .SH LIBRARY @@ -23,15 +23,16 @@ lb lb lbx l l l. Name Type Description x Num The integer to be checked. -low Num The lower bound to check (inclusive). -high Num The upper bound to check (inclusive). +low Num One end of the range to check (inclusive). +high Num The other end of the range to check (inclusive). .TE .SH RETURN -`yes` if `low <= x and x <= high`, otherwise `no` +`yes` if `a <= x and x <= b` or `b <= x and x <= a`, otherwise `no` .SH EXAMPLES .EX assert (7.5).is_between(1, 10) == yes +assert (7.5).is_between(10, 1) == yes assert (7.5).is_between(100, 200) == no assert (7.5).is_between(1, 7.5) == yes .EE diff --git a/src/compile/comparisons.c b/src/compile/comparisons.c index 62196cdf..c2d376ef 100644 --- a/src/compile/comparisons.c +++ b/src/compile/comparisons.c @@ -41,6 +41,7 @@ Text_t compile_comparison(env_t *env, ast_t *ast) { } else { code_err(ast, "I can't do comparisons between ", type_to_text(lhs_t), " and ", type_to_text(rhs_t)); } + assert(operand_t); Text_t lhs, rhs; lhs = compile_to_type(env, binop.lhs, operand_t); diff --git a/src/compile/functions.c b/src/compile/functions.c index 01377a89..f62e00f8 100644 --- a/src/compile/functions.c +++ b/src/compile/functions.c @@ -633,6 +633,7 @@ static void check_unused_vars(env_t *env, arg_ast_t *args, ast_t *body) { // Global/file scoped vars are okay to mutate without reading if (get_binding(env, entry->name) != NULL) continue; ast_t *var = Table$str_get(assigned_vars, entry->name); + assert(var); code_err(var, "This variable was assigned to, but never read from."); } } diff --git a/src/environment.c b/src/environment.c index b3009a5e..0cbeb0fc 100644 --- a/src/environment.c +++ b/src/environment.c @@ -101,7 +101,7 @@ env_t *global_env(bool source_mapping) { "Byte", Type(ByteType), Text("Byte_t"), Text("Byte$info"), {"get_bit", "Byte$get_bit", "func(x:Byte, bit_index:Int -> Bool)"}, // {"hex", "Byte$hex", "func(byte:Byte, uppercase=yes, prefix=no -> Text)"}, // - {"is_between", "Byte$is_between", "func(x:Byte, low:Byte, high:Byte -> Bool)"}, // + {"is_between", "Byte$is_between", "func(x:Byte, a:Byte, b:Byte -> Bool)"}, // {"max", "Byte$max", "Byte"}, // {"min", "Byte$min", "Byte"}, // {"parse", "Byte$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Byte?)"}, // @@ -118,7 +118,7 @@ env_t *global_env(bool source_mapping) { {"gcd", "Int$gcd", "func(x,y:Int -> Int)"}, // {"get_bit", "Int$get_bit", "func(x,bit_index:Int -> Bool)"}, // {"hex", "Int$hex", "func(i:Int, digits=0, uppercase=yes, prefix=yes -> Text)"}, // - {"is_between", "Int$is_between", "func(x:Int,low:Int,high:Int -> Bool)"}, // + {"is_between", "Int$is_between", "func(x:Int, a:Int, b:Int -> Bool)"}, // {"is_prime", "Int$is_prime", "func(x:Int,reps=50 -> Bool)"}, // {"left_shifted", "Int$left_shifted", "func(x,y:Int -> Int)"}, // {"minus", "Int$minus", "func(x,y:Int -> Int)"}, // @@ -151,7 +151,7 @@ env_t *global_env(bool source_mapping) { {"parse", "Int64$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int64?)"}, // {"get_bit", "Int64$get_bit", "func(x:Int64, bit_index:Int -> Bool)"}, // {"hex", "Int64$hex", "func(i:Int64, digits=0, uppercase=yes, prefix=yes -> Text)"}, // - {"is_between", "Int64$is_between", "func(x:Int64,low:Int64,high:Int64 -> Bool)"}, // + {"is_between", "Int64$is_between", "func(x:Int64, a:Int64, b:Int64 -> Bool)"}, // {"max", "Int64$max", "Int64"}, // {"min", "Int64$min", "Int64"}, // {"modulo", "Int64$modulo", "func(x,y:Int64 -> Int64)"}, // @@ -173,7 +173,7 @@ env_t *global_env(bool source_mapping) { {"parse", "Int32$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int32?)"}, // {"get_bit", "Int32$get_bit", "func(x:Int32, bit_index:Int -> Bool)"}, // {"hex", "Int32$hex", "func(i:Int32, digits=0, uppercase=yes, prefix=yes -> Text)"}, // - {"is_between", "Int32$is_between", "func(x:Int32,low:Int32,high:Int32 -> Bool)"}, // + {"is_between", "Int32$is_between", "func(x:Int32, a:Int32, b:Int32 -> Bool)"}, // {"max", "Int32$max", "Int32"}, // {"min", "Int32$min", "Int32"}, // {"modulo", "Int32$modulo", "func(x,y:Int32 -> Int32)"}, // @@ -195,7 +195,7 @@ env_t *global_env(bool source_mapping) { {"parse", "Int16$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int16?)"}, // {"get_bit", "Int16$get_bit", "func(x:Int16, bit_index:Int -> Bool)"}, // {"hex", "Int16$hex", "func(i:Int16, digits=0, uppercase=yes, prefix=yes -> Text)"}, // - {"is_between", "Int16$is_between", "func(x:Int16,low:Int16,high:Int16 -> Bool)"}, // + {"is_between", "Int16$is_between", "func(x:Int16, a:Int16, b:Int16 -> Bool)"}, // {"max", "Int16$max", "Int16"}, // {"min", "Int16$min", "Int16"}, // {"modulo", "Int16$modulo", "func(x,y:Int16 -> Int16)"}, // @@ -217,7 +217,7 @@ env_t *global_env(bool source_mapping) { {"parse", "Int8$parse", "func(text:Text, base:Int?=none, remainder:&Text?=none -> Int8?)"}, // {"get_bit", "Int8$get_bit", "func(x:Int8, bit_index:Int -> Bool)"}, // {"hex", "Int8$hex", "func(i:Int8, digits=0, uppercase=yes, prefix=yes -> Text)"}, // - {"is_between", "Int8$is_between", "func(x:Int8,low:Int8,high:Int8 -> Bool)"}, // + {"is_between", "Int8$is_between", "func(x:Int8, a:Int8, b:Int8 -> Bool)"}, // {"max", "Int8$max", "Int8"}, // {"min", "Int8$min", "Int8"}, // {"modulo", "Int8$modulo", "func(x,y:Int8 -> Int8)"}, // @@ -239,7 +239,7 @@ env_t *global_env(bool source_mapping) { {"clamped", "Num$clamped", "func(x,low,high:Num -> Num)"}, // {"percent", "Num$percent", "func(n:Num,precision=0.01 -> Text)"}, // {"with_precision", "Num$with_precision", "func(n:Num,precision:Num -> Num)"}, // - {"is_between", "Num$is_between", "func(x:Num,low:Num,high:Num -> Bool)"}, // + {"is_between", "Num$is_between", "func(x:Num, a:Num, b:Num -> Bool)"}, // {"isinf", "Num$isinf", "func(n:Num -> Bool)"}, // {"isfinite", "Num$isfinite", "func(n:Num -> Bool)"}, // {"modulo", "Num$mod", "func(x,y:Num -> Num)"}, // @@ -269,7 +269,7 @@ env_t *global_env(bool source_mapping) { {"clamped", "Num32$clamped", "func(x,low,high:Num32 -> Num32)"}, // {"percent", "Num32$percent", "func(n:Num32,precision=Num32(.01) -> Text)"}, // {"with_precision", "Num32$with_precision", "func(n:Num32,precision:Num32 -> Num32)"}, // - {"is_between", "Num32$is_between", "func(x:Num32,low:Num32,high:Num32 -> Bool)"}, // + {"is_between", "Num32$is_between", "func(x:Num32, a:Num32, b:Num32 -> Bool)"}, // {"isinf", "Num32$isinf", "func(n:Num32 -> Bool)"}, // {"isfinite", "Num32$isfinite", "func(n:Num32 -> Bool)"}, // C(2_SQRTPI), C(E), C(PI_2), C(2_PI), C(1_PI), C(LN10), C(LN2), C(LOG2E), C(PI), C(PI_4), C(SQRT2), diff --git a/src/stdlib/bigint.c b/src/stdlib/bigint.c index c9bfb68e..e2935332 100644 --- a/src/stdlib/bigint.c +++ b/src/stdlib/bigint.c @@ -102,7 +102,9 @@ CONSTFUNC Int_t Int$clamped(Int_t x, Int_t low, Int_t high) { public CONSTFUNC bool Int$is_between(const Int_t x, const Int_t low, const Int_t high) { - return Int$compare_value(low, x) <= 0 && Int$compare_value(x, high) <= 0; + int32_t low_cmp = Int$compare_value(x, low); + int32_t high_cmp = Int$compare_value(x, high); + return (low_cmp >= 0 && high_cmp <= 0) || (low_cmp <= 0 && high_cmp >= 0); } public diff --git a/src/stdlib/bytes.c b/src/stdlib/bytes.c index 2b8dab4d..9874f222 100644 --- a/src/stdlib/bytes.c +++ b/src/stdlib/bytes.c @@ -31,7 +31,7 @@ PUREFUNC public Text_t Byte$as_text(const void *b, bool colorize, const TypeInfo public CONSTFUNC bool Byte$is_between(const Byte_t x, const Byte_t low, const Byte_t high) { - return low <= x && x <= high; + return (low <= x && x <= high) || (high <= x && x <= low); } public diff --git a/src/stdlib/intX.c.h b/src/stdlib/intX.c.h index 3e6648f7..04c8ef3b 100644 --- a/src/stdlib/intX.c.h +++ b/src/stdlib/intX.c.h @@ -116,7 +116,7 @@ PUREFUNC bool NAMESPACED(equal)(const void *x, const void *y, const TypeInfo_t * } public CONSTFUNC bool NAMESPACED(is_between)(const INT_T x, const INT_T low, const INT_T high) { - return low <= x && x <= high; + return (low <= x && x <= high) || (high <= x && x <= low); } public CONSTFUNC INT_T NAMESPACED(clamped)(INT_T x, INT_T min, INT_T max) { diff --git a/src/stdlib/numX.c.h b/src/stdlib/numX.c.h index ed91fbf5..22db3ca3 100644 --- a/src/stdlib/numX.c.h +++ b/src/stdlib/numX.c.h @@ -155,7 +155,7 @@ CONSTFUNC NUM_T NAMESPACED(mix)(NUM_T amount, NUM_T x, NUM_T y) { public CONSTFUNC bool NAMESPACED(is_between)(const NUM_T x, const NUM_T low, const NUM_T high) { - return low <= x && x <= high; + return (low <= x && x <= high) || (high <= x && x <= low); } public CONSTFUNC NUM_T NAMESPACED(clamped)(NUM_T x, NUM_T low, NUM_T high) { diff --git a/test/integers.tm b/test/integers.tm index 67175f7a..2d07beda 100644 --- a/test/integers.tm +++ b/test/integers.tm @@ -94,6 +94,7 @@ func main() assert (3).is_between(1, 5) == yes assert (3).is_between(1, 3) == yes + assert (3).is_between(5, 1) == yes assert (3).is_between(100, 200) == no assert (6).get_bit(1) == no -- cgit v1.2.3 From ce49f93da58d007c0a52ee82e2421adfe06012f9 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Thu, 1 Jan 2026 15:18:07 -0500 Subject: Bugfix for integer comparison --- src/stdlib/bigint.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stdlib/bigint.c b/src/stdlib/bigint.c index e2935332..8bffbaf1 100644 --- a/src/stdlib/bigint.c +++ b/src/stdlib/bigint.c @@ -72,8 +72,8 @@ static bool Int$is_none(const void *i, const TypeInfo_t *info) { public PUREFUNC int32_t Int$compare_value(const Int_t x, const Int_t y) { if (likely(x.small & y.small & 1L)) return (x.small > y.small) - (x.small < y.small); - else if (x.small & 1) return -mpz_cmp_si(y.big, x.small); - else if (y.small & 1) return mpz_cmp_si(x.big, y.small); + else if (x.small & 1) return -mpz_cmp_si(y.big, (x.small >> 2)); + else if (y.small & 1) return mpz_cmp_si(x.big, (y.small >> 2)); else return x.big == y.big ? 0 : mpz_cmp(x.big, y.big); } -- cgit v1.2.3