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> {
|
fn emit_expr_with_env(&mut self, expr: &Expr, captured: &HashSet<&str>) -> Result<String, CGenError> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Var(ident) => {
|
Expr::Var(ident) => {
|
||||||
|
let escaped = self.escape_c_keyword(&ident.name);
|
||||||
if captured.contains(ident.name.as_str()) {
|
if captured.contains(ident.name.as_str()) {
|
||||||
Ok(format!("env->{}", ident.name))
|
Ok(format!("env->{}", escaped))
|
||||||
} else if self.functions.contains(&ident.name) {
|
} else if self.functions.contains(&ident.name) {
|
||||||
Ok(self.mangle_name(&ident.name))
|
Ok(self.mangle_name(&ident.name))
|
||||||
} else {
|
} else {
|
||||||
Ok(ident.name.clone())
|
Ok(escaped)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Literal(lit) => self.emit_literal(lit),
|
Expr::Literal(lit) => self.emit_literal(lit),
|
||||||
@@ -1381,6 +1382,59 @@ impl CBackend {
|
|||||||
format!("{}_lux", name)
|
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> {
|
fn emit_forward_declarations(&mut self, program: &Program) -> Result<(), CGenError> {
|
||||||
for decl in &program.declarations {
|
for decl in &program.declarations {
|
||||||
if let Declaration::Function(f) = decl {
|
if let Declaration::Function(f) = decl {
|
||||||
@@ -1505,7 +1559,8 @@ impl CBackend {
|
|||||||
|
|
||||||
let param_strs: Result<Vec<_>, _> = params.iter().map(|p| {
|
let param_strs: Result<Vec<_>, _> = params.iter().map(|p| {
|
||||||
let c_type = self.type_expr_to_c(&p.typ)?;
|
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();
|
}).collect();
|
||||||
|
|
||||||
Ok(param_strs?.join(", "))
|
Ok(param_strs?.join(", "))
|
||||||
@@ -1522,7 +1577,8 @@ impl CBackend {
|
|||||||
let variant_name = &ident.name;
|
let variant_name = &ident.name;
|
||||||
Ok(format!("({}){{{}_TAG_{}}}", type_name, type_name, variant_name.to_uppercase()))
|
Ok(format!("({}){{{}_TAG_{}}}", type_name, type_name, variant_name.to_uppercase()))
|
||||||
} else {
|
} 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 l = self.emit_expr(left)?;
|
||||||
let r = self.emit_expr(right)?;
|
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 {
|
let op_str = match op {
|
||||||
BinaryOp::Add => "+",
|
BinaryOp::Add => "+",
|
||||||
BinaryOp::Sub => "-",
|
BinaryOp::Sub => "-",
|
||||||
@@ -1563,10 +1628,47 @@ impl CBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Expr::If { condition, then_branch, else_branch, .. } => {
|
Expr::If { condition, then_branch, else_branch, .. } => {
|
||||||
let cond = self.emit_expr(condition)?;
|
// Check if branches create RC values - if so, use if-statement to avoid
|
||||||
let then_val = self.emit_expr(then_branch)?;
|
// allocating both when only one is needed
|
||||||
let else_val = self.emit_expr(else_branch)?;
|
let then_creates_rc = self.expr_creates_rc_value(then_branch);
|
||||||
Ok(format!("({} ? {} : {})", cond, then_val, else_val))
|
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, .. } => {
|
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 arg_strs: Result<Vec<_>, _> = args.iter().map(|a| self.emit_expr(a)).collect();
|
||||||
let args_str = arg_strs?.join(", ");
|
let args_str = arg_strs?.join(", ");
|
||||||
|
|
||||||
@@ -1699,12 +1810,12 @@ impl CBackend {
|
|||||||
// Generate unique closure ID
|
// Generate unique closure ID
|
||||||
let id = self.fresh_name();
|
let id = self.fresh_name();
|
||||||
|
|
||||||
// Determine parameter types
|
// Determine parameter types (escape C keywords)
|
||||||
let param_pairs: Vec<(String, String)> = params.iter()
|
let param_pairs: Vec<(String, String)> = params.iter()
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
let typ = self.type_expr_to_c(&p.typ)
|
let typ = self.type_expr_to_c(&p.typ)
|
||||||
.unwrap_or_else(|_| "LuxInt".to_string());
|
.unwrap_or_else(|_| "LuxInt".to_string());
|
||||||
(p.name.name.clone(), typ)
|
(self.escape_c_keyword(&p.name.name), typ)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -1713,9 +1824,9 @@ impl CBackend {
|
|||||||
.map(|t| self.type_expr_to_c(t).unwrap_or_else(|_| "LuxInt".to_string()))
|
.map(|t| self.type_expr_to_c(t).unwrap_or_else(|_| "LuxInt".to_string()))
|
||||||
.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()
|
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();
|
.collect();
|
||||||
|
|
||||||
// Store closure info for later emission
|
// Store closure info for later emission
|
||||||
@@ -1770,14 +1881,15 @@ impl CBackend {
|
|||||||
} else {
|
} else {
|
||||||
"LuxInt".to_string()
|
"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
|
// Register RC variable if it creates a new RC value
|
||||||
if self.expr_creates_rc_value(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) {
|
} else if let Some(adt_name) = self.expr_creates_adt_with_pointers(value) {
|
||||||
// ADT with pointer fields - needs field cleanup at scope exit
|
// 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) => {
|
Statement::Expr(e) => {
|
||||||
@@ -2497,6 +2609,10 @@ impl CBackend {
|
|||||||
Expr::Call { func, .. } => {
|
Expr::Call { func, .. } => {
|
||||||
// Check if calling a constructor
|
// Check if calling a constructor
|
||||||
if let Expr::Var(ident) = func.as_ref() {
|
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) {
|
if let Some(type_name) = self.variant_to_type.get(&ident.name) {
|
||||||
return Some(type_name.clone());
|
return Some(type_name.clone());
|
||||||
}
|
}
|
||||||
@@ -2507,10 +2623,25 @@ impl CBackend {
|
|||||||
}
|
}
|
||||||
None
|
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, .. } => {
|
Expr::Block { result, .. } => {
|
||||||
// Type of block is the type of the result expression
|
// Type of block is the type of the result expression
|
||||||
self.infer_expr_type(result)
|
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::List { .. } => Some("LuxList*".to_string()),
|
||||||
Expr::EffectOp { effect, operation, .. } => {
|
Expr::EffectOp { effect, operation, .. } => {
|
||||||
// List operations have known return types
|
// List operations have known return types
|
||||||
@@ -2625,7 +2756,8 @@ impl CBackend {
|
|||||||
Pattern::Wildcard(_) => vec![],
|
Pattern::Wildcard(_) => vec![],
|
||||||
Pattern::Var(ident) => {
|
Pattern::Var(ident) => {
|
||||||
let typ = expected_type.unwrap_or("LuxInt").to_string();
|
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::Literal(_) => vec![],
|
||||||
Pattern::Constructor { name, fields, .. } => {
|
Pattern::Constructor { name, fields, .. } => {
|
||||||
|
|||||||
Reference in New Issue
Block a user