feat(lang): numbers float - a module whose decimal literals are float
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
e6f565a6c4
commit
3ac0d8d5cb
5 changed files with 36 additions and 0 deletions
|
|
@ -240,6 +240,9 @@ program Shade {
|
|||
exactly that decimal, not its Q16.16 approximation. With no float in sight it
|
||||
stays `fixed`, so existing code keeps its meaning. A whole literal expression
|
||||
(`1.0 / 3.0`) is evaluated in the context's type.
|
||||
- **`numbers float`.** A file that begins with `numbers float` (or has it inside its `program`
|
||||
block) takes bare decimal literals as `float`, not `fixed`; the files it imports inherit the
|
||||
mode (runtime files never do). One line in a package's barrel makes the package float.
|
||||
- **Promotion.** `int` and `long` promote to the float type of the other operand;
|
||||
`float` with `double` promotes to `double`.
|
||||
- **Explicit conversions.** `float(x)`, `double(x)`, `int(x)` (truncates toward
|
||||
|
|
|
|||
9
changes/numbers-float.md
Normal file
9
changes/numbers-float.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
bump: minor
|
||||
type: feat
|
||||
**`numbers float` — a module whose decimal literals are `float`.** A bare `1.5` is `fixed`
|
||||
unless a float is in sight, which is the right default for deterministic code and the wrong
|
||||
one for a renderer or a game whose numbers are all IEEE: `Math.sqrt(2.0)` came out in `fixed`,
|
||||
and every literal needed a `float(...)` round it. A file that says `numbers float` (at the top,
|
||||
or inside a `program` block) gets float literals, and passes the mode on to every non-runtime
|
||||
file it imports, so one line in a barrel or an entry file covers a whole package. Runtime files
|
||||
under `runtime/` are never switched, so the engine's own fixed-point code keeps its meaning.
|
||||
|
|
@ -1437,6 +1437,11 @@ function emit_call(e: Node) -> Val {
|
|||
function emit_expr(e: Node) -> Val {
|
||||
if (e == null) { return val("0", "int") }
|
||||
if e.kind == E_INT { return val(itoa(e.ival), "int") }
|
||||
if e.kind == E_FLOAT and is_float_file(e.file) {
|
||||
let ff = val(fp_lit_code(e.s, "float"), "float")
|
||||
ff.lit = e
|
||||
return ff
|
||||
}
|
||||
if e.kind == E_FLOAT { let fv = val(itoa(e.ival), "fixed"); fv.lit = e; return fv }
|
||||
if e.kind == E_PREVAL { return g_prevals[e.ival] }
|
||||
if e.kind == E_BOOL { return val(itoa(e.ival), "bool") }
|
||||
|
|
|
|||
|
|
@ -358,6 +358,7 @@ function static_type(e: Node) -> pointer {
|
|||
}
|
||||
if e.kind == E_STR or e.kind == E_SLICE { return "string" }
|
||||
if e.kind == E_INT { return "int" }
|
||||
if e.kind == E_FLOAT and is_float_file(e.file) { return "float" }
|
||||
if e.kind == E_FLOAT { return "fixed" }
|
||||
if e.kind == E_BOOL { return "bool" }
|
||||
return null
|
||||
|
|
|
|||
|
|
@ -11,6 +11,18 @@ var g_game_name: pointer # the `program` name
|
|||
# build tools parse. While parsing, the location is the current token's; while
|
||||
# lowering, it is the statement being emitted (emit_stmt records it).
|
||||
var g_parse_file: pointer = "" # the file whose tokens are being parsed
|
||||
# `numbers float`: files whose bare decimal literals are float, not fixed. A file that says
|
||||
# so is listed, and so is every non-runtime file it imports (a barrel passes it on).
|
||||
var g_float_files: []pointer = new []pointer
|
||||
function is_float_file(f: pointer) -> bool {
|
||||
if (f == null) { return false }
|
||||
var i = 0
|
||||
while i < len(g_float_files) {
|
||||
if (g_float_files[i] == f) { return true }
|
||||
i += 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
var g_parsing: bool = true # false once lowering starts
|
||||
var g_err_file: pointer = "" # the statement being lowered
|
||||
var g_err_line: int = 0
|
||||
|
|
@ -816,6 +828,11 @@ function parse_one_decl() -> void {
|
|||
}
|
||||
skipnl()
|
||||
}
|
||||
if is_id("numbers") and (toks[pi + 1].text == "float") {
|
||||
pi += 2
|
||||
if not is_float_file(g_parse_file) { push(g_float_files, g_parse_file) }
|
||||
return
|
||||
}
|
||||
if is_id("import") { pi += 1
|
||||
let t = toks[pi]
|
||||
if t.kind != TK_STR { perr("expected \"path\" after import") }
|
||||
|
|
@ -1004,6 +1021,7 @@ function do_import(rel: pointer) -> void {
|
|||
}
|
||||
if already_loaded(full) { return }
|
||||
push(loaded_paths, full)
|
||||
if is_float_file(g_parse_file) and not is_runtime_path(rel) and not is_float_file(full) { push(g_float_files, full) }
|
||||
if (src == null) { perr(`cannot open import {full}`) }
|
||||
# the audio runtime can arrive through atlas.ludic's own import or the Assets
|
||||
# splice, not only through an Audio.* call in the game; a windowed build must
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue