Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ jobs:
.build/chad build examples/hello.ts -o /tmp/hello
/tmp/hello

- name: Run examples
run: bash scripts/run-examples.sh
timeout-minutes: 5

- name: Run self-hosting tests
run: node --import tsx --test tests/self-hosting.test.ts
timeout-minutes: 15
Expand Down Expand Up @@ -188,6 +192,10 @@ jobs:
.build/chad build examples/hello.ts -o /tmp/hello
/tmp/hello

- name: Run examples
run: bash scripts/run-examples.sh
timeout-minutes: 5

- name: Run self-hosting tests
run: node --import tsx --test tests/self-hosting.test.ts
timeout-minutes: 15
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

**[Documentation](https://cs01.github.io/ChadScript/)** · **[Benchmarks](https://cs01.github.io/ChadScript/benchmarks)** · **[GitHub Releases](https://github.com/cs01/ChadScript/releases)**

**A native compiler for TypeScript — no interpreter, no runtime, no VM.**
**As typesafe as Rust. As fast as C. As ergonomic as TypeScript.**

Your code goes through a full compilation pipeline: parse, type-check, emit LLVM IR, and link into a standalone native binary.
ChadScript is a systems programming language that uses TypeScript syntax and compiles directly to native binaries via LLVM. It is **not** a full TypeScript compiler — it's a statically-typed, natively-compiled dialect that shares TypeScript's syntax and feel while imposing stricter rules needed for native code (no generics, by-value closures, no `any`).

ChadScript is self-hosting - the compiler is written in TypeScript and compiles itself into a native binary that doesn't need any JavaScript runtime or Node.js.
Your code goes through a full compilation pipeline: parse, type-check, emit LLVM IR, and link into a standalone native binary with no Node.js, V8, or JavaScript runtime.

ChadScript is self-hosting — the compiler (~45k lines) is written in this same dialect and compiles itself to a native binary.

## Demo

Expand Down
8 changes: 6 additions & 2 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

## What is ChadScript?

ChadScript is a compiler that takes TypeScript source code and produces native ELF binaries via LLVM. It is not a runtime, interpreter, or transpiler. The output is a standalone executable with no dependencies on Node.js, V8, or any JavaScript engine.
ChadScript is a systems programming language that uses TypeScript syntax and compiles to native ELF binaries via LLVM. The goal: **as typesafe as Rust, as fast as C, as ergonomic as TypeScript**.

It is **not** a TypeScript compiler, runtime, interpreter, or transpiler. It's a new language that shares TypeScript's syntax and feel, but imposes stricter rules required for native compilation — no `any`, no user-defined generics, by-value closures, and interfaces are data-only structs. Think of it as a TypeScript-syntax dialect for systems programming.

The output is a standalone native binary with no dependencies on Node.js, V8, or any JavaScript engine.

## Is ChadScript a drop-in replacement for TypeScript?

No. ChadScript supports a practical subset of TypeScript. It compiles to native machine code, so all types must be known at compile time and dynamic features like `eval()` aren't available. See [Language Support](/language/features) for details.
No, and it's not trying to be. ChadScript uses TypeScript syntax as its surface language, but it's a different language with different semantics. Code that relies on runtime JS behavior — closures-by-reference, `any`, generics, `instanceof`, npm packages — won't compile. See [Language Support](/language/features) for what works.

## What TypeScript features are supported?

Expand Down
8 changes: 4 additions & 4 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
layout: home
hero:
name: ChadScript
text: TypeScript to Native Binaries
tagline: "A native compiler for TypeScript. Write TypeScript, ship a standalone binary."
text: As fast as C. As ergonomic as TypeScript.
tagline: "A natively-compiled systems language with TypeScript syntax. Write in a familiar style, ship a standalone binary with no runtime."
actions:
- theme: brand
text: Get Started
Expand All @@ -15,8 +15,8 @@ hero:
features:
- title: No Runtime
details: Compiles to standalone ELF binaries that start in under 2ms.
- title: Familiar Syntax
details: Standard TypeScript syntax — classes, interfaces, async/await, closures.
- title: TypeScript Syntax
details: Uses TypeScript's syntax — classes, interfaces, async/await, closures. Not a full TS compiler; a natively-compiled dialect.
- title: Batteries Included
details: Everything you'd npm install — HTTP, SQLite, fetch, crypto, JSON — is built in. No dependencies.
- title: Single-Binary Deploy
Expand Down
254 changes: 254 additions & 0 deletions scripts/run-examples.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
#!/usr/bin/env bash
# Run all compilable examples and verify they work.
# Server examples are started, verified with curl, then killed.
# Usage: bash scripts/run-examples.sh [--compiler <path>]

set -euo pipefail

COMPILER="${COMPILER:-.build/chad}"
BUILD_DIR="/tmp/chadscript-examples"
PASSED=0
FAILED=0
FAILURES=""

# Parse --compiler flag
while [[ $# -gt 0 ]]; do
case "$1" in
--compiler) COMPILER="$2"; shift 2 ;;
*) echo "Unknown option: $1"; exit 1 ;;
esac
done

# Validate compiler exists — supports both a binary path and "node dist/chad-node.js"
COMPILER_FIRST_WORD="${COMPILER%% *}"
if [ ! -f "$COMPILER_FIRST_WORD" ] && ! command -v "$COMPILER_FIRST_WORD" &>/dev/null; then
echo "Compiler not found: $COMPILER"
echo "Build it first: node dist/chad-node.js build src/chad-native.ts -o .build/chad"
exit 1
fi

mkdir -p "$BUILD_DIR"

# --- Helpers ---

pass() {
echo " PASS: $1"
PASSED=$((PASSED + 1))
}

fail() {
echo " FAIL: $1 — $2"
FAILED=$((FAILED + 1))
FAILURES="${FAILURES}\n $1: $2"
}

compile() {
local src="$1"
local out="$2"
# Use eval-free word splitting so "node dist/chad-node.js" works
if ! $COMPILER build "$src" -o "$out" 2>&1; then
return 1
fi
return 0
}

# Wait for a server to respond on a port (up to N seconds)
wait_for_server() {
local port="$1"
local max_wait="${2:-5}"
local i=0
while [ $i -lt $max_wait ]; do
if curl -s -o /dev/null -w '' "http://localhost:$port/" 2>/dev/null; then
return 0
fi
sleep 1
i=$((i + 1))
done
return 1
}

echo "=== ChadScript Examples Runner ==="
echo "Compiler: $COMPILER"
echo "Build dir: $BUILD_DIR"
echo ""

# --- 1. hello.ts (simple print) ---

echo "[1/8] hello.ts"
if compile examples/hello.ts "$BUILD_DIR/hello"; then
OUTPUT=$("$BUILD_DIR/hello" 2>&1) || true
if echo "$OUTPUT" | grep -q "Hello from ChadScript"; then
pass "hello.ts"
else
fail "hello.ts" "unexpected output: $OUTPUT"
fi
else
fail "hello.ts" "compile failed"
fi

# --- 2. timers.ts (event loop, self-terminating) ---

echo "[2/8] timers.ts"
if compile examples/timers.ts "$BUILD_DIR/timers"; then
# No `timeout` on macOS — use background process + wait with a deadline
"$BUILD_DIR/timers" > "$BUILD_DIR/timers.out" 2>&1 &
TIMER_PID=$!
( sleep 10; kill "$TIMER_PID" 2>/dev/null ) &
WATCHDOG_PID=$!
wait "$TIMER_PID" 2>/dev/null || true
kill "$WATCHDOG_PID" 2>/dev/null || true
wait "$WATCHDOG_PID" 2>/dev/null || true
OUTPUT=$(cat "$BUILD_DIR/timers.out")
if echo "$OUTPUT" | grep -q "tick 3"; then
pass "timers.ts"
else
fail "timers.ts" "didn't see tick 3: $OUTPUT"
fi
else
fail "timers.ts" "compile failed"
fi

# --- 3. cli-parser-demo.ts (argparse) ---

echo "[3/8] cli-parser-demo.ts"
if compile examples/cli-parser-demo.ts "$BUILD_DIR/cli-parser-demo"; then
OUTPUT=$("$BUILD_DIR/cli-parser-demo" -v -o result.txt myfile.txt 2>&1) || true
if echo "$OUTPUT" | grep -q "verbose"; then
pass "cli-parser-demo.ts"
else
fail "cli-parser-demo.ts" "unexpected output: $OUTPUT"
fi
else
fail "cli-parser-demo.ts" "compile failed"
fi

# --- 4. query.ts (sqlite in-memory) ---

echo "[4/8] query.ts"
if compile examples/query.ts "$BUILD_DIR/query"; then
OUTPUT=$("$BUILD_DIR/query" 2>&1) || true
if echo "$OUTPUT" | grep -q "Found"; then
pass "query.ts"
else
fail "query.ts" "unexpected output: $OUTPUT"
fi
else
fail "query.ts" "compile failed"
fi

# --- 5. word-count.ts (file I/O) ---

echo "[5/8] word-count.ts"
if compile examples/word-count.ts "$BUILD_DIR/word-count"; then
# Create a test file to count
echo "hello world foo bar" > "$BUILD_DIR/test-input.txt"
OUTPUT=$("$BUILD_DIR/word-count" "$BUILD_DIR/test-input.txt" 2>&1) || true
if echo "$OUTPUT" | grep -q "words"; then
pass "word-count.ts"
else
fail "word-count.ts" "unexpected output: $OUTPUT"
fi
else
fail "word-count.ts" "compile failed"
fi

# --- 6. string-search.ts (grep-like) ---

echo "[6/8] string-search.ts"
if compile examples/string-search.ts "$BUILD_DIR/string-search"; then
# Create a test file to search
printf "line one\nfind me here\nline three\n" > "$BUILD_DIR/search-input.txt"
OUTPUT=$("$BUILD_DIR/string-search" "find" "$BUILD_DIR/search-input.txt" 2>&1) || true
if echo "$OUTPUT" | grep -q "find"; then
pass "string-search.ts"
else
fail "string-search.ts" "unexpected output: $OUTPUT"
fi
else
fail "string-search.ts" "compile failed"
fi

# --- 7. http-server.ts (server + curl) ---

echo "[7/8] http-server.ts"
if compile examples/http-server.ts "$BUILD_DIR/http-server"; then
PORT=18080
"$BUILD_DIR/http-server" -p "$PORT" &
SERVER_PID=$!

if wait_for_server "$PORT" 5; then
# Test root endpoint
RESP=$(curl -s "http://localhost:$PORT/")
if echo "$RESP" | grep -q "ChadScript"; then
# Test JSON endpoint
RESP2=$(curl -s "http://localhost:$PORT/json")
if echo "$RESP2" | grep -q "message"; then
# Test POST echo
RESP3=$(curl -s -X POST -d 'hello world' "http://localhost:$PORT/echo")
if echo "$RESP3" | grep -q "hello world"; then
pass "http-server.ts"
else
fail "http-server.ts" "POST /echo failed: $RESP3"
fi
else
fail "http-server.ts" "GET /json failed: $RESP2"
fi
else
fail "http-server.ts" "GET / failed: $RESP"
fi
else
fail "http-server.ts" "server didn't start on port $PORT"
fi

kill "$SERVER_PID" 2>/dev/null || true
wait "$SERVER_PID" 2>/dev/null || true
else
fail "http-server.ts" "compile failed"
fi

# --- 8. hackernews/app.ts (full-stack server + curl) ---

echo "[8/8] hackernews/app.ts"
if compile examples/hackernews/app.ts "$BUILD_DIR/hackernews"; then
PORT=18081
"$BUILD_DIR/hackernews" -p "$PORT" &
SERVER_PID=$!

if wait_for_server "$PORT" 5; then
# Test API endpoint
RESP=$(curl -s "http://localhost:$PORT/api/posts")
if echo "$RESP" | grep -q "ChadScript"; then
# Test HTML page
RESP2=$(curl -s "http://localhost:$PORT/")
if echo "$RESP2" | grep -q "html"; then
pass "hackernews/app.ts"
else
fail "hackernews/app.ts" "GET / didn't return HTML: ${RESP2:0:100}"
fi
else
fail "hackernews/app.ts" "GET /api/posts failed: ${RESP:0:100}"
fi
else
fail "hackernews/app.ts" "server didn't start on port $PORT"
fi

kill "$SERVER_PID" 2>/dev/null || true
wait "$SERVER_PID" 2>/dev/null || true
else
fail "hackernews/app.ts" "compile failed"
fi

# --- Summary ---

echo ""
echo "=== Results ==="
echo "Passed: $PASSED / $((PASSED + FAILED))"
echo "Failed: $FAILED / $((PASSED + FAILED))"

if [ $FAILED -gt 0 ]; then
echo -e "\nFailures:$FAILURES"
exit 1
fi

echo ""
echo "All examples passed!"
Loading