# emit_datetime.ludic — the calendar/clock half of the stdlib: the Duration.*, # Date.* and DateTime.* namespaces (issue #9). Everything is a plain i32 integer # epoch, never a float, so it is deterministic and bit-identical on every # platform: # Duration — a span in whole seconds (Duration.hours(3) == 10800). # Date — a civil day, stored as the count of days since 1970-01-01 (UTC), # so Date arithmetic is ordinary integer add/subtract. # DateTime — an instant, stored as the count of seconds since the 1970 epoch # (UTC), matching Time.now(). # The two civil<->epoch conversions are Howard Hinnant's public-domain algorithms # (chrono-compatible, proleptic Gregorian), emitted once per program as the # @lp_days_from_civil / @lp_civil_from_days prelude and gated by g_uses_datert. # v1 is UTC-only with no leap seconds; instants are assumed non-negative (dates # at or after 1970). Timezones, format/parse and a game-controlled simulated # clock are tracked follow-ups. # --- Duration.* — spans in whole seconds ------------------------------------- function is_duration_ns(meth: pointer) -> bool { if (meth == "seconds") or (meth == "minutes") or (meth == "hours") or (meth == "days") { return true } if (meth == "as_seconds") or (meth == "as_minutes") or (meth == "as_hours") or (meth == "as_days") { return true } return false } function emit_duration_ns(meth: pointer, e: Node) -> Val { let n = emit_expr(e.kids[0]) if (meth == "seconds") { return val(n.code, "int") } # already seconds if (meth == "minutes") { return val(emit_bind(`mul i32 {n.code}, 60`), "int") } if (meth == "hours") { return val(emit_bind(`mul i32 {n.code}, 3600`), "int") } if (meth == "days") { return val(emit_bind(`mul i32 {n.code}, 86400`), "int") } if (meth == "as_seconds") { return val(n.code, "int") } if (meth == "as_minutes") { return val(emit_bind(`sdiv i32 {n.code}, 60`), "int") } if (meth == "as_hours") { return val(emit_bind(`sdiv i32 {n.code}, 3600`), "int") } # as_days: whole days in the span (floor toward zero) return val(emit_bind(`sdiv i32 {n.code}, 86400`), "int") } # --- Date.* — a civil day as days-since-1970 --------------------------------- # load one component (0=year, 1=month, 2=day) of the civil date for epoch-day # code `ed` by calling the civil_from_days prelude into three stack slots. function date_component(ed: pointer, which: int) -> pointer { g_uses_datert = true let yp = emit_alloca("i32") let mp = emit_alloca("i32") let dp = emit_alloca("i32") emit(` call void @lp_civil_from_days(i32 {ed}, ptr {yp}, ptr {mp}, ptr {dp})\n`) var p = yp if (which == 1) { p = mp } if (which == 2) { p = dp } return emit_bind(`load i32, ptr {p}`) } function is_date_ns(meth: pointer) -> bool { if (meth == "new") or (meth == "year") or (meth == "month") or (meth == "day") { return true } if (meth == "weekday") or (meth == "is_leap") or (meth == "days_in_month") { return true } if (meth == "to_epoch") or (meth == "add_days") or (meth == "diff_days") { return true } return false } function emit_date_ns(meth: pointer, e: Node) -> Val { if (meth == "new") { # new(year, month, day) -> Date (epoch-day) g_uses_datert = true let y = emit_expr(e.kids[0]); let m = emit_expr(e.kids[1]); let d = emit_expr(e.kids[2]) return val(emit_bind(`call i32 @lp_days_from_civil(i32 {y.code}, i32 {m.code}, i32 {d.code})`), "int") } if (meth == "year") { let ed = emit_expr(e.kids[0]); return val(date_component(ed.code, 0), "int") } if (meth == "month") { let ed = emit_expr(e.kids[0]); return val(date_component(ed.code, 1), "int") } if (meth == "day") { let ed = emit_expr(e.kids[0]); return val(date_component(ed.code, 2), "int") } if (meth == "weekday") { # 0=Sunday .. 6=Saturday (1970-01-01 was Thursday) let ed = emit_expr(e.kids[0]) let s = emit_bind(`add i32 {ed.code}, 4`) # shift so the epoch's Thursday lands right let r = emit_bind(`srem i32 {s}, 7`) let rr = emit_bind(`add i32 {r}, 7`) # normalise a negative remainder into [0,7) return val(emit_bind(`srem i32 {rr}, 7`), "int") } if (meth == "is_leap") { # proleptic Gregorian leap-year test let y = emit_expr(e.kids[0]) let m4 = emit_bind(`srem i32 {y.code}, 4`); let c4 = emit_bind(`icmp eq i32 {m4}, 0`) let m100 = emit_bind(`srem i32 {y.code}, 100`); let c100 = emit_bind(`icmp ne i32 {m100}, 0`) let m400 = emit_bind(`srem i32 {y.code}, 400`); let c400 = emit_bind(`icmp eq i32 {m400}, 0`) let common = emit_bind(`and i1 {c4}, {c100}`) # divisible by 4 but not by 100 let leap = emit_bind(`or i1 {common}, {c400}`) # ...or divisible by 400 return val(emit_bind(`zext i1 {leap} to i32`), "bool") } if (meth == "days_in_month") { # length of (year, month) = next month's day 0 g_uses_datert = true let y = emit_expr(e.kids[0]); let m = emit_expr(e.kids[1]) let this = emit_bind(`call i32 @lp_days_from_civil(i32 {y.code}, i32 {m.code}, i32 1)`) let dec = emit_bind(`icmp eq i32 {m.code}, 12`) # December rolls over to next January let ny = emit_bind(`add i32 {y.code}, 1`) let ny2 = emit_bind(`select i1 {dec}, i32 {ny}, i32 {y.code}`) let nm = emit_bind(`add i32 {m.code}, 1`) let nm2 = emit_bind(`select i1 {dec}, i32 1, i32 {nm}`) let next = emit_bind(`call i32 @lp_days_from_civil(i32 {ny2}, i32 {nm2}, i32 1)`) return val(emit_bind(`sub i32 {next}, {this}`), "int") } if (meth == "to_epoch") { # midnight UTC of the day, as a DateTime instant let ed = emit_expr(e.kids[0]) return val(emit_bind(`mul i32 {ed.code}, 86400`), "int") } if (meth == "add_days") { # the day `n` days after `ed` let ed = emit_expr(e.kids[0]); let n = emit_expr(e.kids[1]) return val(emit_bind(`add i32 {ed.code}, {n.code}`), "int") } # diff_days(a, b) -> whole days from b to a (a - b) let a = emit_expr(e.kids[0]); let b = emit_expr(e.kids[1]) return val(emit_bind(`sub i32 {a.code}, {b.code}`), "int") } # --- DateTime.* — an instant as seconds-since-1970 --------------------------- # seconds-of-day for instant `t`, normalised into [0, 86400) even for a negative # instant, so hour/minute/second stay correct. function dt_secofday(t: pointer) -> pointer { let r = emit_bind(`srem i32 {t}, 86400`) let rr = emit_bind(`add i32 {r}, 86400`) return emit_bind(`srem i32 {rr}, 86400`) } # the epoch-day (floored) an instant falls on: floor(t / 86400). function dt_epochday(t: pointer) -> pointer { let sod = dt_secofday(t) # t - sod is an exact multiple of 86400 let base = emit_bind(`sub i32 {t}, {sod}`) return emit_bind(`sdiv i32 {base}, 86400`) } function is_datetime_ns(meth: pointer) -> bool { if (meth == "from") or (meth == "date") or (meth == "add") { return true } if (meth == "year") or (meth == "month") or (meth == "day") or (meth == "weekday") { return true } if (meth == "hour") or (meth == "minute") or (meth == "second") { return true } if (meth == "format") or (meth == "parse") { return true } return false } # does the pattern have token `tok` (length tlen) starting at index i? function dt_tok_at(pat: pointer, n: int, i: int, tok: pointer, tlen: int) -> bool { if (i + tlen) > n { return false } var k = 0 while k < tlen { if pat[i + k] != tok[k] { return false }; k += 1 } return true } # DateTime.format(t, "pattern") -> string. The pattern MUST be a string literal; # the tokens YYYY / YY / MM / DD / HH / mm / ss expand to zero-padded fields and # every other character is copied through verbatim. Expanded at compile time into # a fold of @lp_str_concat over literal runs and @lp_dt_pad0 field conversions. function emit_datetime_format(e: Node) -> Val { g_uses_datert = true g_uses_str = true if e.kids[1].kind != E_STR { perr("DateTime.format needs a string-literal pattern") } let t = emit_expr(e.kids[0]) let pat = e.kids[1].s let n = len(pat) # compute all six components once let ed = dt_epochday(t.code) let yp = emit_alloca("i32"); let mp = emit_alloca("i32"); let dp = emit_alloca("i32") emit(` call void @lp_civil_from_days(i32 {ed}, ptr {yp}, ptr {mp}, ptr {dp})\n`) let yv = emit_bind(`load i32, ptr {yp}`) let mv = emit_bind(`load i32, ptr {mp}`) let dv = emit_bind(`load i32, ptr {dp}`) let sod = dt_secofday(t.code) let hh = emit_bind(`sdiv i32 {sod}, 3600`) let m3 = emit_bind(`srem i32 {sod}, 3600`) let mi = emit_bind(`sdiv i32 {m3}, 60`) let ss = emit_bind(`srem i32 {sod}, 60`) let yy = emit_bind(`srem i32 {yv}, 100`) # joined as ordinary text: each field's digits and each piece so far are freed once the next join # has copied them, so only the answer is left (every one of them was once kept) var acc = val(emit_str_const(""), "string") let lit = buf_new() var i = 0 while i < n { var field: pointer = null; var width = 0; var tlen = 0 if dt_tok_at(pat, n, i, "YYYY", 4) { field = yv; width = 4; tlen = 4 } else { if dt_tok_at(pat, n, i, "YY", 2) { field = yy; width = 2; tlen = 2 } else { if dt_tok_at(pat, n, i, "MM", 2) { field = mv; width = 2; tlen = 2 } else { if dt_tok_at(pat, n, i, "DD", 2) { field = dv; width = 2; tlen = 2 } else { if dt_tok_at(pat, n, i, "HH", 2) { field = hh; width = 2; tlen = 2 } else { if dt_tok_at(pat, n, i, "mm", 2) { field = mi; width = 2; tlen = 2 } else { if dt_tok_at(pat, n, i, "ss", 2) { field = ss; width = 2; tlen = 2 } } } } } } } if (field == null) { buf_putc(lit, pat[i]); i += 1 } else { let ls = buf_str(lit) if len(ls) > 0 { acc = emit_str_op("+", acc, val(emit_str_const(ls), "string")) } let piece = fresh_val(emit_bind(`call ptr @lp_dt_pad0(i32 {field}, i32 {width})`), "string") acc = emit_str_op("+", acc, piece) lit.len = 0 # start a fresh literal run i += tlen } } let tail = buf_str(lit) if len(tail) > 0 { acc = emit_str_op("+", acc, val(emit_str_const(tail), "string")) } return acc } # DateTime.parse(s, "pattern") -> DateTime. The pattern MUST be a string literal; # each field is read from a fixed offset (the layout the pattern fixes) and any # non-digit where a digit is expected fails the parse, returning -1. Missing # fields default to 1970-01-01 00:00:00. function emit_datetime_parse(e: Node) -> Val { g_uses_datert = true if e.kids[1].kind != E_STR { perr("DateTime.parse needs a string-literal pattern") } let s = emit_expr(e.kids[0]) let pat = e.kids[1].s let n = len(pat) let failp = emit_alloca("i32") store_at("i32", "0", failp) # component slots, seeded to the epoch defaults let yp = emit_alloca("i32"); store_at("i32", "1970", yp) let mp = emit_alloca("i32"); store_at("i32", "1", mp) let dp = emit_alloca("i32"); store_at("i32", "1", dp) let hp = emit_alloca("i32"); store_at("i32", "0", hp) let np = emit_alloca("i32"); store_at("i32", "0", np) let sp = emit_alloca("i32"); store_at("i32", "0", sp) var off = 0 var i = 0 while i < n { var slot: pointer = null; var width = 0; var tlen = 0 if dt_tok_at(pat, n, i, "YYYY", 4) { slot = yp; width = 4; tlen = 4 } else { if dt_tok_at(pat, n, i, "MM", 2) { slot = mp; width = 2; tlen = 2 } else { if dt_tok_at(pat, n, i, "DD", 2) { slot = dp; width = 2; tlen = 2 } else { if dt_tok_at(pat, n, i, "HH", 2) { slot = hp; width = 2; tlen = 2 } else { if dt_tok_at(pat, n, i, "mm", 2) { slot = np; width = 2; tlen = 2 } else { if dt_tok_at(pat, n, i, "ss", 2) { slot = sp; width = 2; tlen = 2 } } } } } } if (slot == null) { off += 1; i += 1 } else { let rd = emit_bind(`call i32 @lp_dt_rd(ptr {s.code}, i32 {itoa(off)}, i32 {width}, ptr {failp})`) store_at("i32", rd, slot) off += width i += tlen } } let y = emit_bind(`load i32, ptr {yp}`) let mo = emit_bind(`load i32, ptr {mp}`) let d = emit_bind(`load i32, ptr {dp}`) let h = emit_bind(`load i32, ptr {hp}`) let mn = emit_bind(`load i32, ptr {np}`) let sc = emit_bind(`load i32, ptr {sp}`) let ed = emit_bind(`call i32 @lp_days_from_civil(i32 {y}, i32 {mo}, i32 {d})`) let days = emit_bind(`mul i32 {ed}, 86400`) let hs = emit_bind(`mul i32 {h}, 3600`) let ns = emit_bind(`mul i32 {mn}, 60`) let a1 = emit_bind(`add i32 {days}, {hs}`) let a2 = emit_bind(`add i32 {a1}, {ns}`) let inst = emit_bind(`add i32 {a2}, {sc}`) let failed = emit_bind(`load i32, ptr {failp}`) let bad = emit_bind(`icmp ne i32 {failed}, 0`) return val(emit_bind(`select i1 {bad}, i32 -1, i32 {inst}`), "int") } function emit_datetime_ns(meth: pointer, e: Node) -> Val { if (meth == "format") { return emit_datetime_format(e) } if (meth == "parse") { return emit_datetime_parse(e) } if (meth == "from") { # from(year, month, day, hour, minute, second) -> DateTime g_uses_datert = true let y = emit_expr(e.kids[0]); let mo = emit_expr(e.kids[1]); let d = emit_expr(e.kids[2]) let h = emit_expr(e.kids[3]); let mi = emit_expr(e.kids[4]); let s = emit_expr(e.kids[5]) let ed = emit_bind(`call i32 @lp_days_from_civil(i32 {y.code}, i32 {mo.code}, i32 {d.code})`) let days = emit_bind(`mul i32 {ed}, 86400`) let hh = emit_bind(`mul i32 {h.code}, 3600`) let mm = emit_bind(`mul i32 {mi.code}, 60`) let a = emit_bind(`add i32 {days}, {hh}`) let b = emit_bind(`add i32 {a}, {mm}`) return val(emit_bind(`add i32 {b}, {s.code}`), "int") } if (meth == "date") { # the calendar day this instant is on -> Date let t = emit_expr(e.kids[0]) return val(dt_epochday(t.code), "int") } if (meth == "add") { # the instant `dur` seconds after `t` let t = emit_expr(e.kids[0]); let dur = emit_expr(e.kids[1]) return val(emit_bind(`add i32 {t.code}, {dur.code}`), "int") } if (meth == "year") { let t = emit_expr(e.kids[0]); return val(date_component(dt_epochday(t.code), 0), "int") } if (meth == "month") { let t = emit_expr(e.kids[0]); return val(date_component(dt_epochday(t.code), 1), "int") } if (meth == "day") { let t = emit_expr(e.kids[0]); return val(date_component(dt_epochday(t.code), 2), "int") } if (meth == "weekday") { # 0=Sunday .. 6=Saturday let t = emit_expr(e.kids[0]) let ed = dt_epochday(t.code) let s = emit_bind(`add i32 {ed}, 4`) let r = emit_bind(`srem i32 {s}, 7`) let rr = emit_bind(`add i32 {r}, 7`) return val(emit_bind(`srem i32 {rr}, 7`), "int") } if (meth == "hour") { # 0..23 let t = emit_expr(e.kids[0]) let sod = dt_secofday(t.code) return val(emit_bind(`sdiv i32 {sod}, 3600`), "int") } if (meth == "minute") { # 0..59 let t = emit_expr(e.kids[0]) let sod = dt_secofday(t.code) let h = emit_bind(`srem i32 {sod}, 3600`) return val(emit_bind(`sdiv i32 {h}, 60`), "int") } # second: 0..59 let t = emit_expr(e.kids[0]) let sod = dt_secofday(t.code) return val(emit_bind(`srem i32 {sod}, 60`), "int") } # emit_datetime_prelude — the two civil<->epoch conversions, emitted once per # program that uses Date/DateTime (g_uses_datert). Both are Howard Hinnant's # public-domain proleptic-Gregorian algorithms, in pure i32 integer IR, so they # are deterministic and bit-identical everywhere. 719468 is the day count from # 0000-03-01 to 1970-01-01; 146097 is the days in a 400-year era. function emit_datetime_prelude() -> void { # @lp_days_from_civil(y, m, d) -> days since 1970-01-01 emith("define i32 @lp_days_from_civil(i32 %y0, i32 %m, i32 %d) {\n") emith(" %mle2 = icmp sle i32 %m, 2\n") emith(" %ysub = select i1 %mle2, i32 1, i32 0\n") emith(" %y = sub i32 %y0, %ysub\n") emith(" %yneg = icmp slt i32 %y, 0\n") emith(" %ym399 = sub i32 %y, 399\n") emith(" %enum = select i1 %yneg, i32 %ym399, i32 %y\n") emith(" %era = sdiv i32 %enum, 400\n") emith(" %era400 = mul i32 %era, 400\n") emith(" %yoe = sub i32 %y, %era400\n") emith(" %mgt2 = icmp sgt i32 %m, 2\n") emith(" %mshift = select i1 %mgt2, i32 -3, i32 9\n") emith(" %mm = add i32 %m, %mshift\n") emith(" %t153 = mul i32 153, %mm\n") emith(" %t153b = add i32 %t153, 2\n") emith(" %doy0 = sdiv i32 %t153b, 5\n") emith(" %doy1 = add i32 %doy0, %d\n") emith(" %doy = sub i32 %doy1, 1\n") emith(" %yoe365 = mul i32 %yoe, 365\n") emith(" %yoe4 = sdiv i32 %yoe, 4\n") emith(" %yoe100 = sdiv i32 %yoe, 100\n") emith(" %doe0 = add i32 %yoe365, %yoe4\n") emith(" %doe1 = sub i32 %doe0, %yoe100\n") emith(" %doe = add i32 %doe1, %doy\n") emith(" %e146097 = mul i32 %era, 146097\n") emith(" %r0 = add i32 %e146097, %doe\n") emith(" %r = sub i32 %r0, 719468\n") emith(" ret i32 %r\n") emith("}\n") # @lp_civil_from_days(z0, yp, mp, dp): write the civil (year, month, day) of # the epoch-day z0 through the three out-pointers. emith("define void @lp_civil_from_days(i32 %z0, ptr %yp, ptr %mp, ptr %dp) {\n") emith(" %z = add i32 %z0, 719468\n") emith(" %zneg = icmp slt i32 %z, 0\n") emith(" %zm = sub i32 %z, 146096\n") emith(" %enum = select i1 %zneg, i32 %zm, i32 %z\n") emith(" %era = sdiv i32 %enum, 146097\n") emith(" %era146097 = mul i32 %era, 146097\n") emith(" %doe = sub i32 %z, %era146097\n") emith(" %d1460 = sdiv i32 %doe, 1460\n") emith(" %d36524 = sdiv i32 %doe, 36524\n") emith(" %d146096 = sdiv i32 %doe, 146096\n") emith(" %ya = sub i32 %doe, %d1460\n") emith(" %yb = add i32 %ya, %d36524\n") emith(" %yc = sub i32 %yb, %d146096\n") emith(" %yoe = sdiv i32 %yc, 365\n") emith(" %era400 = mul i32 %era, 400\n") emith(" %yy = add i32 %yoe, %era400\n") emith(" %yoe365 = mul i32 %yoe, 365\n") emith(" %yoe4 = sdiv i32 %yoe, 4\n") emith(" %yoe100 = sdiv i32 %yoe, 100\n") emith(" %sub0 = add i32 %yoe365, %yoe4\n") emith(" %sub1 = sub i32 %sub0, %yoe100\n") emith(" %doy = sub i32 %doe, %sub1\n") emith(" %fivedoy = mul i32 5, %doy\n") emith(" %fivedoy2 = add i32 %fivedoy, 2\n") emith(" %mpv = sdiv i32 %fivedoy2, 153\n") emith(" %m153 = mul i32 153, %mpv\n") emith(" %m153b = add i32 %m153, 2\n") emith(" %m153c = sdiv i32 %m153b, 5\n") emith(" %dd0 = sub i32 %doy, %m153c\n") emith(" %dd = add i32 %dd0, 1\n") emith(" %mplt10 = icmp slt i32 %mpv, 10\n") emith(" %mplus3 = add i32 %mpv, 3\n") emith(" %mminus9 = sub i32 %mpv, 9\n") emith(" %mfin = select i1 %mplt10, i32 %mplus3, i32 %mminus9\n") emith(" %mmle2 = icmp sle i32 %mfin, 2\n") emith(" %yinc = select i1 %mmle2, i32 1, i32 0\n") emith(" %yfin = add i32 %yy, %yinc\n") emith(" store i32 %yfin, ptr %yp\n") emith(" store i32 %mfin, ptr %mp\n") emith(" store i32 %dd, ptr %dp\n") emith(" ret void\n") emith("}\n") # @lp_dt_pad0(v, w): a fresh, malloc'd, w-digit zero-padded decimal of v # (v assumed non-negative). Backs DateTime.format's numeric fields. emith("define ptr @lp_dt_pad0(i32 %v, i32 %w) {\n") emith(" %we = zext i32 %w to i64\n") emith(" %sz = add i64 %we, 1\n") emith(" %buf = call ptr @lp_malloc(i64 %sz)\n") emith(" %endp = getelementptr i8, ptr %buf, i32 %w\n") emith(" store i8 0, ptr %endp\n") emith(" %kp = alloca i32\n %vp = alloca i32\n") emith(" %km1 = sub i32 %w, 1\n store i32 %km1, ptr %kp\n store i32 %v, ptr %vp\n") emith(" br label %loop\n") emith("loop:\n %k = load i32, ptr %kp\n %kok = icmp sge i32 %k, 0\n br i1 %kok, label %body, label %done\n") emith("body:\n %vv = load i32, ptr %vp\n %d = srem i32 %vv, 10\n %ch = add i32 %d, 48\n %ch8 = trunc i32 %ch to i8\n") emith(" %cp = getelementptr i8, ptr %buf, i32 %k\n store i8 %ch8, ptr %cp\n") emith(" %vn = sdiv i32 %vv, 10\n store i32 %vn, ptr %vp\n %kn = sub i32 %k, 1\n store i32 %kn, ptr %kp\n br label %loop\n") emith("done:\n ret ptr %buf\n}\n") # @lp_dt_rd(s, off, w, failp): read w decimal digits of s starting at off into an # int; on any non-digit set *failp = 1. Backs DateTime.parse's fixed-width fields. emith("define i32 @lp_dt_rd(ptr %s, i32 %off, i32 %w, ptr %failp) {\n") emith(" %accp = alloca i32\n store i32 0, ptr %accp\n %kp = alloca i32\n store i32 0, ptr %kp\n") emith(" br label %loop\n") emith("loop:\n %k = load i32, ptr %kp\n %kok = icmp slt i32 %k, %w\n br i1 %kok, label %body, label %done\n") emith("body:\n %idx = add i32 %off, %k\n %cp = getelementptr i8, ptr %s, i32 %idx\n %c8 = load i8, ptr %cp\n %c = sext i8 %c8 to i32\n") emith(" %zero = icmp eq i32 %c, 0\n br i1 %zero, label %stop, label %digit\n") # stop at the terminator, never read past it emith("stop:\n store i32 1, ptr %failp\n br label %done\n") emith("digit:\n %lt = icmp slt i32 %c, 48\n %gt = icmp sgt i32 %c, 57\n %isbad = or i1 %lt, %gt\n br i1 %isbad, label %fail, label %ok\n") emith("fail:\n store i32 1, ptr %failp\n br label %next\n") emith("ok:\n %acc0 = load i32, ptr %accp\n %m10 = mul i32 %acc0, 10\n %dg = sub i32 %c, 48\n %acc1 = add i32 %m10, %dg\n store i32 %acc1, ptr %accp\n br label %next\n") emith("next:\n %kn = add i32 %k, 1\n store i32 %kn, ptr %kp\n br label %loop\n") emith("done:\n %r = load i32, ptr %accp\n ret i32 %r\n}\n") } # --- Clock.* — a game-controlled simulated clock ----------------------------- # @L_clock (declared in emit_head, universal) is a plain seconds counter the game # owns: unlike Time.now / Time.since, it never touches the wall clock, so gameplay # that reads Clock.now() is deterministic and replay-safe. Advance it however the # simulation dictates (e.g. by Duration.* each tick), or set it outright. function is_clock_ns(meth: pointer) -> bool { if (meth == "now") or (meth == "set") or (meth == "advance") or (meth == "reset") { return true } return false } function emit_clock_ns(meth: pointer, e: Node) -> Val { if (meth == "now") { # the current simulated instant return val(emit_bind("load i32, ptr @L_clock"), "int") } if (meth == "reset") { # back to the epoch (0) emit(" store i32 0, ptr @L_clock\n") return val("0", "void") } if (meth == "set") { # set the clock to instant t let t = emit_expr(e.kids[0]) emit(` store i32 {t.code}, ptr @L_clock\n`) return val("0", "void") } # advance: move the clock forward by a Duration (seconds) let d = emit_expr(e.kids[0]) let cur = emit_bind("load i32, ptr @L_clock") let nv = emit_bind(`add i32 {cur}, {d.code}`) emit(` store i32 {nv}, ptr @L_clock\n`) return val("0", "void") }