diff --git a/src/ir/mod.rs b/src/ir/mod.rs new file mode 100644 index 0000000..768c189 --- /dev/null +++ b/src/ir/mod.rs @@ -0,0 +1,2 @@ +pub mod ast; +pub mod typed_ast; diff --git a/src/ir/typed_ast.rs b/src/ir/typed_ast.rs new file mode 100644 index 0000000..109ad06 --- /dev/null +++ b/src/ir/typed_ast.rs @@ -0,0 +1,250 @@ +use crate::ir::ast::{Name, Type, ValueConstructor,FormalArgument}; + + +#[derive(Debug, PartialEq, Clone)] +pub enum TypedExpression { + CTrue { type_info: Type }, + CFalse { type_info: Type }, + CInt { value: i32, type_info: Type }, + CReal { value: f64, type_info: Type }, + CString { value: String, type_info: Type }, + CVoid { type_info: Type }, + + Var { name: Name, type_info: Type }, + + // Expressões aritméticas + Add { + left: Box, + right: Box, + type_info: Type, + }, + Sub { + left: Box, + right: Box, + type_info: Type, + }, + Mul{ + left: Box, + right: Box, + type_info: Type, + }, + Div{ + left: Box, + right: Box, + type_info: Type, + }, + + And { + left: Box, + right: Box, + type_info: Type, + }, + Or { + left: Box, + right: Box, + type_info: Type, + }, + Not { + expr: Box, + type_info: Type, + }, + + // Relational expressions over numbers + EQ { + left: Box, + right: Box, + type_info: Type, + }, + NEQ { + left: Box, + right: Box, + type_info: Type, + }, + GT { + left: Box, + right: Box, + type_info: Type, + }, + LT { + left: Box, + right: Box, + type_info: Type, + }, + GTE { + left: Box, + right: Box, + type_info: Type, + }, + LTE { + left: Box, + right: Box, + type_info: Type, + }, + + COk { expr: Box, type_info: Type }, + CErr { expr: Box, type_info: Type }, + CJust { expr: Box, type_info: Type }, + CNothing { type_info: Type }, + + Unwrap { expr: Box, type_info: Type }, + IsError { expr: Box, type_info: Type }, + IsNothing { expr: Box, type_info: Type }, + Propagate { expr: Box, type_info: Type }, + + // List value + ListValue { + elements: Vec, + type_info: Type, + }, + + // Constructor + Constructor { + name: Name, + args: Vec>, + type_info: Type, + }, + + FuncCall { + name: Name, + args: Vec>, + type_info: Type, + }, + +} + +impl TypedExpression { + pub fn type_info(&self) -> &Type { + match self { + TypedExpression::CTrue { type_info } => type_info, + TypedExpression::CFalse { type_info } => type_info, + TypedExpression::CInt { type_info, .. } => type_info, + TypedExpression::CReal { type_info, .. } => type_info, + TypedExpression::CString { type_info, .. } => type_info, + TypedExpression::CVoid { type_info } => type_info, + TypedExpression::Var { type_info, .. } => type_info, + TypedExpression::FuncCall { type_info, .. } => type_info, + + // Arithmetic expressions + TypedExpression::Add { type_info, .. } => type_info, + TypedExpression::Sub { type_info, .. } => type_info, + TypedExpression::Mul { type_info, .. } => type_info, + TypedExpression::Div { type_info, .. } => type_info, + + // Boolean expressions + TypedExpression::And { type_info, .. } => type_info, + TypedExpression::Or { type_info, .. } => type_info, + TypedExpression::Not { type_info, .. } => type_info, + + // Relational expressions + TypedExpression::EQ { type_info, .. } => type_info, + TypedExpression::NEQ { type_info, .. } => type_info, + TypedExpression::GT { type_info, .. } => type_info, + TypedExpression::LT { type_info, .. } => type_info, + TypedExpression::GTE { type_info, .. } => type_info, + TypedExpression::LTE { type_info, .. } => type_info, + + // Error-related expressions + TypedExpression::COk { type_info, .. } => type_info, + TypedExpression::CErr { type_info, .. } => type_info, + TypedExpression::CJust { type_info, .. } => type_info, + TypedExpression::CNothing { type_info } => type_info, + TypedExpression::Unwrap { type_info, .. } => type_info, + TypedExpression::IsError { type_info, .. } => type_info, + TypedExpression::IsNothing { type_info, .. } => type_info, + TypedExpression::Propagate { type_info, .. } => type_info, + + // List and Constructor + TypedExpression::ListValue { type_info, .. } => type_info, + TypedExpression::Constructor { type_info, .. } => type_info, + } + } +} + + +#[derive(Debug, PartialEq, Clone)] +pub struct TypedFunction { + pub name: Name, + pub kind: Type, + pub params: Vec, + pub body: Option>, +} + + +#[derive(Debug, PartialEq, Clone)] +pub enum TypedStatement { + + // Declaração de variável agora contém uma TypedExpression + VarDeclaration { + name: Name, + value: Box, + }, + ValDeclaration { + name: Name, + value: Box, + }, + Assignment { + name: Name, + value: Box, + }, + + IfThenElse { + cond: Box, + stmt_then: Box, + stmt_else_opt: Option>, + }, + While { + cond: Box, + body: Box, + }, + For { + name: Name, + iterable: Box, + body: Box, + }, + + Block(Vec), + + Sequence { + first: Box, + second: Box, + }, + + Assert { + left: Box, + right: Box, + }, + AssertTrue { + expr: Box, + message: String, + }, + AssertFalse { + expr: Box, + message: String, + }, + AssertEQ { + left: Box, + right: Box, + message: String, + }, + AssertNEQ { + left: Box, + right: Box, + message: String, + }, + + TestDef(TypedFunction), + ModTestDef { + name: Name, + body: Box, + }, + + AssertFails(String), + + FuncDef(TypedFunction), + + Return(Box), + + TypeDeclaration(Name, Vec), + + +} diff --git a/src/main.rs b/src/main.rs index b038c00..3d5d895 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,14 +10,95 @@ use std::collections::HashMap; use std::fs::File; use std::io::Write;*/ +use crate::type_checker::check_stmt; +use crate::environment::environment::Environment; +use crate::ir::ast::Type; + pub mod environment; pub mod interpreter; pub mod ir; pub mod parser; pub mod type_checker; +use crate::ir::ast::{Expression, Statement}; + fn main() { - println!("Hello, world!"); + let source_code = " + var x = 10; + var y = true; + if y { + x = x + 5; + } + "; + + println!("--- Código Fonte ---"); + println!("{}", source_code); + + /* + let statements = match parse(source_code) { + Ok((_, stmts)) => stmts, + Err(e) => { + println!("Erro de Parse: {:?}", e); + return; + } + }; + */ + + // Simulação do parser + println!("\n--- [SIMULAÇÃO] Gerando AST manualmente ---"); + let statements = vec![ + // Corresponde a: var x = 10; + Statement::VarDeclaration( + "x".to_string(), + Box::new(Expression::CInt(10)) + ), + // Corresponde a: var y = true; + Statement::VarDeclaration( + "y".to_string(), + Box::new(Expression::CTrue) + ), + // Corresponde a: if y { x = x + 5; } + Statement::IfThenElse( + Box::new(Expression::Var("y".to_string())), + Box::new(Statement::Assignment( + "x".to_string(), + Box::new(Expression::Add( + Box::new(Expression::Var("x".to_string())), + Box::new(Expression::CInt(5)) + )) + )), + None + ) + ]; + + + let mut env: Environment = Environment::new(); + println!("\n--- Verificando Tipos... ---"); + + for stmt in &statements { + match check_stmt(stmt.clone(), &env) { + Ok((typed_stmt, new_env)) => { + println!("\n Statement OK. AST Tipada gerada:"); + println!("{:#?}", typed_stmt); + + env = new_env; + + println!("--- Ambiente após esta statement ---"); + for (name, (is_mutable, var_type)) in env.get_all_variables() { + let mutability_str = if is_mutable { "var" } else { "val" }; + println!(" - {} {}: {:?}", mutability_str, name, var_type); + } + println!("------------------------------------"); + } + Err(e) => { + println!("\n Erro de Tipo Encontrado:"); + println!("{}", e); + return; + } + } + } + + println!("\n\nVerificação de tipos concluída com sucesso para todo o programa!"); } /* fn run_test(name: &str, program: &str) -> String { diff --git a/src/type_checker/expression_type_checker.rs b/src/type_checker/expression_type_checker.rs index 33067f9..6277fca 100644 --- a/src/type_checker/expression_type_checker.rs +++ b/src/type_checker/expression_type_checker.rs @@ -1,306 +1,577 @@ use crate::environment::environment::Environment; -use crate::ir::ast::{Expression, Name, Type}; +use crate::ir::ast::{Expression, Type}; +use crate::ir::typed_ast::TypedExpression; type ErrorMessage = String; -pub fn check_expr(exp: Expression, env: &Environment) -> Result { +pub fn check_expr(exp: Expression, env: &Environment) -> Result { match exp { - Expression::CTrue => Ok(Type::TBool), - Expression::CFalse => Ok(Type::TBool), - Expression::CVoid => Ok(Type::TVoid), - Expression::CInt(_) => Ok(Type::TInteger), - Expression::CReal(_) => Ok(Type::TReal), - Expression::CString(_) => Ok(Type::TString), - Expression::Add(l, r) => check_bin_arithmetic_expression(*l, *r, env), - Expression::Sub(l, r) => check_bin_arithmetic_expression(*l, *r, env), - Expression::Mul(l, r) => check_bin_arithmetic_expression(*l, *r, env), - Expression::Div(l, r) => check_bin_arithmetic_expression(*l, *r, env), - Expression::And(l, r) => check_bin_boolean_expression(*l, *r, env), - Expression::Or(l, r) => check_bin_boolean_expression(*l, *r, env), - Expression::Not(e) => check_not_expression(*e, env), - Expression::EQ(l, r) => check_bin_relational_expression(*l, *r, env), - Expression::GT(l, r) => check_bin_relational_expression(*l, *r, env), - Expression::LT(l, r) => check_bin_relational_expression(*l, *r, env), - Expression::GTE(l, r) => check_bin_relational_expression(*l, *r, env), - Expression::LTE(l, r) => check_bin_relational_expression(*l, *r, env), - Expression::Var(name) => check_var_name(name, env), - Expression::COk(e) => check_result_ok(*e, env), - Expression::CErr(e) => check_result_err(*e, env), - Expression::CJust(e) => check_maybe_just(*e, env), - Expression::CNothing => Ok(Type::TMaybe(Box::new(Type::TAny))), - Expression::IsError(e) => check_iserror_type(*e, env), - Expression::IsNothing(e) => check_isnothing_type(*e, env), - Expression::Unwrap(e) => check_unwrap_type(*e, env), - Expression::Propagate(e) => check_propagate_type(*e, env), - Expression::ListValue(elements) => check_list_value(&elements, env), - Expression::Constructor(name, args) => check_adt_constructor(name, args, env), - - _ => Err("not implemented yet.".to_string()), - } -} - -fn check_var_name(name: Name, env: &Environment) -> Result { - match env.lookup(&name) { - Some((_, t)) => Ok(t.clone()), - None => Err(format!("[Name Error] '{}' is not defined.", name)), - } -} - -fn check_bin_arithmetic_expression( - left: Expression, - right: Expression, - env: &Environment, -) -> Result { - let left_type = check_expr(left, env)?; - let right_type = check_expr(right, env)?; - - match (left_type, right_type) { - (Type::TInteger, Type::TInteger) => Ok(Type::TInteger), - (Type::TInteger, Type::TReal) => Ok(Type::TReal), - (Type::TReal, Type::TInteger) => Ok(Type::TReal), - (Type::TReal, Type::TReal) => Ok(Type::TReal), - _ => Err(String::from("[Type Error] expecting numeric type values.")), - } -} - -fn check_bin_boolean_expression( - left: Expression, - right: Expression, - env: &Environment, -) -> Result { - let left_type = check_expr(left, env)?; - let right_type = check_expr(right, env)?; - match (left_type, right_type) { - (Type::TBool, Type::TBool) => Ok(Type::TBool), - _ => Err(String::from("[Type Error] expecting boolean type values.")), - } -} - -fn check_not_expression(exp: Expression, env: &Environment) -> Result { - let exp_type = check_expr(exp, env)?; - - match exp_type { - Type::TBool => Ok(Type::TBool), - _ => Err(String::from("[Type Error] expecting a boolean type value.")), - } -} - -fn check_bin_relational_expression( - left: Expression, - right: Expression, - env: &Environment, -) -> Result { - let left_type = check_expr(left, env)?; - let right_type = check_expr(right, env)?; - - match (left_type, right_type) { - (Type::TInteger, Type::TInteger) => Ok(Type::TBool), - (Type::TInteger, Type::TReal) => Ok(Type::TBool), - (Type::TReal, Type::TInteger) => Ok(Type::TBool), - (Type::TReal, Type::TReal) => Ok(Type::TBool), - _ => Err(String::from("[Type Error] expecting numeric type values.")), - } -} + Expression::CTrue => Ok(TypedExpression::CTrue { + type_info: Type::TBool, + }), + Expression::CFalse => Ok(TypedExpression::CFalse { + type_info: Type::TBool, + }), + Expression::CVoid => Ok(TypedExpression::CVoid { + type_info: Type::TVoid, + }), + Expression::CInt(v) => Ok(TypedExpression::CInt { + value: v, + type_info: Type::TInteger, + }), + Expression::CReal(v) => Ok(TypedExpression::CReal { + value: v, + type_info: Type::TReal, + }), + Expression::CString(v) => Ok(TypedExpression::CString { + value: v, + type_info: Type::TString, + }), + Expression::Add(l, r) => { + let typed_left = check_expr(*l, env)?; + let typed_right = check_expr(*r, env)?; + + let result_type = match (typed_left.type_info(), typed_right.type_info()) { + (Type::TInteger, Type::TInteger) => Type::TInteger, + (Type::TReal, Type::TReal) | (Type::TInteger, Type::TReal) | (Type::TReal, Type::TInteger) => Type::TReal, + (lt, rt) => return Err(format!("[Type Error] Cannot add non-numeric types '{:?}' and '{:?}'.", lt, rt)), + }; + + Ok(TypedExpression::Add { + left: Box::new(typed_left), + right: Box::new(typed_right), + type_info: result_type, + }) + }, + + Expression::Sub(l, r) => { + let typed_left = check_expr(*l, env)?; + let typed_right = check_expr(*r, env)?; + + let result_type = match (typed_left.type_info(), typed_right.type_info()) { + (Type::TInteger, Type::TInteger) => Type::TInteger, + (Type::TReal, Type::TReal) | (Type::TInteger, Type::TReal) | (Type::TReal, Type::TInteger) => Type::TReal, + (lt, rt) => return Err(format!("[Type Error] Cannot subtract non-numeric types '{:?}' and '{:?}'.", lt, rt)), + }; + + Ok(TypedExpression::Sub { + left: Box::new(typed_left), + right: Box::new(typed_right), + type_info: result_type, + }) + }, + + Expression::Mul(l, r) => { + let typed_left = check_expr(*l, env)?; + let typed_right = check_expr(*r, env)?; + + let result_type = match (typed_left.type_info(), typed_right.type_info()) { + (Type::TInteger, Type::TInteger) => Type::TInteger, + (Type::TReal, Type::TReal) | (Type::TInteger, Type::TReal) | (Type::TReal, Type::TInteger) => Type::TReal, + (lt, rt) => return Err(format!("[Type Error] Cannot multiply non-numeric types '{:?}' and '{:?}'.", lt, rt)), + }; + + Ok(TypedExpression::Mul { + left: Box::new(typed_left), + right: Box::new(typed_right), + type_info: result_type, + }) + }, + + Expression::Div(l, r) => { + let typed_left = check_expr(*l, env)?; + let typed_right = check_expr(*r, env)?; + + let result_type = match (typed_left.type_info(), typed_right.type_info()) { + (Type::TInteger, Type::TInteger) => Type::TInteger, + (Type::TReal, Type::TReal) | (Type::TInteger, Type::TReal) | (Type::TReal, Type::TInteger) => Type::TReal, + (lt, rt) => return Err(format!("[Type Error] Cannot divide non-numeric types '{:?}' and '{:?}'.", lt, rt)), + }; + + Ok(TypedExpression::Div { + left: Box::new(typed_left), + right: Box::new(typed_right), + type_info: result_type, + }) + }, + + // Operações Booleanas + Expression::And(l, r) => { + let typed_left = check_expr(*l, env)?; + let typed_right = check_expr(*r, env)?; + + match (typed_left.type_info(), typed_right.type_info()) { + (Type::TBool, Type::TBool) => (), + (lt, rt) => return Err(format!("[Type Error] 'And' operator expects boolean values, but got '{:?}' and '{:?}'.", lt, rt)), + }; + + Ok(TypedExpression::And { + left: Box::new(typed_left), + right: Box::new(typed_right), + type_info: Type::TBool, + }) + }, + + Expression::Or(l, r) => { + let typed_left = check_expr(*l, env)?; + let typed_right = check_expr(*r, env)?; + + match (typed_left.type_info(), typed_right.type_info()) { + (Type::TBool, Type::TBool) => (), + (lt, rt) => return Err(format!("[Type Error] 'Or' operator expects boolean values, but got '{:?}' and '{:?}'.", lt, rt)), + }; + + Ok(TypedExpression::Or { + left: Box::new(typed_left), + right: Box::new(typed_right), + type_info: Type::TBool, + }) + }, + Expression::Not(e) => { + let typed_e = check_expr(*e, env)?; + if typed_e.type_info() != &Type::TBool { + return Err(format!("[Type Error] 'Not' operator expects a boolean type, but got '{:?}'.", typed_e.type_info())); + } + Ok(TypedExpression::Not { + expr: Box::new(typed_e), + type_info: Type::TBool, + }) + } -fn check_result_ok(exp: Expression, env: &Environment) -> Result { - let exp_type = check_expr(exp, env)?; - return Ok(Type::TResult(Box::new(exp_type), Box::new(Type::TAny))); -} + // Operações Relacionais + Expression::EQ(l, r) => { + let typed_left = check_expr(*l, env)?; + let typed_right = check_expr(*r, env)?; + + match (typed_left.type_info(), typed_right.type_info()) { + (Type::TInteger, Type::TInteger) | (Type::TReal, Type::TReal) + | (Type::TInteger, Type::TReal) | (Type::TReal, Type::TInteger) => (), + (lt, rt) => return Err(format!("[Type Error] Cannot compare non-numeric types '{:?}' and '{:?}'.", lt, rt)), + }; + + Ok(TypedExpression::EQ { + left: Box::new(typed_left), + right: Box::new(typed_right), + type_info: Type::TBool, + }) + }, + + Expression::NEQ(l, r) => { + let typed_left = check_expr(*l, env)?; + let typed_right = check_expr(*r, env)?; + + match (typed_left.type_info(), typed_right.type_info()) { + (Type::TInteger, Type::TInteger) | (Type::TReal, Type::TReal) + | (Type::TInteger, Type::TReal) | (Type::TReal, Type::TInteger) => (), + (lt, rt) => return Err(format!("[Type Error] Cannot compare non-numeric types '{:?}' and '{:?}'.", lt, rt)), + }; + + Ok(TypedExpression::NEQ { + left: Box::new(typed_left), + right: Box::new(typed_right), + type_info: Type::TBool, + }) + }, + + Expression::GT(l, r) => { + let typed_left = check_expr(*l, env)?; + let typed_right = check_expr(*r, env)?; + + match (typed_left.type_info(), typed_right.type_info()) { + (Type::TInteger, Type::TInteger) | (Type::TReal, Type::TReal) + | (Type::TInteger, Type::TReal) | (Type::TReal, Type::TInteger) => (), + (lt, rt) => return Err(format!("[Type Error] Cannot compare non-numeric types '{:?}' and '{:?}'.", lt, rt)), + }; + + Ok(TypedExpression::GT { + left: Box::new(typed_left), + right: Box::new(typed_right), + type_info: Type::TBool, + }) + }, + + Expression::LT(l, r) => { + let typed_left = check_expr(*l, env)?; + let typed_right = check_expr(*r, env)?; + + match (typed_left.type_info(), typed_right.type_info()) { + (Type::TInteger, Type::TInteger) | (Type::TReal, Type::TReal) + | (Type::TInteger, Type::TReal) | (Type::TReal, Type::TInteger) => (), + (lt, rt) => return Err(format!("[Type Error] Cannot compare non-numeric types '{:?}' and '{:?}'.", lt, rt)), + }; + + Ok(TypedExpression::LT { + left: Box::new(typed_left), + right: Box::new(typed_right), + type_info: Type::TBool, + }) + }, + + Expression::GTE(l, r) => { + let typed_left = check_expr(*l, env)?; + let typed_right = check_expr(*r, env)?; + + match (typed_left.type_info(), typed_right.type_info()) { + (Type::TInteger, Type::TInteger) | (Type::TReal, Type::TReal) + | (Type::TInteger, Type::TReal) | (Type::TReal, Type::TInteger) => (), + (lt, rt) => return Err(format!("[Type Error] Cannot compare non-numeric types '{:?}' and '{:?}'.", lt, rt)), + }; + + Ok(TypedExpression::GTE { + left: Box::new(typed_left), + right: Box::new(typed_right), + type_info: Type::TBool, + }) + }, + + Expression::LTE(l, r) => { + let typed_left = check_expr(*l, env)?; + let typed_right = check_expr(*r, env)?; + + match (typed_left.type_info(), typed_right.type_info()) { + (Type::TInteger, Type::TInteger) | (Type::TReal, Type::TReal) + | (Type::TInteger, Type::TReal) | (Type::TReal, Type::TInteger) => (), + (lt, rt) => return Err(format!("[Type Error] Cannot compare non-numeric types '{:?}' and '{:?}'.", lt, rt)), + }; + + Ok(TypedExpression::LTE { + left: Box::new(typed_left), + right: Box::new(typed_right), + type_info: Type::TBool, + }) + }, + + // Variável + Expression::Var(name) => match env.lookup(&name) { + Some((_, t)) => Ok(TypedExpression::Var { + name, + type_info: t.clone(), + }), + None => Err(format!("[Name Error] '{}' is not defined.", name)), + }, + + // Tipos Maybe/Result + Expression::COk(e) => { + let typed_e = check_expr(*e, env)?; + let ok_type = typed_e.type_info().clone(); + Ok(TypedExpression::COk { + expr: Box::new(typed_e), + type_info: Type::TResult(Box::new(ok_type), Box::new(Type::TAny)), + }) + } + Expression::CErr(e) => { + let typed_e = check_expr(*e, env)?; + let err_type = typed_e.type_info().clone(); + Ok(TypedExpression::CErr { + expr: Box::new(typed_e), + type_info: Type::TResult(Box::new(Type::TAny), Box::new(err_type)), + }) + } + Expression::CJust(e) => { + let typed_e = check_expr(*e, env)?; + let just_type = typed_e.type_info().clone(); + Ok(TypedExpression::CJust { + expr: Box::new(typed_e), + type_info: Type::TMaybe(Box::new(just_type)), + }) + } + Expression::CNothing => Ok(TypedExpression::CNothing { + type_info: Type::TMaybe(Box::new(Type::TAny)), + }), + + Expression::IsError(e) => { + let typed_e = check_expr(*e, env)?; + if let Type::TResult(_,_) = typed_e.type_info() { + Ok(TypedExpression::IsError { + expr: Box::new(typed_e), + type_info: Type::TBool, + }) + } else { + Err(String::from("[Type Error] expecting a result type value.")) + } + } + Expression::IsNothing(e) => { + let typed_e = check_expr(*e, env)?; + if let Type::TMaybe(_) = typed_e.type_info() { + Ok(TypedExpression::IsNothing { + expr: Box::new(typed_e), + type_info: Type::TBool, + }) + } else { + Err(String::from("[Type Error] expecting a maybe type value.")) + } + } -fn check_result_err(exp: Expression, env: &Environment) -> Result { - let exp_type = check_expr(exp, env)?; - return Ok(Type::TResult(Box::new(Type::TAny), Box::new(exp_type))); -} + Expression::Unwrap(e) => { + let typed_e = check_expr(*e, env)?; + let unwrapped_type = match typed_e.type_info() { + Type::TMaybe(t) => *t.clone(), + Type::TResult(t, _) => *t.clone(), + t => return Err(format!("[Type Error] 'Unwrap' expects a Maybe or Result type, but got '{:?}'.", t)), + }; + + Ok(TypedExpression::Unwrap { + expr: Box::new(typed_e), + type_info: unwrapped_type, + }) + }, + + Expression::Propagate(e) => { + let typed_e = check_expr(*e, env)?; + let unwrapped_type = match typed_e.type_info() { + Type::TMaybe(t) => *t.clone(), + Type::TResult(t, _) => *t.clone(), + t => return Err(format!("[Type Error] 'Propagate' expects a Maybe or Result type, but got '{:?}'.", t)), + }; + + Ok(TypedExpression::Propagate { + expr: Box::new(typed_e), + type_info: unwrapped_type, + }) + }, + + // Estruturas de Dados + Expression::ListValue(elements) => { + let mut typed_elements = Vec::new(); + for el in elements { + typed_elements.push(check_expr(el, env)?); + } -fn check_unwrap_type(exp: Expression, env: &Environment) -> Result { - let exp_type = check_expr(exp, env)?; + let list_type = if typed_elements.is_empty() { + Type::TList(Box::new(Type::TAny)) + } else { + let first_type = typed_elements[0].type_info(); + for element in typed_elements.iter().skip(1) { + if element.type_info() != first_type { + return Err(format!( + "[Type Error] List elements must have the same type. Expected '{:?}', found '{:?}'.", + first_type, element.type_info() + )); + } + } + Type::TList(Box::new(first_type.clone())) + }; + + Ok(TypedExpression::ListValue { + elements: typed_elements, + type_info: list_type, + }) + }, + + + + Expression::Constructor(name, args) => { + let mut adt_def = None; + for scope in env.stack.iter().rev() { + for (adt_name, constructors) in scope.adts.iter() { + if let Some(constructor) = constructors.iter().find(|c| c.name == name) { + adt_def = Some((adt_name.clone(), constructor.clone(), constructors.clone())); + break; + } + } + if adt_def.is_some() { break; } + } + if adt_def.is_none() { + for (adt_name, constructors) in env.globals.adts.iter() { + if let Some(constructor) = constructors.iter().find(|c| c.name == name) { + adt_def = Some((adt_name.clone(), constructor.clone(), constructors.clone())); + break; + } + } + } - match exp_type { - Type::TMaybe(t) => Ok(*t), - Type::TResult(tl, _) => Ok(*tl), - _ => Err(String::from( - "[Type Error] expecting a maybe or result type value.", - )), - } -} + match adt_def { + Some((adt_type_name, constructor_def, all_constructors)) => { + if args.len() != constructor_def.types.len() { + return Err(format!( + "[Type Error] Constructor '{}' expects {} arguments, but got {}.", + name, constructor_def.types.len(), args.len() + )); + } + + let mut typed_args = Vec::new(); + for (arg_expr, expected_type) in args.into_iter().zip(constructor_def.types.iter()) { + let typed_arg = check_expr(*arg_expr, env)?; + if typed_arg.type_info() != expected_type { + return Err(format!( + "[Type Error] Argument type mismatch in constructor '{}'. Expected '{:?}', found '{:?}'.", + name, expected_type, typed_arg.type_info() + )); + } + typed_args.push(Box::new(typed_arg)); + } + + Ok(TypedExpression::Constructor { + name, + args: typed_args, + type_info: Type::TAlgebraicData(adt_type_name, all_constructors), + }) + } + None => Err(format!( + "[Type Error] Constructor '{}' is not defined in any ADT.", + name + )), + } + }, + + //FuncCall + Expression::FuncCall(name, args) => { + match env.lookup_function(&name) { + Some(function_def) => { + if args.len() != function_def.params.len() { + return Err(format!( + "[Type Error] Function '{}' expects {} arguments, but got {}.", + name, + function_def.params.len(), + args.len() + )); + } + + let mut typed_args = Vec::new(); + for (arg_expr, param_def) in args.into_iter().zip(function_def.params.iter()) { + let typed_arg = check_expr(arg_expr, env)?; + + if typed_arg.type_info() != ¶m_def.argument_type { + return Err(format!( + "[Type Error] Argument type mismatch for parameter '{}' in function '{}'. Expected '{:?}', but found '{:?}'.", + param_def.argument_name, name, param_def.argument_type, typed_arg.type_info() + )); + } + typed_args.push(Box::new(typed_arg)); + } + + Ok(TypedExpression::FuncCall { + name, + args: typed_args, + type_info: function_def.kind.clone(), + }) + } + None => Err(format!("[Name Error] Function '{}' is not defined.", name)), + } + }, -fn check_propagate_type(exp: Expression, env: &Environment) -> Result { - let exp_type = check_expr(exp, env)?; - match exp_type { - Type::TMaybe(t) => Ok(*t), - Type::TResult(tl, _) => Ok(*tl), - _ => Err(String::from( - "[Type Error] expecting a maybe or result type value.", - )), + // _ => Err("not implemented yet.".to_string()), } } -fn check_maybe_just(exp: Expression, env: &Environment) -> Result { - let exp_type = check_expr(exp, env)?; - Ok(Type::TMaybe(Box::new(exp_type))) -} - -fn check_iserror_type(exp: Expression, env: &Environment) -> Result { - let v = check_expr(exp, env)?; - match v { - Type::TResult(_, _) => Ok(Type::TBool), - _ => Err(String::from("[Type Error] expecting a result type value.")), - } -} +#[cfg(test)] +mod tests { + use super::*; + use crate::environment::environment::Environment; + use crate::ir::ast::Expression::*; + use crate::ir::ast::ValueConstructor; + use crate::ir::typed_ast::TypedExpression; -fn check_isnothing_type(exp: Expression, env: &Environment) -> Result { - let exp_type = check_expr(exp, env)?; + #[test] + fn test_add_integers_produces_typed_node() { + let env = Environment::new(); + let add_expr = Expression::Add(Box::new(CInt(10)), Box::new(CInt(20))); - match exp_type { - Type::TMaybe(_) => Ok(Type::TBool), - _ => Err(String::from("[Type Error] expecting a maybe type value.")), - } -} + let result = check_expr(add_expr, &env); -fn check_list_value( - elements: &[Expression], - env: &Environment, -) -> Result { - if elements.is_empty() { - return Ok(Type::TList(Box::new(Type::TAny))); - } + assert!(result.is_ok()); - // Check the type of the first element - let first_type = check_expr(elements[0].clone(), env)?; - - // Check that all other elements have the same type - for element in elements.iter().skip(1) { - let element_type = check_expr(element.clone(), env)?; - if element_type != first_type { - return Err(format!( - "[Type Error] List elements must have the same type. Expected '{:?}', found '{:?}'.", - first_type, element_type - )); - } - } + let typed_expr = result.unwrap(); - Ok(Type::TList(Box::new(first_type))) -} + assert_eq!(typed_expr.type_info(), &Type::TInteger); -fn check_adt_constructor( - name: Name, - args: Vec>, - env: &Environment, -) -> Result { - // Gather all ADTs from all scopes (stack and globals) - let mut found = None; - // Search stack scopes first (innermost to outermost) - for scope in env.stack.iter() { - for (adt_name, constructors) in scope.adts.iter() { - if let Some(constructor) = constructors.iter().find(|c| c.name == name) { - found = Some((adt_name.clone(), constructor.clone(), constructors.clone())); - break; - } - } - if found.is_some() { - break; - } - } - // If not found in stack, search globals - if found.is_none() { - for (adt_name, constructors) in env.globals.adts.iter() { - if let Some(constructor) = constructors.iter().find(|c| c.name == name) { - found = Some((adt_name.clone(), constructor.clone(), constructors.clone())); - break; - } + if let TypedExpression::Add { left, right, .. } = typed_expr { + assert_eq!(left.type_info(), &Type::TInteger); + assert_eq!(right.type_info(), &Type::TInteger); + } else { + panic!("Expected a TypedExpression::Add node, but got another variant."); } } - match found { - Some((adt_type_name, constructor, constructors)) => { - // Check that we have the right number of arguments - if args.len() != constructor.types.len() { - return Err(format!( - "[Type Error] Constructor '{}' expects {} arguments, but got {}.", - name, - constructor.types.len(), - args.len() - )); - } - // Check each argument's type - for (arg, expected_type) in args.iter().zip(constructor.types.iter()) { - let arg_type = check_expr(*arg.clone(), env)?; - if arg_type != *expected_type { - return Err(format!( - "[Type Error] Argument type mismatch in constructor '{}'. Expected '{:?}', found '{:?}'.", - name, expected_type, arg_type - )); - } - } - // Return the algebraic type - Ok(Type::TAlgebraicData(adt_type_name, constructors)) - } - None => Err(format!( - "[Type Error] Constructor '{}' is not defined in any ADT.", - name - )), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::environment::environment::Environment; - use crate::ir::ast::Expression::*; - use crate::ir::ast::Type::*; - use crate::ir::ast::{Type, ValueConstructor}; #[test] fn check_constant() { let env = Environment::new(); let c10 = CInt(10); - assert_eq!(check_expr(c10, &env), Ok(TInteger)); + + let result = check_expr(c10, &env); + + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + + assert_eq!(typed_expr.type_info(), &Type::TInteger); + if let TypedExpression::CInt { value, .. } = typed_expr { + assert_eq!(value, 10); + } else { + panic!("Expected a CInt variant"); + } } #[test] fn check_add_integers() { + let env = Environment::new(); - let c10 = CInt(10); let c20 = CInt(20); let add = Add(Box::new(c10), Box::new(c20)); - assert_eq!(check_expr(add, &env), Ok(TInteger)); + + let result = check_expr(add, &env); + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + assert_eq!(typed_expr.type_info(), &Type::TInteger); + assert!(matches!(typed_expr, TypedExpression::Add { .. })); } #[test] fn check_add_reals() { + let env = Environment::new(); - let c10 = CReal(10.5); let c20 = CReal(20.3); let add = Add(Box::new(c10), Box::new(c20)); - assert_eq!(check_expr(add, &env), Ok(TReal)); + + let result = check_expr(add, &env); + + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + assert_eq!(typed_expr.type_info(), &Type::TReal); + assert!(matches!(typed_expr, TypedExpression::Add { .. })); } #[test] fn check_add_real_and_integer() { + let env = Environment::new(); - let c10 = CInt(10); let c20 = CReal(20.3); let add = Add(Box::new(c10), Box::new(c20)); - assert_eq!(check_expr(add, &env), Ok(TReal)); + + let result = check_expr(add, &env); + + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + assert_eq!(typed_expr.type_info(), &Type::TReal); + assert!(matches!(typed_expr, TypedExpression::Add { .. })); } #[test] fn check_add_integer_and_real() { + let env = Environment::new(); - let c10 = CReal(10.5); let c20 = CInt(20); let add = Add(Box::new(c10), Box::new(c20)); - assert_eq!(check_expr(add, &env), Ok(TReal)); + let result = check_expr(add, &env); + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + assert_eq!(typed_expr.type_info(), &Type::TReal); + assert!(matches!(typed_expr, TypedExpression::Add { .. })); } #[test] @@ -360,45 +631,84 @@ mod tests { #[test] fn check_ok_result() { + let env = Environment::new(); let e1 = CReal(10.0); let e2 = COk(Box::new(e1)); + + let result = check_expr(e2, &env); + + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + assert_eq!( - check_expr(e2, &env), - Ok(TResult(Box::new(TReal), Box::new(TAny))) + typed_expr.type_info(), + &Type::TResult(Box::new(Type::TReal), Box::new(Type::TAny)) ); + assert!(matches!(typed_expr, TypedExpression::COk { .. })); } #[test] fn check_err_result() { + let env = Environment::new(); let e1 = CInt(1); let e2 = CErr(Box::new(e1)); + + let result = check_expr(e2, &env); + + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + assert_eq!( - check_expr(e2, &env), - Ok(TResult(Box::new(TAny), Box::new(TInteger))) + typed_expr.type_info(), + &Type::TResult(Box::new(Type::TAny), Box::new(Type::TInteger)) ); + assert!(matches!(typed_expr, TypedExpression::CErr { .. })); } #[test] fn check_just_integer() { + let env = Environment::new(); let e1 = CInt(5); let e2 = CJust(Box::new(e1)); - assert_eq!(check_expr(e2, &env), Ok(TMaybe(Box::new(TInteger)))) + + let result = check_expr(e2, &env); + + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + + assert_eq!( + typed_expr.type_info(), + &Type::TMaybe(Box::new(Type::TInteger)) + ); + assert!(matches!(typed_expr, TypedExpression::CJust { .. })); } #[test] fn check_is_error_result_positive() { + let env = Environment::new(); let e1 = CTrue; let e2 = COk(Box::new(e1)); let e3 = IsError(Box::new(e2)); - assert_eq!(check_expr(e3, &env), Ok(TBool)); + + let result = check_expr(e3, &env); + + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + + assert_eq!(typed_expr.type_info(), &Type::TBool); + assert!(matches!(typed_expr, TypedExpression::IsError { .. })); } #[test] @@ -417,7 +727,17 @@ mod tests { fn check_nothing() { let env = Environment::new(); - assert_eq!(check_expr(CNothing, &env), Ok(TMaybe(Box::new(TAny)))); + let result = check_expr(CNothing, &env); + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + + assert_eq!( + typed_expr.type_info(), + &Type::TMaybe(Box::new(Type::TAny)) + ); + + assert!(matches!(typed_expr, TypedExpression::CNothing { .. })); } #[test] @@ -427,7 +747,14 @@ mod tests { let e2 = CJust(Box::new(e1)); let e3 = IsNothing(Box::new(e2)); - assert_eq!(check_expr(e3, &env), Ok(TBool)); + let result = check_expr(e3, &env); + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + + assert_eq!(typed_expr.type_info(), &Type::TBool); + + assert!(matches!(typed_expr, TypedExpression::IsNothing { .. })); } #[test] @@ -449,7 +776,14 @@ mod tests { let e2 = CJust(Box::new(e1)); let e3 = Unwrap(Box::new(e2)); - assert_eq!(check_expr(e3, &env), Ok(TInteger)); + let result = check_expr(e3, &env); + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + + assert_eq!(typed_expr.type_info(), &Type::TInteger); + + assert!(matches!(typed_expr, TypedExpression::Unwrap { .. })); } #[test] @@ -466,22 +800,39 @@ mod tests { #[test] fn check_unwrap_result() { - let env = Environment::new(); - let e1 = CTrue; - let e2 = COk(Box::new(e1)); - let e3 = Unwrap(Box::new(e2)); + let env: Environment = Environment::new(); + let e1: Expression = CTrue; + let e2: Expression = COk(Box::new(e1)); + let e3: Expression = Unwrap(Box::new(e2)); + + + let result = check_expr(e3, &env); - assert_eq!(check_expr(e3, &env), Ok(TBool)); + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + + assert_eq!(typed_expr.type_info(), &Type::TBool); + + assert!(matches!(typed_expr, TypedExpression::Unwrap { .. })); } #[test] fn check_propagate_maybe() { - let env = Environment::new(); - let e1 = CInt(5); - let e2 = CJust(Box::new(e1)); - let e3 = Propagate(Box::new(e2)); + + let env: Environment = Environment::new(); + let e1: Expression = CInt(5); + let e2: Expression = CJust(Box::new(e1)); + let e3: Expression = Propagate(Box::new(e2)); + + let result = check_expr(e3, &env); + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + + assert_eq!(typed_expr.type_info(), &Type::TInteger); - assert_eq!(check_expr(e3, &env), Ok(TInteger)); + assert!(matches!(typed_expr, TypedExpression::Propagate { .. })); } #[test] @@ -498,12 +849,20 @@ mod tests { #[test] fn check_propagate_result() { - let env = Environment::new(); - let e1 = CTrue; - let e2 = COk(Box::new(e1)); - let e3 = Propagate(Box::new(e2)); + let env: Environment = Environment::new(); + let e1: Expression = CTrue; + let e2: Expression = COk(Box::new(e1)); + let e3: Expression = Propagate(Box::new(e2)); + + let result = check_expr(e3, &env); + + assert!(result.is_ok()); + + let typed_expr = result.unwrap(); + + assert_eq!(typed_expr.type_info(), &Type::TBool); - assert_eq!(check_expr(e3, &env), Ok(TBool)); + assert!(matches!(typed_expr, TypedExpression::Propagate { .. })); } #[test] @@ -511,35 +870,22 @@ mod tests { let env = Environment::new(); let exp = Expression::Var("x".to_string()); - // Should fail - x is not defined assert!(check_expr(exp, &env).is_err()); } #[test] - fn test_defined_variable() { - let mut env = Environment::new(); + fn test_defined_variable_typed() { + let mut env: Environment = Environment::new(); env.map_variable("x".to_string(), true, Type::TInteger); let exp = Expression::Var("x".to_string()); - // Should succeed and return integer type - assert_eq!(check_expr(exp, &env), Ok(Type::TInteger)); - } + let result = check_expr(exp, &env); + assert!(result.is_ok()); - #[test] - fn test_adt_constructor_valid() { - let mut env = Environment::new(); - let figure_type = vec![ - ValueConstructor::new("Circle".to_string(), vec![Type::TInteger]), - ValueConstructor::new( - "Rectangle".to_string(), - vec![Type::TInteger, Type::TInteger], - ), - ]; - env.map_adt("Figure".to_string(), figure_type); + let typed_expr = result.unwrap(); + assert_eq!(typed_expr.type_info(), &Type::TInteger); - let circle = Constructor("Circle".to_string(), vec![Box::new(CInt(5))]); - let result = check_expr(circle, &env); - assert!(result.is_ok()); + assert!(matches!(typed_expr, TypedExpression::Var { .. })); } #[test] @@ -574,39 +920,71 @@ mod tests { ]; env.map_adt("Figure".to_string(), figure_type); - let rectangle = Constructor("Rectangle".to_string(), vec![Box::new(CInt(5))]); // Missing second argument + let rectangle = Constructor("Rectangle".to_string(), vec![Box::new(CInt(5))]); let result = check_expr(rectangle, &env); assert!(result.is_err()); } #[test] - fn test_adt_constructor_undefined() { - let env = Environment::new(); + fn test_adt_constructor_valid() { + + let mut env = Environment::new(); + let figure_constructors = vec![ + ValueConstructor::new("Circle".to_string(), vec![Type::TInteger]), + ValueConstructor::new( + "Rectangle".to_string(), + vec![Type::TInteger, Type::TInteger], + ), + ]; + env.map_adt("Figure".to_string(), figure_constructors.clone()); + let circle = Constructor("Circle".to_string(), vec![Box::new(CInt(5))]); + + let result = check_expr(circle, &env); - assert!(result.is_err()); + + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + + + assert_eq!( + typed_expr.type_info(), + &Type::TAlgebraicData("Figure".to_string(), figure_constructors) + ); + + + assert!(matches!(typed_expr, TypedExpression::Constructor { .. })); } #[test] fn test_adt_constructor_with_mutable_vars() { let mut env = Environment::new(); - let figure_type = vec![ + let figure_constructors = vec![ ValueConstructor::new("Circle".to_string(), vec![Type::TInteger]), ValueConstructor::new( "Rectangle".to_string(), vec![Type::TInteger, Type::TInteger], ), ]; - env.map_adt("Figure".to_string(), figure_type); - - // Create a mutable variable to use in constructor + env.map_adt("Figure".to_string(), figure_constructors.clone()); env.map_variable("radius".to_string(), true, Type::TInteger); let circle = Constructor( "Circle".to_string(), vec![Box::new(Var("radius".to_string()))], ); + let result = check_expr(circle, &env); - assert!(result.is_ok()); + + assert!(result.is_ok()); + let typed_expr = result.unwrap(); + + assert_eq!( + typed_expr.type_info(), + &Type::TAlgebraicData("Figure".to_string(), figure_constructors) + ); + + assert!(matches!(typed_expr, TypedExpression::Constructor { .. })); } } diff --git a/src/type_checker/statement_type_checker.rs b/src/type_checker/statement_type_checker.rs index 5c6835e..a058a29 100644 --- a/src/type_checker/statement_type_checker.rs +++ b/src/type_checker/statement_type_checker.rs @@ -1,234 +1,190 @@ use crate::environment::environment::Environment; -use crate::ir::ast::{Expression, Function, Name, Statement, Type, ValueConstructor}; +use crate::ir::ast::{Expression, Statement, Type}; // Não remover Expression use crate::type_checker::expression_type_checker::check_expr; +use crate::ir::typed_ast::{TypedStatement, TypedFunction}; type ErrorMessage = String; pub fn check_stmt( stmt: Statement, env: &Environment, -) -> Result, ErrorMessage> { +) -> Result<(TypedStatement,Environment), ErrorMessage> { match stmt { - Statement::VarDeclaration(var, expr) => check_var_declaration_stmt(var, expr, env), - Statement::ValDeclaration(var, expr) => check_val_declaration_stmt(var, expr, env), - Statement::Sequence(stmt1, stmt2) => check_squence_stmt(stmt1, stmt2, env), - Statement::Assignment(name, exp) => check_assignment_stmt(name, exp, env), - Statement::IfThenElse(cond, stmt_then, stmt_else_opt) => { - check_if_then_else_stmt(cond, stmt_then, stmt_else_opt, env) - } - Statement::While(cond, stmt) => check_while_stmt(cond, stmt, env), - Statement::For(var, expr, stmt) => check_for_stmt(var, expr, stmt, env), - Statement::FuncDef(function) => check_func_def_stmt(function, env), - Statement::TypeDeclaration(name, cons) => check_adt_declarations_stmt(name, cons, env), - Statement::Return(exp) => check_return_stmt(exp, env), - _ => Err("Not implemented yet".to_string()), - } -} - -fn check_squence_stmt( - stmt1: Box, - stmt2: Box, - env: &Environment, -) -> Result, ErrorMessage> { - let new_env = check_stmt(*stmt1, &env)?; - check_stmt(*stmt2, &new_env) -} - -fn check_assignment_stmt( - name: Name, - exp: Box, - env: &Environment, -) -> Result, ErrorMessage> { - let mut new_env = env.clone(); - let exp_type = check_expr(*exp, &new_env)?; - - match new_env.lookup(&name) { - Some((mutable, var_type)) => { - if !mutable { - Err(format!("[Type Error] cannot reassign '{:?}' variable, since it was declared as a constant value.", name)) - } else if var_type == Type::TAny { - new_env.map_variable(name.clone(), true, exp_type); - Ok(new_env) - } else if var_type == exp_type { - Ok(new_env) + Statement::VarDeclaration(var, expr) => { + let mut new_env = env.clone(); + + let typed_expr = check_expr(*expr, &new_env)?; + + let var_type = typed_expr.type_info().clone(); + + if new_env.lookup(&var).is_none() { + new_env.map_variable(var.clone(), true, var_type); + + let typed_stmt = TypedStatement::VarDeclaration { + name: var, + value: Box::new(typed_expr), + }; + + Ok((typed_stmt, new_env)) } else { - Err(format!( - "[Type Error] expected '{:?}', found '{:?}'.", - var_type, exp_type - )) + Err(format!("[Type Error] variable '{:?}' already declared", var)) } - } - None => Err(format!("[Type Error] variable '{:?}' not declared.", name)), - } -} - -fn check_var_declaration_stmt( - name: Name, - exp: Box, - env: &Environment, -) -> Result, ErrorMessage> { - let mut new_env = env.clone(); - let var_type = new_env.lookup(&name); - let exp_type = check_expr(*exp, &new_env)?; - - if var_type.is_none() { - new_env.map_variable(name.clone(), true, exp_type); - Ok(new_env) - } else { - Err(format!( - "[Type Error] variable '{:?}' already declared", - name - )) - } -} + }, + Statement::ValDeclaration(var, expr) => { + let mut new_env = env.clone(); + let typed_expr = check_expr(*expr, &new_env)?; + let var_type = typed_expr.type_info().clone(); + + if new_env.lookup(&var).is_none() { + new_env.map_variable(var.clone(), false, var_type); + let typed_stmt = TypedStatement::ValDeclaration { + name: var, + value: Box::new(typed_expr), + }; + Ok((typed_stmt, new_env)) + } else { + Err(format!("[Type Error] variable '{:?}' already declared", var)) + } + }, + + Statement::Sequence(stmt1, stmt2) => { + let (typed_stmt1, env1) = check_stmt(*stmt1, env)?; + let (typed_stmt2, env2) = check_stmt(*stmt2, &env1)?; + + let typed_sequence = TypedStatement::Sequence { + first: Box::new(typed_stmt1), + second: Box::new(typed_stmt2), + }; + Ok((typed_sequence, env2)) + }, + + Statement::Assignment(name, exp) => { + let mut new_env = env.clone(); + let typed_exp = check_expr(*exp, &new_env)?; + let exp_type = typed_exp.type_info(); + + match new_env.lookup(&name) { + Some((mutable, var_type)) => { + if !mutable { + return Err(format!("[Type Error] cannot reassign '{:?}' variable, since it was declared as a constant value.", name)); + } + if var_type == Type::TAny || &var_type == exp_type { + let final_type = if var_type == Type::TAny { exp_type.clone() } else { var_type }; + new_env.map_variable(name.clone(), true, final_type); + let typed_stmt = TypedStatement::Assignment { name, value: Box::new(typed_exp) }; + Ok((typed_stmt, new_env)) + } else { + Err(format!("[Type Error] expected '{:?}', found '{:?}'.", var_type, exp_type)) + } + } + None => Err(format!("[Type Error] variable '{:?}' not declared.", name)), + } + }, -fn check_val_declaration_stmt( - name: Name, - exp: Box, - env: &Environment, -) -> Result, ErrorMessage> { - let mut new_env = env.clone(); - let var_type = new_env.lookup(&name); - let exp_type = check_expr(*exp, &new_env)?; - - if var_type.is_none() { - new_env.map_variable(name.clone(), false, exp_type); - Ok(new_env) - } else { - Err(format!( - "[Type Error] variable '{:?}' already declared", - name - )) - } -} + Statement::IfThenElse(cond, stmt_then, stmt_else_opt) => { + let typed_cond = check_expr(*cond, env)?; + if typed_cond.type_info() != &Type::TBool { + return Err("[Type Error] 'if' condition must be boolean.".to_string()); + } -fn check_if_then_else_stmt( - cond: Box, - stmt_then: Box, - stmt_else_opt: Option>, - env: &Environment, -) -> Result, ErrorMessage> { - let mut new_env = env.clone(); - let cond_type = check_expr(*cond, &new_env)?; - if cond_type != Type::TBool { - return Err( - "[Type Error] a condition in a 'if' statement must be of type boolean.".to_string(), - ); - } - let then_env = check_stmt(*stmt_then, &new_env)?; - if let Some(stmt_else) = stmt_else_opt { - let else_env = check_stmt(*stmt_else, &new_env)?; - new_env = merge_environments(&then_env, &else_env)?; - } else { - new_env = merge_environments(&new_env, &then_env)?; - } - Ok(new_env) -} + let (typed_then_stmt, then_env) = check_stmt(*stmt_then, env)?; + + if let Some(stmt_else) = stmt_else_opt { + let (typed_else_stmt, else_env) = check_stmt(*stmt_else, env)?; + let final_env = merge_environments(&then_env, &else_env)?; + let typed_stmt = TypedStatement::IfThenElse { + cond: Box::new(typed_cond), + stmt_then: Box::new(typed_then_stmt), + stmt_else_opt: Some(Box::new(typed_else_stmt)), + }; + Ok((typed_stmt, final_env)) + } else { + let final_env = merge_environments(env, &then_env)?; + let typed_stmt = TypedStatement::IfThenElse { + cond: Box::new(typed_cond), + stmt_then: Box::new(typed_then_stmt), + stmt_else_opt: None, + }; + Ok((typed_stmt, final_env)) + } + }, -fn check_while_stmt( - cond: Box, - stmt: Box, - env: &Environment, -) -> Result, ErrorMessage> { - let mut new_env = env.clone(); - let cond_type = check_expr(*cond, &new_env)?; - if cond_type != Type::TBool { - return Err( - "[Type Error] a condition in a 'while' statement must be of type boolean.".to_string(), - ); - } - new_env = check_stmt(*stmt, &new_env)?; - Ok(new_env) -} + Statement::While(cond, stmt) => { + let typed_cond = check_expr(*cond, env)?; + if typed_cond.type_info() != &Type::TBool { + return Err("[Type Error] 'while' condition must be boolean.".to_string()); + } -fn check_for_stmt( - var: Name, - expr: Box, - stmt: Box, - env: &Environment, -) -> Result, ErrorMessage> { - let mut new_env = env.clone(); - let _var_type = env.lookup(&var); - let expr_type = check_expr(*expr, &new_env)?; - match expr_type { - Type::TList(base_type) => { - if let Some((_, t)) = env.lookup(&var) { - if t == *base_type || *base_type == Type::TAny { - new_env = check_stmt(*stmt, &new_env)?; - return Ok(new_env); - } else { - return Err(format!( - "[TypeError] Type mismatch between {:?} and {:?}", - t, base_type - )); - } + let (typed_body, final_env) = check_stmt(*stmt, env)?; + let typed_stmt = TypedStatement::While { + cond: Box::new(typed_cond), + body: Box::new(typed_body), + }; + Ok((typed_stmt, final_env)) + }, + + Statement::For(var, expr, stmt) => { + let typed_iterable = check_expr(*expr, env)?; + + if let Type::TList(base_type) = typed_iterable.type_info() { + let mut loop_env = env.clone(); + loop_env.push(); + loop_env.map_variable(var.clone(), false, *base_type.clone()); + + let (typed_body, _) = check_stmt(*stmt, &loop_env)?; + loop_env.pop(); + + let typed_stmt = TypedStatement::For { + name: var, + iterable: Box::new(typed_iterable), + body: Box::new(typed_body), + }; + + Ok((typed_stmt, env.clone())) } else { - new_env.map_variable(var.clone(), false, *base_type); - new_env = check_stmt(*stmt, &new_env)?; - return Ok(new_env); + Err(format!("[TypeError] Expecting a List type in 'for' loop, but found a {:?}.", typed_iterable.type_info())) } - } - _ => { - return Err(format!( - "[TypeError] Expecting a List type, but found a {:?}", - expr_type - )) - } - } -} - -fn check_func_def_stmt( - function: Function, - env: &Environment, -) -> Result, ErrorMessage> { - let mut new_env = env.clone(); - new_env.push(); - - for formal_arg in function.params.iter() { - new_env.map_variable( - formal_arg.argument_name.clone(), - false, - formal_arg.argument_type.clone(), - ); - } + }, + + Statement::FuncDef(function) => { + let mut new_env = env.clone(); + + new_env.map_function(function.clone()); + + + let typed_func = TypedFunction { + name: function.name, + kind: function.kind, + params: function.params, + body: None, + }; + + let typed_stmt = TypedStatement::FuncDef(typed_func); + Ok((typed_stmt, new_env)) + }, + + Statement::TypeDeclaration(name, cons) => { + let mut new_env = env.clone(); + new_env.map_adt(name.clone(), cons.clone()); + let typed_stmt = TypedStatement::TypeDeclaration(name, cons); + Ok((typed_stmt, new_env)) + }, + + Statement::Return(exp) => { + let typed_exp = check_expr(*exp, env)?; + let mut new_env = env.clone(); + + new_env.map_variable("return".to_string(), false, typed_exp.type_info().clone()); + + let typed_stmt = TypedStatement::Return(Box::new(typed_exp)); + Ok((typed_stmt, new_env)) + }, - if let Some(body) = function.body.clone() { - new_env = check_stmt(*body, &new_env)?; + _ => Err("Not implemented yet".to_string()), } - new_env.pop(); - new_env.map_function(function); - - Ok(new_env) -} - -fn check_adt_declarations_stmt( - name: Name, - cons: Vec, - env: &Environment, -) -> Result, ErrorMessage> { - let mut new_env = env.clone(); - new_env.map_adt(name.clone(), cons); - Ok(new_env) } -fn check_return_stmt( - exp: Box, - env: &Environment, -) -> Result, ErrorMessage> { - let mut new_env = env.clone(); - assert!(new_env.scoped_function()); - - let ret_type = check_expr(*exp, &new_env)?; - - match new_env.lookup(&"return".to_string()) { - Some(_) => Ok(new_env), - None => { - new_env.map_variable("return".to_string(), false, ret_type); - Ok(new_env) - } - } -} fn merge_environments( env1: &Environment, @@ -236,20 +192,14 @@ fn merge_environments( ) -> Result, ErrorMessage> { let mut merged = env1.clone(); - // Get all variables defined in either environment for (name, (mutable2, type2)) in env2.get_all_variables() { match env1.lookup(&name) { Some((mutable1, type1)) => { - // Variable exists in both branches - // Check mutability first - if either is constant, result must be constant let final_mutable = mutable1 && mutable2; - // Then check types if type1 == Type::TAny { - // If type1 is TAny, use type2 merged.map_variable(name.clone(), final_mutable, type2.clone()); } else if type2 == Type::TAny { - // If type2 is TAny, keep type1 merged.map_variable(name.clone(), final_mutable, type1.clone()); } else if type1 != type2 { return Err(format!( @@ -257,12 +207,10 @@ fn merge_environments( name, type1, type2 )); } else { - // Types match, update with combined mutability merged.map_variable(name.clone(), final_mutable, type1.clone()); } } None => { - // Variable only exists in else branch - it's conditionally defined merged.map_variable(name.clone(), mutable2, type2.clone()); } } @@ -282,41 +230,42 @@ mod tests { use crate::ir::ast::Function; use crate::ir::ast::Statement::*; use crate::ir::ast::Type; + use crate::ir::typed_ast::{TypedStatement}; + #[test] fn check_assignment() { - let env: Environment = Environment::new(); - // Declare variable 'a' first - let env = check_stmt( - Statement::VarDeclaration("a".to_string(), Box::new(CTrue)), + let env = Environment::new(); + let (_, env_after_decl) = check_stmt( + Statement::VarDeclaration("a".to_string(), Box::new(Expression::CTrue)), &env, - ) - .unwrap(); - let assignment = Assignment("a".to_string(), Box::new(CTrue)); + ).unwrap(); - match check_stmt(assignment, &env) { - Ok(_) => assert!(true), - Err(s) => assert!(false, "{}", s), - } + let assignment_stmt = Statement::Assignment("a".to_string(), Box::new(Expression::CFalse)); + + let result = check_stmt(assignment_stmt, &env_after_decl); + + assert!(result.is_ok()); + let (typed_stmt, _final_env) = result.unwrap(); + + assert!(matches!(typed_stmt, TypedStatement::Assignment { .. })); } #[test] fn check_assignment_error2() { let env: Environment = Environment::new(); - // Declare variable 'a' first - let env = check_stmt( + + let (_, env_after_decl) = check_stmt( Statement::VarDeclaration("a".to_string(), Box::new(CTrue)), &env, ) .unwrap(); + let assignment1 = Assignment("a".to_string(), Box::new(CTrue)); let assignment2 = Assignment("a".to_string(), Box::new(CInt(1))); let program = Sequence(Box::new(assignment1), Box::new(assignment2)); - assert!( - matches!(check_stmt(program, &env), Err(_)), - "[Type Error on '__main__()'] 'a' has mismatched types: expected 'TBool', found 'TInteger'." - ); + assert!(check_stmt(program, &env_after_decl).is_err()); } #[test] @@ -363,7 +312,6 @@ mod tests { #[ignore = "not yet implemented"] fn check_func_def() { let env: Environment = Environment::new(); - let func = FuncDef(Function { name: "add".to_string(), kind: Type::TInteger, @@ -376,22 +324,28 @@ mod tests { Box::new(Var("b".to_string())), ))))), }); - match check_stmt(func, &env) { - Ok(_) => assert!(true), - Err(s) => assert!(false, "{}", s), - } + + let result = check_stmt(func, &env); + + assert!(result.is_ok()); + let (typed_stmt, new_env) = result.unwrap(); + + assert!(matches!(typed_stmt, TypedStatement::FuncDef(..))); + + assert!(new_env.lookup_function(&"add".to_string()).is_some()); } #[test] fn test_if_else_consistent_types() { let env = Environment::new(); - // Declare variable 'x' first - let env = check_stmt( + + let (_, env_after_decl) = check_stmt( Statement::VarDeclaration("x".to_string(), Box::new(Expression::CInt(0))), &env, ) .unwrap(); - let stmt = Statement::IfThenElse( + + let if_stmt = Statement::IfThenElse( Box::new(Expression::CTrue), Box::new(Statement::Assignment( "x".to_string(), @@ -403,8 +357,17 @@ mod tests { ))), ); - // Should succeed - x is consistently an integer in both branches - assert!(check_stmt(stmt, &env).is_ok()); + let result = check_stmt(if_stmt, &env_after_decl); + + assert!(result.is_ok()); + let (typed_stmt, final_env) = result.unwrap(); + + assert!(matches!(typed_stmt, TypedStatement::IfThenElse { .. })); + + assert_eq!( + final_env.lookup(&"x".to_string()), + Some((true, Type::TInteger)) + ); } #[test] @@ -422,19 +385,19 @@ mod tests { ))), ); - // Should fail - x has different types in different branches assert!(check_stmt(stmt, &env).is_err()); } - #[test] + #[test] fn test_if_else_partial_definition() { let env = Environment::new(); - // Declare variable 'x' first - let env = check_stmt( + + let (_, env_after_decl) = check_stmt( Statement::VarDeclaration("x".to_string(), Box::new(Expression::CInt(0))), &env, ) .unwrap(); + let stmt = Statement::Sequence( Box::new(Statement::IfThenElse( Box::new(Expression::CTrue), @@ -442,7 +405,7 @@ mod tests { "x".to_string(), Box::new(Expression::CInt(1)), )), - None, + None, )), Box::new(Statement::Assignment( "x".to_string(), @@ -450,26 +413,39 @@ mod tests { )), ); - // Should succeed - x is conditionally defined in then branch - // and later used consistently as an integer - assert!(check_stmt(stmt, &env).is_ok()); + let result = check_stmt(stmt, &env_after_decl); + + assert!(result.is_ok()); + let (_typed_stmt, final_env) = result.unwrap(); + + assert_eq!( + final_env.lookup(&"x".to_string()), + Some((true, Type::TInteger)) + ); } #[test] fn test_variable_assignment() { let env = Environment::new(); - // Declare variable 'x' first - let env = check_stmt( + + let (_, env_after_decl) = check_stmt( Statement::VarDeclaration("x".to_string(), Box::new(Expression::CInt(0))), &env, ) .unwrap(); + let stmt = Statement::Assignment("x".to_string(), Box::new(Expression::CInt(42))); - // Should succeed and add x:integer to environment - let new_env = check_stmt(stmt, &env).unwrap(); + let result = check_stmt(stmt, &env_after_decl); + + assert!(result.is_ok()); + + let (typed_stmt, final_env) = result.unwrap(); + + assert!(matches!(typed_stmt, TypedStatement::Assignment { .. })); + assert_eq!( - new_env.lookup(&"x".to_string()), + final_env.lookup(&"x".to_string()), Some((true, Type::TInteger)) ); } @@ -478,11 +454,14 @@ mod tests { fn test_variable_reassignment_same_type() { let mut env = Environment::new(); env.map_variable("x".to_string(), true, Type::TInteger); - let stmt = Statement::Assignment("x".to_string(), Box::new(Expression::CInt(100))); - // Should succeed - reassigning same type - assert!(check_stmt(stmt, &env).is_ok()); + let result = check_stmt(stmt, &env); + + assert!(result.is_ok()); + + let (typed_stmt, _new_env) = result.unwrap(); + assert!(matches!(typed_stmt, TypedStatement::Assignment { .. })); } #[test] @@ -495,7 +474,6 @@ mod tests { Box::new(Expression::CString("hello".to_string())), ); - // Should fail - trying to reassign different type assert!(check_stmt(stmt, &env).is_err()); } @@ -517,12 +495,11 @@ mod tests { body: None, }; - // Test function scoping env.map_function(global_func.clone()); assert!(env.lookup_function(&"global".to_string()).is_some()); } - #[test] + #[test] fn test_for_valid_integer_list() { let mut env = Environment::new(); env.map_variable("sum".to_string(), true, Type::TInteger); @@ -541,7 +518,13 @@ mod tests { )), )), ); - assert!(check_stmt(stmt, &env).is_ok()); + + let result = check_stmt(stmt, &env); + + assert!(result.is_ok()); + + let (typed_stmt, _final_env) = result.unwrap(); + assert!(matches!(typed_stmt, TypedStatement::For { .. })); } #[test] @@ -559,29 +542,40 @@ mod tests { Box::new(Expression::CInt(1)), )), ); - // Should fail - list contains mixed types (integers and strings) assert!(check_stmt(stmt, &env).is_err()); } #[test] fn test_for_empty_list() { let env = Environment::new(); - // Declare variable 'x' first - let env = check_stmt( + let (_, env_after_decl) = check_stmt( Statement::VarDeclaration("x".to_string(), Box::new(Expression::CInt(0))), &env, ) .unwrap(); + let stmt = Statement::For( "x".to_string(), Box::new(Expression::ListValue(vec![])), - Box::new(Statement::Assignment( - "x".to_string(), - Box::new(Expression::CInt(1)), + Box::new(Statement::VarDeclaration( + "y".to_string(), + Box::new(Expression::Var("x".to_string())), )), ); - // Should succeed - empty list is valid, though no iterations will occur - assert!(check_stmt(stmt, &env).is_ok()); + + let result = check_stmt(stmt, &env_after_decl); + + assert!(result.is_ok()); + + let (typed_stmt, _final_env) = result.unwrap(); + if let TypedStatement::For { iterable, .. } = typed_stmt { + assert_eq!( + iterable.type_info(), + &Type::TList(Box::new(Type::TAny)) + ); + } else { + panic!("Expected a TypedStatement::For node."); + } } #[test] @@ -598,19 +592,19 @@ mod tests { Box::new(Expression::CString("invalid".to_string())), )), ); - // Should fail - trying to assign string to iterator variable when iterating over integers assert!(check_stmt(stmt, &env).is_err()); } #[test] fn test_for_nested_loops() { let env = Environment::new(); - // Declare variable 'sum' first - let env = check_stmt( + + let (_, env_after_decl) = check_stmt( Statement::VarDeclaration("sum".to_string(), Box::new(Expression::CInt(0))), &env, ) .unwrap(); + let stmt = Statement::For( "i".to_string(), Box::new(Expression::ListValue(vec![ @@ -633,17 +627,26 @@ mod tests { )), ); - // Should succeed - nested loops with proper variable usage - assert!(check_stmt(stmt, &env).is_ok()); + let result = check_stmt(stmt, &env_after_decl); + + assert!(result.is_ok()); + let (typed_stmt, final_env) = result.unwrap(); + + assert!(matches!(typed_stmt, TypedStatement::For { .. })); + + assert_eq!( + final_env.lookup(&"sum".to_string()), + Some((true, Type::TInteger)) +); } #[test] fn test_for_variable_scope() { let mut env = Environment::new(); - env.map_variable("x".to_string(), true, Type::TString); // x is defined as string in outer scope + env.map_variable("x".to_string(), true, Type::TString); let stmt = Statement::For( - "x".to_string(), // reusing name x as iterator + "x".to_string(), Box::new(Expression::ListValue(vec![ Expression::CInt(1), Expression::CInt(2), @@ -654,7 +657,6 @@ mod tests { )), ); - // Should not succeed - for loop creates new scope, x is temporarily an integer // TODO: Let discuss this case here next class. assert!(check_stmt(stmt, &env).is_err()); }