fix: C backend String functions, record type aliases, docs cleanup

- Add String.fromChar, chars, substring, toUpper, toLower, replace,
  startsWith, endsWith, join to C backend
- Fix record type alias unification by adding expand_type_alias and
  unify_with_env functions
- Update docs to reflect current implementation status
- Clean up outdated roadmap items and fix inconsistencies
- Add comprehensive language comparison document

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-16 01:06:20 -05:00
parent ba3b713f8c
commit 33b4f57faf
11 changed files with 1694 additions and 571 deletions

View File

@@ -1247,6 +1247,125 @@ impl CBackend {
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_string_from_char(char c) {");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(2, LUX_TAG_STRING);");
self.writeln(" if (!result) return \"\";");
self.writeln(" result[0] = c;");
self.writeln(" result[1] = '\\0';");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxList* lux_string_chars(LuxString s) {");
self.writeln(" size_t len = s ? strlen(s) : 0;");
self.writeln(" LuxList* list = lux_list_new(len);");
self.writeln(" for (size_t i = 0; i < len; i++) {");
self.writeln(" LuxString ch = lux_string_from_char(s[i]);");
self.writeln(" lux_list_push(list, (void*)ch);");
self.writeln(" }");
self.writeln(" return list;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_string_substring(LuxString s, LuxInt start, LuxInt len) {");
self.writeln(" if (!s) return \"\";");
self.writeln(" size_t slen = strlen(s);");
self.writeln(" if (start < 0) start = 0;");
self.writeln(" if ((size_t)start >= slen) return \"\";");
self.writeln(" if (len < 0) len = 0;");
self.writeln(" if ((size_t)(start + len) > slen) len = slen - start;");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" if (!result) return \"\";");
self.writeln(" strncpy(result, s + start, len);");
self.writeln(" result[len] = '\\0';");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_string_to_upper(LuxString s) {");
self.writeln(" if (!s) return \"\";");
self.writeln(" size_t len = strlen(s);");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" if (!result) return \"\";");
self.writeln(" for (size_t i = 0; i < len; i++) {");
self.writeln(" result[i] = (s[i] >= 'a' && s[i] <= 'z') ? s[i] - 32 : s[i];");
self.writeln(" }");
self.writeln(" result[len] = '\\0';");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_string_to_lower(LuxString s) {");
self.writeln(" if (!s) return \"\";");
self.writeln(" size_t len = strlen(s);");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(len + 1, LUX_TAG_STRING);");
self.writeln(" if (!result) return \"\";");
self.writeln(" for (size_t i = 0; i < len; i++) {");
self.writeln(" result[i] = (s[i] >= 'A' && s[i] <= 'Z') ? s[i] + 32 : s[i];");
self.writeln(" }");
self.writeln(" result[len] = '\\0';");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_string_replace(LuxString s, LuxString from, LuxString to) {");
self.writeln(" if (!s || !from || !*from) return s ? lux_string_dup(s) : \"\";");
self.writeln(" if (!to) to = \"\";");
self.writeln(" size_t from_len = strlen(from);");
self.writeln(" size_t to_len = strlen(to);");
self.writeln(" size_t count = 0;");
self.writeln(" const char* p = s;");
self.writeln(" while ((p = strstr(p, from)) != NULL) { count++; p += from_len; }");
self.writeln(" size_t new_len = strlen(s) + count * (to_len - from_len);");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(new_len + 1, LUX_TAG_STRING);");
self.writeln(" if (!result) return \"\";");
self.writeln(" char* out = result;");
self.writeln(" p = s;");
self.writeln(" const char* found;");
self.writeln(" while ((found = strstr(p, from)) != NULL) {");
self.writeln(" size_t prefix_len = found - p;");
self.writeln(" memcpy(out, p, prefix_len);");
self.writeln(" out += prefix_len;");
self.writeln(" memcpy(out, to, to_len);");
self.writeln(" out += to_len;");
self.writeln(" p = found + from_len;");
self.writeln(" }");
self.writeln(" strcpy(out, p);");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxBool lux_string_starts_with(LuxString s, LuxString prefix) {");
self.writeln(" if (!s || !prefix) return 0;");
self.writeln(" size_t prefix_len = strlen(prefix);");
self.writeln(" if (strlen(s) < prefix_len) return 0;");
self.writeln(" return strncmp(s, prefix, prefix_len) == 0;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxBool lux_string_ends_with(LuxString s, LuxString suffix) {");
self.writeln(" if (!s || !suffix) return 0;");
self.writeln(" size_t s_len = strlen(s);");
self.writeln(" size_t suffix_len = strlen(suffix);");
self.writeln(" if (s_len < suffix_len) return 0;");
self.writeln(" return strcmp(s + s_len - suffix_len, suffix) == 0;");
self.writeln("}");
self.writeln("");
self.writeln("static LuxString lux_string_join(LuxList* list, LuxString sep) {");
self.writeln(" if (!list || list->length == 0) return \"\";");
self.writeln(" if (!sep) sep = \"\";");
self.writeln(" size_t sep_len = strlen(sep);");
self.writeln(" size_t total_len = 0;");
self.writeln(" for (int64_t i = 0; i < list->length; i++) {");
self.writeln(" LuxString elem = (LuxString)list->elements[i];");
self.writeln(" if (elem) total_len += strlen(elem);");
self.writeln(" if (i > 0) total_len += sep_len;");
self.writeln(" }");
self.writeln(" LuxString result = (LuxString)lux_rc_alloc(total_len + 1, LUX_TAG_STRING);");
self.writeln(" if (!result) return \"\";");
self.writeln(" char* out = result;");
self.writeln(" for (int64_t i = 0; i < list->length; i++) {");
self.writeln(" if (i > 0) { strcpy(out, sep); out += sep_len; }");
self.writeln(" LuxString elem = (LuxString)list->elements[i];");
self.writeln(" if (elem) { strcpy(out, elem); out += strlen(elem); }");
self.writeln(" }");
self.writeln(" *out = '\\0';");
self.writeln(" return result;");
self.writeln("}");
self.writeln("");
self.writeln("// Default evidence with built-in handlers");
self.writeln("static LuxEvidence default_evidence = {");
self.writeln(" .console = &default_console_handler,");
@@ -2863,6 +2982,72 @@ impl CBackend {
let s = self.emit_expr(&args[0])?;
return Ok(format!("lux_string_parseFloat({})", s));
}
"fromChar" => {
let c = self.emit_expr(&args[0])?;
// Create temp variable and track for cleanup (returns RC-managed string)
let temp = format!("_fromchar_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_from_char({});", temp, c));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"chars" => {
let s = self.emit_expr(&args[0])?;
// Create temp variable and track for cleanup (returns RC-managed list)
let temp = format!("_chars_{}", self.fresh_name());
self.writeln(&format!("LuxList* {} = lux_string_chars({});", temp, s));
self.register_rc_var(&temp, "LuxList*");
return Ok(temp);
}
"substring" => {
let s = self.emit_expr(&args[0])?;
let start = self.emit_expr(&args[1])?;
let len = self.emit_expr(&args[2])?;
let temp = format!("_substr_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_substring({}, {}, {});", temp, s, start, len));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"toUpper" => {
let s = self.emit_expr(&args[0])?;
let temp = format!("_upper_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_to_upper({});", temp, s));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"toLower" => {
let s = self.emit_expr(&args[0])?;
let temp = format!("_lower_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_to_lower({});", temp, s));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"replace" => {
let s = self.emit_expr(&args[0])?;
let from = self.emit_expr(&args[1])?;
let to = self.emit_expr(&args[2])?;
let temp = format!("_replace_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_replace({}, {}, {});", temp, s, from, to));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
"startsWith" => {
let s = self.emit_expr(&args[0])?;
let prefix = self.emit_expr(&args[1])?;
return Ok(format!("lux_string_starts_with({}, {})", s, prefix));
}
"endsWith" => {
let s = self.emit_expr(&args[0])?;
let suffix = self.emit_expr(&args[1])?;
return Ok(format!("lux_string_ends_with({}, {})", s, suffix));
}
"join" => {
let list = self.emit_expr(&args[0])?;
let sep = self.emit_expr(&args[1])?;
let temp = format!("_join_{}", self.fresh_name());
self.writeln(&format!("LuxString {} = lux_string_join({}, {});", temp, list, sep));
self.register_rc_var(&temp, "LuxString");
return Ok(temp);
}
_ => {}
}
}
@@ -3876,6 +4061,8 @@ impl CBackend {
self.writeln("");
// Execute top-level let bindings with run expressions
// Track if main was already called via a run expression
let mut main_called_via_run = false;
for decl in &program.declarations {
if let Declaration::Let(let_decl) = decl {
if matches!(&let_decl.value, Expr::Run { .. }) {
@@ -3883,6 +4070,10 @@ impl CBackend {
if let Expr::Call { func, .. } = expr.as_ref() {
if let Expr::Var(fn_name) = func.as_ref() {
let mangled = self.mangle_name(&fn_name.name);
// Track if this is a call to main
if fn_name.name == "main" {
main_called_via_run = true;
}
// Pass default evidence if function uses effects
if self.effectful_functions.contains(&fn_name.name) {
self.writeln(&format!("{}(&default_evidence);", mangled));
@@ -3896,8 +4087,8 @@ impl CBackend {
}
}
// If there's a main function, call it
if has_main {
// If there's a main function and it wasn't already called via run, call it
if has_main && !main_called_via_run {
// Check if main uses effects (Console typically)
if self.effectful_functions.contains("main") {
self.writeln("main_lux(&default_evidence);");