feat: add Int.toFloat/Float.toInt JS backend support and fix Map C codegen

- 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 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 02:05:40 -05:00
parent a5762d0397
commit 3cdde02eb2
2 changed files with 29 additions and 13 deletions

View File

@@ -2072,7 +2072,7 @@ impl CBackend {
self.writeln(" LuxMap* result = lux_map_new(map->capacity);"); self.writeln(" LuxMap* result = lux_map_new(map->capacity);");
self.writeln(" result->length = map->length;"); self.writeln(" result->length = map->length;");
self.writeln(" for (int64_t i = 0; i < map->length; i++) {"); 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(" result->values[i] = map->values[i];");
self.writeln(" lux_incref(map->values[i]);"); self.writeln(" lux_incref(map->values[i]);");
self.writeln(" }"); self.writeln(" }");
@@ -2092,7 +2092,7 @@ impl CBackend {
self.writeln(" result->keys = (LuxString*)realloc(result->keys, sizeof(LuxString) * result->capacity);"); 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(" result->values = (void**)realloc(result->values, sizeof(void*) * result->capacity);");
self.writeln(" }"); 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(" result->values[result->length] = value;");
self.writeln(" lux_incref(value);"); self.writeln(" lux_incref(value);");
self.writeln(" result->length++;"); self.writeln(" result->length++;");
@@ -2111,7 +2111,7 @@ impl CBackend {
self.writeln(" LuxMap* result = lux_map_new(map->capacity);"); self.writeln(" LuxMap* result = lux_map_new(map->capacity);");
self.writeln(" for (int64_t i = 0; i < map->length; i++) {"); self.writeln(" for (int64_t i = 0; i < map->length; i++) {");
self.writeln(" if (strcmp(map->keys[i], key) != 0) {"); 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(" result->values[result->length] = map->values[i];");
self.writeln(" lux_incref(map->values[i]);"); self.writeln(" lux_incref(map->values[i]);");
self.writeln(" result->length++;"); self.writeln(" result->length++;");
@@ -4677,7 +4677,7 @@ impl CBackend {
// Sort keys: simple insertion sort // Sort keys: simple insertion sort
self.writeln(&format!("for (int64_t _i = 0; _i < {}->length; _i++) {{", map)); self.writeln(&format!("for (int64_t _i = 0; _i < {}->length; _i++) {{", map));
self.indent += 1; 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.writeln(&format!("lux_list_push({}, _ks);", temp));
self.indent -= 1; self.indent -= 1;
self.writeln("}"); self.writeln("}");

View File

@@ -1082,16 +1082,24 @@ impl JsBackend {
} }
} }
// Int module // Int/Float module operations
if let Expr::Field { object, field, .. } = func.as_ref() { if let Expr::Field { object, field, .. } = func.as_ref() {
if let Expr::Var(module_name) = object.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])?; 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])?; 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 // Special case: Int module operations
if effect.name == "Int" && operation.name == "toFloat" { if effect.name == "Int" {
let arg = self.emit_expr(&args[0])?; 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 // Special case: Float module operations
if effect.name == "Float" && operation.name == "toInt" { if effect.name == "Float" {
let arg = self.emit_expr(&args[0])?; 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) // Special case: Result module operations (not an effect)