feat(text): Text.to_float; a void function's return <value> is an error

Text.to_float reads a leading decimal number (strtod), the twin of Text.to_int, for data files.
A return with a value in a function that returns nothing now says so where it is, instead of
reaching clang as 'store void'. Seed regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-23 19:19:28 +03:00
parent f5ab5cf88a
commit c57eafcacc
8 changed files with 38133 additions and 37958 deletions

5
changes/text-to-float.md Normal file
View file

@ -0,0 +1,5 @@
bump: minor
type: feat
**`Text.to_float(s)`** - a leading decimal number read from a string as a `float` (`strtod`
underneath), the twin of `Text.to_int`. A game's data files carry fractions - an effect's
strength, a model's reach - and there was no way to read one back.

View file

@ -0,0 +1,5 @@
bump: patch
type: fix
**`return <value>` in a function that returns nothing is an error.** It used to reach clang as
`store void`, which fails the link with an LLVM message about void types and no line in the
program; it now says where, in words.

View file

@ -0,0 +1,27 @@
---
id: text-to_float
name: Text.to_float
category: text
kind: namespace-method
tokens: Text.to_float
sig: Text.to_float(s) -> float
tip: Parse a leading decimal number from a string.
order: 11
ns: Text
member: to_float
---
Reads an optional sign, the leading digits, a decimal point and an exponent from <code>s</code> and returns them as a <code>float</code>, stopping at the first character that cannot continue a number - so <code>Text.to_float("0.25 m")</code> is <code>0.25</code>. A string with no leading number yields <code>0.0</code>. It is the float twin of <code>Text.to_int</code>, for reading a data file or a typed value.
Parameters:
- `s` — the string to parse
```ludic
program ParseSpeed {
var typed: string = "3.2"
handler Apply phase Update {
let speed = Text.to_float(typed) # 3.2
}
}
```

View file

@ -117,6 +117,7 @@ function emit_for(st: Node) -> void {
}
function emit_return(st: Node) -> void {
if (st.a != null) and (ret_ty == "void") { perr("a function that returns nothing cannot return a value") }
if (st.a != null) {
let v = emit_expr(st.a)
store_at(llty(ret_ty), coerce_code(v, ret_ty), "%retval")

View file

@ -6,6 +6,7 @@
function is_text_ns(meth: pointer) -> bool {
if (meth == "length") or (meth == "char_at") or (meth == "slice") { return true }
if (meth == "equals") or (meth == "concat") or (meth == "to_int") or (meth == "from_int") { return true }
if (meth == "to_float") { return true }
if (meth == "starts_with") or (meth == "ends_with") { return true }
if (meth == "contains") or (meth == "index_of") { return true }
if (meth == "upper") or (meth == "lower") or (meth == "trim") or (meth == "repeat") { return true }
@ -47,6 +48,11 @@ function emit_text_ns(meth: pointer, e: Node) -> Val {
let c = emit_bind(`load i8, ptr {a}`)
return val(emit_bind(`zext i8 {c} to i32`), "int")
}
if (meth == "to_float") { # parse a leading decimal number, 0.0 if none
fp_declare("declare double @strtod(ptr, ptr)\n")
let d = emit_bind(`call double @strtod(ptr {s.code}, ptr null)`)
return val(emit_bind(`fptrunc double {d} to float`), "float")
}
if (meth == "to_int") { # parse a leading integer, 0 if none
return val(emit_bind(`call i32 @atoi(ptr {s.code})`), "int")
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1352,6 +1352,7 @@ program LudicLsp {
if (meth == "equals") { return "Text.equals(a, b) -> bool" }
if (meth == "concat") { return "Text.concat(a, b) -> string" }
if (meth == "to_int") { return "Text.to_int(s) -> int" }
if (meth == "to_float") { return "Text.to_float(s) -> float" }
if (meth == "from_int") { return "Text.from_int(n) -> string" }
if (meth == "upper") { return "Text.upper(s) -> string" }
if (meth == "lower") { return "Text.lower(s) -> string" }