Phase 7e: polymorphic print(x) + str(x) (retire print_int/print_str)
One `print` instead of two C-style names: `print(x)` writes an int OR a string followed by a newline, dispatching on the operand type (int -> %d, string -> %s). `print(int)` emits byte-identically to the old print_int, so every existing call and every smoke-test output is unchanged. print_str was only ever the raw IR-to-stdout dump in ir_flush (no newline), which is not "printing a line" — so it now uses file_write to a new file_stdout() stream, keeping the emitted IR byte-for-byte identical. That frees `print` to have consistent always-newline semantics. Two reseeds: (A) add print + str + file_stdout keeping the intrinsics; (B) migrate the 61 print_int calls to print, ir_flush to file_write(file_stdout()), and delete print_int/print_str (+ the now-dead @.fmt_str). str(x) (the interpolation converter from 7d) is now also a documented standalone builtin. Vocabulary: print/str/file_stdout in, print_int/print_str out (ludic_syntax.h, grammar, LudicTokens.kt). LANGUAGE.md updated. Reseeded (22551 lines); C-free fixpoint holds; goldens identical; 18/18; vocab + doc-fences clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
311042e5ab
commit
e9c15cc620
23 changed files with 2947 additions and 2924 deletions
|
|
@ -31,7 +31,7 @@ program RPG2D {
|
|||
}
|
||||
|
||||
handler Report phase Render {
|
||||
for (t, v) in query [Transform, Velocity] { print_int(t.x); print_int(v.speed2) }
|
||||
for (t, v) in query [Transform, Velocity] { print(t.x); print(v.speed2) }
|
||||
quit()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ program Hello {
|
|||
|
||||
handler Report phase Update {
|
||||
for (p) in query [Pos] {
|
||||
print_int(p.x)
|
||||
print_int(p.y)
|
||||
print(p.x)
|
||||
print(p.y)
|
||||
}
|
||||
quit()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,11 @@ program Arena {
|
|||
|
||||
handler Fight phase Update {
|
||||
for (f) in query [Fighter] {
|
||||
print_int(damage(f.attack, f.armour, 0))
|
||||
print_int(damage(f.attack, f.armour, 20))
|
||||
print_int(hits_to_kill(f.hp, f.attack, f.armour))
|
||||
print(damage(f.attack, f.armour, 0))
|
||||
print(damage(f.attack, f.armour, 20))
|
||||
print(hits_to_kill(f.hp, f.attack, f.armour))
|
||||
}
|
||||
print_int(xp_for(3, 4))
|
||||
print(xp_for(3, 4))
|
||||
quit()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,18 +10,18 @@ program Life {
|
|||
model Enemy { Health, Sprite }
|
||||
|
||||
# ---- program lifecycle -----------------------------------------------------
|
||||
@OnStart handler Boot { print_int(1) } # once, at boot
|
||||
@OnQuit handler Bye { print_int(2) } # once, at shutdown
|
||||
@OnStart handler Boot { print(1) } # once, at boot
|
||||
@OnQuit handler Bye { print(2) } # once, at shutdown
|
||||
|
||||
# ---- property / entity lifecycle -------------------------------------------
|
||||
@OnAttach(Sprite) handler Load { print_int(Sprite.id + 700) } # when a Sprite is attached
|
||||
@OnAttach(Sprite) handler Load { print(Sprite.id + 700) } # when a Sprite is attached
|
||||
@OnSpawn(Enemy) handler Init { Health.hp = Health.max } # constructor
|
||||
@OnDespawn(Enemy) handler Clean { print_int(Health.hp + 900) } # destructor (dispatched by kind)
|
||||
@OnDespawn(Enemy) handler Clean { print(Health.hp + 900) } # destructor (dispatched by kind)
|
||||
|
||||
handler Seed phase Start { spawn Enemy { Health { max: 50 } } }
|
||||
|
||||
handler Run phase Render {
|
||||
for (h) in query [Health] { print_int(h.hp) } # 50 — Init set hp to max
|
||||
for (h) in query [Health] { print(h.hp) } # 50 — Init set hp to max
|
||||
for (e) in query [Health] { despawn self() } # Clean fires: 50 + 900 = 950
|
||||
quit()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,13 +24,13 @@ program QueryDecl {
|
|||
# property; `on: Foe` adds the {Foe} kind filter.
|
||||
@Queries(these: [Battle{hp <= 0 and side == 1}, Pos], on: Foe)
|
||||
handler CleanBattle phase LateUpdate {
|
||||
print_int(Pos.x)
|
||||
print(Pos.x)
|
||||
despawn self()
|
||||
}
|
||||
|
||||
@Queries(these: [Battle])
|
||||
handler Census phase Render {
|
||||
print_int(Battle.hp)
|
||||
print(Battle.hp)
|
||||
}
|
||||
|
||||
handler Bye phase Render { quit() }
|
||||
|
|
|
|||
|
|
@ -7,32 +7,32 @@
|
|||
program SceneDemo {
|
||||
const R_N: int = 0
|
||||
|
||||
handler Boot phase Start { set_reg(R_N, 0); print_int(1000) }
|
||||
handler Boot phase Start { set_reg(R_N, 0); print(1000) }
|
||||
|
||||
scene Title start {
|
||||
on enter { print_int(1) }
|
||||
on exit { print_int(2) }
|
||||
on enter { print(1) }
|
||||
on exit { print(2) }
|
||||
layer Main {
|
||||
handler Tick phase Update {
|
||||
set_reg(R_N, reg(R_N) + 1)
|
||||
print_int(100 + reg(R_N))
|
||||
print(100 + reg(R_N))
|
||||
if reg(R_N) >= 2 { become Play }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scene Play {
|
||||
on enter { print_int(3); set_reg(R_N, 0) }
|
||||
on exit { print_int(4) }
|
||||
on enter { print(3); set_reg(R_N, 0) }
|
||||
on exit { print(4) }
|
||||
layer World {
|
||||
handler Step phase Update {
|
||||
set_reg(R_N, reg(R_N) + 1)
|
||||
print_int(200 + reg(R_N))
|
||||
print(200 + reg(R_N))
|
||||
if reg(R_N) >= 2 { quit() }
|
||||
}
|
||||
}
|
||||
layer Hud {
|
||||
handler Draw phase Render { print_int(900) }
|
||||
handler Draw phase Render { print(900) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,24 +3,24 @@
|
|||
program Strings {
|
||||
entry {
|
||||
let greeting = "hello"
|
||||
if greeting == "hello" { print_int(1) } # content comparison
|
||||
if greeting != "world" { print_int(2) }
|
||||
if greeting == "hello" { print(1) } # content comparison
|
||||
if greeting != "world" { print(2) }
|
||||
|
||||
let who = "Ludic"
|
||||
let msg = greeting + ", " + who + "!" # concatenation chains
|
||||
if msg == "hello, Ludic!" { print_int(3) }
|
||||
if msg == "hello, Ludic!" { print(3) }
|
||||
|
||||
# build up a string in a loop
|
||||
var line = ""
|
||||
var i = 0
|
||||
while i < 3 { line = line + "ab"; i = i + 1 }
|
||||
if line == "ababab" { print_int(4) }
|
||||
if line == "ababab" { print(4) }
|
||||
|
||||
if (greeting + who) != greeting { print_int(5) }
|
||||
if (greeting + who) != greeting { print(5) }
|
||||
|
||||
# interpolation: `{expr}` embeds any expression (numbers become text)
|
||||
let n = 42
|
||||
if `{greeting}, {who}!` == "hello, Ludic!" { print_int(6) }
|
||||
if `n={n}, next={n + 1}` == "n=42, next=43" { print_int(7) }
|
||||
if `{greeting}, {who}!` == "hello, Ludic!" { print(6) }
|
||||
if `n={n}, next={n + 1}` == "n=42, next=43" { print(7) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ program Toggles {
|
|||
model Player { Health, Shield }
|
||||
|
||||
# hooks run at the toggle point with the property's data bound by name
|
||||
@OnDisable(Shield) handler Down { print_int(Shield.amount + 1) } # 5 + 1
|
||||
@OnEnable(Shield) handler Up { print_int(Shield.amount + 2) } # 5 + 2
|
||||
@OnDisable(Shield) handler Down { print(Shield.amount + 1) } # 5 + 1
|
||||
@OnEnable(Shield) handler Up { print(Shield.amount + 2) } # 5 + 2
|
||||
|
||||
handler Seed phase Start { spawn Player { Health { max: 50 }, Shield { amount: 5 } } }
|
||||
|
||||
|
|
@ -21,17 +21,17 @@ program Toggles {
|
|||
for (e) in query [Player] { disable Shield on self() } # @OnDisable -> 6
|
||||
var n = 0
|
||||
for (s) in query [Shield] { n += 1 }
|
||||
print_int(n) # 0 — no live Shield now
|
||||
print(n) # 0 — no live Shield now
|
||||
|
||||
for (e) in query [Player] { enable Shield on self() } # @OnEnable -> 7, data intact
|
||||
n = 0
|
||||
for (s) in query [Shield] { n += 1 }
|
||||
print_int(n) # 1 — amount is still 5
|
||||
print(n) # 1 — amount is still 5
|
||||
|
||||
disable Player # whole model off
|
||||
n = 0
|
||||
for (p) in query [Player] { n += 1 }
|
||||
print_int(n) # 0 — model disabled
|
||||
print(n) # 0 — model disabled
|
||||
quit()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue