Implement the calendar/clock half of #9 as plain-i32 integer epochs — no
new type, no floating point (the issue's "integer epochs to avoid drift") —
so every operation is deterministic and bit-identical on every platform:
Duration — a span in whole seconds; seconds/minutes/hours/days build one,
as_seconds/as_minutes/as_hours/as_days read it back. Because a
duration is just an int, `+` and `>` work with no extra machinery
(Duration.minutes(5) + Duration.seconds(30), away > Duration.hours(3)).
Date — a civil day as days-since-1970 (UTC): new/year/month/day/weekday/
is_leap/days_in_month/to_epoch/add_days/diff_days.
DateTime — an instant as seconds-since-1970 (UTC, matching Time.now):
from/date/add/year/month/day/weekday/hour/minute/second.
Time.since(past) = now - past, for offline-progress / "time away" checks.
New selfhost/emit_datetime.ludic (is_/emit_ for the three namespaces, wired
into emit_ns_call + the frag list). The two civil<->epoch conversions are
Howard Hinnant's public-domain proleptic-Gregorian algorithms, emitted once
per program as the @fn_days_from_civil / @fn_civil_from_days prelude and gated
by g_uses_datert; days_in_month is next-month-day-0 (no lookup table). Time
gains `since`. Docs (Duration/Date/DateTime sections, 28 method pages +
time-since), inventory, and LSP hover kept in sync; a registered test checks
component math against hand-computed values. Reseeded; C-free fixpoint holds;
all suites green (26 self-host / 45 regression / 29 tools); check.py,
check-impl.py and validate.py OK.
format/parse, a game-controlled simulated clock, and timezones are tracked
follow-ups; v1 is UTC-only and, on the i32 epoch, valid through 2038.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implement the Vec.* half of #25 under the proper (de-abbreviated) name
Vector, unblocking it with a self-contained value type instead of waiting
on the full #1 type system.
A Vector is two Q16.16 fixed components (x, y) packed into one i64 — a true
by-value type that lives in a register and never allocates (reuses the new
`long`/i64 support; llty maps `Vector` to i64). Fifteen operations, all
deterministic fixed-point reusing fx_mul/fx_div/fx_lerp and the @fn_fx_*
prelude: make/zero/x/y, add/sub/scale/dot, length/distance/normalize/lerp,
rotate/angle/from_angle.
New selfhost/emit_vector.ludic (wired into emit_ns_call + the frag list),
the `Vector` primitive type in llty and the grammars/LSP/JetBrains tokens,
docs (type-vector + 15 Vector.* pages + section), and a registered test.
Reseeded; C-free fixpoint holds; all suites green (45/25/29); site + check.py OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
De-abbreviate the two bare fixed-point conversion builtins:
flr(f) -> int -> floor(f) -> int (fixed -> int, flooring)
fx(i) -> fixed -> fixed(i) -> fixed (int -> fixed; mirrors how the
stringify builtin is `string`)
Updates the compiler dispatch, all call sites, the grammars/LSP/JetBrains
tokens, and the docs (fn-flr -> fn-floor, fn-fx -> fn-fixed). Reseeded;
C-free fixpoint holds; all suites green (45/24/29); site + check.py OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expand the abbreviated pointer types to full words on the language surface:
ptr -> pointer (a raw address / FFI handle)
ptrs -> pointers (a buffer of pointers)
The Ludic type name is distinct from LLVM's own `ptr` spelling: llty() maps
`pointer`/`pointers` to LLVM `ptr`, and the emitted IR keeps `ptr`, so only
the Ludic-level surface changes. Rewrites type annotations across all
sources, the 8 hardcoded pointer type-tags, the `pointers`-buffer indexing
in emit_addr, the grammars/LSP/JetBrains tokens, and the docs
(type-ptr -> type-pointer, type-ptrs -> type-pointers). int/bool keep their
conventional short spelling (like Math).
Reseeded; C-free fixpoint holds; all suites green (45/24/29); site + check.py OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expand the abbreviated string type and its conversion builtin to the full
word everywhere:
str -> string (the immutable-string type)
str(x) -> str -> string(x) -> string (the stringify builtin;
what `{…}` interpolation calls)
Types are recognized by identifier, and llty maps both spellings to LLVM
`ptr`, so this is an atomic source rewrite: type annotations, the Ludic
type tags, the builtin name/dispatch, and the interpolation desugar, plus
the grammars, LSP, docs (type-str -> type-string, fn-str -> fn-string), and
inventory. int/bool stay (universally accepted, like Math).
Reseeded; C-free fixpoint holds; all suites green (45/24/29); site + check.py OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expand the function-declaration keyword to the full word across the whole
language and toolchain:
fn name(...) -> T { ... } -> function name(...) -> T { ... }
Done as a self-hosting migration: teach the parser both spellings, reseed,
rewrite every .ludic definition to `function`, then drop `fn`. The compiler
now rejects `fn`. Touches the parser, all selfhost/tools/runtime/example/test
sources, the grammars (TextMate shared+vscode, ludic_syntax.h, JetBrains
LudicTokens.kt), the LSP and formatter, the Python doc/vocab tools
(check-impl, check-docs, validate, palette, test-lsp), and the docs
(fences, prose, kw-fn -> kw-function).
Reseeded; C-free bootstrap fixpoint holds. All suites green (45 regression,
24 self-host, 29 tool); the docs site generates and check.py passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expand the abbreviated public namespaces to full words, part of the
language-wide de-abbreviation pass:
Mem -> Memory, Sys -> System, Net -> Network, Collide -> Collision
Math stays (universally accepted, like int/bool). Renames the dispatch
strings, LSP signatures, docs (dirs, files, frontmatter), and the
inventory manifest; behavior is byte-identical (the bare rt_ targets are
unchanged). Reseeded; C-free bootstrap fixpoint holds; all suites green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Implement the bulk of the namespaced-stdlib proposal (workshopsoft/ludic#2):
156 namespace methods across Math, Text, List, Ease, Collide, World, Net,
Sys, Save, Mem, extended Screen, Color functions, extended Random, and Time.
All deterministic fixed-point; self-hosting (C-free bootstrap fixpoint holds).
Compiler (selfhost/):
- Math.*: sqrt/sin/cos/tan/atan2/asin/acos (fixed-point runtime prelude —
bit-by-bit isqrt, 256-entry interpolated sine table, Ross atan2), plus
hypot/dist/dist2/deg_to_rad/rad_to_deg/posmod/wrap/ping_pong/snapped/
move_toward/smoothstep/lerp/remap/sign/floor/ceil/round.
- Text.* (complete): upper/lower/trim/repeat/pad, split/join/replace,
and the libc-backed queries.
- List.* (complete): insert/remove_at/remove/sort plus the earlier ops.
- Ease.* (in/out/in_out/back/bounce) and Collide.* (rects/point_rect/
circles/rect_circle).
- Phase 3: World/Net/Sys/Save namespaced over the bare builtins (byte-
identical IR) and Mem.* (bytes/words/copy/fill/peek/poke).
- Screen.* extended (line/circle/fill_circle/triangle/fill_triangle via new
runtime primitives; sprite/sprite_scaled aliases), Color.* functions,
Random.* (value/int/sign), Time.* (frame/delta/elapsed/now — new
game-loop frame counter).
- Fix a lexer bug: fixed-point literals with >4 fractional digits overflowed.
Docs & tooling:
- 129 new per-symbol doc pages; gen.py made data-driven (namespaces
discovered from the docs, no hardcoded list); new check-impl.py enforces
that every implemented namespace method / keyword / type / phase has a
doc page, wired into `x test-tools`. Document the previously-undocumented
keywords (break/continue/where/entry/new/public + and/or/not tokens).
- LSP: namespaced signature help (ns_method_sig) covering every namespace.
Tests: 12 new self-host/regression tests + a golden render for the drawing
primitives. All suites green (selfhost 21, regression 45, tools 29).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The type table lists fixeds (buffer of fixed values) and ptrs (buffer of
pointers) alongside words, but they had no reference pages. Add both, with
compilable examples; coverage inventory updated (types: 10 -> 12).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Pipeline diagram stacks vertically with downward arrows on mobile (row on
>=720px) instead of the awkward wrap.
- Tooltip cards now open on CLICK (works on touch too), the card itself is
clickable and opens the symbol's page in a NEW TAB, and an outside click or
Escape closes it. Replaces the hover-only behavior.
- Reference pages get their own nav — Home / API Reference / Source ↗ — instead
of the landing-only Features/Examples/Get started anchors, and now include the
Source link.
- Unify the container width (1120px) across the landing and all reference pages
so the header and content align between them.
- Reword extern/@export FFI docs from "C-ABI" to "native" for consistency with
the site's no-C wording.
Verified in-browser at 375px and 1280px: vertical pipeline, click→card→new-tab,
outside-click close, correct reference nav, aligned container; 158 examples still
compile.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rebuild the API Reference around one page per symbol and richer, verified content.
Pages & navigation
- One HTML page per symbol (kw-*, type-*, phase-*, screen-*, fn-*, annot-*, op-*)
instead of a single scrolling page; namespace overview pages (ns-screen …
ns-color) and a searchable index (api.html) with client-side fuzzy search.
- Sticky-header scroll offset (scroll-margin) so a jumped-to entry/param/color is
never hidden, plus a flash highlight on the scrolled-to target.
Deep linking in every snippet & example
- Namespace members split: `Screen`→namespace page, `fill_rectangle`→method page;
`Color`→palette page, `Charcoal`→its swatch — separately.
- Named arguments (`width:`) link to that parameter's anchor on the method page.
- Hover any token for a summary card built from the real API data (symbols.json).
Content & coverage
- Full authoritative surface documented from the compiler: every keyword, type,
the 6 phases (Start/Input/FixedUpdate/Update/LateUpdate/Render, each its own
page), all 22 annotations, namespace methods with parameter docs, builtins,
the world_* reflection ABI, networking, operators — 155 symbols.
- Longer, clearer explanations; "model"/"model instance" terminology, not "entity";
descriptive identifiers in every example (Position{column,row}, Velocity{delta_x,
delta_y}, Health{current,maximum}, Player/Enemy) — no Pos/Seg/x/dx.
- Accuracy fixes from compiler ground-truth: world_count() takes no arg,
world_query_next(property, cursor) arg order, event fields bind by name; dropped
`when` and `module` (not in the self-hosted parser).
Tooling
- inventory.json + check.py: coverage guard (every symbol has a page), duplicate-
token guard, and broken-link guard — fail CI so docs can't drift.
- validate.py: compiles every ```ludic example against bin/ludicc (158 compile).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sourced from the compiler's confirmed builtin set and NETWORKING-DESIGN.md, so
the reference reflects the current language surface. 93 -> 104 symbols.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the hardcoded landing page and minimal reference with a generated
documentation site driven by a single source of truth.
- docs/language/**: one file per symbol (93 keywords/types/builtins/namespace
methods/operators/annotations), each with front-matter (id, kind, tokens,
sig, tip) + description + a ```ludic example. Seeded by exploding the former
inline SECTIONS list; these files are now the source of truth.
- docs/site/: site.json (editable hero/features/showcase/messaging, not
hardcoded) + snippets/*.ludic (real programs shown on the landing page).
- tools/docgen/gen.py: generates index.html, api.html, ludic-highlight.js and
symbols.json. The highlighter's symbol tables, hover tips and jump anchors
are GENERATED from the per-symbol files — add a symbol and it is recognized,
tipped and linked in every snippet automatically. Python stdlib only.
- tools/docgen/check.py: verifies the pages contract + that no snippet token
links to a missing reference anchor.
- .forgejo/workflows/docs.yml: rebuilds and publishes to the pages branch on
every push to main touching the docs sources.
Consumes the new Screen.*/Color.*/named-arg API and the 221-color palette.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>