feat: String_ToFloat + Json escape sequence support

- Add bux_str_to_float() C runtime wrapper (strtod)
- Add String_ToFloat() in library/std/String.bux
- Update JsonParser_ParseNumber to use String_ToFloat (decimals now work)
- Update JsonParser_ParseString to decode escape sequences (\n, \t, \r, \", \\, \b, \f)
- Fix use-after-free in JsonParser_ParseString: String_Copy before StringBuilder_Free
This commit is contained in:
2026-06-05 22:35:50 +03:00
parent d0de9c2abf
commit 154579b30d
4 changed files with 36 additions and 6 deletions
+1
View File
@@ -141,6 +141,7 @@ String manipulation utilities.
| `String_FromInt` | `func String_FromInt(n: int64) -> String` | Int to string | | `String_FromInt` | `func String_FromInt(n: int64) -> String` | Int to string |
| `String_FromFloat` | `func String_FromFloat(f: float64) -> String` | Float to string | | `String_FromFloat` | `func String_FromFloat(f: float64) -> String` | Float to string |
| `String_ToInt` | `func String_ToInt(s: String) -> int64` | String to int | | `String_ToInt` | `func String_ToInt(s: String) -> int64` | String to int |
| `String_ToFloat` | `func String_ToFloat(s: String) -> float64` | String to float |
### Find, Replace & Format ### Find, Replace & Format
+5
View File
@@ -221,6 +221,11 @@ char* bux_strncpy(char* dest, const char* src, unsigned int n) {
return strncpy(dest, src, (size_t)n); return strncpy(dest, src, (size_t)n);
} }
double bux_str_to_float(const char* s) {
if (!s) return 0.0;
return strtod(s, NULL);
}
/* String find: returns pointer to first occurrence of needle in haystack, or NULL */ /* String find: returns pointer to first occurrence of needle in haystack, or NULL */
const char* bux_strstr(const char* haystack, const char* needle) { const char* bux_strstr(const char* haystack, const char* needle) {
if (!haystack || !needle) return NULL; if (!haystack || !needle) return NULL;
+24 -6
View File
@@ -236,7 +236,7 @@ func JsonParser_ParseString(p: *JsonParser) -> String {
return ""; return "";
} }
JsonParser_Advance(p); JsonParser_Advance(p);
let start: uint = p.pos; let sb: StringBuilder = StringBuilder_New();
while true { while true {
let c: int = JsonParser_Peek(p); let c: int = JsonParser_Peek(p);
if c == 0 || c == 34 { if c == 0 || c == 34 {
@@ -244,22 +244,40 @@ func JsonParser_ParseString(p: *JsonParser) -> String {
} }
if c == 92 { if c == 92 {
JsonParser_Advance(p); JsonParser_Advance(p);
if JsonParser_Peek(p) == 0 { let esc: int = JsonParser_Peek(p);
if esc == 0 {
p.error = "Unterminated string escape"; p.error = "Unterminated string escape";
StringBuilder_Free(&sb);
return ""; return "";
} }
if esc == 110 { StringBuilder_AppendChar(&sb, 10 as char8); } // \n
else if esc == 116 { StringBuilder_AppendChar(&sb, 9 as char8); } // \t
else if esc == 114 { StringBuilder_AppendChar(&sb, 13 as char8); } // \r
else if esc == 98 { StringBuilder_AppendChar(&sb, 8 as char8); } // \b
else if esc == 102 { StringBuilder_AppendChar(&sb, 12 as char8); } // \f
else if esc == 34 { StringBuilder_AppendChar(&sb, 34 as char8); } // \"
else if esc == 92 { StringBuilder_AppendChar(&sb, 92 as char8); } // \\
else {
// Unknown escape — keep literal
StringBuilder_AppendChar(&sb, 92 as char8);
StringBuilder_AppendChar(&sb, esc as char8);
}
JsonParser_Advance(p); JsonParser_Advance(p);
} else { } else {
StringBuilder_AppendChar(&sb, c as char8);
JsonParser_Advance(p); JsonParser_Advance(p);
} }
} }
let endPos: uint = p.pos;
if JsonParser_Peek(p) != 34 { if JsonParser_Peek(p) != 34 {
p.error = "Unterminated string"; p.error = "Unterminated string";
StringBuilder_Free(&sb);
return ""; return "";
} }
JsonParser_Advance(p); JsonParser_Advance(p);
return String_Slice(p.src, start, endPos - start); let raw: String = StringBuilder_Build(&sb);
let result: String = String_Copy(raw);
StringBuilder_Free(&sb);
return result;
} }
func JsonParser_ParseNumber(p: *JsonParser) -> JsonValue { func JsonParser_ParseNumber(p: *JsonParser) -> JsonValue {
@@ -288,8 +306,8 @@ func JsonParser_ParseNumber(p: *JsonParser) -> JsonValue {
} }
} }
let numStr: String = String_Slice(p.src, start, p.pos - start); let numStr: String = String_Slice(p.src, start, p.pos - start);
let n: int64 = String_ToInt(numStr); let n: float64 = String_ToFloat(numStr);
return Json_Number(n as float64); return Json_Number(n);
} }
func JsonParser_ParseArray(p: *JsonParser) -> JsonValue { func JsonParser_ParseArray(p: *JsonParser) -> JsonValue {
+6
View File
@@ -179,6 +179,12 @@ func String_Replace(s: String, old: String, new: String) -> String {
return result; return result;
} }
extern func bux_str_to_float(s: String) -> float64;
func String_ToFloat(s: String) -> float64 {
return bux_str_to_float(s);
}
func String_FromFloat(f: float64) -> String { func String_FromFloat(f: float64) -> String {
return bux_float_to_string(f); return bux_float_to_string(f);
} }