diff options
| author | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-25 18:24:05 +0000 |
|---|---|---|
| committer | Zuhaitz Méndez Fernández de Aránguiz <zuhaitz@debian> | 2026-01-25 18:24:05 +0000 |
| commit | e3ab29bb4d7174cae65de2275f19105eb3d93d91 (patch) | |
| tree | 89970111c1be4c3ddd291b720d4fdf496f10b2d5 | |
| parent | 489336b2101bf16edeec7bfc4379408eb19b936e (diff) | |
APE shall never kill APE
| -rw-r--r-- | .github/workflows/build-ape.yml | 45 | ||||
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | Makefile | 123 | ||||
| -rw-r--r-- | README.md | 28 | ||||
| -rw-r--r-- | ape/boot/.args | 23 | ||||
| -rw-r--r-- | ape/boot/Makefile | 14 | ||||
| -rw-r--r-- | ape/boot/boot.zc | 118 | ||||
| -rw-r--r-- | ape/boot/hello.zc | 4 | ||||
| -rw-r--r-- | ape/boot/instructions.txt | 6 | ||||
| -rw-r--r-- | ape/zc_entry.c | 257 | ||||
| -rw-r--r-- | examples/games/zen_craft/main.zc | 24 | ||||
| -rw-r--r-- | src/main.c | 26 | ||||
| -rw-r--r-- | src/parser/parser_stmt.c | 17 | ||||
| -rw-r--r-- | src/repl/repl.c | 41 | ||||
| -rw-r--r-- | src/utils/utils.c | 59 |
15 files changed, 726 insertions, 62 deletions
diff --git a/.github/workflows/build-ape.yml b/.github/workflows/build-ape.yml new file mode 100644 index 0000000..3fe5c17 --- /dev/null +++ b/.github/workflows/build-ape.yml @@ -0,0 +1,45 @@ +name: APE Portable Build + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + workflow_dispatch: + +jobs: + build-ape: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y zip wget + + - name: Setup cosmocc + run: | + wget https://cosmo.zip/pub/cosmocc/cosmocc.zip + mkdir -p $HOME/cosmocc + unzip cosmocc.zip -d $HOME/cosmocc + echo "$HOME/cosmocc/bin" >> $GITHUB_PATH + + - name: Build APE + run: | + # Ensure PATH is updated in this step + export PATH="$HOME/cosmocc/bin:$PATH" + make clean + make ape + + - name: Upload zc.com + uses: actions/upload-artifact@v4 + with: + name: zc-portable + path: out/bin/zc.com + + - name: Upload zc-boot.com + uses: actions/upload-artifact@v4 + with: + name: zc-boot-portable + path: out/bin/zc-boot.com @@ -17,4 +17,5 @@ compile_commands.json vgcore.* tests/**/*.o plugins/*.so -.cache
\ No newline at end of file +.cache +out/
\ No newline at end of file @@ -1,10 +1,27 @@ +# OS detection and portable commands +ifeq ($(OS),Windows_NT) + EXE = .exe + RM = rm -rf + MKDIR = mkdir -p + CP = cp -af + LN = ln -sf + INSTALL = install +else + EXE = + RM = rm -rf + MKDIR = mkdir -p + CP = cp -af + LN = ln -sf + INSTALL = install +endif + # Compiler configuration # Default: gcc # To build with clang: make CC=clang # To build with zig: make CC="zig cc" CC = gcc CFLAGS = -Wall -Wextra -g -I./src -I./src/ast -I./src/parser -I./src/codegen -I./plugins -I./src/zen -I./src/utils -I./src/lexer -I./src/analysis -I./src/lsp -TARGET = zc +TARGET = zc$(EXE) LIBS = -lm -lpthread -ldl SRCS = src/main.c \ @@ -46,60 +63,126 @@ INCLUDEDIR = $(PREFIX)/include/zenc PLUGINS = plugins/befunge.so plugins/brainfuck.so plugins/forth.so plugins/lisp.so plugins/regex.so plugins/sql.so +# APE (Actually Portable Executable) configuration +COSMOCC = cosmocc +OUT_STAGE = out/stage +OUT_BIN = out/bin +ZC_ENTRY_O = $(OUT_STAGE)/zc_entry.o +ZC_COM_BIN = $(OUT_STAGE)/zc.com +ZC_COM = $(OUT_BIN)/zc.com +ZC_BOOT_SRC = ape/boot/boot.zc +ZC_BOOT_COM_BIN = $(OUT_STAGE)/zc-boot.com +ZC_BOOT_COM = $(OUT_BIN)/zc-boot.com + # Default target all: $(TARGET) $(PLUGINS) +# APE target +ape: $(ZC_COM) $(ZC_BOOT_COM) + # Build plugins plugins/%.so: plugins/%.c $(CC) $(CFLAGS) -shared -fPIC -o $@ $< # Link $(TARGET): $(OBJS) - @mkdir -p $(dir $@) + @$(MKDIR) $(dir $@) $(CC) $(CFLAGS) -o $@ $^ $(LIBS) @echo "=> Build complete: $(TARGET)" # Compile $(OBJ_DIR)/%.o: %.c - @mkdir -p $(dir $@) + @$(MKDIR) $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ +# APE targets +$(ZC_ENTRY_O): ape/zc_entry.c + @$(MKDIR) $(@D) + $(COSMOCC) -c $< -o $@ + +$(ZC_COM_BIN): $(ZC_ENTRY_O) $(SRCS) + @$(MKDIR) $(@D) + $(MAKE) \ + PLUGINS= \ + CC=$(COSMOCC) \ + OBJ_DIR=obj-ape \ + LIBS="$(abspath $(ZC_ENTRY_O)) -Wl,--wrap=main" \ + TARGET="$(abspath $@)"; + +$(ZC_COM): $(ZC_COM_BIN) + @$(MKDIR) $(@D) + @$(CP) $(ZC_COM_BIN) $(wildcard $(ZC_COM_BIN).*) "$(@D)"; \ + zip -r "$(abspath $@)" std.zc std LICENSE; + +$(ZC_BOOT_COM_BIN): $(ZC_BOOT_SRC) $(ZC_COM) + @$(MKDIR) $(@D) + ./$(ZC_COM) build --cc $(COSMOCC) -o $@ $< + +$(ZC_BOOT_COM): $(ZC_BOOT_COM_BIN) ape/boot/.args + @$(MKDIR) $(@D) + @$(CP) $(ZC_BOOT_COM_BIN) $(wildcard $(ZC_BOOT_COM_BIN).*) "$(@D)"; \ + (cd ape/boot && zip "$(abspath $@)" .args hello.zc instructions.txt Makefile); \ + zip "$(abspath $@)" LICENSE; + # Install install: $(TARGET) - install -d $(BINDIR) - install -m 755 $(TARGET) $(BINDIR)/$(TARGET) + $(INSTALL) -d $(BINDIR) + $(INSTALL) -m 755 $(TARGET) $(BINDIR)/$(TARGET) # Install man pages - install -d $(MANDIR)/man1 $(MANDIR)/man5 $(MANDIR)/man7 - test -f man/zc.1 && install -m 644 man/zc.1 $(MANDIR)/man1/zc.1 || true - test -f man/zc.5 && install -m 644 man/zc.5 $(MANDIR)/man5/zc.5 || true - test -f man/zc.7 && install -m 644 man/zc.7 $(MANDIR)/man7/zc.7 || true + $(INSTALL) -d $(MANDIR)/man1 $(MANDIR)/man5 $(MANDIR)/man7 + test -f man/zc.1 && $(INSTALL) -m 644 man/zc.1 $(MANDIR)/man1/zc.1 || true + test -f man/zc.5 && $(INSTALL) -m 644 man/zc.5 $(MANDIR)/man5/zc.5 || true + test -f man/zc.7 && $(INSTALL) -m 644 man/zc.7 $(MANDIR)/man7/zc.7 || true # Install standard library - install -d $(SHAREDIR) - cp -r std $(SHAREDIR)/ + $(INSTALL) -d $(SHAREDIR) + $(CP) std $(SHAREDIR)/ # Install plugin headers - install -d $(INCLUDEDIR) - install -m 644 plugins/zprep_plugin.h $(INCLUDEDIR)/zprep_plugin.h + $(INSTALL) -d $(INCLUDEDIR) + $(INSTALL) -m 644 plugins/zprep_plugin.h $(INCLUDEDIR)/zprep_plugin.h @echo "=> Installed to $(BINDIR)/$(TARGET)" @echo "=> Man pages installed to $(MANDIR)" @echo "=> Standard library installed to $(SHAREDIR)/std" # Uninstall uninstall: - rm -f $(BINDIR)/$(TARGET) - rm -f $(MANDIR)/man1/zc.1 - rm -f $(MANDIR)/man5/zc.5 - rm -f $(MANDIR)/man7/zc.7 - rm -rf $(SHAREDIR) + $(RM) $(BINDIR)/$(TARGET) + $(RM) $(MANDIR)/man1/zc.1 + $(RM) $(MANDIR)/man5/zc.5 + $(RM) $(MANDIR)/man7/zc.7 + $(RM) $(SHAREDIR) @echo "=> Uninstalled from $(BINDIR)/$(TARGET)" @echo "=> Removed man pages from $(MANDIR)" @echo "=> Removed $(SHAREDIR)" +# Install APE +install-ape: ape + $(INSTALL) -d $(BINDIR) + $(INSTALL) -m 755 $(ZC_COM) $(BINDIR)/zc.com + $(INSTALL) -m 755 $(ZC_BOOT_COM) $(BINDIR)/zc-boot.com + $(LN) $(BINDIR)/zc.com $(BINDIR)/zc + + # Install standard library (shared) + $(INSTALL) -d $(SHAREDIR) + $(CP) std $(SHAREDIR)/ + @echo "=> Installed APE binaries to $(BINDIR)" + @echo "=> Alias 'zc' points to zc.com" + @echo "=> Standard library installed to $(SHAREDIR)/std" + +# Uninstall APE +uninstall-ape: + $(RM) $(BINDIR)/zc + $(RM) $(BINDIR)/zc.com + $(RM) $(BINDIR)/zc-boot.com + $(RM) $(SHAREDIR) + @echo "=> Uninstalled APE binaries from $(BINDIR)" + @echo "=> Removed $(SHAREDIR)" + # Clean clean: - rm -rf $(OBJ_DIR) $(TARGET) out.c plugins/*.so + $(RM) $(OBJ_DIR) obj-ape $(TARGET) out.c plugins/*.so out @echo "=> Clean complete!" # Test @@ -114,4 +197,4 @@ zig: clang: $(MAKE) CC=clang -.PHONY: all clean install uninstall test zig clang +.PHONY: all clean install uninstall install-ape uninstall-ape test zig clang ape @@ -114,6 +114,29 @@ make sudo make install ``` +### Portable Build (APE) + +Zen C can be compiled as an **Actually Portable Executable (APE)** using [Cosmopolitan Libc](https://github.com/jart/cosmopolitan). This produces a single binary (`.com`) that runs natively on Linux, macOS, Windows, FreeBSD, OpenBSD, and NetBSD on both x86_64 and aarch64 architectures. + +**Prerequisites:** +- `cosmocc` toolchain (must be in your PATH) + +**Build & Install:** +```bash +make ape +sudo env "PATH=$PATH" make install-ape +``` + +**Artifacts:** +- `out/bin/zc.com`: The portable Zen-C compiler. Includes the standard library embedded within the executable. +- `out/bin/zc-boot.com`: A self-contained bootstrap installer for setting up new Zen-C projects. + +**Usage:** +```bash +# Run on any supported OS +./out/bin/zc.com build hello.zc -o hello +``` + ### Usage ```bash @@ -1223,5 +1246,8 @@ make test This project uses the following third-party libraries: -* **cJSON** (MIT License): Used for JSON parsing and generation in the Language Server. + +* **[cJSON](https://github.com/DaveGamble/cJSON)** (MIT License): Used for JSON parsing and generation in the Language Server. * Copyright (c) 2009-2017 Dave Gamble and cJSON contributors +* **[zc-ape](https://github.com/OEvgeny/zc-ape)** (MIT License): The original Actually Portable Executable port of Zen-C. The APE integration in this repository is based on this work by [OEvgeny](https://github.com/OEvgeny). +* **[Cosmopolitan Libc](https://github.com/jart/cosmopolitan)** (ISC License): The foundational library that makes APE possible. diff --git a/ape/boot/.args b/ape/boot/.args new file mode 100644 index 0000000..797e7cd --- /dev/null +++ b/ape/boot/.args @@ -0,0 +1,23 @@ +... +:: system -c 'mkdir -p usr/bin' +:: system -c 'echo " Downloading into usr/bin"' +:: system -c 'out=usr/bin/unzip; [ -e "$out" ] || curl -o "$out" https://cosmo.zip/pub/cosmos/bin/unzip' +:: system -c 'out=usr/bin/unzip; [ -e "$out" ] && echo " [+] $out: exists"' +:: system -c 'out=usr/bin/zip; [ -e "$out" ] || curl -o "$out" https://cosmo.zip/pub/cosmos/bin/zip' +:: system -c 'out=usr/bin/zip; [ -e "$out" ] && echo " [+] $out: exists"' +:: system -c 'chmod 755 usr/bin/zip' +:: system -c 'chmod 755 usr/bin/unzip' +:: system -c 'out=usr/cosmocc.zip; [ -e "$out" ] || curl -o "$out" https://cosmo.zip/pub/cosmocc/cosmocc.zip' +:: system -c 'out=usr/cosmocc.zip; [ -e "$out" ] && echo " [+] $out: exists"' +:: system -c 'out=usr; [ -e "$out/bin/cosmocc" ] || usr/bin/unzip -d "$out" usr/cosmocc.zip' +:: system -c 'out=usr; [ -e "$out/bin/cosmocc" ] || unzip -d "$out" usr/cosmocc.zip' +:: system -c 'out=usr; [ -e "$out/bin/cosmocc" ] && echo " [+] $out/bin/cosmocc: exists"' +:: system -c 'out=usr/bin/zc.com; [ -e "$out" ] || curl -o "$out" -L https://github.com/OEvgeny/zc-ape/releases/latest/download/zc.com' +:: system -c 'out=usr/bin/zc.com; [ -e "$out" ] && echo " [+] $out: exists"' +:: system -c 'chmod 755 usr/bin/zc.com' +:: system -c 'out=Makefile; [ -e "$out" ] || curl -o "$out" https://raw.githubusercontent.com/OEvgeny/zc-ape/main/zc-boot/Makefile' +:: system -c 'out=Makefile; [ -e "$out" ] && echo " [+] $out: exists"' +:: system -c 'out=hello.zc; [ -e "$out" ] || curl -o "$out" https://raw.githubusercontent.com/OEvgeny/zc-ape/main/zc-boot/hello.zc' +:: system -c 'out=hello.zc; [ -e "$out" ] && echo " [+] $out: exists"' +:: system -c 'echo " Done."' +:: system -c 'cat /zip/instructions.txt'
\ No newline at end of file diff --git a/ape/boot/Makefile b/ape/boot/Makefile new file mode 100644 index 0000000..05f7a36 --- /dev/null +++ b/ape/boot/Makefile @@ -0,0 +1,14 @@ +export PATH := $(realpath usr/bin):$(PATH) +CC=cosmocc +ZC=zc.com + +all: out/hello.com + +out/hello.com: hello.zc + @mkdir -p out + $(ZC) build --cc $(CC) -o $@ $< + +clean: + rm -rf out + +.PHONY: all clean
\ No newline at end of file diff --git a/ape/boot/boot.zc b/ape/boot/boot.zc new file mode 100644 index 0000000..176f668 --- /dev/null +++ b/ape/boot/boot.zc @@ -0,0 +1,118 @@ +#define _COSMO_SOURCE +#include <libc/cosmo.h> +#include <string.h> +#include <stdlib.h> + +raw { + extern char **environ; +} + +fn normalize_wait(ws: int) -> int { + if ws == -1 { return -1 } + if WIFEXITED(ws) { return WEXITSTATUS(ws) } + if WIFSIGNALED(ws) { return 128 + WTERMSIG(ws) } + return -1; +} + +fn run_exec(argv: char**) -> int { + fflush(NULL); + let ws: int = systemvpe(argv[0], argv, environ); + return normalize_wait(ws); +} + +fn needs_quote(s: char*) -> bool { + for (let p = s; *p; ++p) { + if (*p == ' ' || *p == '\t' || *p == '\n' || *p == '"' || *p == '\'' ) + return true; + } + return false; +} + +fn print_quoted(s: char*) { + if !needs_quote(s) { + print f"{s}"; + return; + } + + print f"'"; + for (let p = s; *p; ++p) { + if (*p == '\'') { + print f"'\"'\"'"; + } else { + print f"{*p:c}"; + } + } + print f"'"; +} + +fn log_cmd(argv: char**) { + print f"$ "; + let i = 0; + while argv[i] != NULL { + if i { print f" " } + print_quoted(argv[i]); + i++; + } + print f"\n"; +} + +fn main(argc: int, argv: string*) { + def delim = "::"; + + let newargc: int = cosmo_args("/zip/.args", &argv); + if newargc != -1 { argc = newargc } + + let i = 1; + while i < argc && strcmp(argv[i], delim) { i++ } + + while i < argc { + if strcmp(argv[i], delim) { + f"Expected '{delim}' before each command, got: {argv[i]}"; + return 2; + } + i++; + + let start = i; + while i < argc && strcmp(argv[i], delim) { i++ } + let cmd_argc = i - start; + + if cmd_argc <= 0 { + f"Empty command after '{delim}'"; + return 2; + } + + let rc = 0; + + if !strcmp(argv[start], "system") { + if cmd_argc != 3 { + !"error: system expects 2 arguments, got {cmd_argc}"; + return -4; + } + + if strcmp(argv[start + 1], "-c") { + !"error: system got '{argv[start + 1]}' instead of -c"; + return -5; + } + + "$ {argv[start + 2]}"; + rc = normalize_wait(system(argv[start + 2])); + + if rc { return rc } + + continue; + } + + autofree let cmdv = (char**)malloc((cmd_argc + 1) * sizeof(char*)); + if !cmdv { return 1 } + for (let k = 0; k < cmd_argc; k++) { cmdv[k] = argv[start + k] } + cmdv[cmd_argc] = NULL; + + log_cmd(cmdv); + + rc = run_exec(cmdv); + + if rc { return rc } + } + + return 0; +} diff --git a/ape/boot/hello.zc b/ape/boot/hello.zc new file mode 100644 index 0000000..8d14292 --- /dev/null +++ b/ape/boot/hello.zc @@ -0,0 +1,4 @@ + +fn main() { + "Hello from Zen-C!"; +} diff --git a/ape/boot/instructions.txt b/ape/boot/instructions.txt new file mode 100644 index 0000000..edbe367 --- /dev/null +++ b/ape/boot/instructions.txt @@ -0,0 +1,6 @@ +Run: + +$ usr/bin/make +$ out/hello.com + +To build and run hello world example. diff --git a/ape/zc_entry.c b/ape/zc_entry.c new file mode 100644 index 0000000..5594edf --- /dev/null +++ b/ape/zc_entry.c @@ -0,0 +1,257 @@ +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <libc/cosmo.h> + +int __real_main(int argc, char **argv); + +static bool streq(const char *a, const char *b) +{ + return a && b && !strcmp(a, b); +} + +static bool has_cc_flag(int argc, char **argv) +{ + for (int i = 1; i < argc; ++i) + { + const char *a = argv[i]; + if (a && streq(a, "--cc")) + { + return true; + } + } + return false; +} + +static const char *get_cc_value(int argc, char **argv) +{ + for (int i = 1; i < argc; ++i) + { + const char *a = argv[i]; + + if (a && streq(a, "--cc")) + { + if (i + 1 < argc) + { + return argv[i + 1]; + } + return NULL; + } + } + return NULL; +} + +static bool cc_is_cosmocc(int argc, char **argv) +{ + const char *cc = get_cc_value(argc, argv); + if (!cc) + { + return false; + } + const char *p = strstr(cc, "cosmocc"); + return p != NULL; +} + +static bool is_cmd(int argc, char **argv, const char *cmd) +{ + // usage: zc [command] [options] <file.zc> + return argc > 1 && streq(argv[1], cmd); +} + +static bool has_o_flag(int argc, char **argv) +{ + for (int i = 1; i < argc; ++i) + { + const char *a = argv[i]; + if (!a) + { + continue; + } + if (streq(a, "-o")) + { + return true; + } + if (!strncmp(a, "-o", 2) && a[2]) + { + return true; + } + } + return false; +} + +static const char *get_out_value(int argc, char **argv) +{ + for (int i = 1; i < argc; ++i) + { + const char *a = argv[i]; + if (!a) + { + continue; + } + + if (streq(a, "-o")) + { + if (i + 1 < argc) + { + return argv[i + 1]; + } + return NULL; + } + if (!strncmp(a, "-o", 2) && a[2]) + { + return a + 2; + } + } + return NULL; +} + +static void inject_default_out(int *pargc, char ***pargv, const char *out) +{ + int argc = *pargc; + char **argv = *pargv; + + if (!(is_cmd(argc, argv, "run") || is_cmd(argc, argv, "build"))) + { + return; + } + if (has_o_flag(argc, argv)) + { + return; + } + + char **newv = (char **)malloc((size_t)(argc + 2 + 1) * sizeof(char *)); + if (!newv) + { + return; + } + + int k = 0; + newv[k++] = argv[0]; + newv[k++] = argv[1]; + newv[k++] = (char *)"-o"; + newv[k++] = (char *)out; + + for (int i = 2; i < argc; ++i) + { + newv[k++] = argv[i]; + } + newv[k] = NULL; + + *pargc = k; + *pargv = newv; +} + +static void unlink_if_exists(const char *path) +{ + if (!path || !*path) + { + return; + } + int old = errno; + if (unlink(path) == -1) + { + if (errno == ENOENT) + { + errno = old; + } + } + else + { + errno = old; + } +} + +static void cleanup_cosmocc_out(const char *out) +{ + char buf[4096]; + + if (snprintf(buf, sizeof(buf), "%s.dbg", out) < (int)sizeof(buf)) + { + unlink_if_exists(buf); + } + + size_t n = strlen(out); + if (n > 4 && !strcmp(out + (n - 4), ".com")) + { + char base[4096]; + if (n - 4 < sizeof(base)) + { + memcpy(base, out, n - 4); + base[n - 4] = 0; + + if (snprintf(buf, sizeof(buf), "%s.aarch64.elf", base) < (int)sizeof(buf)) + { + unlink_if_exists(buf); + } + } + } +} + +static bool out_exists(const char *out) +{ + char buf[4096]; + if (snprintf(buf, sizeof(buf), "%s.dbg", out) < (int)sizeof(buf)) + { + if (access(buf, F_OK) != -1) + { + return true; + } + } + + char base[4096]; + size_t n = strlen(out); + if (n > 4 && !strcmp(out + (n - 4), ".com")) + { + if (n - 4 < sizeof(base)) + { + memcpy(base, out, n - 4); + base[n - 4] = 0; + if (snprintf(buf, sizeof(buf), "%s.aarch64.elf", base) < (int)sizeof(buf)) + { + if (access(buf, F_OK) != -1) + { + return true; + } + } + } + } + return false; +} + +static void ensure_env(const char *key, const char *val) +{ + const char *cur = getenv(key); + if (cur && *cur) + { + return; + } + setenv(key, val, 0); +} + +int __wrap_main(int argc, char **argv) +{ + ensure_env("ZC_ROOT", "/zip"); + + int newargc = cosmo_args("/zip/.args", &argv); + if (newargc != -1) + { + argc = newargc; + } + + inject_default_out(&argc, &argv, "a.out.com"); + + const char *out = get_out_value(argc, argv); + + bool do_cleanup = is_cmd(argc, argv, "run") && cc_is_cosmocc(argc, argv) && !out_exists(out); + + int rc = __real_main(argc, argv); + + if (do_cleanup) + { + cleanup_cosmocc_out(out); + } + + return rc; +} diff --git a/examples/games/zen_craft/main.zc b/examples/games/zen_craft/main.zc index c130e72..90b06a8 100644 --- a/examples/games/zen_craft/main.zc +++ b/examples/games/zen_craft/main.zc @@ -3,20 +3,20 @@ import "raylib.h" as rl; import "math.h"; -const SCREEN_WIDTH = 800; -const SCREEN_HEIGHT = 450; -const TITLE = "Zen Craft"; +def SCREEN_WIDTH = 800; +def SCREEN_HEIGHT = 450; +def TITLE = "Zen Craft"; // Block Types (Enum) -const BLOCK_AIR = 0; -const BLOCK_DIRT = 1; -const BLOCK_GRASS = 2; -const BLOCK_STONE = 3; +def BLOCK_AIR = 0; +def BLOCK_DIRT = 1; +def BLOCK_GRASS = 2; +def BLOCK_STONE = 3; // Physics Constants -const GRAVITY = 18.0; -const JUMP_FORCE = 8.0; -const MOVE_SPEED = 5.0; +def GRAVITY = 18.0; +def JUMP_FORCE = 8.0; +def MOVE_SPEED = 5.0; // ** World / Chunk ** @@ -413,7 +413,7 @@ impl Player { fn main() { rl::InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, TITLE); rl::SetWindowState(rl::FLAG_WINDOW_RESIZABLE); - defer rl::CloseWindow(); + defer { rl::CloseWindow(); } rl::SetTargetFPS(60); @@ -520,4 +520,4 @@ fn main() { rl::EndDrawing(); } -}
\ No newline at end of file +} @@ -56,7 +56,11 @@ void print_usage() int main(int argc, char **argv) { memset(&g_config, 0, sizeof(g_config)); +#ifdef __COSMOPOLITAN__ + strcpy(g_config.cc, "cosmocc"); +#else strcpy(g_config.cc, "gcc"); +#endif if (argc < 2) { @@ -339,9 +343,23 @@ int main(int argc, char **argv) char cmd[8192]; char *outfile = g_config.output_file ? g_config.output_file : "a.out"; - snprintf(cmd, sizeof(cmd), "%s %s %s %s %s -o %s %s -lm %s -I./src %s", g_config.cc, + const char *thread_flag = g_parser_ctx->has_async ? "-lpthread" : ""; + const char *math_flag = "-lm"; + +#ifdef _WIN32 + // Windows might use different flags or none for math/threads + math_flag = ""; + if (g_parser_ctx->has_async) + { + thread_flag = ""; + } +#endif + + // If using cosmocc, it handles these usually, but keeping them is okay for Linux targets + + snprintf(cmd, sizeof(cmd), "%s %s %s %s %s -o %s %s %s %s -I./src %s", g_config.cc, g_config.gcc_flags, g_cflags, g_config.is_freestanding ? "-ffreestanding" : "", "", - outfile, temp_source_file, g_parser_ctx->has_async ? "-lpthread" : "", g_link_flags); + outfile, temp_source_file, math_flag, thread_flag, g_link_flags); if (g_config.verbose) { @@ -368,7 +386,11 @@ int main(int argc, char **argv) if (g_config.mode_run) { char run_cmd[2048]; +#ifdef _WIN32 + sprintf(run_cmd, "%s", outfile); +#else sprintf(run_cmd, "./%s", outfile); +#endif ret = system(run_cmd); remove(outfile); zptr_plugin_mgr_cleanup(); diff --git a/src/parser/parser_stmt.c b/src/parser/parser_stmt.c index 0c3885a..aba6f5e 100644 --- a/src/parser/parser_stmt.c +++ b/src/parser/parser_stmt.c @@ -3179,8 +3179,16 @@ char *run_comptime_block(ParserContext *ctx, Lexer *l) char cmd[4096]; char bin[1024]; +#ifdef _WIN32 + sprintf(bin, "%s.exe", filename); +#else sprintf(bin, "%s.bin", filename); - sprintf(cmd, "gcc %s -o %s > /dev/null 2>&1", filename, bin); +#endif + sprintf(cmd, "%s %s -o %s", g_config.cc, filename, bin); + if (!g_config.verbose) + { + strcat(cmd, " > /dev/null 2>&1"); + } int res = system(cmd); if (res != 0) { @@ -3189,7 +3197,14 @@ char *run_comptime_block(ParserContext *ctx, Lexer *l) char out_file[1024]; sprintf(out_file, "%s.out", filename); + + // Platform-neutral execution +#ifdef _WIN32 + sprintf(cmd, "%s > %s", bin, out_file); +#else sprintf(cmd, "./%s > %s", bin, out_file); +#endif + if (system(cmd) != 0) { zpanic_at(lexer_peek(l), "Comptime execution failed"); diff --git a/src/repl/repl.c b/src/repl/repl.c index 22fe95d..274c14c 100644 --- a/src/repl/repl.c +++ b/src/repl/repl.c @@ -28,6 +28,12 @@ void run_repl(const char *self_path) char history_path[512]; const char *home = getenv("HOME"); +#ifdef _WIN32 + if (!home) + { + home = getenv("USERPROFILE"); + } +#endif if (home) { snprintf(history_path, sizeof(history_path), "%s/.zprep_history", home); @@ -262,7 +268,16 @@ void run_repl(const char *self_path) } char edit_path[256]; - sprintf(edit_path, "/tmp/zprep_edit_%d.zc", rand()); + const char *tmpdir = getenv("TEMP"); + if (!tmpdir) + { + tmpdir = getenv("TMP"); + } + if (!tmpdir) + { + tmpdir = "/tmp"; + } + snprintf(edit_path, sizeof(edit_path), "%s/zprep_edit_%d.zc", tmpdir, rand()); FILE *f = fopen(edit_path, "w"); if (f) { @@ -638,7 +653,17 @@ void run_repl(const char *self_path) strcat(probe_code, "); }"); char tmp_path[256]; - sprintf(tmp_path, "/tmp/zprep_repl_type_%d.zc", rand()); + const char *tmpdir = getenv("TEMP"); + if (!tmpdir) + { + tmpdir = getenv("TMP"); + } + if (!tmpdir) + { + tmpdir = "/tmp"; + } + snprintf(tmp_path, sizeof(tmp_path), "%s/zprep_repl_type_%d.zc", tmpdir, + rand()); FILE *f = fopen(tmp_path, "w"); if (f) { @@ -722,7 +747,17 @@ void run_repl(const char *self_path) strcat(code, "}"); char tmp_path[256]; - sprintf(tmp_path, "/tmp/zprep_repl_time_%d.zc", rand()); + const char *tmpdir = getenv("TEMP"); + if (!tmpdir) + { + tmpdir = getenv("TMP"); + } + if (!tmpdir) + { + tmpdir = "/tmp"; + } + snprintf(tmp_path, sizeof(tmp_path), "%s/zprep_repl_time_%d.zc", tmpdir, + rand()); FILE *f = fopen(tmp_path, "w"); if (f) { diff --git a/src/utils/utils.c b/src/utils/utils.c index df7576f..159326e 100644 --- a/src/utils/utils.c +++ b/src/utils/utils.c @@ -640,7 +640,7 @@ void scan_build_directives(ParserContext *ctx, const char *src) int res = system(cmd); if (res != 0) { - zpanic("Shell directive failed: %s", cmd); + zwarn("Shell directive failed: %s", cmd); } } else if (strncmp(line, "get:", 4) == 0) @@ -671,12 +671,17 @@ void scan_build_directives(ParserContext *ctx, const char *src) { printf("[zprep] Downloading %s...\n", filename); char cmd[8192]; +#ifdef _WIN32 + // On Windows, try curl which is often built-in now + sprintf(cmd, "curl -s -L \"%s\" -o \"%s\"", url, filename); +#else // Try wget, then curl. sprintf(cmd, "wget -q \"%s\" -O \"%s\" || curl -s -L \"%s\" -o \"%s\"", url, filename, url, filename); +#endif if (system(cmd) != 0) { - zpanic("Failed to download %s", url); + zwarn("Failed to download %s", url); } } } @@ -688,6 +693,12 @@ void scan_build_directives(ParserContext *ctx, const char *src) libs++; } +#ifdef _WIN32 + zwarn("pkg-config is usually not available on Windows. Build directive " + "'pkg-config:%s' might fail.", + libs); +#endif + char cmd[4096]; sprintf(cmd, "pkg-config --cflags %s", libs); FILE *fp = popen(cmd, "r"); @@ -695,18 +706,20 @@ void scan_build_directives(ParserContext *ctx, const char *src) { char flags[4096]; flags[0] = 0; - fgets(flags, sizeof(flags), fp); - pclose(fp); - int len = strlen(flags); - if (len > 0 && flags[len - 1] == '\n') + if (fgets(flags, sizeof(flags), fp)) { - flags[len - 1] = 0; + int len = strlen(flags); + if (len > 0 && flags[len - 1] == '\n') + { + flags[len - 1] = 0; + } + if (strlen(g_cflags) > 0) + { + strcat(g_cflags, " "); + } + strcat(g_cflags, flags); } - if (strlen(g_cflags) > 0) - { - strcat(g_cflags, " "); - } - strcat(g_cflags, flags); + pclose(fp); } sprintf(cmd, "pkg-config --libs %s", libs); @@ -715,18 +728,20 @@ void scan_build_directives(ParserContext *ctx, const char *src) { char flags[4096]; flags[0] = 0; - fgets(flags, sizeof(flags), fp); - pclose(fp); - int len = strlen(flags); - if (len > 0 && flags[len - 1] == '\n') - { - flags[len - 1] = 0; - } - if (strlen(g_link_flags) > 0) + if (fgets(flags, sizeof(flags), fp)) { - strcat(g_link_flags, " "); + int len = strlen(flags); + if (len > 0 && flags[len - 1] == '\n') + { + flags[len - 1] = 0; + } + if (strlen(g_link_flags) > 0) + { + strcat(g_link_flags, " "); + } + strcat(g_link_flags, flags); } - strcat(g_link_flags, flags); + pclose(fp); } } |
