No description
Find a file
Orkuncakilkaya e3e1bc7784 fix(tools): formatter bracket rules, LSP compiler diagnostics, vocabulary sync
- ludic-fmt: `rows[i]`, `new []int`, `s[a..b]`, `emit(`, `~x`, list-literal
  and query-tag braces stay tight; member calls hug their paren
  (`Date.new(`, `Prefab.spawn(`); `<< >> & | ^ ~` are operators and
  `&& ||` are not — mirrored in ludic_syntax.h, LudicLexer.kt and the
  TextMate grammar. 149 of 274 tracked sources failed --check before.
- ludic-lsp: `initializationOptions.compilerDiagnostics` / `compilerPath`
  are honoured — on save the document's compilation unit is compiled and
  `file:line: error: msg` is published as a "ludicc" diagnostic (the
  option had been documented but never implemented); the workspace scan
  file is per process; bracket codes spelled as char literals
- Overlay added to LUDIC_PHASES, LudicTokens.kt, ludic-mode.el and the
  grammar (the game uses it; check-vocabulary flagged the drift)
- editors: VS Code snippets/package.json/extension.js (`${workspaceFolder}`
  and `~` expansion, PATH lookup, Apache-2.0, real repo URL), Neovim root
  markers, Helix/Zed/Sublime bin/ paths, Emacs vocabulary, JetBrains
  comments; tools/editors/README.md no longer describes C tooling
