feat(compiler): list literals, typed compound assignment, file:line diagnostics

- `[a, b, c]` list literals (E_LIST → emit_list); static_type learns
  slice-element, `new T`, list, string and literal kinds
- `x op= y` lowers through the same path as `x = x op y` (emit_bin_vals):
  fixed `*=`/`/=` use the Q16.16 64-bit paths, string `+=` concatenates,
  int→long widens; unary `-` keeps a fixed operand's type (arith_ty)
- one `unescape()` table for "strings", 'chars' and `interpolation`;
  `'\''`, `'\\'`, `'\"'` no longer read as 0; unterminated char literals
  and unexpected characters are errors instead of silently skipped
- every diagnostic is `file:line: error: msg` (g_parse_file / g_err_file,
  Node.file + Node.line set by node()); tok_desc() in expectation errors;
  duplicate `function` names and unknown `phase` names are reported in
  source terms (phase_id used to default unknown phases to Overlay)
- interpolation holes skip braces inside string literals
- hand-IR preludes move from the user `@fn_` prefix to `@lp_` so a user
  `is_ws` / `str_eq` / `path_join` no longer collides at link time
- `@ClearColor(expr)` accepts any constant expression; `Os.pid()` added
  (docs page + inventory); `str_starts()` in support/str
- main.ludic: `else if` flag ladder, char literals, stale script comments
- examples/lang/operators.ludic covers all of the above; os.ludic covers
  Os.pid; docs pages for Os.pid and the Overlay phase; ten changesets
- reseeded: selfhost/ludicc.seed.ll is the new compiler's own fixpoint

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-05 01:12:16 +03:00
parent ad548840c7
commit 647dfec334
88 changed files with 30081 additions and 29179 deletions

View file

@ -1,13 +1,13 @@
program T {
function fib(n: int) -> int { if n < 2 { return n }; return fib(n-1) + fib(n-2) }
function fib(n: int) -> int { if n < 2 { return n }; return fib(n - 1) + fib(n - 2) }
entry {
print(fib(10)) # 55
var s = 0
for i in 0 .. 10 { if i == 5 { break }; if i % 2 == 0 { continue }; s = s + i }
for i in 0 .. 10 { if i == 5 { break }; if i % 2 == 0 { continue }; s += i }
print(s) # 1+3 = 4
var i = 0; var t = 0
while true { i = i + 1; if i > 5 { break }; t = t + i }
while true { i += 1; if i > 5 { break }; t += i }
print(t) # 15
if false and (1/0 == 0) { print(999) } else { print(1) } # short-circuit: no div by zero
if false and (1 / 0 == 0) { print(999) } else { print(1) } # short-circuit: no div by zero
}
}

View file

