summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriryuken <eshwarsajja20@gmail.com>2026-01-25 23:57:07 +0530
committerGitHub <noreply@github.com>2026-01-25 23:57:07 +0530
commita2804ffe502ec31fc3bf561b7e59c25622b15e19 (patch)
tree3fb732563d6a282e010bca2d3acb97eb351c925f
parent123a961710aab45f30367cb8faa2c8ce00e49bdb (diff)
parente3ab29bb4d7174cae65de2275f19105eb3d93d91 (diff)
Merge branch 'z-libs:main' into main
-rw-r--r--.github/workflows/build-ape.yml45
-rw-r--r--.gitignore3
-rw-r--r--Makefile123
-rw-r--r--README.md28
-rw-r--r--ape/boot/.args23
-rw-r--r--ape/boot/Makefile14
-rw-r--r--ape/boot/boot.zc118
-rw-r--r--ape/boot/hello.zc4
-rw-r--r--ape/boot/instructions.txt6
-rw-r--r--ape/zc_entry.c257
-rw-r--r--examples/games/zen_craft/main.zc24
-rw-r--r--src/main.c26
-rw-r--r--src/parser/parser_stmt.c17
-rw-r--r--src/repl/repl.c41
-rw-r--r--src/utils/utils.c59
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
diff --git a/.gitignore b/.gitignore
index 4019c72..e3675f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/Makefile b/Makefile
index 48ba909..977542c 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README.md b/README.md
index ce72729..91634a3 100644
--- a/README.md
+++ b/README.md
@@ -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
+}
diff --git a/src/main.c b/src/main.c
index 3f6a51f..62bb98d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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);
}
}