No description
Find a file
Orkuncakilkaya 6e6467b515
Some checks are pending
docs / build-and-deploy (push) Waiting to run
feat(stdlib): Math.exp/log/pow + Ease.elastic transcendentals (issue #25)
Finish the unblocked "Math / Ease" half of #25: the fixed-point
transcendentals deferred from #2. Vec.* stays blocked on the vec2 type
in #1.

- Math.exp, Math.log (natural), Math.pow — deterministic Q16.16 via two
  new prelude fns in emit_math_prelude: @fn_fx_exp2 (range-reduced 5th-order
  Taylor 2^f, then a clamped shift by the integer part) and @fn_fx_log2
  (llvm.ctlz for the exponent + an atanh series on (m-1)/(m+1) for the
  mantissa). exp=2^(x·log2 e), log=log2(x)·ln2, pow=2^(b·log2 a).
- Ease.elastic — ease-out elastic 2^(-10t)·sin((10t-0.75)·2pi/3)+1.
- Pure integer IR, so bit-identical on every platform. Results must fit the
  Q16.16 range (|x| < 32768); larger magnitudes saturate (documented).

Test selfhost/tests/transcend.ludic (registered in the self-host suite) +
docs for all four. Reseeded; the C-free bootstrap fixpoint holds. All suites
green (24 self-host, 45 regression, 29 tool).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-30 01:19:44 +03:00
.claude Baseline: Ludic compiler + toolchain, Phase 1 syntax fixes complete 2026-08-27 15:15:35 +03:00
.forgejo/workflows ci(docs): build in a python:3.12 container, clone source directly 2026-08-29 16:33:25 +03:00
assets Baseline: Ludic compiler + toolchain, Phase 1 syntax fixes complete 2026-08-27 15:15:35 +03:00
docs feat(stdlib): Math.exp/log/pow + Ease.elastic transcendentals (issue #25) 2026-08-30 01:19:44 +03:00
examples feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00
runtime feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00
selfhost feat(stdlib): Math.exp/log/pow + Ease.elastic transcendentals (issue #25) 2026-08-30 01:19:44 +03:00
tools feat(stdlib): Math.exp/log/pow + Ease.elastic transcendentals (issue #25) 2026-08-30 01:19:44 +03:00
.gitignore feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00
BOOTSTRAP.md feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00
COMPILING.md feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00
EVENTS-DESIGN.md feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00
LANGUAGE.md feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00
LIFECYCLE-DESIGN.md feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00
LUANTI-ROADMAP.md feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00
MOBILE-DESIGN.md feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00
NETWORKING-DESIGN.md feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00
README.md feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00
SCENES-DESIGN.md feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00
SYNTAX-REDESIGN.md feat(stdlib): namespaced standard library (issue #2) 2026-08-30 00:26:19 +03:00

Ludic

An AI-first, ahead-of-time compiled game language with an ECS core, a deterministic fixed-point runtime, and a native 2D backend. ludicc lowers Ludic to LLVM IR itself and emits a native binary — and ludicc is itself written in Ludic.

  .ludic  ──►  ludicc  ──►  LLVM IR  ──►  object  ──►  native binary
             (in Ludic)

No C is generated, compiled or linked anywhere in a build. There is no interpreter, no transpiler, and no C runtime: the framebuffer, sprites, PNG decoding, TrueType text, the retained UI, the registers and the RNG are all written in Ludic (runtime/native/*.ludic), and the macOS window is hand-written LLVM IR (runtime/native/cocoa.ll). Beneath that sits only the platform's own ABI — malloc, fwrite, objc_msgSend, CoreGraphics — reached by compiler intrinsics, the same floor Rust and Swift stand on.

The compiler is written in Ludic. selfhost/*.ludic is a Ludic compiler — lexer, parser and LLVM-IR backend — that compiles every example (including the 6-file JRPG) to the byte-exact same binary the original C compiler produced, and compiles its own source to a fixpoint. It is built from a checked-in IR seed (selfhost/ludicc.seed.ll) with clang alone; run bin/x bootstrap-cfree to rebuild it with no C compiler in the loop. The former C compiler is gone.

Neither the compiler nor the games are C. Beneath both sits only the platform's own ABI — malloc, fwrite, objc_msgSend, CoreGraphics — reached by intrinsics, the same floor Rust and Swift stand on. (The wasm/cross-compile/shared-library driver paths lived in the old C compiler and are not yet re-implemented on the self-hosted native toolchain.)

Layout

Path What it is
selfhost/*.ludic the compiler, written in Ludic — lexer, parser, and the LLVM-IR backend (ECS storage, queries, spawn, match/machine, UI, scenes, save/load, fixed-point). Concatenated by bin/x selfhost-build; built from selfhost/ludicc.seed.ll
tools/x/*.ludic the task runner, written in Ludic — one binary (bin/x) that builds, tests, bootstraps and reseeds the whole project, replacing every shell script. bin/x bootstrap-cfree rebuilds the compiler from the IR seed with no C compiler and proves it reproduces its own IR
runtime/native/core.ludic the runtime written in Ludic for the native path (framebuffer, text, registers, RNG, input)
COMPILING.md the native pipeline: ludicc → LLVM IR → exe/dylib, module/export, cross-compilation, rt_* intrinsics
runtime/native/image.ludic PNG decoding, images, sprites, alpha blending and 9-slice — in Ludic
runtime/native/inflate.ludic DEFLATE decompression (RFC 1951), so PNG needs no zlib on any target
runtime/native/truetype.ludic from-scratch TrueType loader + antialiased glyph rasterizer, in Q16.16
runtime/native/ui.ludic retained UI widget tree: layout, 9-slice, focus, events — in Ludic
runtime/native/cocoa.ll the macOS window, written in LLVM IR (Objective-C runtime + CoreGraphics via their C ABI)
runtime/web/wasm.ll the web's platform layer in LLVM IR: the allocator, bulk memory and strings, since wasm32 has no libc
runtime/web/platform.js the browser's window — the same five win_* functions cocoa.ll implements, against a <canvas>
runtime/web/index.html the page a web build is served from
tools/ludic-web/run.mjs runs a headless wasm build under Node, so native and wasm output can be diffed
examples/chronorift.ludic the JRPG written in Ludic (multi-file via import, model-based)
examples/menu.ludic a retained-UI title screen (9-slice, TrueType, focusable buttons)
examples/snake.ludic a second, unrelated game — proves the language is general (same toolchain, no engine hardcoding)
bin/x the task runner — bin/x app examples/<name>.ludic compiles a program; bin/x help lists every command
bin/x test regression suite: builds the compiler, compiles/runs all examples, checks save/load + diagnostics
tools/ludic-tools/ the editor toolchain, in Ludic: ludic-fmt (source formatter) and ludic-lsp (language server) — one lexer and one vocabulary shared by both
tools/editors/ plugins for VS Code and JetBrains, plus configuration for Neovim, Helix, Emacs, Sublime and Zed (README)
bin/x test-tools regression suite for the toolchain: proves formatting never changes a program, drives the language server over real LSP traffic

Build & run

Everything is driven by bin/x, the project's task runner — one native binary, written in Ludic and compiled by Ludic, that replaces every build/test/bootstrap shell script. Bootstrap it once from a clean checkout (the only step Ludic can't do for itself, since compiling Ludic needs a compiler):

clang selfhost/ludicc.seed.ll -o bin/ludicc && bin/ludicc tools/x/main.ludic -o bin/x

Then bin/x build builds the whole toolchain into bin/ — including bin/x itself — and bin/x help lists every command. ludicc compiles Ludic straight to machine code via LLVM IR; see COMPILING.md for the pipeline and the runtime protocol.

bin/x app examples/chronorift.ludic
./build/chronorift            # opens a native window

Headless render (for testing / CI):

bin/x app examples/chronorift.ludic --headless
printf 'ddddwww' | ./build/chronorift_headless   # writes out.ppm
sips -s format png out.ppm --out frame.png

(The shared-library and wasm/web build paths lived in the old C compiler and are not yet re-implemented on the self-hosted native toolchain, so bin/x app builds native windowed and --headless targets today. See COMPILING.md for the pipeline and the runtime protocol.)

Editor support

bin/x tools                     # -> bin/ludic-fmt, bin/ludic-lsp

ludic-lsp speaks LSP 3.17 over stdio, so one binary serves every editor: completion that knows whether you are after a ., inside a query [...] or in a ui block; diagnostics from the compiler itself; go-to-definition and rename across imported files; comment-preserving formatting. ludic-fmt is the same formatter as a CLI, for pre-commit hooks and CI.

Plugins for VS Code and JetBrains IDEs (Community editions included) and drop-in configuration for Neovim, Helix, Emacs, Sublime and Zed are in tools/editors/. Both tools also understand ```ludic fences in Markdown, so this file and LANGUAGE.md get the same highlighting, checking and formatting as the source tree.

Language features implemented

  • property (typed fields + defaults), system (phase, @annotations, reads/writes clauses), const, fn (with requires/ensures parsed).
  • model — named entity kinds (bundles of properties); identity is one int per entity, replacing empty tag properties. Filter with {Kind}.
  • import "file" — multi-file programs (fragments spliced in, include-guarded, per-file diagnostics).
  • ECS queries: for (a, b) in query [A, B, {Tag}] where <expr> { … }.
  • spawn/despawn with entity-slot reuse; nested queries.
  • Control flow: if/else, when, while, numeric for i in a .. b.
  • Types: int, fixed (Q16.16, with correct *// lowering), bool, entity, str. Fixed-point vs int arithmetic is resolved by the typechecker.
  • Deterministic seeded RNG; save()/load() snapshot of the whole ECS World.
  • 2D primitives: clear, fill_rect, frame_rect, put_px, present, key.
  • TrueType text (font_load, text_ttf) with full Unicode + anti-aliasing; arbitrary-size images + draw_9slice.
  • ui — a retained widget tree declared as data (panels, labels, buttons, images; layout, 9-slice skins, keyboard focus + click events).
  • match / machine+state+become — dispatch and state machines.
  • scene / layer / enter — mutually-exclusive game states, each with on enter/on exit hooks and layered systems (layer order = draw order).
  • var — typed module-level state, included in save/load snapshots.
  • module + @export fn — compile a .ludic file to a shared library whose exported functions are ordinary C-ABI symbols.
  • extern fn … = "symbol" — call any C-ABI library, Ludic or otherwise.
  • --target wasm32-unknown-unknown — the same game in a browser: the runtime, the ECS and the graphics stack compiled to wasm, rendering frames identical to the native build's.

The game: Chrono Rift

A playable co-op JRPG in examples/chronorift.ludic, using CC0 Kenney sprites (Tiny Town + Tiny Dungeon), decoded from PNG at runtime by the Ludic-written PNG/DEFLATE decoder — no zlib, no external dependency on any target.

Controls:

  • Overworld: WASD move, K save, L load.
  • Battle (local co-op): Player 1 / Knight — W/S select, Space confirm. Player 2 / Mage — I/K select, J confirm. Each player takes their own turn each round (Attack / Defend / Run; Mage has Attack / Heal / Defend).

Status

  • Compiler pipeline: Ludic → LLVM IR → native binary / shared library
  • No C generated, compiled or linked in a build; runtime written in Ludic
  • Cross-compilation to ELF (x86-64, aarch64) and Windows COFF
  • ECS runtime (properties, systems, phases, queries, entity pooling)
  • Windowed 2D rendering (Cocoa driven from LLVM IR) + headless PPM verification
  • CC0 Kenney PNG sprites (png_load, decoder written in Ludic) + scrolling camera
  • Overworld: tilemap, movement, collision
  • Random encounters + turn-based battle (HP/MP, seeded-RNG damage)
  • Party + local co-op (P1 Knight, P2 Mage, per-player turns)
  • Leveling (XP → stat growth) and game-over / respawn
  • Self-hosting: a Ludic-written compiler compiles its own source to a byte-exact fixpoint, and rebuilds itself from a checked-in IR seed with no C compiler in the loop (selfhost/, bin/x bootstrap-cfree)
  • Snapshot save / load (full ECS World)
  • Two maps (overworld + dungeon) with map switching via the arch
  • Boss encounter (Rift Warden) + victory condition
  • Item shop (gold → potions) at the house; potions usable in battle (Knight ITEM)
  • More skills / enemy variety (future)