- .forgejo workflows clone `${{ github.server_url }}/${{ github.repository }}`
  so a fork or mirror tests itself; the docs publish pushes the same way

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 01:12:26 +03:00
.claude Baseline: Ludic compiler + toolchain, Phase 1 syntax fixes complete 2026-08-27 15:15:35 +03:00
.forgejo fix(tools): formatter bracket rules, LSP compiler diagnostics, vocabulary sync 2026-09-05 01:12:26 +03:00
assets feat(stdlib): Tiled P6 — infinite/chunked maps, .world stitching, base64+zstd (#74) 2026-09-01 16:07:10 +03:00
changes feat(compiler): list literals, typed compound assignment, file:line diagnostics 2026-09-05 01:12:16 +03:00
docs feat(compiler): list literals, typed compound assignment, file:line diagnostics 2026-09-05 01:12:16 +03:00
examples feat(compiler): list literals, typed compound assignment, file:line diagnostics 2026-09-05 01:12:16 +03:00
packages feat(engine): #90 atlas-aware Sprite component, #91 become from listeners, 0.3.x ergonomics batch 2026-09-04 01:36:08 +03:00
runtime feat(engine): #90 atlas-aware Sprite component, #91 become from listeners, 0.3.x ergonomics batch 2026-09-04 01:36:08 +03:00
selfhost feat(compiler): list literals, typed compound assignment, file:line diagnostics 2026-09-05 01:12:16 +03:00
tools fix(tools): formatter bracket rules, LSP compiler diagnostics, vocabulary sync 2026-09-05 01:12:26 +03:00
.editorconfig chore(repo): add .editorconfig, reconcile .gitignore, add x clean 2026-08-30 16:46:10 +03:00
.gitignore feat(pkg): package manager — fetch + MVS resolve + namespace registration (#63) 2026-09-01 07:08:12 +03:00
CHANGELOG.md chore(release): v0.3.0 2026-09-02 08:37:20 +03:00
CODE_OF_CONDUCT.md docs: add CONTRIBUTING, code of conduct, and Forgejo templates 2026-08-30 16:46:30 +03:00
COMPILING.md docs: move design/roadmap docs to the wiki, trim the repo root 2026-08-31 00:36:19 +03:00
CONTRIBUTING.md feat(release): SemVer + ludicc --version, changesets, and x release 2026-08-30 23:46:59 +03:00
LANGUAGE.md feat(engine): #90 atlas-aware Sprite component, #91 become from listeners, 0.3.x ergonomics batch 2026-09-04 01:36:08 +03:00
LICENSE docs: add Apache-2.0 LICENSE for the compiler/runtime source 2026-08-30 16:46:39 +03:00
README.md docs: 0.2 -> 0.3 migration guide 2026-09-02 08:37:13 +03:00
VERSION chore(release): v0.3.0 2026-09-02 08:37:20 +03:00

Ludic

Ludic is an ahead-of-time compiled language for 2D games with an entity-component core, a deterministic fixed-point runtime, and its graphics stack built into the language. ludicc lowers Ludic straight to LLVM IR and emits a native binary — and ludicc is itself written in Ludic, compiles its own source to a byte-exact fixpoint, and rebuilds from a checked-in IR seed with no C compiler in the loop.

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

No C is generated, compiled or linked in a build. No interpreter, no transpiler, no C runtime: the framebuffer, sprites, PNG/DEFLATE decoding, TrueType text, the retained UI, the registers and the RNG are all written in Ludic (runtime/native/*.ludic); only the window seam — five win_* functions — is hand-written LLVM IR against the platform ABI (runtime/native/cocoa.ll), the same floor Rust and Swift stand on.

Backends

Backend Status
Native 2D (macOS/Cocoa window; headless render for CI) Shipping — the default bin/x app target.
Web / wasm32 In progress. The browser platform layer is in-tree and documented — runtime/web/ (the <canvas> window platform.js, the libc-free wasm.ll floor) and a Node harness that diffs native vs. wasm frame-for-frame (tools/ludic-web/run.mjs). Emitting wasm was a capability of the retired C compiler and is not yet re-wired on the self-hosted toolchain; see COMPILING.md.

The same is true of --target cross-compilation and --shared libraries: both are designed and documented, both lived in the old C compiler, and both are pending re-implementation on the self-hosted native toolchain.

Quick start

bin/x is 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) with clang alone:

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

Then build the whole toolchain and run a game:

bin/x build                                  # -> bin/{ludicc,ludic,x,ludic-fmt,ludic-lsp}
bin/x app examples/games/snake.ludic         # compile + open a native window
./build/snake

Render a frame headlessly (what CI checks) — output lands in build/, never the repo root:

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

Run the suites:

bin/x test            # full regression: compiler builds from seed, every example, golden renders
bin/x selfhost-test   # correctness + the self-hosting / C-free bootstrap fixpoints
bin/x help            # every command

Add a dependency (URL-as-identity, MVS resolution, content-addressed store):

bin/x add git.workshopsoft.io/user/pkg   # resolve + fetch + link into ludic_modules/
bin/x get                                # install everything in package.ludic, write the lock
bin/x verify                             # check the locked packages against the store

See docs/PACKAGES.md for the manifest, lockfile and package model.

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). Built from selfhost/ludicc.seed.ll with clang alone.
selfhost/golden/renders.sha256 text baseline of render-output hashes (replaces binary .ppm fixtures); regenerate with bin/x golden.
tools/x/*.ludic the task runner, written in Ludic — one binary (bin/x) that builds, tests, bootstraps and reseeds the project, replacing every shell script.
runtime/native/ the runtime in Ludic for the native path: core (framebuffer, input, RNG), image/inflate (PNG + DEFLATE, no zlib), truetype (glyph rasterizer), ui (retained widget tree); plus cocoa.ll, the macOS window seam in LLVM IR.
runtime/web/ the browser platform layer: platform.js (the <canvas> window), wasm.ll (the libc-free floor), index.html.
examples/ the example tour, grouped by intent — games/, rendering/, ecs/, events/, networking/, lang/, library/. See examples/README.md.
tools/ludic-tools/ the editor toolchain in Ludic: ludic-fmt (formatter) and ludic-lsp (language server) — one lexer, one vocabulary shared by both.
tools/editors/ plugins for VS Code and JetBrains, plus config for Neovim, Helix, Emacs, Sublime and Zed.
docs/ the per-symbol API reference, regenerated into the docs site.
COMPILING.md the native pipeline: ludicc → LLVM IR → exe, the rt_* runtime protocol, and the (pending) wasm/cross-compile/shared-library paths.

The design and roadmap material lives on the wiki: the Events, Networking, Scenes, Lifecycle, Mobile and Syntax-redesign design records, the Bootstrap deep-dive, and the Luanti roadmap. The root keeps only this README plus the two user-facing references, LANGUAGE.md and COMPILING.md.

Language at a glance

  • program / property (typed fields + defaults) / model (named entity kinds) / system (phase, @annotations, reads/writes).
  • ECS queries for (a, b) in query [A, B, {Tag}] where <expr> { … }, spawn/despawn with slot reuse, @-driven lifecycle hooks.
  • An event bus (event / emit / @On, cancellable, @Public promotion) and networking primitives (@Sync, ownership, RPCs) over a built-in loopback transport — all deterministic, all pure Ludic.
  • scene / layer / become, match / machine + state.
  • Types int, fixed (Q16.16), bool, entity, str, byte, typed buffers; a growing namespaced standard library (Math, Vector, Time/Date/ Duration/Clock, Random, Hash, Crypto, sorting, …).
  • Deterministic seeded RNG and save()/load() snapshot of the whole World.
  • Built-in 2D: framebuffer primitives, PNG sprites, TrueType text, 9-slice, and a retained ui widget tree declared as data.

See LANGUAGE.md for the full reference, and examples/README.md for runnable demos of each feature.

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: context-aware completion, diagnostics from the compiler itself, go-to-definition and rename across imported files, and comment-preserving formatting. ludic-fmt is the same formatter as a CLI, for pre-commit hooks and CI. Both also understand ```ludic fences in Markdown. Plugins and drop-in config are in tools/editors/.

Chrono Rift — the flagship game

examples/games/chronorift.ludic is a playable co-op JRPG — overworld, dungeon, random encounters, a turn-based co-op battle, a boss, an item shop and snapshot save/load — split across modules under games/chronorift/. Its art is CC0 Kenney sprites, decoded from PNG at runtime by the Ludic-written PNG/DEFLATE decoder — no zlib, no external dependency.

  • Overworld: WASD move, K save, L load.
  • Battle (local co-op): P1/Knight W/S select, Space confirm; P2/Mage I/K select, J confirm.

Status & roadmap

The compiler self-hosts to a byte-exact fixpoint and rebuilds from its IR seed with no C compiler; the ECS runtime, windowed + headless 2D rendering, the event bus, the deterministic networking stack, scenes, and save/load are all in place and covered by bin/x test. CI gates every push and PR on the build, the test suites, and that C-free fixpoint. The toolchain is versioned with SemVer (ludicc --version); releases and the CHANGELOG.md are cut from changesets by x release. Upgrading from 0.2? See the 0.2 → 0.3 migration guide.

Active work and proposals — the standard library, a fuller type system, rendering/animation/lighting extras, input, filesystem/IO, testing, and re-wiring the web/wasm and cross-compile backends — are tracked as issues, not inlined here:

Contributing

See CONTRIBUTING.md for the development loop (bin/x reseed → bin/x bootstrap-cfree → bin/x test), the code and commit conventions, and how the bootstrap fixpoint works. Issue and pull-request templates live under .forgejo/.

License

The Ludic compiler and runtime source are licensed under the Apache License 2.0 (SPDX-License-Identifier: Apache-2.0) — a permissive license with an explicit patent grant.

The bundled Kenney art under assets/kenney/ is third-party and released under CC0 1.0 (public domain); each pack keeps its own License.txt. Code and assets are licensed separately: Apache-2.0 covers the source, not the art.