refactor(lang): rename builtins flr->floor and fx->fixed

De-abbreviate the two bare fixed-point conversion builtins:
  flr(f) -> int     ->  floor(f) -> int    (fixed -> int, flooring)
  fx(i)  -> fixed   ->  fixed(i) -> fixed  (int -> fixed; mirrors how the
                                            stringify builtin is `string`)

Updates the compiler dispatch, all call sites, the grammars/LSP/JetBrains
tokens, and the docs (fn-flr -> fn-floor, fn-fx -> fn-fixed). Reseeded;
C-free fixpoint holds; all suites green (45/24/29); site + check.py OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-08-30 02:01:56 +03:00
parent effb3f637f
commit 2e0047514b
19 changed files with 80 additions and 80 deletions

View file

@ -157,7 +157,7 @@ both return a pointer you index with `buf[i]` — retype the binding (`words` /
Numeric literals: `42` and `0x1affff` are `int`; a literal with a decimal Numeric literals: `42` and `0x1affff` are `int`; a literal with a decimal
point (`1.5`) is `fixed`. Arithmetic on two `fixed` values lowers to point (`1.5`) is `fixed`. Arithmetic on two `fixed` values lowers to
`fxmul`/`fxdiv`; mixing `int` and `fixed` promotes the `int`. Convert with `fxmul`/`fxdiv`; mixing `int` and `fixed` promotes the `int`. Convert with
`fx(i)` (int→fixed) and `flr(f)` (fixed→int). `fixed(i)` (int→fixed) and `floor(f)` (fixed→int).
## Properties, entities, queries ## Properties, entities, queries
@ -717,7 +717,7 @@ literal; test any pointer/record/slice with `x == null` / `x != null` (an unset
``` ```
# math min max abs clamp (int) # math min max abs clamp (int)
# rng seed(i) rng_range(lo,hi)->int rng_chance(pct)->bool (deterministic) # rng seed(i) rng_range(lo,hi)->int rng_chance(pct)->bool (deterministic)
# fixed fx(i)->fixed flr(f)->int # fixed fixed(i)->fixed floor(f)->int
# tilemap map_size(w,h) map_row(y,str) tile(x,y)->int # tilemap map_size(w,h) map_row(y,str) tile(x,y)->int
# 2D draw clear(color) fill_rect(x,y,w,h,color) frame_rect(...) put_px(x,y,color) # 2D draw clear(color) fill_rect(x,y,w,h,color) frame_rect(...) put_px(x,y,color)
# draw_sprite(id,x,y) draw_sprite_scaled(id,x,y,scale) present() # draw_sprite(id,x,y) draw_sprite_scaled(id,x,y,scale) present()

View file

@ -1269,7 +1269,7 @@ system Generate phase Update {
for bp in pending_chunks() { for bp in pending_chunks() {
manip vm = Map.borrow_chunk(bp) manip vm = Map.borrow_chunk(bp)
for p in vm.area { for p in vm.area {
let h = flr(noise2d(fx(p.x) / 64.0, fx(p.z) / 64.0) * 24.0) + 8 let h = floor(noise2d(fixed(p.x) / 64.0, fixed(p.z) / 64.0) * 24.0) + 8
vm[p] = if p.y <= h { NODE_STONE } else { NODE_AIR } vm[p] = if p.y <= h { NODE_STONE } else { NODE_AIR }
} }
vm.commit() vm.commit()
@ -1436,7 +1436,7 @@ game Voxel {
manip vm = Map.stage(bp) manip vm = Map.stage(bp)
for p in vm.area { for p in vm.area {
let n = noise2d(p.x as f32 / 64.0, p.z as f32 / 64.0, seed()) let n = noise2d(p.x as f32 / 64.0, p.z as f32 / 64.0, seed())
let h = flr(n * 24.0) + 8 let h = floor(n * 24.0) + 8
vm[p] = if p.y > h { NODE_AIR } vm[p] = if p.y > h { NODE_AIR }
else if p.y == h { NODE_GRASS } else if p.y == h { NODE_GRASS }
else { NODE_STONE } else { NODE_STONE }

View file

@ -1,15 +1,15 @@
--- ---
id: fn-fx id: fn-fixed
name: fx name: fixed
category: builtins category: builtins
kind: builtin kind: builtin
tokens: fx tokens: fixed
sig: fx(n) -> fixed sig: fixed(n) -> fixed
tip: Lift an integer into a Q16.16 fixed-point value. tip: Lift an integer into a Q16.16 fixed-point value.
order: 50 order: 50
--- ---
Converts an integer into a <code>fixed</code> value (Ludic's Q16.16 fixed-point type), so it can take part in fractional arithmetic. Ludic has no floating point; <code>fixed</code> is how you carry sub-pixel precision for smooth movement and physics-like accumulation. Use <code>fx</code> when you need to combine an <code>int</code> with fixed-point values or divide to get a fraction — for example <code>fx(1) / fx(4)</code> is <code>0.25</code>. Convert back to a whole number for drawing with <code>flr</code>. Converts an integer into a <code>fixed</code> value (Ludic's Q16.16 fixed-point type), so it can take part in fractional arithmetic. Ludic has no floating point; <code>fixed</code> is how you carry sub-pixel precision for smooth movement and physics-like accumulation. Use <code>fixed</code> when you need to combine an <code>int</code> with fixed-point values or divide to get a fraction — for example <code>fixed(1) / fixed(4)</code> is <code>0.25</code>. Convert back to a whole number for drawing with <code>flr</code>.
Parameters: Parameters:
- `n` — the integer to lift into fixed-point - `n` — the integer to lift into fixed-point
@ -17,10 +17,10 @@ Parameters:
```ludic ```ludic
program SmoothAccumulate { program SmoothAccumulate {
handler ComputeStep phase Start { handler ComputeStep phase Start {
let full_speed = fx(3) let full_speed = fixed(3)
let half_speed = full_speed / fx(2) let half_speed = full_speed / fixed(2)
let two_steps = half_speed + half_speed let two_steps = half_speed + half_speed
print(flr(two_steps)) print(floor(two_steps))
} }
} }
``` ```

View file

@ -1,15 +1,15 @@
--- ---
id: fn-flr id: fn-floor
name: flr name: floor
category: builtins category: builtins
kind: builtin kind: builtin
tokens: flr tokens: floor
sig: flr(x) -> int sig: floor(x) -> int
tip: Floor a fixed-point value down to the nearest integer. tip: Floor a fixed-point value down to the nearest integer.
order: 50 order: 50
--- ---
Converts a <code>fixed</code> (Q16.16) value back to an <code>int</code> by discarding the fractional part, rounding toward negative infinity. It is the counterpart to <code>fx</code>: you accumulate motion in fixed-point for sub-pixel smoothness, then <code>flr</code> the result to get the whole-pixel column or row to draw at. Because it floors rather than rounds, <code>flr(fx(3) / fx(2))</code> is <code>1</code>, not <code>2</code>. Use it wherever a fixed value must become an integer coordinate, count, or index. Converts a <code>fixed</code> (Q16.16) value back to an <code>int</code> by discarding the fractional part, rounding toward negative infinity. It is the counterpart to <code>fx</code>: you accumulate motion in fixed-point for sub-pixel smoothness, then <code>floor</code> the result to get the whole-pixel column or row to draw at. Because it floors rather than rounds, <code>floor(fixed(3) / fixed(2))</code> is <code>1</code>, not <code>2</code>. Use it wherever a fixed value must become an integer coordinate, count, or index.
Parameters: Parameters:
- `x` — the fixed-point value to floor - `x` — the fixed-point value to floor
@ -20,8 +20,8 @@ program FixedToPixels {
handler DrawWorld phase Render { handler DrawWorld phase Render {
Screen.clear(Color.MidnightBlue) Screen.clear(Color.MidnightBlue)
let smooth_column = fx(5) + fx(1) / fx(2) let smooth_column = fixed(5) + fixed(1) / fixed(2)
let pixel_x = flr(smooth_column) * TILE_SIZE let pixel_x = floor(smooth_column) * TILE_SIZE
Screen.fill_rectangle(x: pixel_x, y: 32, width: TILE_SIZE, height: TILE_SIZE, color: Color.LimeGreen) Screen.fill_rectangle(x: pixel_x, y: 32, width: TILE_SIZE, height: TILE_SIZE, color: Color.LimeGreen)
Screen.show() Screen.show()
} }

View file

@ -11,7 +11,7 @@ ns: Math
member: floor member: floor
--- ---
Takes a <code>fixed</code> value and returns the largest <code>int</code> not greater than it — rounding toward negative infinity, so <code>Math.floor(2.7)</code> is <code>2</code> and <code>Math.floor(-0.2)</code> is <code>-1</code>. This is the fixed-to-int conversion you reach for when turning a smooth position into a whole tile or pixel index. It is the same operation as the bare <code>flr(x)</code>. Takes a <code>fixed</code> value and returns the largest <code>int</code> not greater than it — rounding toward negative infinity, so <code>Math.floor(2.7)</code> is <code>2</code> and <code>Math.floor(-0.2)</code> is <code>-1</code>. This is the fixed-to-int conversion you reach for when turning a smooth position into a whole tile or pixel index. It is the same operation as the bare <code>floor(x)</code>.
Parameters: Parameters:
- `x` — the `fixed` value to round down - `x` — the `fixed` value to round down

View file

@ -23,8 +23,8 @@ program Physics {
handler MeasureSpeed phase Update { handler MeasureSpeed phase Update {
for (velocity) in query [Velocity, {Projectile}] { for (velocity) in query [Velocity, {Projectile}] {
let speed = c_hypot(a: fx(velocity.delta_x), b: fx(velocity.delta_y)) let speed = c_hypot(a: fixed(velocity.delta_x), b: fixed(velocity.delta_y))
Screen.status(string(flr(speed))) Screen.status(string(floor(speed)))
} }
} }
} }

View file

@ -9,11 +9,11 @@ tip: Q16.16 fixed-point for deterministic fractional math — no floats.
order: 1 order: 1
--- ---
`fixed` is Q16.16 fixed-point: a fractional number stored in 32 bits, giving you decimals without floating-point. Ludic uses it precisely because it is <b>deterministic</b> — the same computation gives the same bits on every machine, which is what a reproducible simulation and lockstep networking need. A literal with a decimal point (`1.5`) is a `fixed`; arithmetic on two `fixed` values does fixed-point multiply/divide, and mixing an `int` with a `fixed` promotes the `int`. Lift an `int` in with `fx(value)` and take the floor back out with `flr(value)`. `fixed` is Q16.16 fixed-point: a fractional number stored in 32 bits, giving you decimals without floating-point. Ludic uses it precisely because it is <b>deterministic</b> — the same computation gives the same bits on every machine, which is what a reproducible simulation and lockstep networking need. A literal with a decimal point (`1.5`) is a `fixed`; arithmetic on two `fixed` values does fixed-point multiply/divide, and mixing an `int` with a `fixed` promotes the `int`. Lift an `int` in with `fixed(value)` and take the floor back out with `floor(value)`.
```ludic ```ludic
const GRAVITY: fixed = 0.5 const GRAVITY: fixed = 0.5
var velocity_y: fixed = fx(0) var velocity_y: fixed = fixed(0)
var next_velocity: fixed = velocity_y + GRAVITY var next_velocity: fixed = velocity_y + GRAVITY
var pixel_row: int = flr(next_velocity) var pixel_row: int = floor(next_velocity)
``` ```

View file

@ -15,9 +15,9 @@ A <code>fixeds</code> is a raw buffer whose elements are <code>fixed</code> (Q16
program Heights { program Heights {
handler Setup phase Start { handler Setup phase Start {
let heights: fixeds = words(4) let heights: fixeds = words(4)
heights[0] = fx(2) heights[0] = fixed(2)
heights[1] = heights[0] + fx(1) heights[1] = heights[0] + fixed(1)
print(flr(heights[1])) print(floor(heights[1]))
} }
} }
``` ```

View file

@ -358,8 +358,8 @@ function tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fix
for ci in 0 .. nc { for ci in 0 .. nc {
let last = tt_u16(d, endpts + ci * 2) let last = tt_u16(d, endpts + ci * 2)
for k in start .. last + 1 { for k in start .. last + 1 {
let X = fx(xs[k]) let X = fixed(xs[k])
let Y = fx(ys[k]) let Y = fixed(ys[k])
ol_pt(a * X + c * Y + dx, b * X + e * Y + dy, (flags[k] & 1)) ol_pt(a * X + c * Y + dx, b * X + e * Y + dy, (flags[k] & 1))
} }
if ol_ne < 256 { if ol_ne < 256 {
@ -414,8 +414,8 @@ function tt_load_outline(id: int, gid: int, a: fixed, b: fixed, c: fixed, e: fix
var odx = dx var odx = dx
var ody = dy var ody = dy
if (flags & 2) != 0 { if (flags & 2) != 0 {
odx = a * fx(arg1) + c * fx(arg2) + dx odx = a * fixed(arg1) + c * fixed(arg2) + dx
ody = b * fx(arg1) + e * fx(arg2) + dy ody = b * fixed(arg1) + e * fixed(arg2) + dy
} }
tt_load_outline(id, cgid, a * ca + c * cb, b * ca + e * cb, a * cc + c * ce, b * cc + e * ce, odx, ody, depth + 1) tt_load_outline(id, cgid, a * ca + c * cb, b * ca + e * cb, a * cc + c * ce, b * cc + e * ce, odx, ody, depth + 1)
more = 0 more = 0

View file

@ -25,15 +25,15 @@ function tt_isqrt(v: int) -> int {
# flatten one quadratic Bézier into line segments, subdivided by chord length # flatten one quadratic Bézier into line segments, subdivided by chord length
function ed_quad(x0: fixed, y0: fixed, cx: fixed, cy: fixed, x1: fixed, y1: fixed) -> void { function ed_quad(x0: fixed, y0: fixed, cx: fixed, cy: fixed, x1: fixed, y1: fixed) -> void {
let dx = flr(x1) - flr(x0) let dx = floor(x1) - floor(x0)
let dy = flr(y1) - flr(y0) let dy = floor(y1) - floor(y0)
var n = tt_isqrt(dx * dx + dy * dy) / 3 var n = tt_isqrt(dx * dx + dy * dy) / 3
if n < 2 { n = 2 } if n < 2 { n = 2 }
if n > 24 { n = 24 } if n > 24 { n = 24 }
var px = x0 var px = x0
var py = y0 var py = y0
for i in 1 .. n + 1 { for i in 1 .. n + 1 {
let t = fx(i) / n let t = fixed(i) / n
let u = 1.0 - t let u = 1.0 - t
let qx = u * u * x0 + 2.0 * u * t * cx + t * t * x1 let qx = u * u * x0 + 2.0 * u * t * cx + t * t * x1
let qy = u * u * y0 + 2.0 * u * t * cy + t * t * y1 let qy = u * u * y0 + 2.0 * u * t * cy + t * t * y1
@ -53,8 +53,8 @@ var gr_adv: int = 0
function tt_raster(id: int, gid: int, px: int) -> pointer { function tt_raster(id: int, gid: int, px: int) -> pointer {
let upem = tt_upem[id] let upem = tt_upem[id]
let scale = fx(px) / upem let scale = fixed(px) / upem
gr_adv = flr(fx(tt_advance(id, gid)) * scale + 0.5) gr_adv = floor(fixed(tt_advance(id, gid)) * scale + 0.5)
gr_w = 0 gr_w = 0
gr_h = 0 gr_h = 0
ol_n = 0 ol_n = 0
@ -75,12 +75,12 @@ function tt_raster(id: int, gid: int, px: int) -> pointer {
if Y < miny { miny = Y } if Y < miny { miny = Y }
if Y > maxy { maxy = Y } if Y > maxy { maxy = Y }
} }
let x0 = flr(minx) let x0 = floor(minx)
let y0 = flr(miny) let y0 = floor(miny)
var x1 = flr(maxx) + 1 var x1 = floor(maxx) + 1
var y1 = flr(maxy) + 1 var y1 = floor(maxy) + 1
if maxx == fx(flr(maxx)) { x1 = flr(maxx) } if maxx == fixed(floor(maxx)) { x1 = floor(maxx) }
if maxy == fx(flr(maxy)) { y1 = flr(maxy) } if maxy == fixed(floor(maxy)) { y1 = floor(maxy) }
let W = x1 - x0 let W = x1 - x0
let H = y1 - y0 let H = y1 - y0
if W <= 0 { return null } if W <= 0 { return null }
@ -108,28 +108,28 @@ function tt_raster(id: int, gid: int, px: int) -> pointer {
# all off-curve: start at the midpoint of the first and last point # all off-curve: start at the midpoint of the first and last point
let mx = (ol_x[start] + ol_x[start + cnt - 1]) / 2 let mx = (ol_x[start] + ol_x[start + cnt - 1]) / 2
let my = (ol_y[start] + ol_y[start + cnt - 1]) / 2 let my = (ol_y[start] + ol_y[start + cnt - 1]) / 2
sx = (mx * scale - fx(x0)) * TT_SS sx = (mx * scale - fixed(x0)) * TT_SS
sy = (fx(y1) - my * scale) * TT_SS sy = (fixed(y1) - my * scale) * TT_SS
first_on = 0 first_on = 0
} else { } else {
sx = (ol_x[start + first_on] * scale - fx(x0)) * TT_SS sx = (ol_x[start + first_on] * scale - fixed(x0)) * TT_SS
sy = (fx(y1) - ol_y[start + first_on] * scale) * TT_SS sy = (fixed(y1) - ol_y[start + first_on] * scale) * TT_SS
} }
var curx = sx var curx = sx
var cury = sy var cury = sy
var step = 0 var step = 0
while step < cnt { while step < cnt {
let i = (first_on + 1 + step) % cnt let i = (first_on + 1 + step) % cnt
let ix = (ol_x[start + i] * scale - fx(x0)) * TT_SS let ix = (ol_x[start + i] * scale - fixed(x0)) * TT_SS
let iy = (fx(y1) - ol_y[start + i] * scale) * TT_SS let iy = (fixed(y1) - ol_y[start + i] * scale) * TT_SS
if ol_on[start + i] == 1 { if ol_on[start + i] == 1 {
ed_add(curx, cury, ix, iy) ed_add(curx, cury, ix, iy)
curx = ix curx = ix
cury = iy cury = iy
} else { } else {
var j = (i + 1) % cnt var j = (i + 1) % cnt
let jx = (ol_x[start + j] * scale - fx(x0)) * TT_SS let jx = (ol_x[start + j] * scale - fixed(x0)) * TT_SS
let jy = (fx(y1) - ol_y[start + j] * scale) * TT_SS let jy = (fixed(y1) - ol_y[start + j] * scale) * TT_SS
var ex = jx var ex = jx
var ey = jy var ey = jy
if ol_on[start + j] == 1 { if ol_on[start + j] == 1 {
@ -156,7 +156,7 @@ function tt_raster(id: int, gid: int, px: int) -> pointer {
let unit = 255 / (TT_SS * TT_SS) let unit = 255 / (TT_SS * TT_SS)
for sy in 0 .. SH { for sy in 0 .. SH {
let yc = fx(sy) + 0.5 let yc = fixed(sy) + 0.5
var m = 0 var m = 0
for i in 0 .. ed_n { for i in 0 .. ed_n {
let ya = ed_y0[i] let ya = ed_y0[i]
@ -202,20 +202,20 @@ function tt_raster(id: int, gid: int, px: int) -> pointer {
var xa = sc_x[i] var xa = sc_x[i]
var xb = sc_x[i + 1] var xb = sc_x[i + 1]
if xa < 0.0 { xa = 0.0 } if xa < 0.0 { xa = 0.0 }
if xb > fx(SW) { xb = fx(SW) } if xb > fixed(SW) { xb = fixed(SW) }
if xb > xa { if xb > xa {
let ixa = flr(xa) let ixa = floor(xa)
let ixb = flr(xb) + 1 let ixb = floor(xb) + 1
for sx in ixa .. ixb { for sx in ixa .. ixb {
var cxl = xa var cxl = xa
var cxr = xb var cxr = xb
if fx(sx) > cxl { cxl = fx(sx) } if fixed(sx) > cxl { cxl = fixed(sx) }
if fx(sx + 1) < cxr { cxr = fx(sx + 1) } if fixed(sx + 1) < cxr { cxr = fixed(sx + 1) }
let cvr = cxr - cxl let cvr = cxr - cxl
if cvr > 0.0 { if cvr > 0.0 {
let oxp = sx / TT_SS let oxp = sx / TT_SS
if oxp >= 0 { if oxp < W { if oy >= 0 { if oy < H { if oxp >= 0 { if oxp < W { if oy >= 0 { if oy < H {
var v = cover[oy * W + oxp] + flr(cvr * unit) var v = cover[oy * W + oxp] + floor(cvr * unit)
if v > 255 { v = 255 } if v > 255 { v = 255 }
cover[oy * W + oxp] = v cover[oy * W + oxp] = v
} } } } } } } }
@ -332,9 +332,9 @@ function rt_text_ttf(font: int, x: int, y: int, s: string, colour: int, px: int)
if font >= tt_n { return } if font >= tt_n { return }
if px <= 0 { return } if px <= 0 { return }
let upem = tt_upem[font] let upem = tt_upem[font]
let scale = fx(px) / upem let scale = fixed(px) / upem
var baseline = y + flr(fx(tt_asc[font]) * scale + 0.5) var baseline = y + floor(fixed(tt_asc[font]) * scale + 0.5)
let lineh = flr(fx(tt_asc[font] - tt_desc[font] + tt_gap[font]) * scale + 0.5) let lineh = floor(fixed(tt_asc[font] - tt_desc[font] + tt_gap[font]) * scale + 0.5)
var penx = x var penx = x
var i = 0 var i = 0
while s[i] != 0 { while s[i] != 0 {
@ -375,6 +375,6 @@ function rt_text_w(font: int, s: string, px: int) -> int {
function rt_text_h(font: int, px: int) -> int { function rt_text_h(font: int, px: int) -> int {
if font < 0 { return 0 } if font < 0 { return 0 }
if font >= tt_n { return 0 } if font >= tt_n { return 0 }
let scale = fx(px) / tt_upem[font] let scale = fixed(px) / tt_upem[font]
return flr(fx(tt_asc[font] - tt_desc[font] + tt_gap[font]) * scale + 0.5) return floor(fixed(tt_asc[font] - tt_desc[font] + tt_gap[font]) * scale + 0.5)
} }

View file

@ -436,8 +436,8 @@ function emit_call(e: Node) -> Val {
let w = emit_bind(`zext i32 {by} to i64`) let w = emit_bind(`zext i32 {by} to i64`)
return val(emit_bind(`call ptr @malloc(i64 {w})`), "words") return val(emit_bind(`call ptr @malloc(i64 {w})`), "words")
} }
if (name == "fx") { let a = emit_expr(e.kids[0]); return val(emit_bind(`shl i32 {a.code}, 16`), "fixed") } if (name == "fixed") { let a = emit_expr(e.kids[0]); return val(emit_bind(`shl i32 {a.code}, 16`), "fixed") }
if (name == "flr") { let a = emit_expr(e.kids[0]); return val(emit_bind(`ashr i32 {a.code}, 16`), "int") } if (name == "floor") { let a = emit_expr(e.kids[0]); return val(emit_bind(`ashr i32 {a.code}, 16`), "int") }
# The EV2 reflection ABI (the world table), exposed to Ludic so a Ludic mod can # The EV2 reflection ABI (the world table), exposed to Ludic so a Ludic mod can
# introspect the world by name — the same functions a foreign mod binds. Emitted # introspect the world by name — the same functions a foreign mod binds. Emitted
# only for a modding program (ECS + events), so a plain game is unchanged. # only for a modding program (ECS + events), so a plain game is unchanged.

View file

@ -3602,11 +3602,11 @@ declare void @win_close()
@.str3501 = private unnamed_addr constant [22 x i8] c"call ptr @malloc(i64 \00" @.str3501 = private unnamed_addr constant [22 x i8] c"call ptr @malloc(i64 \00"
@.str3502 = private unnamed_addr constant [2 x i8] c")\00" @.str3502 = private unnamed_addr constant [2 x i8] c")\00"
@.str3503 = private unnamed_addr constant [6 x i8] c"words\00" @.str3503 = private unnamed_addr constant [6 x i8] c"words\00"
@.str3504 = private unnamed_addr constant [3 x i8] c"fx\00" @.str3504 = private unnamed_addr constant [6 x i8] c"fixed\00"
@.str3505 = private unnamed_addr constant [9 x i8] c"shl i32 \00" @.str3505 = private unnamed_addr constant [9 x i8] c"shl i32 \00"
@.str3506 = private unnamed_addr constant [5 x i8] c", 16\00" @.str3506 = private unnamed_addr constant [5 x i8] c", 16\00"
@.str3507 = private unnamed_addr constant [6 x i8] c"fixed\00" @.str3507 = private unnamed_addr constant [6 x i8] c"fixed\00"
@.str3508 = private unnamed_addr constant [4 x i8] c"flr\00" @.str3508 = private unnamed_addr constant [6 x i8] c"floor\00"
@.str3509 = private unnamed_addr constant [10 x i8] c"ashr i32 \00" @.str3509 = private unnamed_addr constant [10 x i8] c"ashr i32 \00"
@.str3510 = private unnamed_addr constant [5 x i8] c", 16\00" @.str3510 = private unnamed_addr constant [5 x i8] c", 16\00"
@.str3511 = private unnamed_addr constant [4 x i8] c"int\00" @.str3511 = private unnamed_addr constant [4 x i8] c"int\00"

View file

@ -2,11 +2,11 @@ program T {
entry { entry {
let half = 0.5 let half = 0.5
let a = 1.5 let a = 1.5
print(flr(a + half)) # flr(2.0) = 2 print(floor(a + half)) # floor(2.0) = 2
print(flr(a * 2.0)) # flr(3.0) = 3 print(floor(a * 2.0)) # floor(3.0) = 3
let third = 1.0 / 3.0 let third = 1.0 / 3.0
print(flr(third * 3.0)) # ~flr(1.0) = 1 (may be 0 with rounding) print(floor(third * 3.0)) # ~floor(1.0) = 1 (may be 0 with rounding)
print(flr(fx(5) + a)) # flr(6.5) = 6 print(floor(fixed(5) + a)) # floor(6.5) = 6
if a > half { print(1) } else { print(0) } # 1 if a > half { print(1) } else { print(0) } # 1
} }
} }

View file

@ -35,8 +35,8 @@
"fn-max", "fn-max",
"fn-abs", "fn-abs",
"fn-clamp", "fn-clamp",
"fn-fx", "fn-fixed",
"fn-flr", "fn-floor",
"fn-bytes", "fn-bytes",
"fn-words", "fn-words",
"fn-ui_build", "fn-ui_build",

View file

@ -57,7 +57,7 @@ object LudicVocabulary {
val WIDGETS = setOf("panel", "col", "row", "label", "button", "image", "spacer") val WIDGETS = setOf("panel", "col", "row", "label", "button", "image", "spacer")
val BUILTINS = setOf( val BUILTINS = setOf(
"min", "max", "abs", "clamp", "seed", "rng_range", "rng_chance", "fx", "flr", "min", "max", "abs", "clamp", "seed", "rng_range", "rng_chance", "fixed", "floor",
"map_size", "map_row", "tile", "clear", "present", "fill_rect", "frame_rect", "map_size", "map_row", "tile", "clear", "present", "fill_rect", "frame_rect",
"put_px", "text", "text_int", "font_load", "text_ttf", "text_w", "text_h", "put_px", "text", "text_int", "font_load", "text_ttf", "text_w", "text_h",
"image_load", "draw_image", "draw_image_scaled", "draw_9slice", "png_load", "image_load", "draw_image", "draw_image_scaled", "draw_9slice", "png_load",

View file

@ -204,7 +204,7 @@
{ {
"comment": "the runtime surface — every name here resolves to rt_<name> in runtime/native", "comment": "the runtime surface — every name here resolves to rt_<name> in runtime/native",
"name": "support.function.builtin.ludic", "name": "support.function.builtin.ludic",
"match": "\\b(min|max|abs|clamp|seed|rng_range|rng_chance|fx|flr|map_size|map_row|tile|clear|present|fill_rect|frame_rect|put_px|text|text_int|font_load|text_ttf|text_w|text_h|image_load|draw_image|draw_image_scaled|draw_9slice|png_load|sprites_load|draw_sprite|draw_sprite_scaled|ui_build|ui_open|ui_tick|ui_render|ui_clicked|ui_set_text|ui_set_int|ui_focus|ui_focused|ui_visible|key|reg|set_reg|self|save|load|status|print|string|quit)\\b(?=\\s*\\()" "match": "\\b(min|max|abs|clamp|seed|rng_range|rng_chance|fixed|floor|map_size|map_row|tile|clear|present|fill_rect|frame_rect|put_px|text|text_int|font_load|text_ttf|text_w|text_h|image_load|draw_image|draw_image_scaled|draw_9slice|png_load|sprites_load|draw_sprite|draw_sprite_scaled|ui_build|ui_open|ui_tick|ui_render|ui_clicked|ui_set_text|ui_set_int|ui_focus|ui_focused|ui_visible|key|reg|set_reg|self|save|load|status|print|string|quit)\\b(?=\\s*\\()"
}, },
{ {
"comment": "compiler intrinsics — these lower straight to libc or the OS", "comment": "compiler intrinsics — these lower straight to libc or the OS",

View file

@ -204,7 +204,7 @@
{ {
"comment": "the runtime surface — every name here resolves to rt_<name> in runtime/native", "comment": "the runtime surface — every name here resolves to rt_<name> in runtime/native",
"name": "support.function.builtin.ludic", "name": "support.function.builtin.ludic",
"match": "\\b(min|max|abs|clamp|seed|rng_range|rng_chance|fx|flr|map_size|map_row|tile|clear|present|fill_rect|frame_rect|put_px|text|text_int|font_load|text_ttf|text_w|text_h|image_load|draw_image|draw_image_scaled|draw_9slice|png_load|sprites_load|draw_sprite|draw_sprite_scaled|ui_build|ui_open|ui_tick|ui_render|ui_clicked|ui_set_text|ui_set_int|ui_focus|ui_focused|ui_visible|key|reg|set_reg|self|save|load|status|print|string|quit)\\b(?=\\s*\\()" "match": "\\b(min|max|abs|clamp|seed|rng_range|rng_chance|fixed|floor|map_size|map_row|tile|clear|present|fill_rect|frame_rect|put_px|text|text_int|font_load|text_ttf|text_w|text_h|image_load|draw_image|draw_image_scaled|draw_9slice|png_load|sprites_load|draw_sprite|draw_sprite_scaled|ui_build|ui_open|ui_tick|ui_render|ui_clicked|ui_set_text|ui_set_int|ui_focus|ui_focused|ui_visible|key|reg|set_reg|self|save|load|status|print|string|quit)\\b(?=\\s*\\()"
}, },
{ {
"comment": "compiler intrinsics — these lower straight to libc or the OS", "comment": "compiler intrinsics — these lower straight to libc or the OS",

View file

@ -1404,8 +1404,8 @@ program LudicLsp {
badd("seed", "seed(i: int)") badd("seed", "seed(i: int)")
badd("rng_range", "rng_range(lo: int, hi: int) -> int") badd("rng_range", "rng_range(lo: int, hi: int) -> int")
badd("rng_chance", "rng_chance(pct: int) -> bool") badd("rng_chance", "rng_chance(pct: int) -> bool")
badd("fx", "fx(i: int) -> fixed") badd("fx", "fixed(i: int) -> fixed")
badd("flr", "flr(f: fixed) -> int") badd("flr", "floor(f: fixed) -> int")
badd("map_size", "map_size(w: int, h: int)") badd("map_size", "map_size(w: int, h: int)")
badd("map_row", "map_row(y: int, row: string)") badd("map_row", "map_row(y: int, row: string)")
badd("tile", "tile(x: int, y: int) -> int") badd("tile", "tile(x: int, y: int) -> int")

View file

@ -97,8 +97,8 @@ static const LBuiltin LUDIC_BUILTINS[] = {
{"seed","seed(i: int)","Seed the deterministic RNG."}, {"seed","seed(i: int)","Seed the deterministic RNG."},
{"rng_range","rng_range(lo: int, hi: int) -> int","Deterministic integer in [lo, hi]."}, {"rng_range","rng_range(lo: int, hi: int) -> int","Deterministic integer in [lo, hi]."},
{"rng_chance","rng_chance(pct: int) -> bool","True pct% of the time, deterministically."}, {"rng_chance","rng_chance(pct: int) -> bool","True pct% of the time, deterministically."},
{"fx","fx(i: int) -> fixed","Widen an int to Q16.16 fixed-point."}, {"fixed","fixed(i: int) -> fixed","Widen an int to Q16.16 fixed-point."},
{"flr","flr(f: fixed) -> int","Truncate a fixed-point value toward zero."}, {"floor","floor(f: fixed) -> int","Truncate a fixed-point value toward zero."},
{"map_size","map_size(w: int, h: int)","Set the tilemap dimensions."}, {"map_size","map_size(w: int, h: int)","Set the tilemap dimensions."},
{"map_row","map_row(y: int, row: string)","Fill one tilemap row from a string."}, {"map_row","map_row(y: int, row: string)","Fill one tilemap row from a string."},
{"tile","tile(x: int, y: int) -> int","Tile code at a map cell."}, {"tile","tile(x: int, y: int) -> int","Tile code at a map cell."},