feat: add tuple index access, multiline args, and effect unification fix

- Tuple index: `pair.0`, `pair.1` syntax across parser, typechecker,
  interpreter, C/JS backends, formatter, linter, and symbol table
- Multi-line function args: allow newlines inside argument lists
- Fix effect unification for callback parameters (empty expected
  effects means "no constraint", not "must be pure")

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 16:20:32 -05:00
parent bac63bab2a
commit 542255780d
12 changed files with 176 additions and 8 deletions

View File

@@ -335,7 +335,7 @@ fn references_params(expr: &Expr, params: &[&str]) -> bool {
Statement::Expr(e) => references_params(e, params),
}) || references_params(result, params)
}
Expr::Field { object, .. } => references_params(object, params),
Expr::Field { object, .. } | Expr::TupleIndex { object, .. } => references_params(object, params),
Expr::Lambda { body, .. } => references_params(body, params),
Expr::Tuple { elements, .. } => elements.iter().any(|e| references_params(e, params)),
Expr::List { elements, .. } => elements.iter().any(|e| references_params(e, params)),
@@ -519,7 +519,7 @@ fn has_recursive_calls(func_name: &str, body: &Expr) -> bool {
Expr::Record { fields, .. } => {
fields.iter().any(|(_, e)| has_recursive_calls(func_name, e))
}
Expr::Field { object, .. } => has_recursive_calls(func_name, object),
Expr::Field { object, .. } | Expr::TupleIndex { object, .. } => has_recursive_calls(func_name, object),
Expr::Let { value, body, .. } => {
has_recursive_calls(func_name, value) || has_recursive_calls(func_name, body)
}
@@ -1673,6 +1673,42 @@ impl TypeChecker {
span,
} => self.infer_field(object, field, *span),
Expr::TupleIndex {
object,
index,
span,
} => {
let object_type = self.infer_expr(object);
match &object_type {
Type::Tuple(types) => {
if *index < types.len() {
types[*index].clone()
} else {
self.errors.push(TypeError {
message: format!(
"Tuple index {} out of bounds for tuple with {} elements",
index,
types.len()
),
span: *span,
});
Type::Error
}
}
Type::Var(_) => Type::var(),
_ => {
self.errors.push(TypeError {
message: format!(
"Cannot use tuple index on non-tuple type {}",
object_type
),
span: *span,
});
Type::Error
}
}
}
Expr::Lambda {
params,
return_type,