feat(stdlib): DateTime format/parse + simulated Clock — completes #9
Some checks are pending
docs / build-and-deploy (push) Waiting to run
Some checks are pending
docs / build-and-deploy (push) Waiting to run
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>
This commit is contained in:
parent
1a2c6ec2c7
commit
b5455cd550
17 changed files with 13902 additions and 12278 deletions
7
docs/language/clock/_section.md
Normal file
7
docs/language/clock/_section.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
id: clock
|
||||
title: Clock
|
||||
order: 6
|
||||
---
|
||||
|
||||
A game-controlled simulated clock: a single seconds counter the game owns. Unlike <code>Time.now</code> / <code>Time.since</code>, it never reads the wall clock, so any gameplay that reads <code>Clock.now()</code> is deterministic and replay-safe. Set it outright, or advance it by a <code>Duration</code> each tick to run time at whatever rate the simulation wants. The clock is a plain instant (seconds since 1970), so all the <code>DateTime.*</code> readers work on it directly.
|
||||
22
docs/language/clock/clock-advance.md
Normal file
22
docs/language/clock/clock-advance.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: clock-advance
|
||||
name: Clock.advance
|
||||
category: clock
|
||||
kind: namespace-method
|
||||
tokens: Clock.advance
|
||||
sig: Clock.advance(span)
|
||||
tip: Move the clock forward by a Duration.
|
||||
order: 2
|
||||
ns: Clock
|
||||
member: advance
|
||||
---
|
||||
|
||||
Moves the simulated clock forward by <code>span</code> seconds — pair it with the <code>Duration.*</code> constructors. Advance it by a fixed step each tick to drive a deterministic day/night or season cycle.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
Clock.advance(Duration.minutes(1)) # one in-game minute per call
|
||||
}
|
||||
}
|
||||
```
|
||||
23
docs/language/clock/clock-now.md
Normal file
23
docs/language/clock/clock-now.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
id: clock-now
|
||||
name: Clock.now
|
||||
category: clock
|
||||
kind: namespace-method
|
||||
tokens: Clock.now
|
||||
sig: Clock.now() -> int
|
||||
tip: The current simulated instant.
|
||||
order: 0
|
||||
ns: Clock
|
||||
member: now
|
||||
---
|
||||
|
||||
The current value of the simulated clock, as a <code>DateTime</code> (seconds since 1970). Deterministic — it returns exactly what the game last set or advanced it to, never the wall clock.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let t = Clock.now()
|
||||
let hour = DateTime.hour(t)
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/clock/clock-reset.md
Normal file
22
docs/language/clock/clock-reset.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: clock-reset
|
||||
name: Clock.reset
|
||||
category: clock
|
||||
kind: namespace-method
|
||||
tokens: Clock.reset
|
||||
sig: Clock.reset()
|
||||
tip: Reset the clock to the epoch (0).
|
||||
order: 3
|
||||
ns: Clock
|
||||
member: reset
|
||||
---
|
||||
|
||||
Resets the simulated clock back to <code>0</code> (the 1970 epoch). Handy at the start of a new run or a replay.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
Clock.reset()
|
||||
}
|
||||
}
|
||||
```
|
||||
22
docs/language/clock/clock-set.md
Normal file
22
docs/language/clock/clock-set.md
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
id: clock-set
|
||||
name: Clock.set
|
||||
category: clock
|
||||
kind: namespace-method
|
||||
tokens: Clock.set
|
||||
sig: Clock.set(t)
|
||||
tip: Set the clock to an instant.
|
||||
order: 1
|
||||
ns: Clock
|
||||
member: set
|
||||
---
|
||||
|
||||
Sets the simulated clock to the instant <code>t</code>. Typically called once on load (e.g. from a saved <code>Time.now()</code>), after which gameplay advances it deterministically.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
Clock.set(DateTime.from(2026, 8, 30, 8, 0, 0))
|
||||
}
|
||||
}
|
||||
```
|
||||
23
docs/language/datetime/datetime-format.md
Normal file
23
docs/language/datetime/datetime-format.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
id: datetime-format
|
||||
name: DateTime.format
|
||||
category: datetime
|
||||
kind: namespace-method
|
||||
tokens: DateTime.format
|
||||
sig: DateTime.format(dt, pattern) -> string
|
||||
tip: Render an instant as text using a token pattern.
|
||||
order: 10
|
||||
ns: DateTime
|
||||
member: format
|
||||
---
|
||||
|
||||
Renders the instant <code>dt</code> as a string, filling the tokens in <code>pattern</code> with zero-padded fields and copying every other character through verbatim. The pattern <b>must be a string literal</b> — it is expanded at compile time, so there is no runtime pattern scanner. Recognised tokens: <code>YYYY</code> (4-digit year), <code>YY</code> (2-digit year), <code>MM</code> (month), <code>DD</code> (day), <code>HH</code> (hour, 24h), <code>mm</code> (minute), <code>ss</code> (second).
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let t = DateTime.from(2026, 8, 30, 7, 5, 9)
|
||||
let stamp = DateTime.format(t, "YYYY-MM-DD HH:mm:ss") # "2026-08-30 07:05:09"
|
||||
}
|
||||
}
|
||||
```
|
||||
25
docs/language/datetime/datetime-parse.md
Normal file
25
docs/language/datetime/datetime-parse.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
id: datetime-parse
|
||||
name: DateTime.parse
|
||||
category: datetime
|
||||
kind: namespace-method
|
||||
tokens: DateTime.parse
|
||||
sig: DateTime.parse(text, pattern) -> int
|
||||
tip: Parse text into an instant; -1 on failure.
|
||||
order: 11
|
||||
ns: DateTime
|
||||
member: parse
|
||||
---
|
||||
|
||||
Parses <code>text</code> into a <code>DateTime</code> by reading each token of <code>pattern</code> from the fixed position the pattern lays out; missing fields default to 1970-01-01 00:00:00. Returns <code>-1</code> if a digit was expected but not found (so the caller can detect malformed input). The pattern <b>must be a string literal</b> and uses the same tokens as <code>DateTime.format</code>. <code>text</code> must match the pattern's fixed-width layout.
|
||||
|
||||
```ludic
|
||||
program Demo {
|
||||
handler Step phase Update {
|
||||
let t = DateTime.parse("2026-08-30 07:05:09", "YYYY-MM-DD HH:mm:ss")
|
||||
if t < 0 {
|
||||
# malformed input
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue