Commit graph

4 commits

Author SHA1 Message Date
bc301c8d17 feat(engine): Tiled P1 — rt_tmap model + GID resolver + render + projection (#69)
All checks were successful
bootstrap / cfree-fixpoint (push) Successful in 20s
ci / build-and-test (push) Successful in 1m48s
commit-lint / conventional-commits (push) Successful in 3s
docs / build-and-deploy (push) Successful in 27s
The heart of Tiled support: load a map and draw it.

- rt_tmap model (Tmap/TmLayer/TmTileset in tiled.ludic): map header + ordered
  layers (dense int32 GID arrays, heap-allocated to w*h, lifting the 96x64 cap)
  + tilesets. Built from the intermediate Value tree, so the TMX and TMJ paths
  both feed it.
- GID resolver (Tiled.resolve): gid -> (tileset, localId, flipH/V/D); the three
  flip flags masked off before the local-id lookup, returned alongside. gid==0
  is empty.
- Image-backed render (Tiled.draw): every visible tile layer in file order,
  blitting each tile from its tileset image with flips applied at draw.
- Compatibility projection (Tiled.project / auto on load): a designated
  collision layer projects to the legacy byte tilemap ('#' solid, '=' one-way
  via the oneway property, ' ' empty) so Grid.*/Path.*/esys_move are unchanged.
- Tiled.load resolves external tilesets + images relative to the map file and
  auto-projects a collision/solids/walls layer.
- The grid and physics_tiles demos now run off a loaded map (grid_maze.tmx /
  physics_map.tmx) instead of hand-authored Map.row strings, byte-identically.

Two compiler fixes fell out of this (see the changeset):
- emit_index_addr set g_addr_ty before evaluating the index, so slice[obj.field]
  came back mis-typed; set it last, like the raw-pointer branches.
- @strcmp was declared by both the world table and the fs prelude; centralise
  it in the head prelude so a game that uses Fs/Path links.

Proven by library/tiled_p1.ludic (14 assertions: loads+renders Kenney map
identically from .tmx and .tmj, flip mirroring) + the converted grid/
physics_tiles demos. x test: 92 passed; self-host bootstrap fixpoint intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-01 14:43:29 +03:00
e1537d2d45 feat(compiler): dynamic system registration + --emit-module for binary packages (#64)
The runtime + compiler foundation for prebuilt binary packages, over the
existing reflection C-ABI (EV0–EV8 already gives dynamic components via
ludic_register_prop).

- ludic_register_system(fn, phase) + a registry the frame loop dispatches after
  each phase's own handlers — the systems analogue of ludic_register_prop,
  mirroring the EV6 foreign event-listener array. Emitted for every ECS program;
  a zero-length registry means a non-hosting game is output-identical.
- --emit-module: compile a package to a position-independent module — no main,
  no world table — that declares the host reflection ABI it calls and carries a
  load-time constructor which registers its @System(Phase) functions and runs
  module_init (where it registers its dynamic components). Built as a dylib with
  -undefined dynamic_lookup, it binds ludic_* back to the host image at load.
- @System(Phase) annotation marks a module function as a runtime-registered
  system; the compiler supplies its address (Ludic source cannot take one).
- The reflection ABI (world table) is now emitted for every ECS program, so any
  game can host binary modules with no flag; unused defs dead-strip at -O2, so
  golden renders stay byte-identical and the C-free bootstrap fixpoint holds.

Proven end-to-end: a module dylib registers a component + an Update system; a
host game that never saw its source links it and the system mutates the shared
world each frame. Full suite 87/0, test-tools 30/0, fixpoint intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-01 07:44:18 +03:00
12f2dbe958 feat(types): add Reflect.* — runtime reflection over the world schema (#20)
All checks were successful
bootstrap / cfree-fixpoint (push) Successful in 17s
ci / build-and-test (push) Successful in 1m12s
commit-lint / conventional-commits (push) Successful in 4s
docs / build-and-deploy (push) Successful in 18s
Runtime type reflection: enumerate properties and fields by index, resolve ids
by name, read a field's type, and get/set/has an entity's fields generically —
the foundation the issue calls out for auto-serialization, data-driven tools,
and debug/inspector overlays. Built on the existing EV2 reflection ABI plus a
new EV8 metadata-enumeration layer, all generated at compile time (a table walk,
no heavy runtime introspection), so a binary that never reflects pays nothing.

Surface (Reflect.*, aliased in emit_call.ludic over the world_* reflection ABI):
  - Reflect.prop(name) / field(prop,name)        resolve ids by name (-1 = none)
  - Reflect.prop_count() / prop_name(i)          enumerate properties
  - Reflect.field_count(prop) / field_name(prop,i) / field_type(prop,i)
                                                 enumerate a component's fields
  - Reflect.get / set / has (entity, prop, ...)  read/write/test a field by id
  - Reflect.kind(entity) / model(name)           an entity's model, by id/name

New codegen (emit_world.ludic, EV8): ludic_prop_count / prop_name /
field_count / field_name / field_type, generated the same way as ludic_prop_id
— a switch over the compile-time property/field metadata, falling through to the
mod-registered (dynamic) registries. Field names/types come straight from the
AST, so field_type reports the declared type ("int"/"fixed"/…). A program that
uses Reflect.* force-emits the reflection ABI (g_uses_reflect) so it needs no
@events of its own, exactly like Query.* (#42).

examples/library/reflect.ludic asserts 20 cases including a generic inspector
that sums every field of every component an entity has while naming none of them
— the auto-save / debug-overlay pattern end to end. Wired into x test (now 64
passed). Docs: a Reflect section + 12 per-symbol pages (positioned as an
advanced/tooling surface), inventory/coverage green. Seed reseeded; the C-free
bootstrap fixpoint holds.

Scope: this lands the reflection core and a real consumer (the generic
inspector). The generic value-tree `serialize` the proposal also sketches wants
a tagged-union/any value type from the #1 type-system work, so it is tracked as
a follow-up rather than forced in here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-31 13:28:51 +03:00
23726afa90 refactor(selfhost): reorganise into concern-based subdirectories
All checks were successful
bootstrap / cfree-fixpoint (push) Successful in 12s
ci / build-and-test (push) Successful in 50s
commit-lint / conventional-commits (push) Successful in 3s
docs / build-and-deploy (push) Successful in 2s
Split the flat 38-file selfhost/ into concern-based subdirectories:

  frontend/        lex, parse, parse_game, ast
  support/         str, buf, io
  backend/         core IR + expression/statement lowering
  backend/game/    ECS/scene/event/world lowering
  backend/stdlib/  the namespaced Math.*/Text.*/Crypto.*/… intrinsics

and split the three oversized emitters at responsibility boundaries so
no file mixes concerns:

  emit_game.ludic  -> + emit_world.ludic         (reflection world table,
                                                  tick helpers, @main synthesis)
  emit_expr.ludic  -> + emit_call.ludic          (namespaced builtins, call
                                                  lowering, expr dispatch)
  emit_text.ludic  -> + emit_text_prelude.ludic  (emitted string-builder runtime)

FRAGS in tools/x/selfhost.ludic is updated to the new paths with the link
order preserved, and the Python doc/vocabulary tooling is updated to walk
the new layout. Because the build is a plain in-order concatenation and
every split lands on a blank-line boundary, the regenerated seed is
byte-identical: `x reseed` leaves selfhost/ludicc.seed.ll unchanged,
`x bootstrap-cfree` still reaches its fixed point, and both `x test` (56)
and `x selfhost-test` (29, incl. golden renders) stay green.

Closes #29

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-31 00:26:02 +03:00
Renamed from selfhost/emit_game.ludic (Browse further)