diff --git a/docs/09-projects01.md b/docs/09-projects01.md
index 995eb12..f9f8f37 100644
--- a/docs/09-projects01.md
+++ b/docs/09-projects01.md
@@ -393,4 +393,4 @@ A good starting point for any of them is:
* Review the implementation of the type code generator
* Deadline: 30/06
-The outcomes of the projects must be submitted via pull-requests.
+The outcomes of the projects must be submitted via pull-requests.
\ No newline at end of file
diff --git a/docs/semantics-first-class-fn.md b/docs/semantics-first-class-fn.md
new file mode 100644
index 0000000..87de355
--- /dev/null
+++ b/docs/semantics-first-class-fn.md
@@ -0,0 +1,77 @@
+# Semântica: Funções de Primeira Classe e Closures (Milestone 2)
+
+Objetivo curto
+- Formalizar como funções passam a ser valores em MiniC, qual a representação em runtime e a semântica de captura (closures).
+
+Decisão principal
+- Captura por snapshot no momento da criação: uma lambda carrega uma cópia do *ambiente de execução* (snapshot do `Environment`) quando é criada. Ao chamar a closure, o intérprete restaura esse ambiente capturado, vincula os parâmetros e executa o corpo.
+
+Representação sugerida (estrutura em Rust-like, acho que era assim que o professor fez)
+```
+// representação conceitual
+struct ClosureValue {
+ params: Vec, // nomes + tipos
+ return_type: Type,
+ body: Statement, // AST do corpo
+ captured_env: Environment, // snapshot por criação
+}
+
+// em runtime stored as
+// Value::Fn(FnValue::Closure { decl, captured })
+```
+
+O que é capturado
+- Capturamos um *snapshot completo* do `Environment` (mapa nome → `Value`) no momento da criação da lambda. Isso significa:
+ - variáveis livres ficam com o valor que tinham na criação;
+ - a closure não observa mudanças subsequentes em bindings fora do seu escopo local.
+
+Semântica da chamada (ideia em pseudocódigo)
+```
+fn call_closure(closure, args, env) {
+ // 1. snapshot do caller
+ caller_snapshot = env.snapshot()
+
+ // 2. entra no ambiente capturado (lexical scope)
+ env.restore(closure.captured_env.clone())
+
+ // 3. bind dos parâmetros com os argumentos (sobrepondo nomes capturados)
+ for (param, arg) in zip(closure.params, args) {
+ env.declare(param.name.clone(), arg)
+ }
+
+ // 4. executar o corpo
+ result = exec_stmt(&closure.body, env)
+
+ // 5. restaurar ambiente do caller
+ env.restore(caller_snapshot)
+
+ return result.unwrap_or(Value::Void)
+}
+```
+
+Decisão sobre declarações sem inicializador
+- Não declarar uma variável de tipo função sem init (a não ser que queiram mais trabalho, ai só avisar no grupo pra gente rever isso).
+
+Invariantes de tipo
+- Uma `Expr::Lambda` tem tipo `Type::Fun(param_types, Box::new(return_type))` no type checker.
+- `types_compatible` deve comparar aridade e compatibilidade por posição de `Type::Fun`.
+
+Mensagens de erro (sugeridas)
+- Chamar algo não-função: `RuntimeError: 'X' is not a function` ou `attempting to call a non-function value`.
+- Número de argumentos errado: `function 'f' expects N arguments, got M`.
+- Atribuição de tipo inválida: `assignment to f: expected fn(...)->..., got ...`.
+
+Testes obrigatórios (mínimos) — mapeados
+- Pessoa 2 (Sistema de Tipos):
+ - `fn(int) -> int f;` — válido
+ - `fn(float) -> int f; f = fn(int x) -> int { return x; }` — inválido (TypeError)
+
+- Pessoa 3 (Lambdas / Chamadas — type-checker):
+ - `fn(int x) -> int { return x * 2; }` — type-check OK
+ - `f(true)` com `f: fn(int)->int` — inválido (TypeError)
+
+- Pessoa 4 (Runtime: função como valor):
+ - declarar `f = fn(int x) -> int { return x * 2 }; print(f(21));` → saída `42`
+
+- Pessoa 5 (Closures):
+ - `int y = 10; fn(int)->int f = fn(int x)->int { return x + y; }; y = 20; print(f(1));` → saída `11` (captura por snapshot)
diff --git a/src/interpreter/eval_expr.rs b/src/interpreter/eval_expr.rs
index 49fcbef..03f0179 100644
--- a/src/interpreter/eval_expr.rs
+++ b/src/interpreter/eval_expr.rs
@@ -45,7 +45,6 @@ use crate::ir::ast::{CheckedExpr, Expr, Literal};
use super::exec_stmt::exec_stmt;
use super::value::{FnValue, RuntimeError, Value};
-/// Evaluate a checked expression to a runtime value.
pub fn eval_expr(expr: &CheckedExpr, env: &mut Environment) -> Result {
match &expr.exp {
Expr::Literal(lit) => Ok(eval_literal(lit)),
@@ -145,12 +144,36 @@ pub fn eval_expr(expr: &CheckedExpr, env: &mut Environment) -> Result {
let arg_vals: Result, RuntimeError> =
args.iter().map(|a| eval_expr(a, env)).collect();
- eval_call(name, arg_vals?, env)
+
+ let callee = env
+ .get(name)
+ .cloned()
+ .ok_or_else(|| RuntimeError::new(format!("undefined function '{}'", name)))?;
+
+ eval_call_value(callee, arg_vals?, env, Some(name))
+ }
+
+ Expr::CallExpr { chmd, args } => {
+ let callee_val = eval_expr(chmd, env)?;
+ let arg_vals: Result, RuntimeError> =
+ args.iter().map(|a| eval_expr(a, env)).collect();
+
+ eval_call_value(callee_val, arg_vals?, env, None)
+ }
+
+ Expr::Lambda { params, return_tipo, crp } => {
+ let captured = env.snapshot();
+ let decl = crate::ir::ast::CheckedFunDecl {
+ name: "".to_string(),
+ params: params.clone(),
+ return_type: return_tipo.clone(),
+ body: crp.clone(),
+ };
+ Ok(Value::Fn(FnValue::Closure { decl, captured }))
}
}
}
-/// Dispatch a function call via the unified environment.
pub fn eval_call(
name: &str,
args: Vec,
@@ -171,15 +194,91 @@ pub fn eval_call(
for ((param_name, _), val) in decl.params.iter().zip(args.into_iter()) {
env.declare(param_name.clone(), val);
}
- let result = exec_stmt(&decl.body, env)?;
+
+ let result = exec_stmt(&decl.body, env);
env.restore(snapshot);
- Ok(result.unwrap_or(Value::Void))
+
+ match result {
+ Ok(result) => Ok(result.unwrap_or(Value::Void)),
+ Err(err) => Err(err),
+ }
}
+
Some(_) => Err(RuntimeError::new(format!("'{}' is not a function", name))),
None => Err(RuntimeError::new(format!("undefined function '{}'", name))),
}
}
+fn eval_call_value(
+ callee: Value,
+ args: Vec,
+ env: &mut Environment,
+ callee_name: Option<&str>,
+) -> Result {
+ match callee {
+ Value::Fn(FnValue::Native(f)) => (f)(args),
+
+ Value::Fn(FnValue::UserDefined(decl)) => {
+ if args.len() != decl.params.len() {
+ return Err(RuntimeError::new(format!(
+ "function '{}' expects {} arguments, got {}",
+ decl.name,
+ decl.params.len(),
+ args.len()
+ )));
+ }
+
+ let snapshot = env.snapshot();
+ for ((param_name, _), val) in decl.params.iter().zip(args.into_iter()) {
+ env.declare(param_name.clone(), val);
+ }
+
+ let result = exec_stmt(&decl.body, env);
+ env.restore(snapshot);
+
+ match result {
+ Ok(result) => Ok(result.unwrap_or(Value::Void)),
+ Err(err) => Err(err),
+ }
+ }
+
+ Value::Fn(FnValue::Closure { decl, captured }) => {
+ if args.len() != decl.params.len() {
+ return Err(RuntimeError::new(format!(
+ "function '{}' expects {} arguments, got {}",
+ decl.name,
+ decl.params.len(),
+ args.len()
+ )));
+ }
+
+ let caller_snapshot = env.snapshot();
+
+ env.restore(captured);
+
+ for ((param_name, _), val) in decl.params.iter().zip(args.into_iter()) {
+ env.declare(param_name.clone(), val);
+ }
+
+ let result = exec_stmt(&decl.body, env);
+ env.restore(caller_snapshot);
+
+ match result {
+ Ok(result) => Ok(result.unwrap_or(Value::Void)),
+ Err(err) => Err(err),
+ }
+ }
+
+ other => {
+ if let Some(name) = callee_name {
+ Err(RuntimeError::new(format!("'{}' is not a function", name)))
+ } else {
+ Err(RuntimeError::new(format!("value is not callable: {}", other)))
+ }
+ }
+ }
+}
+
// --- Helpers ---
fn eval_literal(lit: &Literal) -> Value {
@@ -237,4 +336,4 @@ fn values_equal(a: &Value, b: &Value) -> bool {
(Value::Str(x), Value::Str(y)) => x == y,
_ => false,
}
-}
+}
\ No newline at end of file
diff --git a/src/interpreter/exec_stmt.rs b/src/interpreter/exec_stmt.rs
index ceeda2c..608d465 100644
--- a/src/interpreter/exec_stmt.rs
+++ b/src/interpreter/exec_stmt.rs
@@ -47,7 +47,11 @@ pub fn exec_stmt(stmt: &CheckedStmt, env: &mut Environment) -> ExecResult
match &stmt.stmt {
// --- Variable declaration ---
Statement::Decl { name, init, .. } => {
- let val = eval_expr(init, env)?;
+ let val = if let Some(init_expr) = init {
+ eval_expr(init_expr, env)?
+ } else {
+ Value::Void
+ };
env.declare(name.clone(), val);
Ok(None)
}
diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs
index 2abdc6e..e03c2cf 100644
--- a/src/interpreter/value.rs
+++ b/src/interpreter/value.rs
@@ -61,6 +61,7 @@
//! needs `Value`, and `Value` needs to reference the callable type.
use std::fmt;
+use std::collections::HashMap;
use crate::ir::ast::CheckedFunDecl;
@@ -72,6 +73,11 @@ pub type NativeFn = fn(Vec) -> Result;
pub enum FnValue {
UserDefined(CheckedFunDecl),
Native(NativeFn),
+
+ Closure {
+ decl: CheckedFunDecl,
+ captured: HashMap,
+ },
}
impl PartialEq for FnValue {
@@ -79,6 +85,7 @@ impl PartialEq for FnValue {
match (self, other) {
(FnValue::UserDefined(a), FnValue::UserDefined(b)) => a == b,
(FnValue::Native(a), FnValue::Native(b)) => (*a as usize) == (*b as usize),
+ (FnValue::Closure { decl: da, .. }, FnValue::Closure { decl: db, .. }) => da == db,
_ => false,
}
}
@@ -89,6 +96,7 @@ impl fmt::Debug for FnValue {
match self {
FnValue::UserDefined(decl) => write!(f, "UserDefined({})", decl.name),
FnValue::Native(_) => write!(f, "Native()"),
+ FnValue::Closure { decl, .. } => write!(f, "Closure({})", decl.name),
}
}
}
@@ -148,4 +156,4 @@ impl fmt::Display for RuntimeError {
}
}
-impl std::error::Error for RuntimeError {}
+impl std::error::Error for RuntimeError {}
\ No newline at end of file
diff --git a/src/ir/ast.rs b/src/ir/ast.rs
index fc400dd..d7f5266 100644
--- a/src/ir/ast.rs
+++ b/src/ir/ast.rs
@@ -105,6 +105,25 @@ pub enum Expr {
name: String,
args: Vec>,
},
+
+ /// Chamada de função por expressão: chmd(args)
+ /// apenas para não mexer em Call, mas depois podemos mesclar os dois (Call pode ser um caso especial de CallExpr onde callee é um Ident).
+ /// Ex.: 'f(42)', '(funçãolambda)(42)', etc.)
+ CallExpr {
+ chmd: Box>,
+ args: Vec>,
+ },
+
+ /// Função Lambda: `fn(params) -> return_tipo { crp }`
+ /// regra pra não ficar ambiguo:
+ /// 'fn(...) -> ...' é tipo, ou seja 'Type::Fun'
+ /// 'fn(...) -> ... { ... }' é expressão, ou seja 'Expr::Lambda'
+ Lambda {
+ params: Vec,
+ return_tipo: Type,
+ crp: Box>,
+ },
+
/// Array literal: [ expr, expr, ... ]
ArrayLit(Vec>),
/// Index expression: `base[index]`
@@ -128,7 +147,8 @@ pub enum Statement {
Decl {
name: String,
ty: Type,
- init: Box>,
+ /// Adicionado para suportar algo como 'fn(int) -> int f;'
+ init: Option>>,
},
Assign {
target: Box>,
diff --git a/src/parser/expressions.rs b/src/parser/expressions.rs
index 8cfcab4..e514472 100644
--- a/src/parser/expressions.rs
+++ b/src/parser/expressions.rs
@@ -34,9 +34,11 @@
//! recursing on the right-hand side, which would accidentally produce
//! right-associative trees.
-use crate::ir::ast::{Expr, ExprD, UncheckedExpr};
+use crate::ir::ast::{Expr, ExprD, Param, UncheckedExpr};
use crate::parser::identifiers::identifier;
+use crate::parser::functions::type_name;
use crate::parser::literals::literal;
+use crate::parser::statements::block_statement;
use nom::{
branch::alt,
bytes::complete::tag,
@@ -65,11 +67,51 @@ pub fn parse_call(input: &str) -> IResult<&str, (String, Vec)> {
Ok((rest, (name.to_string(), args)))
}
+/// Parser de Lambda
+fn lambda_expr(input: &str) -> IResult<&str, UncheckedExpr> {
+ // fn ( Type name, ... ) -> Type { ... }
+ let (rest, _) = preceded(multispace0, tag("fn"))(input)?;
+ let (rest, params) = delimited(
+ preceded(multispace0, tag("(")),
+ separated_list0(
+ preceded(multispace0, tag(",")),
+ map(
+ tuple((
+ preceded(multispace0, type_name),
+ preceded(nom::character::complete::multispace1, identifier),
+ )),
+ |(ty, name)| -> Param { (name.to_string(), ty) },
+ ),
+ ),
+ preceded(multispace0, tag(")")),
+ )(rest)?;
+ let (rest, _) = preceded(multispace0, tag("->"))(rest)?;
+ let (rest, return_tipo) = preceded(multispace0, type_name)(rest)?;
+ let (rest, crp) = preceded(multispace0, block_statement)(rest)?;
+ Ok((
+ rest,
+ wrap(Expr::Lambda {
+ params,
+ return_tipo,
+ crp: Box::new(crp),
+ }),
+ ))
+}
+
+/// Parser de lista de args
+fn arg_list(input: &str) -> IResult<&str, Vec> {
+ delimited(
+ preceded(multispace0, tag("(")),
+ separated_list0(preceded(multispace0, tag(",")), preceded(multispace0, expression)),
+ preceded(multispace0, tag(")")),
+ )(input)
+}
+
/// Atom: literal, call, array literal, identifier, or parenthesized expression.
fn atom(input: &str) -> IResult<&str, UncheckedExpr> {
alt((
+ lambda_expr,
map(literal, |l| wrap(Expr::Literal(l.into()))),
- map(parse_call, |(name, args)| wrap(Expr::Call { name, args })),
map(
delimited(
preceded(multispace0, char('[')),
@@ -94,6 +136,21 @@ fn atom(input: &str) -> IResult<&str, UncheckedExpr> {
fn primary(input: &str) -> IResult<&str, UncheckedExpr> {
let (mut rest, mut acc) = atom(input)?;
loop {
+ if let Ok((r, args)) = arg_list(rest) {
+ acc = if let Expr::Ident(name) = &acc.exp {
+ wrap(Expr::Call {
+ name: name.clone(),
+ args,
+ })
+ } else {
+ wrap(Expr::CallExpr {
+ chmd: Box::new(acc),
+ args,
+ })
+ };
+ rest = r;
+ continue;
+ }
let index_parse = delimited(
preceded(multispace0, char('[')),
preceded(multispace0, expression),
diff --git a/src/parser/functions.rs b/src/parser/functions.rs
index 845a59c..3d0cf0e 100644
--- a/src/parser/functions.rs
+++ b/src/parser/functions.rs
@@ -39,6 +39,7 @@ pub fn type_name(input: &str) -> IResult<&str, Type> {
preceded(
multispace0,
alt((
+ fun_type,
// 2D arrays must be tried before 1D (longer prefix first)
map(tag("int[][]"), |_| Type::Array(Box::new(Type::Array(Box::new(Type::Int))))),
map(tag("float[][]"), |_| Type::Array(Box::new(Type::Array(Box::new(Type::Float))))),
@@ -57,6 +58,36 @@ pub fn type_name(input: &str) -> IResult<&str, Type> {
)(input)
}
+/// Parse a function type: `fn(T1, T2, ...) -> Ret`.
+///
+/// This parser must reject lambdas like:
+/// `fn(int x) -> int { return x; }`
+fn fun_type(input: &str) -> IResult<&str, Type> {
+ let (rest, _) = preceded(multispace0, tag("fn"))(input)?;
+
+ let (rest, params) = delimited(
+ preceded(multispace0, tag("(")),
+ separated_list0(
+ preceded(multispace0, tag(",")),
+ preceded(multispace0, type_name),
+ ),
+ preceded(multispace0, tag(")")),
+ )(rest)?;
+
+ let (rest, _) = preceded(multispace0, tag("->"))(rest)?;
+ let (rest, ret) = preceded(multispace0, type_name)(rest)?;
+
+ // avoid confusing function types with lambdas
+ if rest.trim_start().starts_with("{") {
+ return Err(nom::Err::Error(nom::error::Error::new(
+ rest,
+ nom::error::ErrorKind::Tag,
+ )));
+ }
+
+ Ok((rest, Type::Fun(params, Box::new(ret))))
+}
+
/// Parse a typed parameter (C-style): `Type name`.
fn param(input: &str) -> IResult<&str, (String, Type)> {
map(
@@ -79,6 +110,7 @@ pub fn fun_decl(input: &str) -> IResult<&str, UncheckedFunDecl> {
preceded(multispace0, tag(")")),
)(rest)?;
let (rest, body) = preceded(multispace0, statement)(rest)?;
+
Ok((
rest,
FunDecl {
@@ -88,4 +120,4 @@ pub fn fun_decl(input: &str) -> IResult<&str, UncheckedFunDecl> {
body: Box::new(body),
},
))
-}
+}
\ No newline at end of file
diff --git a/src/parser/identifiers.rs b/src/parser/identifiers.rs
index f6bdecb..5691461 100644
--- a/src/parser/identifiers.rs
+++ b/src/parser/identifiers.rs
@@ -28,7 +28,7 @@ use nom::{
};
/// Reserved words: boolean literals and type names.
-const RESERVED: &[&str] = &["true", "false", "int", "float", "bool", "str", "void", "return"];
+const RESERVED: &[&str] = &["true", "false", "int", "float", "bool", "str", "void", "return", "fn"];
/// Parse an identifier (variable name).
/// Must start with letter or underscore; subsequent chars may be letter, digit, or underscore.
diff --git a/src/parser/statements.rs b/src/parser/statements.rs
index 9dcfef5..888ca5e 100644
--- a/src/parser/statements.rs
+++ b/src/parser/statements.rs
@@ -82,21 +82,27 @@ fn return_statement(input: &str) -> IResult<&str, UncheckedStmt> {
Ok((rest, wrap(Statement::Return(expr.map(Box::new)))))
}
-/// Parse a variable declaration: `Type ident = expr ;`. Must come before assignment.
+/// Parse a variable declaration: `Type ident [= expr] ;`.
+/// Must come before assignment.
fn decl_statement(input: &str) -> IResult<&str, UncheckedStmt> {
map(
tuple((
type_name,
preceded(nom::character::complete::multispace1, identifier),
- preceded(multispace0, nom::bytes::complete::tag("=")),
- preceded(multispace0, expression),
+ opt(preceded(
+ multispace0,
+ preceded(
+ nom::bytes::complete::tag("="),
+ preceded(multispace0, expression),
+ ),
+ )),
preceded(multispace0, char(';')),
)),
- |(ty, name, _, init, _)| {
+ |(ty, name, init, _)| {
wrap(Statement::Decl {
name: name.to_string(),
ty,
- init: Box::new(init),
+ init: init.map(Box::new),
})
},
)(input)
@@ -104,7 +110,7 @@ fn decl_statement(input: &str) -> IResult<&str, UncheckedStmt> {
/// Parse a block statement: `{ stmt* }`.
/// Each statement inside the block carries its own terminator (`;` or `}`).
-fn block_statement(input: &str) -> IResult<&str, UncheckedStmt> {
+pub(crate) fn block_statement(input: &str) -> IResult<&str, UncheckedStmt> {
map(
delimited(
preceded(multispace0, char('{')),
@@ -206,4 +212,4 @@ pub fn assignment(input: &str) -> IResult<&str, UncheckedStmt> {
})
},
)(input)
-}
+}
\ No newline at end of file
diff --git a/src/semantic/type_checker.rs b/src/semantic/type_checker.rs
index e17c4b3..9783e18 100644
--- a/src/semantic/type_checker.rs
+++ b/src/semantic/type_checker.rs
@@ -44,7 +44,7 @@
//! Centralising compatibility logic here means all callers (declaration,
//! assignment, call-argument checking) share one consistent definition.
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
use crate::environment::Environment;
use crate::ir::ast::{
@@ -152,18 +152,29 @@ fn type_check_stmt(
if env.get(name).is_some() {
return Err(TypeError::new(format!("redeclaration of variable: {}", name)));
}
- let init_checked = type_check_expr_to_typed(init, env)?;
- if !types_compatible(&init_checked.ty, ty) {
- return Err(TypeError::new(format!(
- "declaration of {}: expected {:?}, got {:?}",
- name, ty, init_checked.ty
- )));
- }
+ let init_checked = if let Some(init_expr) = init {
+ let checked = type_check_expr_to_typed(init_expr, env)?;
+ if !types_compatible(&checked.ty, ty) {
+ return Err(TypeError::new(format!(
+ "declaration of {}: expected {:?}, got {:?}",
+ name, ty, checked.ty
+ )));
+ }
+ Some(Box::new(checked))
+ } else {
+ if !matches!(ty, Type::Fun(_, _)) {
+ return Err(TypeError::new(format!(
+ "variable '{}' must be initialized",
+ name
+ )));
+ }
+ None
+ };
env.declare(name.clone(), ty.clone());
Statement::Decl {
name: name.clone(),
ty: ty.clone(),
- init: Box::new(init_checked),
+ init: init_checked,
}
}
Statement::Assign { target, value } => {
@@ -268,32 +279,16 @@ fn check_call(
args: &[CheckedExpr],
env: &Environment,
) -> Result<(), TypeError> {
- match env.get(name) {
- Some(Type::Fun(param_tys, _)) => {
- if args.len() != param_tys.len() {
- return Err(TypeError::new(format!(
- "function '{}' expects {} arguments, got {}",
- name,
- param_tys.len(),
- args.len()
- )));
- }
- for (i, (arg, param_ty)) in args.iter().zip(param_tys.iter()).enumerate() {
- if !types_compatible(&arg.ty, param_ty) {
- return Err(TypeError::new(format!(
- "argument {} to {}: expected {:?}, got {:?}",
- i + 1,
- name,
- param_ty,
- arg.ty
- )));
- }
- }
- Ok(())
- }
- Some(_) => Err(TypeError::new(format!("'{}' is not a function", name))),
- None => Err(TypeError::new(format!("undefined function: {}", name))),
- }
+ let callee_ty = env
+ .get(name)
+ .ok_or_else(|| TypeError::new(format!("undefined function: {}", name)))?;
+
+ type_of_function_call(
+ callee_ty,
+ args,
+ &format!("function '{}'", name),
+ )
+ .map(|_| ())
}
fn type_check_assign_target(
@@ -333,6 +328,83 @@ fn type_check_assign_target(
}
}
+fn type_of_function_call(
+ callee_ty: &Type,
+ args: &[CheckedExpr],
+ label: &str,
+) -> Result {
+ match callee_ty {
+ Type::Fun(param_tys, return_ty) => {
+ if args.len() != param_tys.len() {
+ return Err(TypeError::new(format!(
+ "{} expects {} arguments, got {}",
+ label,
+ param_tys.len(),
+ args.len()
+ )));
+ }
+
+ for (i, (arg, param_ty)) in args.iter().zip(param_tys.iter()).enumerate() {
+ if !types_compatible(&arg.ty, param_ty) {
+ return Err(TypeError::new(format!(
+ "argument {} to {}: expected {:?}, got {:?}",
+ i + 1,
+ label,
+ param_ty,
+ arg.ty
+ )));
+ }
+ }
+
+ Ok((**return_ty).clone())
+ }
+
+ other => {
+ if label == "function value" {
+ Err(TypeError::new("attempting to call a non-function value"))
+ }
+ else {
+ Err(TypeError::new(format!(
+ "{} is not a function, got {:?}",
+ label, other
+ )))
+ }
+ }
+ }
+}
+
+fn type_check_lambda_body(
+ params: &[(String, Type)],
+ return_tipo: &Type,
+ crp: &UncheckedStmt,
+ env: &Environment,
+) -> Result {
+ let mut lambda_env = Environment::::new();
+ lambda_env.restore(env.snapshot());
+
+ let mut seen_params = HashSet::new();
+
+ for (name, ty) in params.iter() {
+ if ty == &Type::Unit {
+ return Err(TypeError::new(format!(
+ "lambda parameter '{}' cannot have type void",
+ name
+ )));
+ }
+
+ if !seen_params.insert(name.clone()) {
+ return Err(TypeError::new(format!(
+ "duplicate lambda parameter: {}",
+ name
+ )));
+ }
+
+ lambda_env.declare(name.clone(), ty.clone());
+ }
+
+ type_check_stmt(crp, &mut lambda_env, return_tipo)
+}
+
fn type_check_expr_to_typed(
e: &UncheckedExpr,
env: &Environment,
@@ -401,12 +473,48 @@ fn type_check_expr_inner(
)),
Expr::Call { name, args } => {
let args_checked: Result, _> =
- args.iter().map(|a| type_check_expr_to_typed(a, env)).collect();
+ args.iter().map(|a| type_check_expr_to_typed(a, env)).collect();
+
+ let args_checked = args_checked?;
+
+ check_call(name, &args_checked, env)?;
+
Ok(Expr::Call {
name: name.clone(),
- args: args_checked?,
+ args: args_checked,
+ })
+ }
+
+ Expr::CallExpr { chmd, args } => {
+ let chmd_checked = type_check_expr_to_typed(chmd, env)?;
+
+ let args_checked: Result, _> =
+ args.iter().map(|a| type_check_expr_to_typed(a, env)).collect();
+
+ let args_checked = args_checked?;
+
+ type_of_function_call(&chmd_checked.ty, &args_checked, "function value")?;
+
+ Ok(Expr::CallExpr {
+ chmd: Box::new(chmd_checked),
+ args: args_checked,
+ })
+ }
+
+ Expr::Lambda {
+ params,
+ return_tipo,
+ crp,
+ } => {
+ let body_checked = type_check_lambda_body(params, return_tipo, crp, env)?;
+
+ Ok(Expr::Lambda {
+ params: params.clone(),
+ return_tipo: return_tipo.clone(),
+ crp: Box::new(body_checked),
})
}
+
Expr::ArrayLit(elems) => {
let elems_checked: Result, _> =
elems.iter().map(|e| type_check_expr_to_typed(e, env)).collect();
@@ -426,10 +534,6 @@ fn type_check_expr(
match &e.exp {
Expr::Literal(l) => Ok(literal_type(l)),
Expr::Ident(name) => match env.get(name) {
- Some(Type::Fun(_, _)) => Err(TypeError::new(format!(
- "cannot use function '{}' as a value",
- name
- ))),
Some(ty) => Ok(ty.clone()),
None => Err(TypeError::new(format!("undeclared variable: {}", name))),
},
@@ -488,36 +592,43 @@ fn type_check_expr(
Expr::Call { name, args } => {
let args_checked: Result, _> =
args.iter().map(|a| type_check_expr_to_typed(a, env)).collect();
+
let args_checked = args_checked?;
- match env.get(name) {
- Some(Type::Fun(param_tys, return_ty)) => {
- if args_checked.len() != param_tys.len() {
- return Err(TypeError::new(format!(
- "function '{}' expects {} arguments, got {}",
- name,
- param_tys.len(),
- args_checked.len()
- )));
- }
- for (i, (arg, param_ty)) in
- args_checked.iter().zip(param_tys.iter()).enumerate()
- {
- if !types_compatible(&arg.ty, param_ty) {
- return Err(TypeError::new(format!(
- "argument {} to {}: expected {:?}, got {:?}",
- i + 1,
- name,
- param_ty,
- arg.ty
- )));
- }
- }
- Ok((**return_ty).clone())
- }
- Some(_) => Err(TypeError::new(format!("'{}' is not a function", name))),
- None => Err(TypeError::new(format!("undefined function: {}", name))),
- }
+
+ let callee_ty = env
+ .get(name)
+ .ok_or_else(|| TypeError::new(format!("undefined function: {}", name)))?;
+
+ type_of_function_call(
+ callee_ty,
+ &args_checked,
+ &format!("function '{}'", name),
+ )
+ }
+
+ Expr::CallExpr { chmd, args } => {
+ let chmd_checked = type_check_expr_to_typed(chmd, env)?;
+
+ let args_checked: Result, _> =
+ args.iter().map(|a| type_check_expr_to_typed(a, env)).collect();
+
+ let args_checked = args_checked?;
+
+ type_of_function_call(&chmd_checked.ty, &args_checked, "function value")
}
+
+ Expr::Lambda {
+ params,
+ return_tipo,
+ crp,
+ } => {
+ type_check_lambda_body(params, return_tipo, crp, env)?;
+
+ let param_tys = params.iter().map(|(_, ty)| ty.clone()).collect();
+
+ Ok(Type::Fun(param_tys, Box::new(return_tipo.clone())))
+ }
+
Expr::ArrayLit(elems) => {
if elems.is_empty() {
return Err(TypeError::new("empty array literal needs type annotation"));
@@ -580,6 +691,19 @@ fn types_compatible(a: &Type, b: &Type) -> bool {
| (Type::Unit, Type::Unit) => true,
(Type::Int, Type::Float) | (Type::Float, Type::Int) => true,
(Type::Array(a), Type::Array(b)) => types_compatible(a, b),
+ (Type::Fun(params_a, ret_a), Type::Fun(params_b, ret_b)) => {
+ if params_a.len() != params_b.len() {
+ return false;
+ }
+
+ for (a, b) in params_a.iter().zip(params_b.iter()) {
+ if a != b {
+ return false;
+ }
+ }
+
+ ret_a == ret_b
+ },
_ => false,
}
}
diff --git a/tests/interpreter.rs b/tests/interpreter.rs
index 51696c9..cfbf22d 100644
--- a/tests/interpreter.rs
+++ b/tests/interpreter.rs
@@ -256,3 +256,62 @@ fn test_stdlib_pow_float_args() {
"#;
assert!(run(src).is_ok(), "{}", run(src).unwrap_err());
}
+
+// LAMBDA
+
+/// Executa o pipeline completo: Parser -> Type Check -> Interpret
+fn run_full(src: &str) -> Result<(), String> {
+ let (_, unchecked) = program(src).map_err(|e| format!("Parser Error: {:?}", e))?;
+
+ // Passando &unchecked (Referência)
+ let checked = type_check(&unchecked).map_err(|e| format!("Type Error: {}", e.message))?;
+
+ // Execução
+ interpret(&checked).map_err(|e| format!("Runtime Error: {}", e.message))?;
+
+ Ok(())
+}
+
+#[test]
+fn test_exec_lambda_simple_math() {
+ let src = r#"
+ void main() {
+ fn(int) -> int dobrar = fn(int n) -> int { return n * 2; };
+ print(dobrar(10));
+ }
+ "#;
+
+ let result = run_full(src);
+ assert!(result.is_ok(), "Falha na execução da lambda básica. Erro: {:?}", result.err());
+}
+
+#[test]
+fn test_exec_closure_capture_success() {
+ // Este teste valida se o seu interpretador injeta o HashMap 'captured' no ambiente
+ let src = r#"
+ void main() {
+ int base = 100;
+ fn(int) -> int somar = fn(int n) -> int { return n + base; };
+ print(somar(50));
+ }
+ "#;
+
+ let result = run_full(src);
+ assert!(
+ result.is_ok(),
+ "A Closure falhou! Provavelmente a variável 'base' não foi injetada no ambiente da lambda. Erro: {:?}",
+ result.err()
+ );
+}
+
+#[test]
+fn test_exec_immediate_lambda_call() {
+ // Testa (fn(x)->x{...})(val)
+ let src = r#"
+ void main() {
+ int x = (fn(int a, int b) -> int { return a + b; })(5, 5);
+ print(x);
+ }
+ "#;
+ assert!(run_full(src).is_ok());
+}
\ No newline at end of file
diff --git a/tests/interpreter_closure.rs b/tests/interpreter_closure.rs
new file mode 100644
index 0000000..ed642cc
--- /dev/null
+++ b/tests/interpreter_closure.rs
@@ -0,0 +1,139 @@
+use mini_c::{interpreter::interpret, parser::program, semantic::type_check};
+
+fn run(src: &str) -> Result<(), String> {
+ let (_, unchecked) = program(src).map_err(|e| format!("parse error: {:?}", e))?;
+ let checked = type_check(&unchecked).map_err(|e| format!("type error: {}", e.message))?;
+ interpret(&checked).map_err(|e| format!("runtime error: {}", e.message))
+}
+
+#[test]
+fn closure_snapshot_captures_creation_time_value() {
+ let src = r#"
+ void main() {
+ int y = 10;
+ fn(int) -> int f = fn(int x) -> int { return x + y; };
+ y = 20;
+ print(f(1));
+ }
+ "#;
+
+ assert!(run(src).is_ok());
+}
+
+#[test]
+fn closures_capture_different_snapshots() {
+ let src = r#"
+ void main() {
+ int y = 10;
+
+ fn(int) -> int f =
+ fn(int x) -> int {
+ return x + y;
+ };
+
+ y = 20;
+
+ fn(int) -> int g =
+ fn(int x) -> int {
+ return x + y;
+ };
+
+ print(f(1));
+ print(g(1));
+ }
+ "#;
+
+ assert!(run(src).is_ok());
+}
+
+#[test]
+fn closure_snapshot_captures_multiple_variables() {
+ let src = r#"
+ void main() {
+ int a = 2;
+ int b = 3;
+
+ fn(int) -> int f = fn(int x) -> int {
+ return x + a + b;
+ };
+
+ a = 100;
+ b = 200;
+ print(f(1));
+ }
+ "#;
+
+ assert!(run(src).is_ok());
+}
+
+#[test]
+fn closure_called_through_variable_is_accepted() {
+ let src = r#"
+ void main() {
+ fn(int) -> int inc = fn(int x) -> int { return x + 1; };
+ print(inc(41));
+ }
+ "#;
+
+ assert!(run(src).is_ok());
+}
+
+#[test]
+fn closure_can_be_called_multiple_times() {
+ let src = r#"
+ void main() {
+ int y = 10;
+
+ fn(int) -> int f = fn(int x) -> int {
+ return x + y;
+ };
+
+ print(f(1));
+ print(f(2));
+ print(f(3));
+ }
+ "#;
+
+ assert!(run(src).is_ok());
+}
+
+#[test]
+fn caller_environment_is_restored_after_closure_call() {
+ let src = r#"
+ void main() {
+ int y = 10;
+
+ fn(int) -> int f = fn(int x) -> int {
+ return x + y;
+ };
+
+ print(f(1));
+ y = 50;
+ print(y);
+ }
+ "#;
+
+ assert!(run(src).is_ok());
+}
+
+#[test]
+fn nested_closure_still_uses_captured_snapshot() {
+ let src = r#"
+ void main() {
+ int y = 10;
+
+ fn(int) -> int make = fn(int x) -> int {
+ return x + y;
+ };
+
+ fn(int) -> int call = fn(int z) -> int {
+ return make(z);
+ };
+
+ y = 20;
+ print(call(1));
+ }
+ "#;
+
+ assert!(run(src).is_ok());
+}
\ No newline at end of file
diff --git a/tests/interpreter_fn_value.rs b/tests/interpreter_fn_value.rs
new file mode 100644
index 0000000..d5e2881
--- /dev/null
+++ b/tests/interpreter_fn_value.rs
@@ -0,0 +1,32 @@
+use mini_c::interpreter::interpret;
+use mini_c::parser::program;
+use mini_c::semantic::type_check;
+
+fn run(src: &str) -> Result<(), String> {
+ let (_, unchecked) = program(src).map_err(|e| format!("parse error: {:?}", e))?;
+ let checked = type_check(&unchecked).map_err(|e| format!("type error: {}", e.message))?;
+ interpret(&checked).map_err(|e| format!("runtime error: {}", e.message))
+}
+
+#[test]
+fn test_fn_value_runtime() {
+ let src = r#"
+ void main() {
+ fn(int) -> int f = fn(int x) -> int { return x * 2; };
+ print(f(21));
+ }
+ "#;
+ assert!(run(src).is_ok(), "expected runtime to succeed");
+}
+
+#[test]
+fn test_fn_value_call_through_variable() {
+ let src = r#"
+ void main() {
+ fn(int) -> int f = fn(int x) -> int { return x * 2; };
+ fn(int) -> int g = f;
+ print(g(21));
+ }
+ "#;
+ assert!(run(src).is_ok(), "expected function call through variable to succeed");
+}
\ No newline at end of file
diff --git a/tests/parser.rs b/tests/parser.rs
index eca6640..5583172 100644
--- a/tests/parser.rs
+++ b/tests/parser.rs
@@ -340,6 +340,7 @@ fn test_decl_statement() {
assert!(matches!(result.stmt, Statement::Decl { ref name, ref ty, .. }
if name == "x" && ty == &Type::Int));
if let Statement::Decl { ref init, .. } = result.stmt {
+ let init = init.as_ref().expect("Decl init should be present");
assert_eq!(init.exp, Expr::Literal(Literal::Int(42)));
}
@@ -658,7 +659,7 @@ fn test_multidimensional_indexed_assignment() {
#[test]
fn test_nested_index() {
let result = expression("arr[i][j]").unwrap().1;
- assert!(matches!(result.exp, Expr::Index { ref base, ref index }
+ assert!(matches!(result.exp, Expr::Index { base: _, ref index }
if matches!(index.exp, Expr::Ident(ref s) if s == "j")));
if let Expr::Index { ref base, .. } = result.exp {
assert!(matches!(base.exp, Expr::Index { ref base, ref index }
diff --git a/tests/type_checker.rs b/tests/type_checker.rs
index 3357161..3b6d8dd 100644
--- a/tests/type_checker.rs
+++ b/tests/type_checker.rs
@@ -27,6 +27,7 @@ fn test_type_check_int_float_coercion() {
let prog = result.unwrap();
let main_fn = prog.functions.iter().find(|f| f.name == "main").unwrap();
if let mini_c::ir::ast::Statement::Decl { ref init, .. } = main_fn.body.stmt {
+ let init = init.as_ref().expect("Decl init should be present");
assert_eq!(init.ty, Type::Float);
} else {
panic!("expected Decl");
@@ -202,3 +203,26 @@ fn test_type_check_print_wrong_arity() {
let result = parse_and_type_check("void main() { print(1, 2); }");
assert!(result.is_err(), "expected arity error for print(1, 2)");
}
+
+// LAMBDA
+
+#[test]
+fn test_type_check_lambda_capture() {
+ let src = r#"
+ void main() {
+ int externo = 10;
+ fn(int) -> int f = fn(int x) -> int { return x + externo; };
+ }
+ "#;
+ let (_, unchecked) = program(src).expect("Falha no parser");
+ // Usando &unchecked para evitar erro de mismatched types (E0308)
+ let result = type_check(&unchecked);
+ assert!(result.is_ok(), "Type Checker não encontrou a variável capturada 'externo'");
+}
+
+#[test]
+fn test_type_check_void_lambda() {
+ let src = "void main() { fn() -> void f = fn() -> void { return; }; }";
+ let (_, unchecked) = program(src).expect("Falha no parser");
+ assert!(type_check(&unchecked).is_ok());
+}
\ No newline at end of file
diff --git a/tests/type_fn_assign.rs b/tests/type_fn_assign.rs
new file mode 100644
index 0000000..d3b880d
--- /dev/null
+++ b/tests/type_fn_assign.rs
@@ -0,0 +1,28 @@
+use mini_c::{parser::program, semantic::type_check};
+
+fn type_check_src(src: &str) -> Result<(), String> {
+ let (_, unchecked) = program(src).map_err(|e| format!("parse error: {:?}", e))?;
+ type_check(&unchecked).map(|_| ()).map_err(|e| format!("type error: {}", e.message))
+}
+
+#[test]
+fn test_assign_compatible_fn_type() {
+ let src = r#"
+ void main() {
+ fn(int) -> int f = fn(int x) -> int { return x; };
+ int r = f(5);
+ }
+ "#;
+ assert!(type_check_src(src).is_ok(), "expected type-check to succeed");
+}
+
+#[test]
+fn test_assign_incompatible_fn_type() {
+ let src = r#"
+ void main() {
+ fn(float) -> int f = fn(int x) -> int { return x; };
+ }
+ "#;
+ let res = type_check_src(src);
+ assert!(res.is_err(), "expected type error for incompatible function assignment");
+}
diff --git a/tests/type_function_compat.rs b/tests/type_function_compat.rs
new file mode 100644
index 0000000..e2997b0
--- /dev/null
+++ b/tests/type_function_compat.rs
@@ -0,0 +1,131 @@
+use mini_c::{interpreter::interpret, parser::program, semantic::type_check};
+
+fn type_check_src(src: &str) -> Result<(), String> {
+ let (_, unchecked) = program(src).map_err(|e| format!("parse error: {:?}", e))?;
+ type_check(&unchecked).map(|_| ()).map_err(|e| format!("type error: {}", e.message))
+}
+
+fn run_src(src: &str) -> Result<(), String> {
+ let (_, unchecked) = program(src).map_err(|e| format!("parse error: {:?}", e))?;
+ let checked = type_check(&unchecked).map_err(|e| format!("type error: {}", e.message))?;
+ interpret(&checked).map_err(|e| format!("runtime error: {}", e.message))
+}
+
+// ---------------------------------------------------------------------------
+// ✅ CASOS VÁLIDOS
+// ---------------------------------------------------------------------------
+
+#[test]
+fn test_valid_uninitialized_fn_decl() {
+ let src = r#"
+ void main() {
+ fn(int) -> int f;
+ }
+ "#;
+ assert!(type_check_src(src).is_ok(), "Expected valid uninitialized fn declaration to pass type checker");
+}
+
+#[test]
+fn test_valid_fn_assign_and_call() {
+ let src = r#"
+ void main() {
+ fn(int) -> int f;
+ f = fn(int x) -> int { return x * 3; };
+ int r = f(4);
+ print(r);
+ }
+ "#;
+ assert!(run_src(src).is_ok(), "Expected valid function assignment and call to succeed at runtime");
+}
+
+// ---------------------------------------------------------------------------
+// ❌ CASOS INVÁLIDOS
+// ---------------------------------------------------------------------------
+
+#[test]
+fn test_invalid_uninitialized_int_decl() {
+ let src = r#"
+ void main() {
+ int x;
+ }
+ "#;
+ let res = type_check_src(src);
+ assert!(res.is_err(), "Expected type checker to reject uninitialized int variable");
+ assert!(res.unwrap_err().contains("must be initialized"));
+}
+
+#[test]
+fn test_incompatible_fn_type_assignment() {
+ let src = r#"
+ void main() {
+ fn(float) -> int f;
+ f = fn(int x) -> int { return x; };
+ }
+ "#;
+ let res = type_check_src(src);
+ assert!(res.is_err(), "Expected type checker to reject incompatible function type assignment");
+ assert!(res.unwrap_err().contains("assignment to f"));
+}
+
+#[test]
+fn test_incompatible_fn_type_declaration() {
+ let src = r#"
+ void main() {
+ fn(float) -> int f = fn(int x) -> int { return x; };
+ }
+ "#;
+ let res = type_check_src(src);
+ assert!(res.is_err(), "Expected type checker to reject incompatible function type initialization");
+ assert!(res.unwrap_err().contains("declaration of f"));
+}
+
+#[test]
+fn test_call_non_function() {
+ let src = r#"
+ void main() {
+ int x = 42;
+ x(5);
+ }
+ "#;
+ let res = type_check_src(src);
+ assert!(res.is_err(), "Expected type checker to reject calling a non-function value");
+ assert!(res.unwrap_err().contains("is not a function"));
+}
+
+#[test]
+fn test_call_non_function_literal() {
+ let src = r#"
+ void main() {
+ int y = 1(5);
+ }
+ "#;
+ let res = type_check_src(src);
+ assert!(res.is_err(), "Expected type checker to reject calling a non-function literal");
+ assert!(res.unwrap_err().contains("attempting to call a non-function value"));
+}
+
+#[test]
+fn test_wrong_argument_count() {
+ let src = r#"
+ void main() {
+ fn(int) -> int f = fn(int x) -> int { return x; };
+ f(1, 2);
+ }
+ "#;
+ let res = type_check_src(src);
+ assert!(res.is_err(), "Expected type checker to reject calling function with wrong number of arguments");
+ assert!(res.unwrap_err().contains("expects 1 arguments, got 2"));
+}
+
+#[test]
+fn test_uninitialized_fn_call_runtime_error() {
+ let src = r#"
+ void main() {
+ fn(int) -> int f;
+ f(5);
+ }
+ "#;
+ let res = run_src(src);
+ assert!(res.is_err(), "Expected calling an uninitialized function variable to fail at runtime");
+ assert!(res.unwrap_err().contains("is not a function"));
+}
diff --git a/tests/type_function_integration_tests.rs b/tests/type_function_integration_tests.rs
new file mode 100644
index 0000000..83050fd
--- /dev/null
+++ b/tests/type_function_integration_tests.rs
@@ -0,0 +1,139 @@
+use mini_c::parser::functions::{fun_decl, type_name};
+use mini_c::parser::statements::statement;
+
+// =========================
+// ✅ DECLARAÇÕES COM TYPE::FUN
+// =========================
+
+#[test]
+fn test_statement_decl_with_function_type_no_init() {
+ let input = "fn(int) -> int f;";
+
+ let result = statement(input);
+ assert!(result.is_ok());
+
+ let (rest, _) = result.unwrap();
+ assert_eq!(rest.trim(), "");
+}
+
+#[test]
+fn test_statement_decl_with_nested_function_type_no_init() {
+ let input = "fn(fn(int) -> int) -> int g;";
+
+ let result = statement(input);
+ assert!(result.is_ok());
+
+ let (rest, _) = result.unwrap();
+ assert_eq!(rest.trim(), "");
+}
+
+#[test]
+fn test_statement_decl_with_function_type_and_init() {
+ let input = "fn(int) -> int f = foo;";
+
+ let result = statement(input);
+ assert!(result.is_ok());
+
+ let (rest, _) = result.unwrap();
+ assert_eq!(rest.trim(), "");
+}
+
+// =========================
+// ✅ FUNÇÕES COM PARÂMETROS TYPE::FUN
+// =========================
+
+#[test]
+fn test_fun_decl_param_with_function_type() {
+ let input = "int apply(fn(int) -> int f, int x) { return x; }";
+
+ let result = fun_decl(input);
+ assert!(result.is_ok());
+
+ let (rest, _) = result.unwrap();
+ assert_eq!(rest.trim(), "");
+}
+
+#[test]
+fn test_fun_decl_nested_function_type_in_param() {
+ let input = "int h(fn(fn(int) -> int) -> int f) { return 0; }";
+
+ let result = fun_decl(input);
+ assert!(result.is_ok());
+
+ let (rest, _) = result.unwrap();
+ assert_eq!(rest.trim(), "");
+}
+
+// =========================
+// ✅ FUNÇÕES COM RETORNO TYPE::FUN
+// =========================
+
+#[test]
+fn test_fun_decl_returning_function_type() {
+ let input = "fn(int) -> int make() { return foo; }";
+
+ let result = fun_decl(input);
+ assert!(result.is_ok());
+
+ let (rest, _) = result.unwrap();
+ assert_eq!(rest.trim(), "");
+}
+
+#[test]
+fn test_fun_decl_returning_nested_function_type() {
+ let input = "fn(int) -> fn(int) -> int make() { return foo; }";
+
+ let result = fun_decl(input);
+ assert!(result.is_ok());
+
+ let (rest, _) = result.unwrap();
+ assert_eq!(rest.trim(), "");
+}
+
+// =========================
+// ✅ ESPAÇOS / ROBUSTEZ
+// =========================
+
+#[test]
+fn test_fun_decl_with_many_spaces() {
+ let input = "
+ int apply(
+ fn(int) -> int f,
+ int x
+ )
+ {
+ return x;
+ }
+ ";
+
+ let result = fun_decl(input);
+ assert!(result.is_ok());
+
+ let (rest, _) = result.unwrap();
+ assert_eq!(rest.trim(), "");
+}
+
+#[test]
+fn test_statement_decl_with_many_spaces() {
+ let input = "
+ fn( int , float ) -> bool g ;
+ ";
+
+ let result = statement(input);
+ assert!(result.is_ok());
+
+ let (rest, _) = result.unwrap();
+ assert_eq!(rest.trim(), "");
+}
+
+// =========================
+// ❌ NÃO CONFUNDIR COM LAMBDA
+// =========================
+
+#[test]
+fn test_type_name_still_rejects_lambda() {
+ let input = "fn(int x) -> int { return x; }";
+
+ let result = type_name(input);
+ assert!(result.is_err());
+}
\ No newline at end of file
diff --git a/tests/type_function_tests.rs b/tests/type_function_tests.rs
new file mode 100644
index 0000000..1bc3ef3
--- /dev/null
+++ b/tests/type_function_tests.rs
@@ -0,0 +1,200 @@
+use mini_c::parser::functions::type_name;
+use mini_c::ir::ast::Type;
+
+// =========================
+// ✅ CASOS VÁLIDOS
+// =========================
+
+#[test]
+fn test_fun_type_simple() {
+ let input = "fn(int) -> int";
+
+ let result = type_name(input);
+ assert!(result.is_ok());
+
+ let (rest, ty): (&str, Type) = result.unwrap();
+ assert_eq!(rest.trim(), "");
+
+ match ty {
+ Type::Fun(params, ret) => {
+ assert_eq!(params.len(), 1);
+ assert_eq!(params[0], Type::Int);
+ assert_eq!(*ret, Type::Int);
+ }
+ _ => panic!("Expected function type"),
+ }
+}
+
+#[test]
+fn test_fun_type_multiple_params() {
+ let input = "fn(int, float) -> bool";
+
+ let (_, ty) = type_name(input).unwrap();
+
+ match ty {
+ Type::Fun(params, ret) => {
+ assert_eq!(params.len(), 2);
+ assert_eq!(params[0], Type::Int);
+ assert_eq!(params[1], Type::Float);
+ assert_eq!(*ret, Type::Bool);
+ }
+ _ => panic!("Expected function type"),
+ }
+}
+
+#[test]
+fn test_fun_type_no_params() {
+ let input = "fn() -> int";
+
+ let (_, ty) = type_name(input).unwrap();
+
+ match ty {
+ Type::Fun(params, ret) => {
+ assert_eq!(params.len(), 0);
+ assert_eq!(*ret, Type::Int);
+ }
+ _ => panic!("Expected function type"),
+ }
+}
+
+#[test]
+fn test_fun_type_nested() {
+ let input = "fn(fn(int) -> int) -> int";
+
+ let (_, ty) = type_name(input).unwrap();
+
+ match ty {
+ Type::Fun(params, ret) => {
+ assert_eq!(params.len(), 1);
+
+ match ¶ms[0] {
+ Type::Fun(inner_params, inner_ret) => {
+ assert_eq!(inner_params.len(), 1);
+ assert_eq!(inner_params[0], Type::Int);
+ assert_eq!(**inner_ret, Type::Int);
+ }
+ _ => panic!("Expected nested function type"),
+ }
+
+ assert_eq!(*ret, Type::Int);
+ }
+ _ => panic!("Expected function type"),
+ }
+}
+
+#[test]
+fn test_fun_type_spaces_everywhere() {
+ let input = " fn( int , float ) -> bool ";
+
+ let (_, ty) = type_name(input).unwrap();
+
+ match ty {
+ Type::Fun(params, ret) => {
+ assert_eq!(params.len(), 2);
+ assert_eq!(params[0], Type::Int);
+ assert_eq!(params[1], Type::Float);
+ assert_eq!(*ret, Type::Bool);
+ }
+ _ => panic!("Expected function type"),
+ }
+}
+
+// =========================
+// ❌ CASOS INVÁLIDOS
+// =========================
+
+#[test]
+fn test_lambda_not_type() {
+ let input = "fn(int x) -> int { return x; }";
+
+ let result = type_name(input);
+
+ assert!(result.is_err());
+}
+
+#[test]
+fn test_invalid_comma() {
+ let input = "fn(,) -> int";
+
+ let result = type_name(input);
+
+ assert!(result.is_err());
+}
+
+#[test]
+fn test_missing_arrow() {
+ let input = "fn(int) int";
+
+ let result = type_name(input);
+
+ assert!(result.is_err());
+}
+
+#[test]
+fn test_unclosed_paren() {
+ let input = "fn(int -> int";
+
+ let result = type_name(input);
+
+ assert!(result.is_err());
+}
+
+#[test]
+fn test_missing_return_type() {
+ let input = "fn(int) ->";
+
+ let result = type_name(input);
+
+ assert!(result.is_err());
+}
+
+#[test]
+fn test_random_garbage() {
+ let input = "fn(abc) => ???";
+
+ let result = type_name(input);
+
+ assert!(result.is_err());
+}
+
+// =========================
+// 🔥 EDGE CASES
+// =========================
+
+#[test]
+fn test_deeply_nested_functions() {
+ let input = "fn(fn(fn(int) -> int) -> int) -> int";
+
+ let result = type_name(input);
+
+ assert!(result.is_ok());
+}
+
+#[test]
+fn test_trailing_input() {
+ let input = "fn(int) -> int extra";
+
+ let (rest, _): (&str, Type) = type_name(input).unwrap();
+
+ assert!(rest.trim().starts_with("extra"));
+}
+
+// =========================
+// 💡 ROBUSTEZ
+// =========================
+
+#[test]
+fn test_many_spaces_and_newlines() {
+ let input = "
+ fn(
+ int,
+ float
+ )
+ ->
+ bool
+ ";
+
+ let result = type_name(input);
+
+ assert!(result.is_ok());
+}
\ No newline at end of file
diff --git a/tests/type_lambda_call.rs b/tests/type_lambda_call.rs
new file mode 100644
index 0000000..845f1bd
--- /dev/null
+++ b/tests/type_lambda_call.rs
@@ -0,0 +1,107 @@
+use mini_c::{parser::program, semantic::type_check};
+
+fn type_check_src(src: &str) -> Result<(), String> {
+ let (rest, unchecked) = program(src)
+ .map_err(|e| format!("parse error: {:?}", e))?;
+
+ if !rest.trim().is_empty() {
+ return Err(format!("unparsed input: {:?}", rest));
+ }
+
+ type_check(&unchecked)
+ .map(|_| ())
+ .map_err(|e| format!("type error: {}", e.message))
+}
+
+#[test]
+fn test_lambda_type_check_ok() {
+ let src = r#"
+ void main() {
+ fn(int) -> int f = fn(int x) -> int {
+ return x * 2;
+ };
+
+ int r = f(3);
+ }
+ "#;
+
+ assert!(
+ type_check_src(src).is_ok(),
+ "lambda should type-check"
+ );
+}
+
+#[test]
+fn test_lambda_call_wrong_arg() {
+ let src = r#"
+ void main() {
+ fn(int) -> int f = fn(int x) -> int {
+ return x;
+ };
+
+ f(true);
+ }
+ "#;
+
+ let res = type_check_src(src);
+
+ assert!(
+ res.is_err(),
+ "expected type error when calling with wrong arg type"
+ );
+}
+
+#[test]
+fn test_lambda_wrong_return_type() {
+ let src = r#"
+ void main() {
+ fn(int) -> int f = fn(int x) -> int {
+ return true;
+ };
+ }
+ "#;
+
+ let res = type_check_src(src);
+
+ assert!(
+ res.is_err(),
+ "expected type error when lambda returns bool instead of int"
+ );
+}
+
+#[test]
+fn test_lambda_call_wrong_number_of_args() {
+ let src = r#"
+ void main() {
+ fn(int) -> int f = fn(int x) -> int {
+ return x;
+ };
+
+ f();
+ }
+ "#;
+
+ let res = type_check_src(src);
+
+ assert!(
+ res.is_err(),
+ "expected type error when calling function with wrong number of args"
+ );
+}
+
+#[test]
+fn test_call_non_function_value() {
+ let src = r#"
+ void main() {
+ int x = 10;
+ x(1);
+ }
+ "#;
+
+ let res = type_check_src(src);
+
+ assert!(
+ res.is_err(),
+ "expected type error when calling a non-function value"
+ );
+}
\ No newline at end of file