@ -23,7 +23,7 @@ program T {
let payload = "score=9001;level=12"
let mac = Crypto.hmac_sha256(key, payload)
if Crypto.verify_hmac(key, payload, mac) { print(1) } else { print(0) } # 1: untampered
if Crypto.verify_hmac(key, "score=9999;level=12", mac) { print(1) } else { print(0) } # 0: tampered payload
if Crypto.verify_hmac(key, "score=9999;level=12", mac) { print(1) } else { print(0) } # 0: tampered payload
let forged = "0000000000000000000000000000000000000000000000000000000000000000"
if Crypto.verify_hmac(key, payload, forged) { print(1) } else { print(0) } # 0: forged mac

View file

@ -2,7 +2,7 @@ program T {
entry {
# Duration — spans in whole seconds
print(Duration.hours(3)) # 10800
print(Duration.minutes(5) + Duration.seconds(30)) # 330
print(Duration.minutes(5) + Duration.seconds(30)) # 330
print(Duration.days(2)) # 172800
print(Duration.as_hours(10800)) # 3
print(Duration.as_days(172800)) # 2
@ -25,7 +25,7 @@ program T {
let feb = Date.add_days(Date.new(2026, 1, 1), 31)
print(Date.month(feb)) # 2
print(Date.day(feb)) # 1
print(Date.diff_days(Date.new(2026, 1, 1), Date.new(2025, 1, 1))) # 365
print(Date.diff_days(Date.new(2026, 1, 1), Date.new(2025, 1, 1))) # 365
# DateTime — instant as seconds-since-1970
let t = DateTime.from(2000, 1, 1, 12, 30, 15)
@ -38,6 +38,6 @@ program T {
print(DateTime.second(t)) # 15
print(DateTime.weekday(t)) # 6
print(DateTime.date(t)) # 10957
print(DateTime.hour(DateTime.add(0, Duration.hours(1)))) # 1
print(DateTime.hour(DateTime.add(0, Duration.hours(1)))) # 1
}
}

View file

@ -8,7 +8,7 @@ program T {
print(DateTime.format(t, "DD/MM/YY")) # 30/08/26
# parse — round-trips to the same instant; missing fields default to midnight
print(DateTime.parse("2026-08-30", "YYYY-MM-DD") - Date.to_epoch(Date.new(2026, 8, 30))) # 0
print(DateTime.parse("2026-08-30", "YYYY-MM-DD") - Date.to_epoch(Date.new(2026, 8, 30))) # 0
print(DateTime.parse("zz", "YYYY")) # -1 (non-digit -> parse fails)
# Clock — the game-controlled simulated clock

View file

@ -38,7 +38,7 @@ program T {
let t = Portal(2, 40)
match t {
Empty, Wall => { print(100) }
Portal(x, y) => { print(x * y) } # 80
Portal(x, y) => { print(x * y) } # 80
_ => { print(999) }
}

View file

@ -16,7 +16,7 @@ program T {
print(x > y) # 1
print(y < x) # 1
print(x == x) # 1
print(0 - sq) # -1000000000000 (unary negate stays 64-bit)
print(-sq) # -1000000000000 (unary negate stays 64-bit)
print(big) # 1000000
print(Text.length(string(sq))) # 13 — string(long) renders all 13 digits
}

View file

@ -3,11 +3,11 @@ program T {
# min / max / abs / clamp, now reachable under the Math namespace
print(Math.min(3, 7)) # 3
print(Math.max(3, 7)) # 7
print(Math.abs(0 - 5)) # 5
print(Math.abs(-5)) # 5
print(Math.clamp(12, 0, 10)) # 10
# sign -> -1 / 0 / 1
print(Math.sign(0 - 4)) # -1
print(Math.sign(-4)) # -1
print(Math.sign(0)) # 0
print(Math.sign(9)) # 1

View file

@ -10,11 +10,11 @@ program T {
print(Math.floor(Math.dist2(0.0, 0.0, 3.0, 4.0))) # 25
print(Math.floor(Math.deg_to_rad(180.0) * 1000.0)) # 3142
print(Math.floor(Math.rad_to_deg(3.1415926))) # 180
print(Math.posmod(0 - 1, 5)) # 4
print(Math.posmod(-1, 5)) # 4
print(Math.wrap(12, 0, 10)) # 2
print(Math.ping_pong(13, 10)) # 7
print(Math.floor(Math.snapped(2.3, 0.5) * 10.0)) # 25
print(Math.floor(Math.move_toward(0.0, 10.0, 3.0) * 10.0)) # 30
print(Math.floor(Math.smoothstep(0.0, 10.0, 5.0) * 1000.0)) # 500
print(Math.floor(Math.smoothstep(0.0, 10.0, 5.0) * 1000.0)) # 500
}
}

View file

@ -3,7 +3,7 @@ program T {
print(Math.round(Math.rad_to_deg(Math.atan2(1.0, 1.0)))) # 45
print(Math.round(Math.rad_to_deg(Math.atan2(1.0, 0.0)))) # 90
print(Math.round(Math.rad_to_deg(Math.atan2(0.0, 0.0 - 1.0)))) # 180
print(Math.round(Math.rad_to_deg(Math.atan2(0.0 - 1.0, 0.0 - 1.0)))) # -135
print(Math.round(Math.rad_to_deg(Math.atan2(0.0 - 1.0, 0.0 - 1.0)))) # -135
print(Math.round(Math.rad_to_deg(Math.asin(0.5)))) # 30
print(Math.round(Math.rad_to_deg(Math.acos(0.5)))) # 60
print(Math.round(Math.rad_to_deg(Math.asin(1.0)))) # 90

View file

@ -1,6 +1,6 @@
program T {
property P { x: int = 0, y: int = 7, next: P }
function bump(p: P) -> void { p.x = p.x + 100 }
function bump(p: P) -> void { p.x += 100 }
entry {
let a = new P
print(a.y) # 7 default

View file

@ -9,7 +9,7 @@ program T {
print(Math.round(Math.pow(2.0, 10.0))) # 1024 -> 1024
print(Math.round(Math.pow(2.0, 0.5) * 1000)) # sqrt 2 -> 1414
print(Math.round(Math.pow(9.0, 0.5))) # 3 -> 3
print(Math.round(Math.pow(2.0, 0.0 - 2.0) * 1000)) # 0.25 -> 250
print(Math.round(Math.pow(2.0, 0.0 - 2.0) * 1000)) # 0.25 -> 250
print(Math.round(Ease.elastic(0.0) * 1000)) # anchored -> 0
print(Math.round(Ease.elastic(1.0) * 1000)) # settles -> 1000
print(Math.round(Ease.elastic(0.5) * 1000)) # overshoot -> 1016

View file

@ -13,13 +13,13 @@ program T {
print(floor(Vector.y(d))) # 2
print(floor(Vector.dot(a, b))) # 11
print(floor(Vector.distance(a, b))) # 2 (sqrt 8)
print(floor(Vector.x(Vector.scale(a, 2.0)))) # 6
print(floor(Vector.x(Vector.scale(a, 2.0)))) # 6
let n = Vector.normalize(a)
print(floor(Vector.length(n) * 1000)) # 999 (unit)
print(floor(Vector.x(Vector.from_angle(0.0)) * 1000)) # 999 (cos 0)
let r = Vector.rotate(Vector.make(1.0, 0.0), 1.5707963) # rotate 90 deg
let r = Vector.rotate(Vector.make(1.0, 0.0), 1.5707963) # rotate 90 deg
print(floor(Vector.y(r) * 1000)) # 999 (-> (0,1))
print(floor(Vector.angle(Vector.make(0.0, 1.0)) * 1000)) # pi/2 -> 1570
print(floor(Vector.angle(Vector.make(0.0, 1.0)) * 1000)) # pi/2 -> 1570
let m = Vector.lerp(Vector.zero(), Vector.make(10.0, 20.0), 0.5)
print(floor(Vector.x(m))) # 5
print(floor(Vector.y(m))) # 10