From 3cdde02eb24e2d27279a4c1f3dca8b05f74b0a0c Mon Sep 17 00:00:00 2001 From: Brandon Lucas Date: Thu, 19 Feb 2026 02:05:40 -0500 Subject: [PATCH] feat: add Int.toFloat/Float.toInt JS backend support and fix Map C codegen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - JS backend: Add Int/Float module dispatch in both Call and EffectOp paths for toFloat, toInt, and toString operations - C backend: Fix lux_strdup → lux_string_dup in Map module codegen Co-Authored-By: Claude Opus 4.6 --- src/codegen/c_backend.rs | 8 ++++---- src/codegen/js_backend.rs | 34 +++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/codegen/c_backend.rs b/src/codegen/c_backend.rs index 72aaedf..a7a9619 100644 --- a/src/codegen/c_backend.rs +++ b/src/codegen/c_backend.rs @@ -2072,7 +2072,7 @@ impl CBackend { self.writeln(" LuxMap* result = lux_map_new(map->capacity);"); self.writeln(" result->length = map->length;"); self.writeln(" for (int64_t i = 0; i < map->length; i++) {"); - self.writeln(" result->keys[i] = lux_strdup(map->keys[i]);"); + self.writeln(" result->keys[i] = lux_string_dup(map->keys[i]);"); self.writeln(" result->values[i] = map->values[i];"); self.writeln(" lux_incref(map->values[i]);"); self.writeln(" }"); @@ -2092,7 +2092,7 @@ impl CBackend { self.writeln(" result->keys = (LuxString*)realloc(result->keys, sizeof(LuxString) * result->capacity);"); self.writeln(" result->values = (void**)realloc(result->values, sizeof(void*) * result->capacity);"); self.writeln(" }"); - self.writeln(" result->keys[result->length] = lux_strdup(key);"); + self.writeln(" result->keys[result->length] = lux_string_dup(key);"); self.writeln(" result->values[result->length] = value;"); self.writeln(" lux_incref(value);"); self.writeln(" result->length++;"); @@ -2111,7 +2111,7 @@ impl CBackend { self.writeln(" LuxMap* result = lux_map_new(map->capacity);"); self.writeln(" for (int64_t i = 0; i < map->length; i++) {"); self.writeln(" if (strcmp(map->keys[i], key) != 0) {"); - self.writeln(" result->keys[result->length] = lux_strdup(map->keys[i]);"); + self.writeln(" result->keys[result->length] = lux_string_dup(map->keys[i]);"); self.writeln(" result->values[result->length] = map->values[i];"); self.writeln(" lux_incref(map->values[i]);"); self.writeln(" result->length++;"); @@ -4677,7 +4677,7 @@ impl CBackend { // Sort keys: simple insertion sort self.writeln(&format!("for (int64_t _i = 0; _i < {}->length; _i++) {{", map)); self.indent += 1; - self.writeln(&format!("LuxString _ks = lux_strdup({}->keys[_i]);", map)); + self.writeln(&format!("LuxString _ks = lux_string_dup({}->keys[_i]);", map)); self.writeln(&format!("lux_list_push({}, _ks);", temp)); self.indent -= 1; self.writeln("}"); diff --git a/src/codegen/js_backend.rs b/src/codegen/js_backend.rs index 570dd34..b8a4be8 100644 --- a/src/codegen/js_backend.rs +++ b/src/codegen/js_backend.rs @@ -1082,16 +1082,24 @@ impl JsBackend { } } - // Int module + // Int/Float module operations if let Expr::Field { object, field, .. } = func.as_ref() { if let Expr::Var(module_name) = object.as_ref() { - if module_name.name == "Int" && field.name == "toFloat" { + if module_name.name == "Int" { let arg = self.emit_expr(&args[0])?; - return Ok(arg); // JS numbers are already floats + match field.name.as_str() { + "toFloat" => return Ok(arg), + "toString" => return Ok(format!("String({})", arg)), + _ => {} + } } - if module_name.name == "Float" && field.name == "toInt" { + if module_name.name == "Float" { let arg = self.emit_expr(&args[0])?; - return Ok(format!("Math.trunc({})", arg)); + match field.name.as_str() { + "toInt" => return Ok(format!("Math.trunc({})", arg)), + "toString" => return Ok(format!("String({})", arg)), + _ => {} + } } } } @@ -1183,15 +1191,23 @@ impl JsBackend { } // Special case: Int module operations - if effect.name == "Int" && operation.name == "toFloat" { + if effect.name == "Int" { let arg = self.emit_expr(&args[0])?; - return Ok(arg); // JS numbers are already floats + match operation.name.as_str() { + "toFloat" => return Ok(arg), // JS numbers are already floats + "toString" => return Ok(format!("String({})", arg)), + _ => {} + } } // Special case: Float module operations - if effect.name == "Float" && operation.name == "toInt" { + if effect.name == "Float" { let arg = self.emit_expr(&args[0])?; - return Ok(format!("Math.trunc({})", arg)); + match operation.name.as_str() { + "toInt" => return Ok(format!("Math.trunc({})", arg)), + "toString" => return Ok(format!("String({})", arg)), + _ => {} + } } // Special case: Result module operations (not an effect)