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>
Finish issue #9 by adding the two remaining acceptance items on top of the
calendar/clock core, still pure-integer and deterministic:
DateTime.format(dt, pattern) -> string render an instant via a token
pattern (YYYY/YY/MM/DD/HH/mm/ss;
other chars pass through)
DateTime.parse(text, pattern) -> int read an instant back; -1 on a
non-digit where one is expected
Clock.now/set/advance/reset a game-controlled simulated clock
(the @L_clock global) that never
touches the wall clock, so gameplay
reading Clock.now() is replay-safe
format/parse take a string-LITERAL pattern and are expanded at compile time
(field offsets are then constant), folding @fn_str_concat over literal runs and
two small runtime helpers: @fn_dt_pad0 (zero-padded field) and @fn_dt_rd
(fixed-width digit reader that stops at the terminator and flags malformed
input). Clock is a universal i32 global declared in emit_head, so it works in
entry and game programs alike.
Adds examples/offline_rewards.ludic — the issue's worked "you were away N hours"
example, driven from its own entry and asserted in the regression suite — plus
selfhost/tests/datetime2.ludic (format/parse round-trip, parse failure, clock),
docs (Clock section + 4 pages, DateTime.format/parse pages), inventory and LSP
hover. Reseeded; C-free fixpoint holds; all suites green (27 self-host / 46
regression / 29 tools); check.py (366 symbols), check-impl.py (218 ns-methods)
and validate.py OK.
With this, #9's scope is fully delivered: DateTime/Date/Duration + core ops,
format/parse, a deterministic simulated clock, docs + offline-rewards example,
and tests. (v1 stays UTC-only, no leap seconds, i32 epoch valid through 2038.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>