feat: add toString, string concat, keyword escaping, and conditional RC
- Add escape_c_keyword() to mangle C reserved words (double, int, etc.) - Implement toString() mapping to lux_int_to_string() - Add string concatenation detection for BinaryOp::Add using lux_string_concat() - Add is_string_expr() helper for string expression detection - Update infer_expr_type() for toString, string concat, and if expressions - Implement complex conditionals RC handling: use if-statements instead of ternaries when branches create RC values to avoid allocating unused branches Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -292,12 +292,13 @@ impl CBackend {
|
||||
fn emit_expr_with_env(&mut self, expr: &Expr, captured: &HashSet<&str>) -> Result<String, CGenError> {
|
||||
match expr {
|
||||
Expr::Var(ident) => {
|
||||
let escaped = self.escape_c_keyword(&ident.name);
|
||||
if captured.contains(ident.name.as_str()) {
|
||||
Ok(format!("env->{}", ident.name))
|
||||
Ok(format!("env->{}", escaped))
|
||||
} else if self.functions.contains(&ident.name) {
|
||||
Ok(self.mangle_name(&ident.name))
|
||||
} else {
|
||||
Ok(ident.name.clone())
|
||||
Ok(escaped)
|
||||
}
|
||||
}
|
||||
Expr::Literal(lit) => self.emit_literal(lit),
|
||||
@@ -1381,6 +1382,59 @@ impl CBackend {
|
||||
format!("{}_lux", name)
|
||||
}
|
||||
|
||||
/// Escape C reserved keywords by adding underscore prefix
|
||||
fn escape_c_keyword(&self, name: &str) -> String {
|
||||
// C reserved keywords that might conflict with Lux variable names
|
||||
const C_KEYWORDS: &[&str] = &[
|
||||
// C89/C90 keywords
|
||||
"auto", "break", "case", "char", "const", "continue", "default",
|
||||
"do", "double", "else", "enum", "extern", "float", "for", "goto",
|
||||
"if", "int", "long", "register", "return", "short", "signed",
|
||||
"sizeof", "static", "struct", "switch", "typedef", "union",
|
||||
"unsigned", "void", "volatile", "while",
|
||||
// C99 keywords
|
||||
"inline", "restrict", "_Bool", "_Complex", "_Imaginary",
|
||||
// C11 keywords
|
||||
"_Alignas", "_Alignof", "_Atomic", "_Generic", "_Noreturn",
|
||||
"_Static_assert", "_Thread_local",
|
||||
// Common type aliases and macros that might conflict
|
||||
"bool", "true", "false", "NULL",
|
||||
// Standard library identifiers that might conflict
|
||||
"stdin", "stdout", "stderr", "errno", "main",
|
||||
];
|
||||
|
||||
if C_KEYWORDS.contains(&name) {
|
||||
format!("_{}", name)
|
||||
} else {
|
||||
name.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if an expression is a string expression (for concatenation detection)
|
||||
fn is_string_expr(&self, expr: &Expr) -> bool {
|
||||
match expr {
|
||||
// String literals
|
||||
Expr::Literal(lit) => matches!(lit.kind, LiteralKind::String(_)),
|
||||
|
||||
// toString function call
|
||||
Expr::Call { func, .. } => {
|
||||
if let Expr::Var(ident) = func.as_ref() {
|
||||
ident.name == "toString"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// String concatenation with +
|
||||
Expr::BinaryOp { op, left, right, .. } => {
|
||||
matches!(op, BinaryOp::Add)
|
||||
&& (self.is_string_expr(left) || self.is_string_expr(right))
|
||||
}
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_forward_declarations(&mut self, program: &Program) -> Result<(), CGenError> {
|
||||
for decl in &program.declarations {
|
||||
if let Declaration::Function(f) = decl {
|
||||
@@ -1505,7 +1559,8 @@ impl CBackend {
|
||||
|
||||
let param_strs: Result<Vec<_>, _> = params.iter().map(|p| {
|
||||
let c_type = self.type_expr_to_c(&p.typ)?;
|
||||
Ok(format!("{} {}", c_type, p.name.name))
|
||||
let escaped_name = self.escape_c_keyword(&p.name.name);
|
||||
Ok(format!("{} {}", c_type, escaped_name))
|
||||
}).collect();
|
||||
|
||||
Ok(param_strs?.join(", "))
|
||||
@@ -1522,7 +1577,8 @@ impl CBackend {
|
||||
let variant_name = &ident.name;
|
||||
Ok(format!("({}){{{}_TAG_{}}}", type_name, type_name, variant_name.to_uppercase()))
|
||||
} else {
|
||||
Ok(ident.name.clone())
|
||||
// Escape C reserved keywords
|
||||
Ok(self.escape_c_keyword(&ident.name))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1530,6 +1586,15 @@ impl CBackend {
|
||||
let l = self.emit_expr(left)?;
|
||||
let r = self.emit_expr(right)?;
|
||||
|
||||
// Check for string concatenation
|
||||
if matches!(op, BinaryOp::Add) {
|
||||
let left_is_string = self.is_string_expr(left);
|
||||
let right_is_string = self.is_string_expr(right);
|
||||
if left_is_string || right_is_string {
|
||||
return Ok(format!("lux_string_concat({}, {})", l, r));
|
||||
}
|
||||
}
|
||||
|
||||
let op_str = match op {
|
||||
BinaryOp::Add => "+",
|
||||
BinaryOp::Sub => "-",
|
||||
@@ -1563,10 +1628,47 @@ impl CBackend {
|
||||
}
|
||||
|
||||
Expr::If { condition, then_branch, else_branch, .. } => {
|
||||
let cond = self.emit_expr(condition)?;
|
||||
let then_val = self.emit_expr(then_branch)?;
|
||||
let else_val = self.emit_expr(else_branch)?;
|
||||
Ok(format!("({} ? {} : {})", cond, then_val, else_val))
|
||||
// Check if branches create RC values - if so, use if-statement to avoid
|
||||
// allocating both when only one is needed
|
||||
let then_creates_rc = self.expr_creates_rc_value(then_branch);
|
||||
let else_creates_rc = self.expr_creates_rc_value(else_branch);
|
||||
|
||||
if then_creates_rc || else_creates_rc {
|
||||
// Use if-statement pattern to avoid allocating unused branch
|
||||
let result_type = self.infer_expr_type(then_branch)
|
||||
.or_else(|| self.infer_expr_type(else_branch))
|
||||
.unwrap_or_else(|| "LuxInt".to_string());
|
||||
let result_var = format!("_if_result_{}", self.fresh_name());
|
||||
|
||||
// Declare result variable
|
||||
self.writeln(&format!("{} {};", result_type, result_var));
|
||||
|
||||
// Emit condition
|
||||
let cond = self.emit_expr(condition)?;
|
||||
self.writeln(&format!("if ({}) {{", cond));
|
||||
self.indent += 1;
|
||||
|
||||
// Emit then branch
|
||||
let then_val = self.emit_expr(then_branch)?;
|
||||
self.writeln(&format!("{} = {};", result_var, then_val));
|
||||
self.indent -= 1;
|
||||
self.writeln("} else {");
|
||||
self.indent += 1;
|
||||
|
||||
// Emit else branch
|
||||
let else_val = self.emit_expr(else_branch)?;
|
||||
self.writeln(&format!("{} = {};", result_var, else_val));
|
||||
self.indent -= 1;
|
||||
self.writeln("}");
|
||||
|
||||
Ok(result_var)
|
||||
} else {
|
||||
// Simple ternary for non-RC values
|
||||
let cond = self.emit_expr(condition)?;
|
||||
let then_val = self.emit_expr(then_branch)?;
|
||||
let else_val = self.emit_expr(else_branch)?;
|
||||
Ok(format!("({} ? {} : {})", cond, then_val, else_val))
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Let { name, value, body, .. } => {
|
||||
@@ -1594,6 +1696,15 @@ impl CBackend {
|
||||
}
|
||||
}
|
||||
|
||||
// Check for built-in functions like toString
|
||||
if let Expr::Var(ident) = func.as_ref() {
|
||||
if ident.name == "toString" {
|
||||
// toString converts Int to String
|
||||
let arg = self.emit_expr(&args[0])?;
|
||||
return Ok(format!("lux_int_to_string({})", arg));
|
||||
}
|
||||
}
|
||||
|
||||
let arg_strs: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
|
||||
let args_str = arg_strs?.join(", ");
|
||||
|
||||
@@ -1699,12 +1810,12 @@ impl CBackend {
|
||||
// Generate unique closure ID
|
||||
let id = self.fresh_name();
|
||||
|
||||
// Determine parameter types
|
||||
// Determine parameter types (escape C keywords)
|
||||
let param_pairs: Vec<(String, String)> = params.iter()
|
||||
.map(|p| {
|
||||
let typ = self.type_expr_to_c(&p.typ)
|
||||
.unwrap_or_else(|_| "LuxInt".to_string());
|
||||
(p.name.name.clone(), typ)
|
||||
(self.escape_c_keyword(&p.name.name), typ)
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -1713,9 +1824,9 @@ impl CBackend {
|
||||
.map(|t| self.type_expr_to_c(t).unwrap_or_else(|_| "LuxInt".to_string()))
|
||||
.unwrap_or_else(|| "LuxInt".to_string());
|
||||
|
||||
// Determine captured variable types (default to LuxInt for now)
|
||||
// Determine captured variable types (default to LuxInt for now, escape C keywords)
|
||||
let env_fields: Vec<(String, String)> = free_vars.iter()
|
||||
.map(|v| (v.clone(), "LuxInt".to_string()))
|
||||
.map(|v| (self.escape_c_keyword(v), "LuxInt".to_string()))
|
||||
.collect();
|
||||
|
||||
// Store closure info for later emission
|
||||
@@ -1770,14 +1881,15 @@ impl CBackend {
|
||||
} else {
|
||||
"LuxInt".to_string()
|
||||
};
|
||||
self.writeln(&format!("{} {} = {};", typ, name.name, val));
|
||||
let escaped_name = self.escape_c_keyword(&name.name);
|
||||
self.writeln(&format!("{} {} = {};", typ, escaped_name, val));
|
||||
|
||||
// Register RC variable if it creates a new RC value
|
||||
if self.expr_creates_rc_value(value) {
|
||||
self.register_rc_var(&name.name, &typ);
|
||||
self.register_rc_var(&escaped_name, &typ);
|
||||
} else if let Some(adt_name) = self.expr_creates_adt_with_pointers(value) {
|
||||
// ADT with pointer fields - needs field cleanup at scope exit
|
||||
self.register_rc_var_with_adt(&name.name, &typ, Some(adt_name));
|
||||
self.register_rc_var_with_adt(&escaped_name, &typ, Some(adt_name));
|
||||
}
|
||||
}
|
||||
Statement::Expr(e) => {
|
||||
@@ -2497,6 +2609,10 @@ impl CBackend {
|
||||
Expr::Call { func, .. } => {
|
||||
// Check if calling a constructor
|
||||
if let Expr::Var(ident) = func.as_ref() {
|
||||
// toString returns LuxString
|
||||
if ident.name == "toString" {
|
||||
return Some("LuxString".to_string());
|
||||
}
|
||||
if let Some(type_name) = self.variant_to_type.get(&ident.name) {
|
||||
return Some(type_name.clone());
|
||||
}
|
||||
@@ -2507,10 +2623,25 @@ impl CBackend {
|
||||
}
|
||||
None
|
||||
}
|
||||
Expr::BinaryOp { op, left, right, .. } => {
|
||||
// String concatenation with + returns LuxString
|
||||
if matches!(op, BinaryOp::Add) {
|
||||
if self.is_string_expr(left) || self.is_string_expr(right) {
|
||||
return Some("LuxString".to_string());
|
||||
}
|
||||
}
|
||||
// For other binary ops, infer from operands
|
||||
self.infer_expr_type(left).or_else(|| self.infer_expr_type(right))
|
||||
}
|
||||
Expr::Block { result, .. } => {
|
||||
// Type of block is the type of the result expression
|
||||
self.infer_expr_type(result)
|
||||
}
|
||||
Expr::If { then_branch, else_branch, .. } => {
|
||||
// Type of if is the type of either branch (they should match)
|
||||
self.infer_expr_type(then_branch)
|
||||
.or_else(|| self.infer_expr_type(else_branch))
|
||||
}
|
||||
Expr::List { .. } => Some("LuxList*".to_string()),
|
||||
Expr::EffectOp { effect, operation, .. } => {
|
||||
// List operations have known return types
|
||||
@@ -2625,7 +2756,8 @@ impl CBackend {
|
||||
Pattern::Wildcard(_) => vec![],
|
||||
Pattern::Var(ident) => {
|
||||
let typ = expected_type.unwrap_or("LuxInt").to_string();
|
||||
vec![(ident.name.clone(), scrutinee.to_string(), typ)]
|
||||
let escaped_name = self.escape_c_keyword(&ident.name);
|
||||
vec![(escaped_name, scrutinee.to_string(), typ)]
|
||||
}
|
||||
Pattern::Literal(_) => vec![],
|
||||
Pattern::Constructor { name, fields, .. } => {
|
||||
|
||||
Reference in New Issue
Block a user