From d28a4659af50e210a694221b43595956773ed9fc Mon Sep 17 00:00:00 2001 From: Arthur Luiz <132619258+The3rdMega@users.noreply.github.com> Date: Fri, 20 Jun 2025 12:24:37 -0300 Subject: [PATCH 01/19] =?UTF-8?q?Adicionar=20parser=20para=20fun=C3=A7?= =?UTF-8?q?=C3=B5es=20de=20teste?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Este commit adiciona a função parse_test_function_definition_statement, que realiza o parser de TestDef em r-python, bem como testes para garantir que o parser funcione corretamente. Este commit também altera a função parse_function_definition_statement, para rejeitar funções cujo identificador se inicia com "test", também possuindo um teste para realizar esta checagem. Observações: - Parser de Declaração de Funções comuns está quebrado, isso dado a função keyword, que está consumindo o espaço após de KEYWORD_DEF e assumindo que def é uma palavra maior, quebrando parsers que utilizam a keyword def. --- src/parser/parser_stmt.rs | 138 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 131 insertions(+), 7 deletions(-) diff --git a/src/parser/parser_stmt.rs b/src/parser/parser_stmt.rs index bdcebdb..c5aced1 100644 --- a/src/parser/parser_stmt.rs +++ b/src/parser/parser_stmt.rs @@ -2,8 +2,8 @@ use nom::{ branch::alt, bytes::complete::tag, character::complete::{char, multispace0, multispace1}, - combinator::{map, opt}, - error::Error, + combinator::{map, opt, map_res}, + error::{Error, ErrorKind}, multi::separated_list0, sequence::{delimited, preceded, tuple}, IResult, @@ -17,6 +17,7 @@ use crate::parser::parser_common::{ }; use crate::parser::parser_expr::parse_expression; use crate::parser::parser_type::parse_type; +use crate::ir::ast::Type; // TODO: Check if needed pub fn parse_statement(input: &str) -> IResult<&str, Statement> { alt(( @@ -27,6 +28,7 @@ pub fn parse_statement(input: &str) -> IResult<&str, Statement> { parse_while_statement, parse_for_statement, parse_assert_statement, + parse_test_function_definition_statement, // FIXME: Being Implemented parse_function_definition_statement, ))(input) } @@ -150,7 +152,7 @@ fn parse_assert_statement(input: &str) -> IResult<&str, Statement> { } fn parse_function_definition_statement(input: &str) -> IResult<&str, Statement> { - map( + map_res( tuple(( keyword(DEF_KEYWORD), preceded(multispace1, identifier), @@ -171,12 +173,59 @@ fn parse_function_definition_statement(input: &str) -> IResult<&str, Statement> parse_block, )), |(_, name, args, _, t, block)| { - Statement::FuncDef(Function { + if name.starts_with("test") { + // Força falha aqui para evitar aceitar funções test_ no parser geral + Err(nom::Err::Error(Error::new(name, ErrorKind::Tag))) + } else { + Ok(Statement::FuncDef(Function { + name: name.to_string(), + kind: t, + params: args, + body: Some(Box::new(block)), + })) + } + }, + )(input) +} + + +// TODO: Fazer testes para essa função de parser +fn parse_test_function_definition_statement(input: &str) -> IResult<&str, Statement> { + map_res( + tuple(( + //keyword(DEF_KEYWORD), + tag("def"), + preceded(multispace1, identifier), + //identifier, + delimited( + char::<&str, Error<&str>>(LEFT_PAREN), + multispace0, // Permite `()` com espaços, mas sem argumentos + char::<&str, Error<&str>>(RIGHT_PAREN), + ), + opt(preceded( + preceded(multispace0, tag("->")), + preceded(multispace0, parse_type), + )), + parse_block, + )), + |(_, name, _,opt_ret_type, block)| { + if !name.starts_with("test") { + return Err(nom::Err::Error(Error::new(name, ErrorKind::Tag))); + } + + // Se tiver tipo declarado, só aceita Bool + if let Some(ret_type) = opt_ret_type { + if ret_type != Type::TBool { + return Err(nom::Err::Error(Error::new(name, ErrorKind::Tag))); + } + } + + Ok(Statement::TestDef(Function { name: name.to_string(), - kind: t, - params: args, + kind: Type::TBool, // Sempre bool (mesmo se não declarou) + params: Vec::new(), // Nenhum argumento body: Some(Box::new(block)), - }) + })) }, )(input) } @@ -343,4 +392,79 @@ mod tests { let parsed = parse_formal_argument(input).unwrap().1; assert_eq!(parsed, expected); } + + #[test] + fn test_parse_test_function_definition_statement_valid() { + let input = "def test_example(): x = 1; end"; + let expected = Statement::TestDef(Function { + name: "test_example".to_string(), + kind: Type::TBool, + params: vec![], + body: Some(Box::new(Statement::Block(vec![ + Statement::Assignment("x".to_string(), Box::new(Expression::CInt(1))), + ]))), + }); + let parsed = parse_test_function_definition_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + + #[test] + fn test_parse_test_function_definition_statement_with_spaces() { + let input = "def test_spaces( ): x = 2; end"; + let expected = Statement::TestDef(Function { + name: "test_spaces".to_string(), + kind: Type::TBool, + params: vec![], + body: Some(Box::new(Statement::Block(vec![ + Statement::Assignment("x".to_string(), Box::new(Expression::CInt(2))), + ]))), + }); + let parsed = parse_test_function_definition_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + + #[test] + fn test_parse_test_function_definition_statement_args() { + let input = "def test_with_args(x: Int, y: Int): x = y; end"; + + // O parser deve falhar, pois funções de teste não podem ter argumentos. + let parsed = parse_test_function_definition_statement(input); + + assert!(parsed.is_err(), "Funções de teste com argumentos devem ser rejeitadas"); + } + + #[test] + fn test_parse_test_function_definition_statement_invalid_return_type() { + let input = "def test_with_invalid_return() -> Int: x = 2; end"; + + // O parser deve falhar, pois funções de teste não podem ter argumentos. + let parsed = parse_test_function_definition_statement(input); + + assert!(parsed.is_err(), "Funções de teste não devem especificar tipo de retorno"); + } + + #[test] + fn test_parse_test_function_definition_statement_valid_return_type() { + let input = "def test_with_valid_return() -> Boolean: x = 2; end"; + let expected = Statement::TestDef(Function { + name: "test_with_valid_return".to_string(), + kind: Type::TBool, + params: vec![], + body: Some(Box::new(Statement::Block(vec![ + Statement::Assignment("x".to_string(), Box::new(Expression::CInt(2))), + ]))), + }); + let parsed = parse_test_function_definition_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + #[test] + #[ignore] + fn test_parse_function_definition_statement_reject_test_function() { + let input = "def test_function_rejected() -> Int: x = 2; end"; + + let parsed = parse_function_definition_statement(input); + assert!(parsed.is_err(), "Funções comuns não devem iniciar com 'test'"); + } + + } From 00400baf9da1ffa215c279443c848a00a7e7acf6 Mon Sep 17 00:00:00 2001 From: Arthur Luiz <132619258+The3rdMega@users.noreply.github.com> Date: Fri, 20 Jun 2025 12:37:24 -0300 Subject: [PATCH 02/19] =?UTF-8?q?Corrigido=20retorno=20de=20Erro=20nas=20f?= =?UTF-8?q?un=C3=A7=C3=B5es=20de=20parser=20de=20fun=C3=A7=C3=B5es=20comun?= =?UTF-8?q?s=20e=20fun=C3=A7=C3=B5es=20de=20teste?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agora retornamos Error ao invés de Error, para retornar no erro a string original de texto no parser que deu erro --- src/parser/parser_stmt.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/parser/parser_stmt.rs b/src/parser/parser_stmt.rs index c5aced1..44c703c 100644 --- a/src/parser/parser_stmt.rs +++ b/src/parser/parser_stmt.rs @@ -175,7 +175,7 @@ fn parse_function_definition_statement(input: &str) -> IResult<&str, Statement> |(_, name, args, _, t, block)| { if name.starts_with("test") { // Força falha aqui para evitar aceitar funções test_ no parser geral - Err(nom::Err::Error(Error::new(name, ErrorKind::Tag))) + Err(nom::Err::Error(Error::new(input, ErrorKind::Tag))) } else { Ok(Statement::FuncDef(Function { name: name.to_string(), @@ -210,13 +210,13 @@ fn parse_test_function_definition_statement(input: &str) -> IResult<&str, Statem )), |(_, name, _,opt_ret_type, block)| { if !name.starts_with("test") { - return Err(nom::Err::Error(Error::new(name, ErrorKind::Tag))); + return Err(nom::Err::Error(Error::new(input, ErrorKind::Tag))); } // Se tiver tipo declarado, só aceita Bool if let Some(ret_type) = opt_ret_type { if ret_type != Type::TBool { - return Err(nom::Err::Error(Error::new(name, ErrorKind::Tag))); + return Err(nom::Err::Error(Error::new(input, ErrorKind::Tag))); } } From 040373c34a755611be718301b3571650cd0b22fe Mon Sep 17 00:00:00 2001 From: Arthur Luiz <132619258+The3rdMega@users.noreply.github.com> Date: Fri, 20 Jun 2025 13:47:05 -0300 Subject: [PATCH 03/19] Commit de teste do terminal --- src/parser/parser_stmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/parser_stmt.rs b/src/parser/parser_stmt.rs index 44c703c..d4a2c76 100644 --- a/src/parser/parser_stmt.rs +++ b/src/parser/parser_stmt.rs @@ -465,6 +465,6 @@ mod tests { let parsed = parse_function_definition_statement(input); assert!(parsed.is_err(), "Funções comuns não devem iniciar com 'test'"); } - + //FIXME: Mudança de teste para testar o commit do github MUDANÇA } From 4a1af4dacfb8c1fd6ca324f6645d0b35d04fc56d Mon Sep 17 00:00:00 2001 From: The3rdMega <132619258+The3rdMega@users.noreply.github.com> Date: Tue, 24 Jun 2025 09:12:16 -0300 Subject: [PATCH 04/19] Alterado Parser de Test Functions e Implementado Parser de Asserts Ainda falta implementar testes para os Asserts --- src/ir/ast.rs | 10 +- src/parser/parser_common.rs | 4 + src/parser/parser_stmt.rs | 253 ++++++++++++++++++++++++------------ 3 files changed, 181 insertions(+), 86 deletions(-) diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 1172bb8..79e487d 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -134,11 +134,11 @@ pub enum Statement { For(Name, Box, Box), Block(Vec), Sequence(Box, Box), - Assert(Box, Box), - AssertTrue(Box, String), - AssertFalse(Box, String), - AssertEQ(Box, Box, String), - AssertNEQ(Box, Box, String), + Assert(Box, Box), //Segundo expression deve ser String + AssertTrue(Box, Box), //Segundo expression deve ser String + AssertFalse(Box, Box), //Segundo expression deve ser String + AssertEQ(Box, Box, Box), //Terceiro expression deve ser String + AssertNEQ(Box, Box, Box), //Terceiro expression deve ser String TestDef(Function), ModTestDef(Name, Box), AssertFails(String), diff --git a/src/parser/parser_common.rs b/src/parser/parser_common.rs index 065a742..1d239ea 100644 --- a/src/parser/parser_common.rs +++ b/src/parser/parser_common.rs @@ -33,6 +33,10 @@ pub const WHILE_KEYWORD: &str = "while"; pub const FOR_KEYWORD: &str = "for"; pub const IN_KEYWORD: &str = "in"; pub const ASSERT_KEYWORD: &str = "assert"; +pub const ASSERTEQ_KEYWORD: &str = "asserteq"; +pub const ASSERTNEQ_KEYWORD: &str = "assertneq"; +pub const ASSERTTRUE_KEYWORD: &str = "asserttrue"; +pub const ASSERTFALSE_KEYWORD: &str = "assertfalse"; pub const VAR_KEYWORD: &str = "var"; pub const VAL_KEYWORD: &str = "val"; pub const DEF_KEYWORD: &str = "def"; diff --git a/src/parser/parser_stmt.rs b/src/parser/parser_stmt.rs index d4a2c76..00df98a 100644 --- a/src/parser/parser_stmt.rs +++ b/src/parser/parser_stmt.rs @@ -2,22 +2,23 @@ use nom::{ branch::alt, bytes::complete::tag, character::complete::{char, multispace0, multispace1}, - combinator::{map, opt, map_res}, - error::{Error, ErrorKind}, + combinator::{map, opt}, + error::Error, multi::separated_list0, sequence::{delimited, preceded, tuple}, IResult, }; +use crate::ir::ast::Type; use crate::ir::ast::{FormalArgument, Function, Statement}; use crate::parser::parser_common::{ - identifier, keyword, ASSERT_KEYWORD, COLON_CHAR, COMMA_CHAR, DEF_KEYWORD, ELSE_KEYWORD, + identifier, keyword, ASSERTEQ_KEYWORD, ASSERTFALSE_KEYWORD, ASSERTNEQ_KEYWORD, + ASSERTTRUE_KEYWORD, ASSERT_KEYWORD, COLON_CHAR, COMMA_CHAR, DEF_KEYWORD, ELSE_KEYWORD, END_KEYWORD, EQUALS_CHAR, FOR_KEYWORD, FUNCTION_ARROW, IF_KEYWORD, IN_KEYWORD, LEFT_PAREN, RIGHT_PAREN, SEMICOLON_CHAR, VAL_KEYWORD, VAR_KEYWORD, WHILE_KEYWORD, }; use crate::parser::parser_expr::parse_expression; -use crate::parser::parser_type::parse_type; -use crate::ir::ast::Type; // TODO: Check if needed +use crate::parser::parser_type::parse_type; // TODO: Check if needed pub fn parse_statement(input: &str) -> IResult<&str, Statement> { alt(( @@ -28,6 +29,10 @@ pub fn parse_statement(input: &str) -> IResult<&str, Statement> { parse_while_statement, parse_for_statement, parse_assert_statement, + parse_asserteq_statement, + parse_assertneq_statement, + parse_assertfalse_statement, + parse_asserttrue_statement, parse_test_function_definition_statement, // FIXME: Being Implemented parse_function_definition_statement, ))(input) @@ -151,8 +156,120 @@ fn parse_assert_statement(input: &str) -> IResult<&str, Statement> { )(input) } +fn parse_asserteq_statement(input: &str) -> IResult<&str, Statement> { + map( + tuple(( + keyword(ASSERTEQ_KEYWORD), + delimited( + char::<&str, Error<&str>>(LEFT_PAREN), + separated_list0( + tuple(( + multispace0, + char::<&str, Error<&str>>(COMMA_CHAR), + multispace0, + )), + parse_expression, + ), + char::<&str, Error<&str>>(RIGHT_PAREN), + ), + )), + |(_, args)| { + if args.len() != 3 { + panic!("AssertEQ statement requires exactly 3 arguments"); + } + Statement::AssertEQ( + Box::new(args[0].clone()), + Box::new(args[1].clone()), + Box::new(args[2].clone()), + ) + }, + )(input) +} + +fn parse_assertneq_statement(input: &str) -> IResult<&str, Statement> { + map( + tuple(( + keyword(ASSERTNEQ_KEYWORD), + delimited( + char::<&str, Error<&str>>(LEFT_PAREN), + separated_list0( + tuple(( + multispace0, + char::<&str, Error<&str>>(COMMA_CHAR), + multispace0, + )), + parse_expression, + ), + char::<&str, Error<&str>>(RIGHT_PAREN), + ), + )), + |(_, args)| { + if args.len() != 3 { + panic!("AssertNEQ statement requires exactly 3 arguments"); + } + Statement::AssertNEQ( + Box::new(args[0].clone()), + Box::new(args[1].clone()), + Box::new(args[2].clone()), + ) + }, + )(input) +} + +fn parse_asserttrue_statement(input: &str) -> IResult<&str, Statement> { + map( + tuple(( + keyword(ASSERTTRUE_KEYWORD), + delimited( + char::<&str, Error<&str>>(LEFT_PAREN), + separated_list0( + tuple(( + multispace0, + char::<&str, Error<&str>>(COMMA_CHAR), + multispace0, + )), + parse_expression, + ), + char::<&str, Error<&str>>(RIGHT_PAREN), + ), + )), + |(_, args)| { + if args.len() != 2 { + panic!("AssertTrue statement requires exactly 2 arguments"); + } + Statement::AssertTrue(Box::new(args[0].clone()), Box::new(args[1].clone())) + }, + )(input) +} + +fn parse_assertfalse_statement(input: &str) -> IResult<&str, Statement> { + map( + tuple(( + keyword(ASSERTFALSE_KEYWORD), + delimited( + char::<&str, Error<&str>>(LEFT_PAREN), + separated_list0( + tuple(( + multispace0, + char::<&str, Error<&str>>(COMMA_CHAR), + multispace0, + )), + parse_expression, + ), + char::<&str, Error<&str>>(RIGHT_PAREN), + ), + )), + |(_, args)| { + if args.len() != 2 { + panic!("AssertFalse statement requires exactly 2 arguments"); + } + Statement::AssertFalse(Box::new(args[0].clone()), Box::new(args[1].clone())) + }, + )(input) +} + fn parse_function_definition_statement(input: &str) -> IResult<&str, Statement> { - map_res( + map( tuple(( keyword(DEF_KEYWORD), preceded(multispace1, identifier), @@ -173,28 +290,22 @@ fn parse_function_definition_statement(input: &str) -> IResult<&str, Statement> parse_block, )), |(_, name, args, _, t, block)| { - if name.starts_with("test") { - // Força falha aqui para evitar aceitar funções test_ no parser geral - Err(nom::Err::Error(Error::new(input, ErrorKind::Tag))) - } else { - Ok(Statement::FuncDef(Function { - name: name.to_string(), - kind: t, - params: args, - body: Some(Box::new(block)), - })) - } + Statement::FuncDef(Function { + name: name.to_string(), + kind: t, + params: args, + body: Some(Box::new(block)), + }) }, )(input) } - -// TODO: Fazer testes para essa função de parser +// TODO: alterar, não há mais necessidade de checar se o nome é test, usaremos uma keyword "test" fn parse_test_function_definition_statement(input: &str) -> IResult<&str, Statement> { - map_res( + map( tuple(( //keyword(DEF_KEYWORD), - tag("def"), + tag("test"), preceded(multispace1, identifier), //identifier, delimited( @@ -202,30 +313,15 @@ fn parse_test_function_definition_statement(input: &str) -> IResult<&str, Statem multispace0, // Permite `()` com espaços, mas sem argumentos char::<&str, Error<&str>>(RIGHT_PAREN), ), - opt(preceded( - preceded(multispace0, tag("->")), - preceded(multispace0, parse_type), - )), parse_block, )), - |(_, name, _,opt_ret_type, block)| { - if !name.starts_with("test") { - return Err(nom::Err::Error(Error::new(input, ErrorKind::Tag))); - } - - // Se tiver tipo declarado, só aceita Bool - if let Some(ret_type) = opt_ret_type { - if ret_type != Type::TBool { - return Err(nom::Err::Error(Error::new(input, ErrorKind::Tag))); - } - } - - Ok(Statement::TestDef(Function { + |(_, name, _, block)| { + Statement::TestDef(Function { name: name.to_string(), - kind: Type::TBool, // Sempre bool (mesmo se não declarou) - params: Vec::new(), // Nenhum argumento + kind: Type::TBool, // Sempre bool (mesmo se não declarou) + params: Vec::new(), // Nenhum argumento body: Some(Box::new(block)), - })) + }) }, )(input) } @@ -395,14 +491,15 @@ mod tests { #[test] fn test_parse_test_function_definition_statement_valid() { - let input = "def test_example(): x = 1; end"; + let input = "test test_example(): x = 1; end"; let expected = Statement::TestDef(Function { name: "test_example".to_string(), kind: Type::TBool, params: vec![], - body: Some(Box::new(Statement::Block(vec![ - Statement::Assignment("x".to_string(), Box::new(Expression::CInt(1))), - ]))), + body: Some(Box::new(Statement::Block(vec![Statement::Assignment( + "x".to_string(), + Box::new(Expression::CInt(1)), + )]))), }); let parsed = parse_test_function_definition_statement(input).unwrap().1; assert_eq!(parsed, expected); @@ -410,14 +507,15 @@ mod tests { #[test] fn test_parse_test_function_definition_statement_with_spaces() { - let input = "def test_spaces( ): x = 2; end"; + let input = "test test_spaces( ): x = 2; end"; let expected = Statement::TestDef(Function { name: "test_spaces".to_string(), kind: Type::TBool, params: vec![], - body: Some(Box::new(Statement::Block(vec![ - Statement::Assignment("x".to_string(), Box::new(Expression::CInt(2))), - ]))), + body: Some(Box::new(Statement::Block(vec![Statement::Assignment( + "x".to_string(), + Box::new(Expression::CInt(2)), + )]))), }); let parsed = parse_test_function_definition_statement(input).unwrap().1; assert_eq!(parsed, expected); @@ -425,46 +523,39 @@ mod tests { #[test] fn test_parse_test_function_definition_statement_args() { - let input = "def test_with_args(x: Int, y: Int): x = y; end"; - + let input = "test test_with_args(x: Int, y: Int): x = y; end"; + // O parser deve falhar, pois funções de teste não podem ter argumentos. let parsed = parse_test_function_definition_statement(input); - - assert!(parsed.is_err(), "Funções de teste com argumentos devem ser rejeitadas"); + + assert!( + parsed.is_err(), + "Funções de teste com argumentos devem ser rejeitadas" + ); } - + #[test] - fn test_parse_test_function_definition_statement_invalid_return_type() { - let input = "def test_with_invalid_return() -> Int: x = 2; end"; - + fn test_parse_test_function_definition_statement_invalid_return() { + let input = "test test_with_invalid_return() -> Int: x = 2; end"; + // O parser deve falhar, pois funções de teste não podem ter argumentos. let parsed = parse_test_function_definition_statement(input); - - assert!(parsed.is_err(), "Funções de teste não devem especificar tipo de retorno"); + + assert!( + parsed.is_err(), + "Funções de teste não devem especificar tipo de retorno" + ); } - + #[test] fn test_parse_test_function_definition_statement_valid_return_type() { - let input = "def test_with_valid_return() -> Boolean: x = 2; end"; - let expected = Statement::TestDef(Function { - name: "test_with_valid_return".to_string(), - kind: Type::TBool, - params: vec![], - body: Some(Box::new(Statement::Block(vec![ - Statement::Assignment("x".to_string(), Box::new(Expression::CInt(2))), - ]))), - }); - let parsed = parse_test_function_definition_statement(input).unwrap().1; - assert_eq!(parsed, expected); - } - #[test] - #[ignore] - fn test_parse_function_definition_statement_reject_test_function() { - let input = "def test_function_rejected() -> Int: x = 2; end"; - - let parsed = parse_function_definition_statement(input); - assert!(parsed.is_err(), "Funções comuns não devem iniciar com 'test'"); - } - //FIXME: Mudança de teste para testar o commit do github MUDANÇA + let input = "test test_with_valid_return() -> Boolean: x = 2; end"; + let parsed = parse_test_function_definition_statement(input); + assert!( + parsed.is_err(), + "Funções de teste não devem especificar tipo de retorno" + ); + } + //TODO: Implementar testes para os ASSERTS } From c3e2abb1fa7862740eef27f9166fe8eb5867e961 Mon Sep 17 00:00:00 2001 From: The3rdMega <132619258+The3rdMega@users.noreply.github.com> Date: Wed, 25 Jun 2025 12:14:51 -0300 Subject: [PATCH 05/19] =?UTF-8?q?Tipo=20de=20TestDef=20agora=20=C3=A9=20se?= =?UTF-8?q?mpre=20TVoid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/parser/parser_stmt.rs | 52 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/src/parser/parser_stmt.rs b/src/parser/parser_stmt.rs index 00df98a..a84c3cf 100644 --- a/src/parser/parser_stmt.rs +++ b/src/parser/parser_stmt.rs @@ -318,7 +318,7 @@ fn parse_test_function_definition_statement(input: &str) -> IResult<&str, Statem |(_, name, _, block)| { Statement::TestDef(Function { name: name.to_string(), - kind: Type::TBool, // Sempre bool (mesmo se não declarou) + kind: Type::TVoid, // Sempre void params: Vec::new(), // Nenhum argumento body: Some(Box::new(block)), }) @@ -494,7 +494,7 @@ mod tests { let input = "test test_example(): x = 1; end"; let expected = Statement::TestDef(Function { name: "test_example".to_string(), - kind: Type::TBool, + kind: Type::TVoid, params: vec![], body: Some(Box::new(Statement::Block(vec![Statement::Assignment( "x".to_string(), @@ -510,7 +510,7 @@ mod tests { let input = "test test_spaces( ): x = 2; end"; let expected = Statement::TestDef(Function { name: "test_spaces".to_string(), - kind: Type::TBool, + kind: Type::TVoid, params: vec![], body: Some(Box::new(Statement::Block(vec![Statement::Assignment( "x".to_string(), @@ -558,4 +558,50 @@ mod tests { ); } //TODO: Implementar testes para os ASSERTS + + #[test] + fn test_parse_asserteq_statement() { + let input = "asserteq(1, 2, \"msg\")"; + let expected = Statement::AssertEQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CInt(2)), + Box::new(Expression::CString("msg".to_string())), + ); + let parsed = parse_asserteq_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + + #[test] + fn test_parse_assertneq_statement() { + let input = "assertneq(3, 4, \"fail\")"; + let expected = Statement::AssertNEQ( + Box::new(Expression::CInt(3)), + Box::new(Expression::CInt(4)), + Box::new(Expression::CString("fail".to_string())), + ); + let parsed = parse_assertneq_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + + #[test] + fn test_parse_asserttrue_statement() { + let input = "asserttrue(True, \"should be true\")"; + let expected = Statement::AssertTrue( + Box::new(Expression::CTrue), + Box::new(Expression::CString("should be true".to_string())), + ); + let parsed = parse_asserttrue_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + + #[test] + fn test_parse_assertfalse_statement() { + let input = "assertfalse(False, \"should be false\")"; + let expected = Statement::AssertFalse( + Box::new(Expression::CFalse), + Box::new(Expression::CString("should be false".to_string())), + ); + let parsed = parse_assertfalse_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } } From e0ad68427a11575ec27190e17826b5bbc9962dd1 Mon Sep 17 00:00:00 2001 From: The3rdMega <132619258+The3rdMega@users.noreply.github.com> Date: Wed, 25 Jun 2025 14:15:04 -0300 Subject: [PATCH 06/19] Adicionado Interpretador de Assert e Hashmap de Testes no Scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Asserts executam no interpretador e retornam um Computation Continue caso passem ou Erro caso falhem. Environments podem armazenar declaração de testes (ainda a ser implementado pelo TestDef) --- src/environment/environment.rs | 27 +++ src/interpreter/statement_execute.rs | 322 +++++++++++++++++++++++++++ 2 files changed, 349 insertions(+) diff --git a/src/environment/environment.rs b/src/environment/environment.rs index 1acfba3..f5b06ba 100644 --- a/src/environment/environment.rs +++ b/src/environment/environment.rs @@ -9,6 +9,7 @@ pub struct Scope { pub variables: HashMap, pub functions: HashMap, pub adts: HashMap>, + pub tests: HashMap, } impl Scope { @@ -17,6 +18,7 @@ impl Scope { variables: HashMap::new(), functions: HashMap::new(), adts: HashMap::new(), + tests: HashMap::new(), } } @@ -30,6 +32,11 @@ impl Scope { return (); } + fn map_test(&mut self, test: Function) -> () { + self.tests.insert(test.name.clone(), test); + return (); + } + fn map_adt(&mut self, name: Name, adt: Vec) -> () { self.adts.insert(name.clone(), adt); return (); @@ -45,6 +52,10 @@ impl Scope { self.functions.get(name) } + fn lookup_test(&self, name: &Name) -> Option<&Function> { + self.tests.get(name) + } + fn lookup_adt(&self, name: &Name) -> Option<&Vec> { self.adts.get(name) } @@ -78,6 +89,13 @@ impl Environment { } } + pub fn map_test(&mut self, test: Function) -> () { + match self.stack.front_mut() { + None => self.globals.map_test(test), + Some(top) => top.map_test(test), + } + } + pub fn map_adt(&mut self, name: Name, cons: Vec) -> () { match self.stack.front_mut() { None => self.globals.map_adt(name, cons), @@ -103,6 +121,15 @@ impl Environment { self.globals.lookup_function(name) } + pub fn lookup_test(&self, name: &Name) -> Option<&Function> { + for scope in self.stack.iter() { + if let Some(test) = scope.lookup_test(name) { + return Some(test); + } + } + self.globals.lookup_test(name) + } + pub fn lookup_adt(&self, name: &Name) -> Option<&Vec> { for scope in self.stack.iter() { if let Some(cons) = scope.lookup_adt(name) { diff --git a/src/interpreter/statement_execute.rs b/src/interpreter/statement_execute.rs index 3badbea..3787c2a 100644 --- a/src/interpreter/statement_execute.rs +++ b/src/interpreter/statement_execute.rs @@ -47,6 +47,149 @@ pub fn execute(stmt: Statement, env: &Environment) -> Result { + let value = match eval(*exp, &new_env)? { + ExpressionResult::Value(expr) => expr, + ExpressionResult::Propagate(expr) => { + return Ok(Computation::PropagateError(expr, new_env)) + } + }; + + match value { + Expression::CTrue => Ok(Computation::Continue(new_env)), + Expression::CFalse => { + // Avalia a mensagem + let error_msg = match eval(*msg, &new_env)? { + ExpressionResult::Value(Expression::CString(s)) => s, + ExpressionResult::Propagate(expr) => { + return Ok(Computation::PropagateError(expr, new_env)) + } + _ => "Assertion failed".to_string(), + }; + Err(error_msg) + } + _ => Err("Condition must evaluate to a boolean".to_string()), + } + } + + Statement::AssertTrue(exp, msg) => { + let value = match eval(*exp, &new_env)? { + ExpressionResult::Value(expr) => expr, + ExpressionResult::Propagate(expr) => { + return Ok(Computation::PropagateError(expr, new_env)) + } + }; + + match value { + Expression::CTrue => Ok(Computation::Continue(new_env)), + Expression::CFalse => { + // Avalia a mensagem + let error_msg = match eval(*msg, &new_env)? { + ExpressionResult::Value(Expression::CString(s)) => s, + ExpressionResult::Propagate(expr) => { + return Ok(Computation::PropagateError(expr, new_env)) + } + _ => "Assertion failed".to_string(), + }; + Err(error_msg) + } + _ => Err("Condition must evaluate to a boolean".to_string()), + } + } + + Statement::AssertFalse(exp, msg) => { + let value = match eval(*exp, &new_env)? { + ExpressionResult::Value(expr) => expr, + ExpressionResult::Propagate(expr) => { + return Ok(Computation::PropagateError(expr, new_env)) + } + }; + + match value { + Expression::CFalse => Ok(Computation::Continue(new_env)), + Expression::CTrue => { + // Avalia a mensagem + let error_msg = match eval(*msg, &new_env)? { + ExpressionResult::Value(Expression::CString(s)) => s, + ExpressionResult::Propagate(expr) => { + return Ok(Computation::PropagateError(expr, new_env)) + } + _ => "Assertion failed".to_string(), + }; + Err(error_msg) + } + _ => Err("Condition must evaluate to a boolean".to_string()), + } + } + + Statement::AssertEQ(exp1, exp2, msg) => { + let value1 = match eval(*exp1, &new_env)? { + ExpressionResult::Value(expr1) => expr1, + ExpressionResult::Propagate(expr1) => { + return Ok(Computation::PropagateError(expr1, new_env)) + } + }; + + let value2 = match eval(*exp2, &new_env)? { + ExpressionResult::Value(expr2) => expr2, + ExpressionResult::Propagate(expr2) => { + return Ok(Computation::PropagateError(expr2, new_env)) + } + }; + + let comparator = Expression::EQ(Box::new(value1), Box::new(value2)); + + match eval(comparator, &new_env)? { + ExpressionResult::Value(Expression::CTrue) => Ok(Computation::Continue(new_env)), + ExpressionResult::Value(Expression::CFalse) => { + // Avalia a mensagem + let error_msg = match eval(*msg, &new_env)? { + ExpressionResult::Value(Expression::CString(s)) => s, + ExpressionResult::Propagate(expr) => { + return Ok(Computation::PropagateError(expr, new_env)) + } + _ => "Assertion failed".to_string(), + }; + Err(error_msg) + } + _ => Err("Condition must evaluate to a boolean".to_string()), + } + } + + Statement::AssertNEQ(exp1, exp2, msg) => { + let value1 = match eval(*exp1, &new_env)? { + ExpressionResult::Value(expr1) => expr1, + ExpressionResult::Propagate(expr1) => { + return Ok(Computation::PropagateError(expr1, new_env)) + } + }; + + let value2 = match eval(*exp2, &new_env)? { + ExpressionResult::Value(expr2) => expr2, + ExpressionResult::Propagate(expr2) => { + return Ok(Computation::PropagateError(expr2, new_env)) + } + }; + + let comparator = Expression::NEQ(Box::new(value1), Box::new(value2)); + + match eval(comparator, &new_env)? { + ExpressionResult::Value(Expression::CTrue) => Ok(Computation::Continue(new_env)), + ExpressionResult::Value(Expression::CFalse) => { + // Avalia a mensagem + let error_msg = match eval(*msg, &new_env)? { + ExpressionResult::Value(Expression::CString(s)) => s, + ExpressionResult::Propagate(expr) => { + return Ok(Computation::PropagateError(expr, new_env)) + } + _ => "Assertion failed".to_string(), + }; + Err(error_msg) + } + _ => Err("Condition must evaluate to a boolean".to_string()), + } + } + Statement::ValDeclaration(name, exp) => { let value = match eval(*exp, &new_env)? { ExpressionResult::Value(expr) => expr, @@ -776,4 +919,183 @@ mod tests { assert_eq!(sum_expr, Expression::CInt(12)); } } + + mod assert_statement_tests { + use super::*; + + #[test] + fn test_execute_assert_true() { + let env = create_test_env(); + let stmt = Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString("ok".to_string())), + ); + let result = execute(stmt, &env); + assert!(result.is_ok(), "Assert with true condition should succeed"); + } + + #[test] + fn test_execute_assert_false() { + let env = create_test_env(); + let stmt = Statement::Assert( + Box::new(Expression::CFalse), + Box::new(Expression::CString("fail msg".to_string())), + ); + let result = execute(stmt.clone(), &env); + assert!(result.is_err(), "Assert with false condition should fail"); + //assert_eq!(result.unwrap_err(), "fail msg"); + let computation = match execute(stmt, &env) { + Ok(Computation::Continue(_)) => "error".to_string(), + Ok(Computation::Return(_, _)) => "error".to_string(), + Ok(Computation::PropagateError(_, _)) => "error".to_string(), + Err(e) => e.to_string(), + }; + assert_eq!(computation, "fail msg".to_string()); + } + + #[test] + fn test_execute_asserteq_true() { + let env = create_test_env(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CInt(1)), + Box::new(Expression::CString("should not fail".to_string())), + ); + let result = execute(stmt, &env); + assert!(result.is_ok(), "AssertEQ with equal values should succeed"); + } + + #[test] + fn test_execute_asserteq_false() { + let env = create_test_env(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CInt(2)), + Box::new(Expression::CString("eq fail".to_string())), + ); + let result = execute(stmt.clone(), &env); + assert!( + result.is_err(), + "AssertEQ with different values should fail" + ); + //assert_eq!(result.unwrap_err(), "eq fail"); + + let computation = match execute(stmt, &env) { + Ok(Computation::Continue(_)) => "error".to_string(), + Ok(Computation::Return(_, _)) => "error".to_string(), + Ok(Computation::PropagateError(_, _)) => "error".to_string(), + Err(e) => e.to_string(), + }; + assert_eq!(computation, "eq fail".to_string()); + } + + #[test] + fn test_execute_assertneq_true() { + let env = create_test_env(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CInt(2)), + Box::new(Expression::CString("should not fail".to_string())), + ); + let result = execute(stmt, &env); + assert!( + result.is_ok(), + "AssertNEQ with different values should succeed" + ); + } + + #[test] + fn test_execute_assertneq_false() { + let env = create_test_env(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CInt(3)), + Box::new(Expression::CInt(3)), + Box::new(Expression::CString("neq fail".to_string())), + ); + let result = execute(stmt.clone(), &env); + assert!(result.is_err(), "AssertNEQ with equal values should fail"); + //assert_eq!(result.unwrap_err(), "neq fail"); + + let computation = match execute(stmt, &env) { + Ok(Computation::Continue(_)) => "error".to_string(), + Ok(Computation::Return(_, _)) => "error".to_string(), + Ok(Computation::PropagateError(_, _)) => "error".to_string(), + Err(e) => e.to_string(), + }; + assert_eq!(computation, "neq fail".to_string()); + } + + #[test] + fn test_execute_asserttrue_true() { + let env = create_test_env(); + let stmt = Statement::AssertTrue( + Box::new(Expression::CTrue), + Box::new(Expression::CString("ok".to_string())), + ); + let result = execute(stmt, &env); + assert!( + result.is_ok(), + "AssertTrue with true condition should succeed" + ); + } + + #[test] + fn test_execute_asserttrue_false() { + let env = create_test_env(); + let stmt = Statement::AssertTrue( + Box::new(Expression::CFalse), + Box::new(Expression::CString("asserttrue fail".to_string())), + ); + let result = execute(stmt.clone(), &env); + assert!( + result.is_err(), + "AssertTrue with false condition should fail" + ); + //assert_eq!(result.unwrap_err(), "asserttrue fail"); + + let computation = match execute(stmt, &env) { + Ok(Computation::Continue(_)) => "error".to_string(), + Ok(Computation::Return(_, _)) => "error".to_string(), + Ok(Computation::PropagateError(_, _)) => "error".to_string(), + Err(e) => e.to_string(), + }; + assert_eq!(computation, "asserttrue fail".to_string()); + } + + #[test] + fn test_execute_assertfalse_false() { + let env = create_test_env(); + let stmt = Statement::AssertFalse( + Box::new(Expression::CFalse), + Box::new(Expression::CString("ok".to_string())), + ); + let result = execute(stmt, &env); + assert!( + result.is_ok(), + "AssertFalse with false condition should succeed" + ); + } + + #[test] + fn test_execute_assertfalse_true() { + let env = create_test_env(); + let stmt = Statement::AssertFalse( + Box::new(Expression::CTrue), + Box::new(Expression::CString("assertfalse fail".to_string())), + ); + let result = execute(stmt.clone(), &env); + assert!( + result.is_err(), + "AssertFalse with true condition should fail" + ); + //assert_eq!(result.unwrap_err(), "assertfalse fail"); + let computation = match execute(stmt, &env) { + Ok(Computation::Continue(_)) => "error".to_string(), + Ok(Computation::Return(_, _)) => "error".to_string(), + Ok(Computation::PropagateError(_, _)) => "error".to_string(), + Err(e) => e.to_string(), + }; + assert_eq!(computation, "assertfalse fail".to_string()); + } + } } From 44909f9c8098fe922189b66ea37dd45d4c836614 Mon Sep 17 00:00:00 2001 From: The3rdMega <132619258+The3rdMega@users.noreply.github.com> Date: Wed, 25 Jun 2025 14:33:26 -0300 Subject: [PATCH 07/19] =?UTF-8?q?Implementa=20Defini=C3=A7=C3=A3o=20de=20T?= =?UTF-8?q?estes=20no=20Interpretador?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interpreter/statement_execute.rs | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/interpreter/statement_execute.rs b/src/interpreter/statement_execute.rs index 3787c2a..dec5b6a 100644 --- a/src/interpreter/statement_execute.rs +++ b/src/interpreter/statement_execute.rs @@ -320,6 +320,11 @@ pub fn execute(stmt: Statement, env: &Environment) -> Result { + new_env.map_test(teste.clone()); + Ok(Computation::Continue(new_env)) + } + Statement::Return(exp) => { let exp_value = match eval(*exp, &new_env)? { ExpressionResult::Value(expr) => expr, @@ -1098,4 +1103,28 @@ mod tests { assert_eq!(computation, "assertfalse fail".to_string()); } } + mod testdef_statement_tests { + use super::*; + + #[test] + fn test_execute_testdef() { + let env = create_test_env(); + let test_def = Statement::TestDef(Function { + name: "test_example".to_string(), + kind: Type::TVoid, + params: Vec::new(), + body: Some(Box::new(Statement::Block(vec![Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString("Test passed".to_string())), + )]))), + }); + let programa = Statement::Block(vec![test_def.clone()]); + match execute(programa, &env) { + Ok(Computation::Continue(new_env)) => { + assert!(new_env.lookup_test(&"test_example".to_string()).is_some()); + } + _ => panic!("Test definition execution failed"), + } + } + } } From f5d811096da5802709fbc1299541a0848fba01a0 Mon Sep 17 00:00:00 2001 From: The3rdMega <132619258+The3rdMega@users.noreply.github.com> Date: Thu, 26 Jun 2025 09:37:58 -0300 Subject: [PATCH 08/19] =?UTF-8?q?Finalizada=20Implementa=C3=A7=C3=A3o=20do?= =?UTF-8?q?=20Interpretador=20de=20Testes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Testes agora armazenam TestResult no environment. A função run_tests no interpretador recebe um programa, executa ele por inteiro e depois roda todos os testes presentes no programa, retornando um Vec Testes definidos em um programa são armazenados no escopo no hashmap tests. Vec ainda não está sendo armazenado no environment. Mas é possível implementar de maneira indolor. Interpretador de Asserts finalizada, agora eles retornam Computation::Continue ou Err(e), com a string de erro presente dentro do Assert. Cargo.toml agora requer uso de IndexMap para armazenar TestResults ordenados na ordem de definição dos testes. --- Cargo.lock | 23 +++ Cargo.toml | 1 + src/environment/environment.rs | 34 ++++- src/interpreter/statement_execute.rs | 206 +++++++++++++++++++++++++++ 4 files changed, 262 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 488482e..28e7f4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,28 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "memchr" version = "2.7.4" @@ -59,6 +81,7 @@ name = "r-python" version = "0.1.0" dependencies = [ "approx", + "indexmap", "nom", "once_cell", ] diff --git a/Cargo.toml b/Cargo.toml index 9ebcc09..3d92060 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" nom = "7.0" approx = "0.5.1" once_cell = "1.10" +indexmap = "2.2" diff --git a/src/environment/environment.rs b/src/environment/environment.rs index f5b06ba..77c149f 100644 --- a/src/environment/environment.rs +++ b/src/environment/environment.rs @@ -1,6 +1,7 @@ use crate::ir::ast::Function; use crate::ir::ast::Name; use crate::ir::ast::ValueConstructor; +use indexmap::IndexMap; use std::collections::HashMap; use std::collections::LinkedList; @@ -9,7 +10,7 @@ pub struct Scope { pub variables: HashMap, pub functions: HashMap, pub adts: HashMap>, - pub tests: HashMap, + pub tests: IndexMap, } impl Scope { @@ -18,7 +19,7 @@ impl Scope { variables: HashMap::new(), functions: HashMap::new(), adts: HashMap::new(), - tests: HashMap::new(), + tests: IndexMap::new(), } } @@ -130,6 +131,19 @@ impl Environment { self.globals.lookup_test(name) } + pub fn scrape_tests(&self) -> Vec { + let mut tests = Vec::new(); + for scope in self.stack.iter() { + for test in scope.tests.values() { + tests.push(test.clone()); + } + } + for test in self.globals.tests.values() { + tests.push(test.clone()); + } + tests + } + pub fn lookup_adt(&self, name: &Name) -> Option<&Vec> { for scope in self.stack.iter() { if let Some(cons) = scope.lookup_adt(name) { @@ -174,6 +188,22 @@ impl Environment { } } +pub struct TestResult { + pub name: Name, + pub result: bool, + pub error: Option, +} + +impl TestResult { + pub fn new(name: Name, result: bool, error: Option) -> Self { + TestResult { + name, + result, + error, + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/interpreter/statement_execute.rs b/src/interpreter/statement_execute.rs index dec5b6a..07bf8f6 100644 --- a/src/interpreter/statement_execute.rs +++ b/src/interpreter/statement_execute.rs @@ -1,5 +1,6 @@ use super::expression_eval::{eval, ExpressionResult}; use crate::environment::environment::Environment; +use crate::environment::environment::TestResult; use crate::ir::ast::{Expression, Statement}; pub enum Computation { @@ -32,6 +33,42 @@ pub fn run( } } +pub fn run_tests(stmt: &Statement) -> Result, String> { + let env = match run(stmt.clone(), &Environment::new()) { + Ok(env) => env, + Err(e) => return Err(e), + }; + + let mut results = Vec::new(); + + for test in env.scrape_tests() { + let test_env = env.clone(); + + let stmt = match &test.body { + Some(body) => *body.clone(), + None => continue, + }; + + match execute(stmt, &test_env) { + Ok(Computation::Continue(_)) | Ok(Computation::Return(_, _)) => { + results.push(TestResult::new(test.name.clone(), true, None)); + } + Err(e) => { + results.push(TestResult::new(test.name.clone(), false, Some(e))); + } + Ok(Computation::PropagateError(e, _)) => { + results.push(TestResult::new( + test.name.clone(), + false, + Some(format!("Propagated error: {:?}", e)), + )); + } + } + } + + Ok(results) +} + pub fn execute(stmt: Statement, env: &Environment) -> Result { let mut new_env = env.clone(); @@ -1127,4 +1164,173 @@ mod tests { } } } + mod run_tests_tests { + + use super::*; + + #[test] + fn test_run_tests() { + let test_def = Statement::TestDef(Function { + name: "test_example".to_string(), + kind: Type::TVoid, + params: Vec::new(), + body: Some(Box::new(Statement::Block(vec![Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString("Test passed".to_string())), + )]))), + }); + let programa = Statement::Block(vec![test_def.clone()]); + match run_tests(&programa) { + Ok(resultados) => { + assert_eq!(resultados.len(), 1); + assert_eq!(resultados[0].name, "test_example"); + assert!(resultados[0].result); + assert!(resultados[0].error.is_none()); + } + _ => panic!("Test execution failed"), + } + } + + #[test] + fn test_run_tests_scope() { + let test_def = Statement::TestDef(Function { + name: "test_example".to_string(), + kind: Type::TVoid, + params: Vec::new(), + body: Some(Box::new(Statement::Block(vec![Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString("Test passed".to_string())), + )]))), + }); + + let teste_def2 = Statement::TestDef(Function { + name: "test_example2".to_string(), + kind: Type::TVoid, + params: Vec::new(), + body: Some(Box::new(Statement::Block(vec![Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString("Test 2 passed".to_string())), + )]))), + }); + + let assign1 = Statement::Assignment("x".to_string(), Box::new(Expression::CInt(10))); + let assign2 = Statement::Assignment("y".to_string(), Box::new(Expression::CInt(20))); + + let ifelse = Statement::IfThenElse( + Box::new(Expression::CTrue), + Box::new(test_def), + Some(Box::new(teste_def2)), + ); + + let programa = Statement::Block(vec![assign1, assign2, ifelse]); + + let resultado_final = match run_tests(&programa) { + Ok(resultados) => resultados, + Err(e) => panic!("Test execution failed: {}", e), + }; + + assert_eq!(resultado_final.len(), 1); + assert_eq!(resultado_final[0].name, "test_example"); + assert_eq!(resultado_final[0].result, true); + } + + #[test] + fn test_run_tests_with_assert_fail() { + let teste1 = Statement::TestDef(Function { + name: "test_fail".to_string(), + kind: Type::TVoid, + params: Vec::new(), + body: Some(Box::new(Statement::Block(vec![Statement::Assert( + Box::new(Expression::CFalse), + Box::new(Expression::CString("This test should fail".to_string())), + )]))), + }); + let programa = Statement::Block(vec![teste1]); + match run_tests(&programa) { + Ok(resultados) => { + assert_eq!(resultados.len(), 1); + assert_eq!(resultados[0].name, "test_fail"); + assert!(!resultados[0].result); + assert_eq!( + resultados[0].error, + Some("This test should fail".to_string()) + ); + } + Err(e) => panic!("Test execution failed: {}", e), + } + } + + #[test] + fn test_run_tests_without_asserts() { + let teste = Statement::TestDef(Function { + name: "test_no_assert".to_string(), + kind: Type::TVoid, + params: Vec::new(), + body: Some(Box::new(Statement::Block(vec![Statement::VarDeclaration( + "x".to_string(), + Box::new(Expression::CInt(42)), + )]))), + }); + let programa = Statement::Block(vec![teste]); + match run_tests(&programa) { + Ok(resultados) => { + assert_eq!(resultados.len(), 1); + assert_eq!(resultados[0].name, "test_no_assert"); + assert!(resultados[0].result); + assert!(resultados[0].error.is_none()); + } + Err(e) => panic!("Test execution failed: {}", e), + } + } + + #[test] + fn test_run_tests_with_multiple_tests() { + let teste1 = Statement::TestDef(Function { + name: "test_one".to_string(), + kind: Type::TVoid, + params: Vec::new(), + body: Some(Box::new(Statement::Block(vec![Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString("Test one passed".to_string())), + )]))), + }); + let teste2 = Statement::TestDef(Function { + name: "test_two".to_string(), + kind: Type::TVoid, + params: Vec::new(), + body: Some(Box::new(Statement::Block(vec![Statement::Assert( + Box::new(Expression::CFalse), + Box::new(Expression::CString("Test two failed".to_string())), + )]))), + }); + let teste3 = Statement::TestDef(Function { + name: "test_three".to_string(), + kind: Type::TVoid, + params: Vec::new(), + body: Some(Box::new(Statement::Block(vec![Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString("Test three passed".to_string())), + )]))), + }); + let programa = Statement::Block(vec![teste1, teste2, teste3]); + + match run_tests(&programa) { + Ok(resultados) => { + assert_eq!(resultados.len(), 3); + assert_eq!(resultados[0].name, "test_one"); + assert!(resultados[0].result); + assert!(resultados[0].error.is_none()); + + assert_eq!(resultados[1].name, "test_two"); + assert!(!resultados[1].result); + assert_eq!(resultados[1].error, Some("Test two failed".to_string())); + + assert_eq!(resultados[2].name, "test_three"); + assert!(resultados[2].result); + assert!(resultados[2].error.is_none()); + } + Err(e) => panic!("Test execution failed: {}", e), + } + } + } } From 31bf3ace80a4327e8c1a56afefe291922bf37269 Mon Sep 17 00:00:00 2001 From: michelenakagomi Date: Sun, 29 Jun 2025 20:34:45 -0300 Subject: [PATCH 09/19] Update statement_type_checker.rs --- src/type_checker/statement_type_checker.rs | 152 +++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/src/type_checker/statement_type_checker.rs b/src/type_checker/statement_type_checker.rs index 5c6835e..acc7534 100644 --- a/src/type_checker/statement_type_checker.rs +++ b/src/type_checker/statement_type_checker.rs @@ -21,6 +21,13 @@ pub fn check_stmt( 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), + + Statement::Assert(expr1, expr2) => check_assert(expr1, expr2, env), + Statement::AssertTrue(expr, _) => check_assert_true(expr, env), + Statement::AssertFalse(expr, _) => check_assert_false(expr, env), + Statement::AssertEQ(lhs, rhs, _) => check_assert_eq(lhs, rhs, env), + Statement::AssertNEQ(lhs, rhs, _) => check_assert_neq(lhs, rhs, env), + _ => Err("Not implemented yet".to_string()), } } @@ -230,6 +237,71 @@ fn check_return_stmt( } } + +fn check_assert( + expr1: Box, + expr2: Box, + env: &Environment, +) -> Result, ErrorMessage> { + let type1 = check_expr(*expr1, env)?; + let type2 = check_expr(*expr2, env)?; + + if type1 != Type::TBool { + Err("[Type Error] First Assert expression must be of type Boolean.".to_string()) + } else if type2 != Type::TString { + Err("[Type Error] Second Assert expression must be of type CString.".to_string()) + } else { + Ok(env.clone()) + } +} + + +fn check_assert_true(expr: Box, env: &Environment) -> Result, ErrorMessage> { + let expr_type = check_expr(*expr, env)?; + if expr_type != Type::TBool { + Err("[Type Error] AssertTrue expression must be of type Boolean.".to_string()) + } else { + Ok(env.clone()) + } +} + +fn check_assert_false(expr: Box, env: &Environment) -> Result, ErrorMessage> { + let expr_type = check_expr(*expr, env)?; + if expr_type != Type::TBool { + Err("[Type Error] AssertFalse expression must be of type Boolean.".to_string()) + } else { + Ok(env.clone()) + } +} + +fn check_assert_eq(lhs: Box, rhs: Box, env: &Environment) -> Result, ErrorMessage> { + let lhs_type = check_expr(*lhs, env)?; + let rhs_type = check_expr(*rhs, env)?; + if lhs_type != rhs_type { + Err(format!( + "[Type Error] AssertEQ expressions must have the same type. Found {:?} and {:?}.", + lhs_type, rhs_type + )) + } else { + Ok(env.clone()) + } +} + +fn check_assert_neq(lhs: Box, rhs: Box, env: &Environment) -> Result, ErrorMessage> { + let lhs_type = check_expr(*lhs, env)?; + let rhs_type = check_expr(*rhs, env)?; + if lhs_type != rhs_type { + Err(format!( + "[Type Error] AssertNEQ expressions must have the same type. Found {:?} and {:?}.", + lhs_type, rhs_type + )) + } else { + Ok(env.clone()) + } +} + + + fn merge_environments( env1: &Environment, env2: &Environment, @@ -658,4 +730,84 @@ mod tests { // TODO: Let discuss this case here next class. assert!(check_stmt(stmt, &env).is_err()); } + +#[test] +fn test_assert_bool_ok() { + let env: Environment = Environment::new(); + let stmt = Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString("msg".to_string())), + ); + assert!(check_stmt(stmt, &env).is_ok()); +} + +#[test] +fn test_assert_bool_error() { + let env: Environment = Environment::new(); + let stmt = Statement::Assert( + Box::new(Expression::CInt(1)), // não booleano + Box::new(Expression::CTrue), // segundo argumento pode ser qualquer um válido + ); + assert!(check_stmt(stmt, &env).is_err()); +} + +#[test] +fn test_assert_true_ok() { + let env = Environment::new(); + let stmt = Statement::AssertTrue(Box::new(Expression::CTrue), "ok".to_string()); + assert!(check_stmt(stmt, &env).is_ok()); +} + +#[test] +fn test_assert_false_ok() { + let env = Environment::new(); + let stmt = Statement::AssertFalse(Box::new(Expression::CFalse), "false".to_string()); + assert!(check_stmt(stmt, &env).is_ok()); +} + +#[test] +fn test_assert_eq_same_type() { + let env = Environment::new(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CInt(2)), + "eq".to_string(), + ); + assert!(check_stmt(stmt, &env).is_ok()); +} + +#[test] +fn test_assert_eq_mismatch_type() { + let env = Environment::new(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CString("x".to_string())), + "eq".to_string(), + ); + assert!(check_stmt(stmt, &env).is_err()); +} + +#[test] +fn test_assert_neq_same_type() { + let env = Environment::new(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CInt(2)), + "neq".to_string(), + ); + assert!(check_stmt(stmt, &env).is_ok()); +} + +#[test] +fn test_assert_neq_mismatch_type() { + let env = Environment::new(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CString("x".to_string())), + "neq".to_string(), + ); + assert!(check_stmt(stmt, &env).is_err()); +} + + } From 01648fe33aac91ef92fcae544e309fbd173bb0eb Mon Sep 17 00:00:00 2001 From: Arthur Luiz <132619258+The3rdMega@users.noreply.github.com> Date: Sun, 29 Jun 2025 22:33:37 -0300 Subject: [PATCH 10/19] [Fix] Asserts Type Checker now checks Error Messages in the Asserts --- src/type_checker/statement_type_checker.rs | 152 ++++++++++++++++++--- 1 file changed, 134 insertions(+), 18 deletions(-) diff --git a/src/type_checker/statement_type_checker.rs b/src/type_checker/statement_type_checker.rs index acc7534..ec336da 100644 --- a/src/type_checker/statement_type_checker.rs +++ b/src/type_checker/statement_type_checker.rs @@ -22,11 +22,11 @@ pub fn check_stmt( Statement::TypeDeclaration(name, cons) => check_adt_declarations_stmt(name, cons, env), Statement::Return(exp) => check_return_stmt(exp, env), - Statement::Assert(expr1, expr2) => check_assert(expr1, expr2, env), - Statement::AssertTrue(expr, _) => check_assert_true(expr, env), - Statement::AssertFalse(expr, _) => check_assert_false(expr, env), - Statement::AssertEQ(lhs, rhs, _) => check_assert_eq(lhs, rhs, env), - Statement::AssertNEQ(lhs, rhs, _) => check_assert_neq(lhs, rhs, env), + Statement::Assert(expr1, errmsg) => check_assert(expr1, errmsg, env), + Statement::AssertTrue(expr1, errmsg) => check_assert_true(expr1, errmsg, env), + Statement::AssertFalse(expr1, errmsg) => check_assert_false(expr1, errmsg, env), + Statement::AssertEQ(lhs, rhs, errmsg) => check_assert_eq(lhs, rhs, errmsg, env), + Statement::AssertNEQ(lhs, rhs, errmsg) => check_assert_neq(lhs, rhs, errmsg, env), _ => Err("Not implemented yet".to_string()), } @@ -256,45 +256,57 @@ fn check_assert( } -fn check_assert_true(expr: Box, env: &Environment) -> Result, ErrorMessage> { - let expr_type = check_expr(*expr, env)?; +fn check_assert_true(expr1: Box, expr2: Box, env: &Environment) -> Result, ErrorMessage> { + let expr_type = check_expr(*expr1, env)?; + let expr_type2 = check_expr(*expr2, env)?; if expr_type != Type::TBool { Err("[Type Error] AssertTrue expression must be of type Boolean.".to_string()) + } else if expr_type2 != Type::TString { + Err("[Type Error] Second Assert expression must be of type CString.".to_string()) } else { Ok(env.clone()) } } -fn check_assert_false(expr: Box, env: &Environment) -> Result, ErrorMessage> { - let expr_type = check_expr(*expr, env)?; +fn check_assert_false(expr1: Box, expr2: Box, env: &Environment) -> Result, ErrorMessage> { + let expr_type = check_expr(*expr1, env)?; + let expr_type2 = check_expr(*expr2, env)?; if expr_type != Type::TBool { Err("[Type Error] AssertFalse expression must be of type Boolean.".to_string()) + } else if expr_type2 != Type::TString { + Err("[Type Error] Second Assert expression must be of type CString.".to_string()) } else { Ok(env.clone()) } } -fn check_assert_eq(lhs: Box, rhs: Box, env: &Environment) -> Result, ErrorMessage> { +fn check_assert_eq(lhs: Box, rhs: Box, err: Box, env: &Environment) -> Result, ErrorMessage> { let lhs_type = check_expr(*lhs, env)?; let rhs_type = check_expr(*rhs, env)?; + let err_type = check_expr(*err, env)?; if lhs_type != rhs_type { Err(format!( "[Type Error] AssertEQ expressions must have the same type. Found {:?} and {:?}.", lhs_type, rhs_type )) + } else if err_type != Type::TString { + Err("[Type Error] Third Assert expression must be of type CString.".to_string()) } else { Ok(env.clone()) } } -fn check_assert_neq(lhs: Box, rhs: Box, env: &Environment) -> Result, ErrorMessage> { +fn check_assert_neq(lhs: Box, rhs: Box, err: Box, env: &Environment) -> Result, ErrorMessage> { let lhs_type = check_expr(*lhs, env)?; let rhs_type = check_expr(*rhs, env)?; + let err_type = check_expr(*err, env)?; if lhs_type != rhs_type { Err(format!( "[Type Error] AssertNEQ expressions must have the same type. Found {:?} and {:?}.", lhs_type, rhs_type )) + } else if err_type != Type::TString { + Err("[Type Error] Third Assert expression must be of type CString.".to_string()) } else { Ok(env.clone()) } @@ -746,7 +758,7 @@ fn test_assert_bool_error() { let env: Environment = Environment::new(); let stmt = Statement::Assert( Box::new(Expression::CInt(1)), // não booleano - Box::new(Expression::CTrue), // segundo argumento pode ser qualquer um válido + Box::new(Expression::CString("msg".to_string())), // segundo argumento pode ser qualquer um válido ); assert!(check_stmt(stmt, &env).is_err()); } @@ -754,14 +766,14 @@ fn test_assert_bool_error() { #[test] fn test_assert_true_ok() { let env = Environment::new(); - let stmt = Statement::AssertTrue(Box::new(Expression::CTrue), "ok".to_string()); + let stmt = Statement::AssertTrue(Box::new(Expression::CTrue), Box::new(Expression::CString("ok".to_string()))); assert!(check_stmt(stmt, &env).is_ok()); } #[test] fn test_assert_false_ok() { let env = Environment::new(); - let stmt = Statement::AssertFalse(Box::new(Expression::CFalse), "false".to_string()); + let stmt = Statement::AssertFalse(Box::new(Expression::CFalse), Box::new(Expression::CString("false".to_string()))); assert!(check_stmt(stmt, &env).is_ok()); } @@ -771,7 +783,7 @@ fn test_assert_eq_same_type() { let stmt = Statement::AssertEQ( Box::new(Expression::CInt(1)), Box::new(Expression::CInt(2)), - "eq".to_string(), + Box::new(Expression::CString("eq".to_string())), ); assert!(check_stmt(stmt, &env).is_ok()); } @@ -782,7 +794,7 @@ fn test_assert_eq_mismatch_type() { let stmt = Statement::AssertEQ( Box::new(Expression::CInt(1)), Box::new(Expression::CString("x".to_string())), - "eq".to_string(), + Box::new(Expression::CString("eq".to_string())), ); assert!(check_stmt(stmt, &env).is_err()); } @@ -793,7 +805,7 @@ fn test_assert_neq_same_type() { let stmt = Statement::AssertNEQ( Box::new(Expression::CInt(1)), Box::new(Expression::CInt(2)), - "neq".to_string(), + Box::new(Expression::CString("neq".to_string())), ); assert!(check_stmt(stmt, &env).is_ok()); } @@ -804,10 +816,114 @@ fn test_assert_neq_mismatch_type() { let stmt = Statement::AssertNEQ( Box::new(Expression::CTrue), Box::new(Expression::CString("x".to_string())), - "neq".to_string(), + Box::new(Expression::CString("neq".to_string())), + ); + assert!(check_stmt(stmt, &env).is_err()); +} + +#[test] +fn test_assert_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); +} + +#[test] +fn test_assert_true_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::AssertTrue( + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); +} + +#[test] +fn test_assert_false_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::AssertFalse( + Box::new(Expression::CFalse), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); +} + +#[test] +fn test_assert_eq_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); +} + +#[test] +fn test_assert_neq_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CFalse), + Box::new(Expression::CTrue), // Error message must be a string ); assert!(check_stmt(stmt, &env).is_err()); } +#[test] +fn test_assert_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString("assert".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); +} + +#[test] +fn test_assert_true_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::AssertTrue( + Box::new(Expression::CTrue), + Box::new(Expression::CString("asserttrue".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); +} + +#[test] +fn test_assert_false_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::AssertFalse( + Box::new(Expression::CFalse), + Box::new(Expression::CString("assertfalse".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); +} + +#[test] +fn test_assert_eq_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), + Box::new(Expression::CString("eq".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); +} + +#[test] +fn test_assert_neq_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CFalse), + Box::new(Expression::CString("neq".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); +} + } From b6f55e6e4e5bc334c6aac9d0d990d28f8ae03187 Mon Sep 17 00:00:00 2001 From: The3rdMega <132619258+The3rdMega@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:10:24 -0300 Subject: [PATCH 11/19] =?UTF-8?q?Implementados=20Mais=20testes=20e=20agora?= =?UTF-8?q?=20cada=20Fun=C3=A7=C3=A3o=20de=20teste=20executa=20no=20seu=20?= =?UTF-8?q?pr=C3=B3prio=20escopo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interpreter/statement_execute.rs | 88 ++++- src/parser/parser_stmt.rs | 27 ++ src/type_checker/statement_type_checker.rs | 376 +++++++++++---------- 3 files changed, 311 insertions(+), 180 deletions(-) diff --git a/src/interpreter/statement_execute.rs b/src/interpreter/statement_execute.rs index 07bf8f6..7ccf60e 100644 --- a/src/interpreter/statement_execute.rs +++ b/src/interpreter/statement_execute.rs @@ -42,7 +42,8 @@ pub fn run_tests(stmt: &Statement) -> Result, String> { let mut results = Vec::new(); for test in env.scrape_tests() { - let test_env = env.clone(); + let mut test_env = env.clone(); + test_env.push(); let stmt = match &test.body { Some(body) => *body.clone(), @@ -64,6 +65,7 @@ pub fn run_tests(stmt: &Statement) -> Result, String> { )); } } + test_env.pop(); } Ok(results) @@ -1260,6 +1262,46 @@ mod tests { } } + #[test] + fn test_run_tests_with_second_assert_fail() { + let teste1 = Statement::TestDef(Function { + name: "test_fail".to_string(), + kind: Type::TVoid, + params: Vec::new(), + body: Some(Box::new(Statement::Block(vec![ + Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString("This test should pass".to_string())), + ), + Statement::Assert( + Box::new(Expression::CFalse), + Box::new(Expression::CString( + "This second test should fail".to_string(), + )), + ), + Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString( + "This test shouldn't run, but should pass".to_string(), + )), + ), + ]))), + }); + let programa = Statement::Block(vec![teste1]); + match run_tests(&programa) { + Ok(resultados) => { + assert_eq!(resultados.len(), 1); + assert_eq!(resultados[0].name, "test_fail"); + assert!(!resultados[0].result); + assert_eq!( + resultados[0].error, + Some("This second test should fail".to_string()) + ); + } + Err(e) => panic!("Test execution failed: {}", e), + } + } + #[test] fn test_run_tests_without_asserts() { let teste = Statement::TestDef(Function { @@ -1332,5 +1374,49 @@ mod tests { Err(e) => panic!("Test execution failed: {}", e), } } + #[test] + fn test_test_scope_isolation() { + // test_one: define x = 1, passa se x == 1 + let test_one = Statement::TestDef(Function { + name: "test_one".to_string(), + kind: Type::TBool, + params: vec![], + body: Some(Box::new(Statement::Block(vec![ + Statement::VarDeclaration("x".to_string(), Box::new(Expression::CInt(1))), + Statement::AssertEQ( + Box::new(Expression::Var("x".to_string())), + Box::new(Expression::CInt(1)), + Box::new(Expression::CString("x should be 1".to_string())), + ), + ]))), + }); + + // test_two: espera que x NÃO exista + let test_two = Statement::TestDef(Function { + name: "test_two".to_string(), + kind: Type::TBool, + params: vec![], + body: Some(Box::new(Statement::Block(vec![Statement::AssertFalse( + Box::new(Expression::Var("x".to_string())), + Box::new(Expression::CString("x should not be visible".to_string())), + )]))), + }); + + let stmt = Statement::Block(vec![test_one, test_two]); + + let results = run_tests(&stmt).unwrap(); + + assert_eq!(results.len(), 2); + + let r1 = &results[0]; + let r2 = &results[1]; + + assert_eq!(r1.name, "test_one"); + assert!(r1.result); + + assert_eq!(r2.name, "test_two"); + assert!(!r2.result); + assert_eq!(r2.error, Some("Variable 'x' not found".to_string())); // Erro é propagado de Expression::Var + } } } diff --git a/src/parser/parser_stmt.rs b/src/parser/parser_stmt.rs index a84c3cf..35180da 100644 --- a/src/parser/parser_stmt.rs +++ b/src/parser/parser_stmt.rs @@ -505,6 +505,33 @@ mod tests { assert_eq!(parsed, expected); } + #[test] + fn test_parse_test_function_definition_statement_valid_multiple_statements() { + let input = r#"test test_example(): + x = 1; + y = 2; + assert(x == 1, "x deveria ser 1"); + end"#; + let expected = Statement::TestDef(Function { + name: "test_example".to_string(), + kind: Type::TVoid, + params: vec![], + body: Some(Box::new(Statement::Block(vec![ + Statement::Assignment("x".to_string(), Box::new(Expression::CInt(1))), + Statement::Assignment("y".to_string(), Box::new(Expression::CInt(2))), + Statement::Assert( + Box::new(Expression::EQ( + Box::new(Expression::Var("x".to_string())), + Box::new(Expression::CInt(1)), + )), + Box::new(Expression::CString("x deveria ser 1".to_string())), + ), + ]))), + }); + let parsed = parse_test_function_definition_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + #[test] fn test_parse_test_function_definition_statement_with_spaces() { let input = "test test_spaces( ): x = 2; end"; diff --git a/src/type_checker/statement_type_checker.rs b/src/type_checker/statement_type_checker.rs index ec336da..e94bc5e 100644 --- a/src/type_checker/statement_type_checker.rs +++ b/src/type_checker/statement_type_checker.rs @@ -237,7 +237,6 @@ fn check_return_stmt( } } - fn check_assert( expr1: Box, expr2: Box, @@ -249,38 +248,50 @@ fn check_assert( if type1 != Type::TBool { Err("[Type Error] First Assert expression must be of type Boolean.".to_string()) } else if type2 != Type::TString { - Err("[Type Error] Second Assert expression must be of type CString.".to_string()) + Err("[Type Error] Second Assert expression must be of type String.".to_string()) } else { Ok(env.clone()) } } - -fn check_assert_true(expr1: Box, expr2: Box, env: &Environment) -> Result, ErrorMessage> { +fn check_assert_true( + expr1: Box, + expr2: Box, + env: &Environment, +) -> Result, ErrorMessage> { let expr_type = check_expr(*expr1, env)?; let expr_type2 = check_expr(*expr2, env)?; if expr_type != Type::TBool { Err("[Type Error] AssertTrue expression must be of type Boolean.".to_string()) } else if expr_type2 != Type::TString { - Err("[Type Error] Second Assert expression must be of type CString.".to_string()) + Err("[Type Error] Second AssertTrue expression must be of type String.".to_string()) } else { Ok(env.clone()) } } -fn check_assert_false(expr1: Box, expr2: Box, env: &Environment) -> Result, ErrorMessage> { +fn check_assert_false( + expr1: Box, + expr2: Box, + env: &Environment, +) -> Result, ErrorMessage> { let expr_type = check_expr(*expr1, env)?; let expr_type2 = check_expr(*expr2, env)?; if expr_type != Type::TBool { Err("[Type Error] AssertFalse expression must be of type Boolean.".to_string()) } else if expr_type2 != Type::TString { - Err("[Type Error] Second Assert expression must be of type CString.".to_string()) + Err("[Type Error] Second AssertFalse expression must be of type String.".to_string()) } else { Ok(env.clone()) } } -fn check_assert_eq(lhs: Box, rhs: Box, err: Box, env: &Environment) -> Result, ErrorMessage> { +fn check_assert_eq( + lhs: Box, + rhs: Box, + err: Box, + env: &Environment, +) -> Result, ErrorMessage> { let lhs_type = check_expr(*lhs, env)?; let rhs_type = check_expr(*rhs, env)?; let err_type = check_expr(*err, env)?; @@ -290,13 +301,18 @@ fn check_assert_eq(lhs: Box, rhs: Box, err: Box, rhs: Box, err: Box, env: &Environment) -> Result, ErrorMessage> { +fn check_assert_neq( + lhs: Box, + rhs: Box, + err: Box, + env: &Environment, +) -> Result, ErrorMessage> { let lhs_type = check_expr(*lhs, env)?; let rhs_type = check_expr(*rhs, env)?; let err_type = check_expr(*err, env)?; @@ -306,14 +322,12 @@ fn check_assert_neq(lhs: Box, rhs: Box, err: Box, env2: &Environment, @@ -743,187 +757,191 @@ mod tests { assert!(check_stmt(stmt, &env).is_err()); } -#[test] -fn test_assert_bool_ok() { - let env: Environment = Environment::new(); - let stmt = Statement::Assert( - Box::new(Expression::CTrue), - Box::new(Expression::CString("msg".to_string())), - ); - assert!(check_stmt(stmt, &env).is_ok()); -} - -#[test] -fn test_assert_bool_error() { - let env: Environment = Environment::new(); - let stmt = Statement::Assert( - Box::new(Expression::CInt(1)), // não booleano - Box::new(Expression::CString("msg".to_string())), // segundo argumento pode ser qualquer um válido - ); - assert!(check_stmt(stmt, &env).is_err()); -} - -#[test] -fn test_assert_true_ok() { - let env = Environment::new(); - let stmt = Statement::AssertTrue(Box::new(Expression::CTrue), Box::new(Expression::CString("ok".to_string()))); - assert!(check_stmt(stmt, &env).is_ok()); -} + #[test] + fn test_assert_bool_ok() { + let env: Environment = Environment::new(); + let stmt = Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString("msg".to_string())), + ); + assert!(check_stmt(stmt, &env).is_ok()); + } -#[test] -fn test_assert_false_ok() { - let env = Environment::new(); - let stmt = Statement::AssertFalse(Box::new(Expression::CFalse), Box::new(Expression::CString("false".to_string()))); - assert!(check_stmt(stmt, &env).is_ok()); -} + #[test] + fn test_assert_bool_error() { + let env: Environment = Environment::new(); + let stmt = Statement::Assert( + Box::new(Expression::CInt(1)), // não booleano + Box::new(Expression::CString("msg".to_string())), // segundo argumento pode ser qualquer um válido + ); + assert!(check_stmt(stmt, &env).is_err()); + } -#[test] -fn test_assert_eq_same_type() { - let env = Environment::new(); - let stmt = Statement::AssertEQ( - Box::new(Expression::CInt(1)), - Box::new(Expression::CInt(2)), - Box::new(Expression::CString("eq".to_string())), - ); - assert!(check_stmt(stmt, &env).is_ok()); -} + #[test] + fn test_assert_true_ok() { + let env = Environment::new(); + let stmt = Statement::AssertTrue( + Box::new(Expression::CTrue), + Box::new(Expression::CString("ok".to_string())), + ); + assert!(check_stmt(stmt, &env).is_ok()); + } -#[test] -fn test_assert_eq_mismatch_type() { - let env = Environment::new(); - let stmt = Statement::AssertEQ( - Box::new(Expression::CInt(1)), - Box::new(Expression::CString("x".to_string())), - Box::new(Expression::CString("eq".to_string())), - ); - assert!(check_stmt(stmt, &env).is_err()); -} + #[test] + fn test_assert_false_ok() { + let env = Environment::new(); + let stmt = Statement::AssertFalse( + Box::new(Expression::CFalse), + Box::new(Expression::CString("false".to_string())), + ); + assert!(check_stmt(stmt, &env).is_ok()); + } -#[test] -fn test_assert_neq_same_type() { - let env = Environment::new(); - let stmt = Statement::AssertNEQ( - Box::new(Expression::CInt(1)), - Box::new(Expression::CInt(2)), - Box::new(Expression::CString("neq".to_string())), - ); - assert!(check_stmt(stmt, &env).is_ok()); -} + #[test] + fn test_assert_eq_same_type() { + let env = Environment::new(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CInt(2)), + Box::new(Expression::CString("eq".to_string())), + ); + assert!(check_stmt(stmt, &env).is_ok()); + } -#[test] -fn test_assert_neq_mismatch_type() { - let env = Environment::new(); - let stmt = Statement::AssertNEQ( - Box::new(Expression::CTrue), - Box::new(Expression::CString("x".to_string())), - Box::new(Expression::CString("neq".to_string())), - ); - assert!(check_stmt(stmt, &env).is_err()); -} + #[test] + fn test_assert_eq_mismatch_type() { + let env = Environment::new(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CString("x".to_string())), + Box::new(Expression::CString("eq".to_string())), + ); + assert!(check_stmt(stmt, &env).is_err()); + } -#[test] -fn test_assert_error_msg_not_string() { - let env = Environment::new(); - let stmt = Statement::Assert( - Box::new(Expression::CTrue), - Box::new(Expression::CTrue), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_err()); -} + #[test] + fn test_assert_neq_same_type() { + let env = Environment::new(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CInt(2)), + Box::new(Expression::CString("neq".to_string())), + ); + assert!(check_stmt(stmt, &env).is_ok()); + } -#[test] -fn test_assert_true_error_msg_not_string() { - let env = Environment::new(); - let stmt = Statement::AssertTrue( - Box::new(Expression::CTrue), - Box::new(Expression::CTrue), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_err()); -} + #[test] + fn test_assert_neq_mismatch_type() { + let env = Environment::new(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CString("x".to_string())), + Box::new(Expression::CString("neq".to_string())), + ); + assert!(check_stmt(stmt, &env).is_err()); + } -#[test] -fn test_assert_false_error_msg_not_string() { - let env = Environment::new(); - let stmt = Statement::AssertFalse( - Box::new(Expression::CFalse), - Box::new(Expression::CTrue), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_err()); -} + #[test] + fn test_assert_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); + } -#[test] -fn test_assert_eq_error_msg_not_string() { - let env = Environment::new(); - let stmt = Statement::AssertEQ( - Box::new(Expression::CTrue), - Box::new(Expression::CTrue), - Box::new(Expression::CTrue), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_err()); -} + #[test] + fn test_assert_true_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::AssertTrue( + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); + } -#[test] -fn test_assert_neq_error_msg_not_string() { - let env = Environment::new(); - let stmt = Statement::AssertNEQ( - Box::new(Expression::CTrue), - Box::new(Expression::CFalse), - Box::new(Expression::CTrue), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_err()); -} + #[test] + fn test_assert_false_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::AssertFalse( + Box::new(Expression::CFalse), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); + } -#[test] -fn test_assert_error_msg_string() { - let env = Environment::new(); - let stmt = Statement::Assert( - Box::new(Expression::CTrue), - Box::new(Expression::CString("assert".to_string())), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_ok()); -} + #[test] + fn test_assert_eq_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); + } -#[test] -fn test_assert_true_error_msg_string() { - let env = Environment::new(); - let stmt = Statement::AssertTrue( - Box::new(Expression::CTrue), - Box::new(Expression::CString("asserttrue".to_string())), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_ok()); -} + #[test] + fn test_assert_neq_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CFalse), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); + } -#[test] -fn test_assert_false_error_msg_string() { - let env = Environment::new(); - let stmt = Statement::AssertFalse( - Box::new(Expression::CFalse), - Box::new(Expression::CString("assertfalse".to_string())), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_ok()); -} + #[test] + fn test_assert_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString("assert".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); + } -#[test] -fn test_assert_eq_error_msg_string() { - let env = Environment::new(); - let stmt = Statement::AssertEQ( - Box::new(Expression::CTrue), - Box::new(Expression::CTrue), - Box::new(Expression::CString("eq".to_string())), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_ok()); -} + #[test] + fn test_assert_true_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::AssertTrue( + Box::new(Expression::CTrue), + Box::new(Expression::CString("asserttrue".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); + } -#[test] -fn test_assert_neq_error_msg_string() { - let env = Environment::new(); - let stmt = Statement::AssertNEQ( - Box::new(Expression::CTrue), - Box::new(Expression::CFalse), - Box::new(Expression::CString("neq".to_string())), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_ok()); -} + #[test] + fn test_assert_false_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::AssertFalse( + Box::new(Expression::CFalse), + Box::new(Expression::CString("assertfalse".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); + } + #[test] + fn test_assert_eq_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), + Box::new(Expression::CString("eq".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); + } + #[test] + fn test_assert_neq_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CFalse), + Box::new(Expression::CString("neq".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); + } } From d28184caa29ad74c2c8222a37cb76308028868c2 Mon Sep 17 00:00:00 2001 From: Carlos-Vic <120799840+Carlos-Vic@users.noreply.github.com> Date: Tue, 1 Jul 2025 15:09:34 -0300 Subject: [PATCH 12/19] =?UTF-8?q?Implementa=C3=A7=C3=A3o=20e=20Testes=20de?= =?UTF-8?q?=20DefTest=20no=20TypeChecker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/type_checker/statement_type_checker.rs | 93 +++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/src/type_checker/statement_type_checker.rs b/src/type_checker/statement_type_checker.rs index e94bc5e..3fc131d 100644 --- a/src/type_checker/statement_type_checker.rs +++ b/src/type_checker/statement_type_checker.rs @@ -27,6 +27,7 @@ pub fn check_stmt( Statement::AssertFalse(expr1, errmsg) => check_assert_false(expr1, errmsg, env), Statement::AssertEQ(lhs, rhs, errmsg) => check_assert_eq(lhs, rhs, errmsg, env), Statement::AssertNEQ(lhs, rhs, errmsg) => check_assert_neq(lhs, rhs, errmsg, env), + Statement::TestDef(function) => check_test_function_stmt(function, env), _ => Err("Not implemented yet".to_string()), } @@ -328,6 +329,34 @@ fn check_assert_neq( } } +fn check_test_function_stmt( + function: Function, + env: &Environment, +) -> Result, ErrorMessage> { + if env.lookup_function(&function.name).is_some() { + return Err(format!("[Type Error] Test function '{}' already exists", function.name)); + } + if !function.params.is_empty() { + return Err("[Type Error] Test functions must not have parameters".into()); + } + if function.kind != Type::TVoid { + return Err("[Type Error] Test functions must return void".into()); + } + + let mut new_env = env.clone(); + new_env.push(); + + if let Some(body) = function.body.clone() { + new_env = check_stmt(*body, &new_env)?; + } + + new_env.pop(); + + let mut final_env = env.clone(); + final_env.map_function(function); + Ok(final_env) +} + fn merge_environments( env1: &Environment, env2: &Environment, @@ -365,7 +394,7 @@ fn merge_environments( } } } - + //TODO: should we merge ADTs and functions? Ok(merged) @@ -944,4 +973,66 @@ mod tests { ); assert!(check_stmt(stmt, &env).is_ok()); } + + #[test] + fn test_check_valid_test_function() { + let env: Environment = Environment::new(); + let stmt = TestDef(Function { + name: "valid_function".to_string(), + kind: Type::TVoid, + params: vec![], + body: None, + }); + + assert!(check_stmt(stmt, &env).is_ok()); + } + + #[test] + fn test_check_test_function_with_params() { + let env: Environment = Environment::new(); + let stmt = TestDef(Function { + name: "invalid_function".to_string(), + kind: Type::TVoid, + params: vec![FormalArgument::new("param".to_string(), Type::TString)], // Must have no parameters + body: None, + }); + + assert!(check_stmt(stmt, &env).is_err()); + } + + #[test] + fn test_check_test_function_with_non_void_return() { + let env: Environment = Environment::new(); + let stmt = TestDef(Function { + name: "invalid_function".to_string(), + kind: Type::TInteger, // Must be TVoid! + params: vec![], + body: None, + }); + + assert!(check_stmt(stmt, &env).is_err()); + } + + #[test] + fn test_check_duplicate_test_function() { + let mut env: Environment = Environment::new(); + let first_func = TestDef(Function { + name: "duplicate".to_string(), + kind: Type::TVoid, + params: vec![], + body: None, + }); + + env = check_stmt(first_func, &env).unwrap(); + + let stmt = TestDef(Function { + name: "duplicate".to_string(), + kind: Type::TVoid, + params: vec![], + body: None, + }); + + assert!(check_stmt(stmt, &env).is_err()); + } + } From d64cf50df2d08e484c3c1e53a51e373ffd8750df Mon Sep 17 00:00:00 2001 From: Carlos-Vic <120799840+Carlos-Vic@users.noreply.github.com> Date: Tue, 1 Jul 2025 16:16:30 -0300 Subject: [PATCH 13/19] =?UTF-8?q?Corre=C3=A7=C3=A3o=20na=20fun=C3=A7=C3=A3?= =?UTF-8?q?o=20check=5Ftest=5Ffunction=5Fstmt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Alterado lookup_function -> lookup_test Alterado map_function -> map_test --- src/type_checker/statement_type_checker.rs | 40 +++++++++++++++------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/type_checker/statement_type_checker.rs b/src/type_checker/statement_type_checker.rs index 3fc131d..0117789 100644 --- a/src/type_checker/statement_type_checker.rs +++ b/src/type_checker/statement_type_checker.rs @@ -333,27 +333,24 @@ fn check_test_function_stmt( function: Function, env: &Environment, ) -> Result, ErrorMessage> { - if env.lookup_function(&function.name).is_some() { - return Err(format!("[Type Error] Test function '{}' already exists", function.name)); + if env.lookup_test(&function.name).is_some() { + return Err(format!("[Type Error] Test function '{}' already exists.", function.name)); } if !function.params.is_empty() { - return Err("[Type Error] Test functions must not have parameters".into()); + return Err("[Type Error] Test functions must not have parameters.".into()); } if function.kind != Type::TVoid { - return Err("[Type Error] Test functions must return void".into()); + return Err("[Type Error] Test functions must return void.".into()); } - let mut new_env = env.clone(); - new_env.push(); - if let Some(body) = function.body.clone() { - new_env = check_stmt(*body, &new_env)?; + let mut scoped_env = env.clone(); + scoped_env.push(); + check_stmt(*body, &scoped_env)?; } - new_env.pop(); - let mut final_env = env.clone(); - final_env.map_function(function); + final_env.map_test(function); Ok(final_env) } @@ -981,9 +978,26 @@ mod tests { name: "valid_function".to_string(), kind: Type::TVoid, params: vec![], - body: None, + body: Some(Box::new(Block(vec![ + // 1. Declaramos as variáveis localmente + Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), + Statement::VarDeclaration("b".to_string(), Box::new(Expression::CInt(5))), + + // 2. Usamos AssertEQ para verificar o resultado + Statement::AssertEQ( + // Expressão: a + b + Box::new(Expression::Add( + Box::new(Expression::Var("a".to_string())), + Box::new(Expression::Var("b".to_string())) + )), + // Valor esperado: 15 + Box::new(Expression::CInt(15)), + // Mensagem de erro + Box::new(Expression::CString("A soma de 10 + 5 deveria ser 15".to_string())) + ) + ]))), }); - + assert!(check_stmt(stmt, &env).is_ok()); } From 630ca36da93feb0d21aff4636b7d2bc05fc84013 Mon Sep 17 00:00:00 2001 From: Carlos-Vic <120799840+Carlos-Vic@users.noreply.github.com> Date: Tue, 1 Jul 2025 17:21:12 -0300 Subject: [PATCH 14/19] =?UTF-8?q?Corre=C3=A7=C3=A3o=20da=20implementa?= =?UTF-8?q?=C3=A7=C3=A3o=20no=20TypeChecker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adicionada a função auxiliar check_block para validar blocos com escopo. Refatorada check_test_function_stmt para usar check_block, corrigir ownership e utilizar lookup_test/map_test. Criada suíte de testes unitários cobrindo casos válidos e inválidos para TestDef. --- src/type_checker/statement_type_checker.rs | 76 +++++++++++++++++----- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/src/type_checker/statement_type_checker.rs b/src/type_checker/statement_type_checker.rs index 0117789..fc64271 100644 --- a/src/type_checker/statement_type_checker.rs +++ b/src/type_checker/statement_type_checker.rs @@ -33,6 +33,25 @@ pub fn check_stmt( } } +pub fn check_block( + stmt: Statement, + env: &Environment, +) -> Result, ErrorMessage> { + match stmt { + Statement::Block(stmts) => { + let mut block_env = env.clone(); + block_env.push(); + + for s in stmts { + block_env = check_stmt(s, &block_env)?; + } + block_env.pop(); + Ok(block_env) + } + _ => Err("Expected a block statement".to_string()), + } +} + fn check_squence_stmt( stmt1: Box, stmt2: Box, @@ -333,7 +352,7 @@ fn check_test_function_stmt( function: Function, env: &Environment, ) -> Result, ErrorMessage> { - if env.lookup_test(&function.name).is_some() { + if env.lookup_test(&function.name).is_some() { return Err(format!("[Type Error] Test function '{}' already exists.", function.name)); } if !function.params.is_empty() { @@ -342,11 +361,8 @@ fn check_test_function_stmt( if function.kind != Type::TVoid { return Err("[Type Error] Test functions must return void.".into()); } - - if let Some(body) = function.body.clone() { - let mut scoped_env = env.clone(); - scoped_env.push(); - check_stmt(*body, &scoped_env)?; + if let Some(ref body) = function.body { + check_block((**body).clone(), env)?; } let mut final_env = env.clone(); @@ -979,25 +995,18 @@ mod tests { kind: Type::TVoid, params: vec![], body: Some(Box::new(Block(vec![ - // 1. Declaramos as variáveis localmente Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), Statement::VarDeclaration("b".to_string(), Box::new(Expression::CInt(5))), - - // 2. Usamos AssertEQ para verificar o resultado Statement::AssertEQ( - // Expressão: a + b Box::new(Expression::Add( Box::new(Expression::Var("a".to_string())), Box::new(Expression::Var("b".to_string())) )), - // Valor esperado: 15 Box::new(Expression::CInt(15)), - // Mensagem de erro - Box::new(Expression::CString("A soma de 10 + 5 deveria ser 15".to_string())) + Box::new(Expression::CString("A soma deveria ser 15".to_string())) ) ]))), }); - assert!(check_stmt(stmt, &env).is_ok()); } @@ -1021,7 +1030,18 @@ mod tests { name: "invalid_function".to_string(), kind: Type::TInteger, // Must be TVoid! params: vec![], - body: None, + body: Some(Box::new(Block(vec![ + Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), + Statement::VarDeclaration("b".to_string(), Box::new(Expression::CInt(5))), + Statement::AssertEQ( + Box::new(Expression::Add( + Box::new(Expression::Var("a".to_string())), + Box::new(Expression::Var("b".to_string())) + )), + Box::new(Expression::CInt(15)), + Box::new(Expression::CString("A soma deveria ser 15".to_string())) + ) + ]))), }); assert!(check_stmt(stmt, &env).is_err()); @@ -1034,7 +1054,18 @@ mod tests { name: "duplicate".to_string(), kind: Type::TVoid, params: vec![], - body: None, + body: Some(Box::new(Block(vec![ + Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), + Statement::VarDeclaration("b".to_string(), Box::new(Expression::CInt(5))), + Statement::AssertEQ( + Box::new(Expression::Add( + Box::new(Expression::Var("a".to_string())), + Box::new(Expression::Var("b".to_string())) + )), + Box::new(Expression::CInt(15)), + Box::new(Expression::CString("A soma deveria ser 15".to_string())) + ) + ]))), }); env = check_stmt(first_func, &env).unwrap(); @@ -1043,7 +1074,18 @@ mod tests { name: "duplicate".to_string(), kind: Type::TVoid, params: vec![], - body: None, + body: Some(Box::new(Block(vec![ + Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), + Statement::VarDeclaration("b".to_string(), Box::new(Expression::CInt(5))), + Statement::AssertEQ( + Box::new(Expression::Add( + Box::new(Expression::Var("a".to_string())), + Box::new(Expression::Var("b".to_string())) + )), + Box::new(Expression::CInt(15)), + Box::new(Expression::CString("A soma deveria ser 15".to_string())) + ) + ]))), }); assert!(check_stmt(stmt, &env).is_err()); From eec04f24694184e565134db1913a335cb981f06e Mon Sep 17 00:00:00 2001 From: Arthur Luiz <132619258+The3rdMega@users.noreply.github.com> Date: Wed, 2 Jul 2025 18:55:31 -0300 Subject: [PATCH 15/19] [Update] Prepared for Presentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Separados os testes a apresentar para o Bonifácio em Módulos para fácil acesso. - Adicionados TODO's para fácil navegação do código na apresentação. - Testes do TypeChecker de TesteDef agora checam se a mensagem de erro foi a mensagem desejada. --- src/environment/environment.rs | 2 +- src/interpreter/statement_execute.rs | 5 + src/parser/parser_stmt.rs | 291 +++++------ src/type_checker/statement_type_checker.rs | 548 +++++++++++---------- 4 files changed, 445 insertions(+), 401 deletions(-) diff --git a/src/environment/environment.rs b/src/environment/environment.rs index 77c149f..165e121 100644 --- a/src/environment/environment.rs +++ b/src/environment/environment.rs @@ -19,7 +19,7 @@ impl Scope { variables: HashMap::new(), functions: HashMap::new(), adts: HashMap::new(), - tests: IndexMap::new(), + tests: IndexMap::new(), //TODO: Apresentar Mudança no Environment } } diff --git a/src/interpreter/statement_execute.rs b/src/interpreter/statement_execute.rs index 7ccf60e..a1ce894 100644 --- a/src/interpreter/statement_execute.rs +++ b/src/interpreter/statement_execute.rs @@ -964,6 +964,7 @@ mod tests { } } + //TODO: Apresentar Interpretador Asserts (Tests) mod assert_statement_tests { use super::*; @@ -1142,6 +1143,8 @@ mod tests { assert_eq!(computation, "assertfalse fail".to_string()); } } + + //TODO: Apresentar Interpretador TestDef (Tests) mod testdef_statement_tests { use super::*; @@ -1166,6 +1169,8 @@ mod tests { } } } + + //TODO: Apresentar Interpretador RunTests (Tests) mod run_tests_tests { use super::*; diff --git a/src/parser/parser_stmt.rs b/src/parser/parser_stmt.rs index 35180da..8af4d75 100644 --- a/src/parser/parser_stmt.rs +++ b/src/parser/parser_stmt.rs @@ -18,7 +18,7 @@ use crate::parser::parser_common::{ RIGHT_PAREN, SEMICOLON_CHAR, VAL_KEYWORD, VAR_KEYWORD, WHILE_KEYWORD, }; use crate::parser::parser_expr::parse_expression; -use crate::parser::parser_type::parse_type; // TODO: Check if needed +use crate::parser::parser_type::parse_type; pub fn parse_statement(input: &str) -> IResult<&str, Statement> { alt(( @@ -489,146 +489,155 @@ mod tests { assert_eq!(parsed, expected); } - #[test] - fn test_parse_test_function_definition_statement_valid() { - let input = "test test_example(): x = 1; end"; - let expected = Statement::TestDef(Function { - name: "test_example".to_string(), - kind: Type::TVoid, - params: vec![], - body: Some(Box::new(Statement::Block(vec![Statement::Assignment( - "x".to_string(), - Box::new(Expression::CInt(1)), - )]))), - }); - let parsed = parse_test_function_definition_statement(input).unwrap().1; - assert_eq!(parsed, expected); - } - - #[test] - fn test_parse_test_function_definition_statement_valid_multiple_statements() { - let input = r#"test test_example(): - x = 1; - y = 2; - assert(x == 1, "x deveria ser 1"); - end"#; - let expected = Statement::TestDef(Function { - name: "test_example".to_string(), - kind: Type::TVoid, - params: vec![], - body: Some(Box::new(Statement::Block(vec![ - Statement::Assignment("x".to_string(), Box::new(Expression::CInt(1))), - Statement::Assignment("y".to_string(), Box::new(Expression::CInt(2))), - Statement::Assert( - Box::new(Expression::EQ( - Box::new(Expression::Var("x".to_string())), - Box::new(Expression::CInt(1)), - )), - Box::new(Expression::CString("x deveria ser 1".to_string())), - ), - ]))), - }); - let parsed = parse_test_function_definition_statement(input).unwrap().1; - assert_eq!(parsed, expected); + //TODO: Apresentar Parser de TestDef (Testes) + mod testdef_tests { + use super::*; + + #[test] + fn test_parse_test_function_definition_statement_valid() { + let input = "test test_example(): x = 1; end"; + let expected = Statement::TestDef(Function { + name: "test_example".to_string(), + kind: Type::TVoid, + params: vec![], + body: Some(Box::new(Statement::Block(vec![Statement::Assignment( + "x".to_string(), + Box::new(Expression::CInt(1)), + )]))), + }); + let parsed = parse_test_function_definition_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + + #[test] + fn test_parse_test_function_definition_statement_valid_multiple_statements() { + let input = r#"test test_example(): + x = 1; + y = 2; + assert(x == 1, "x deveria ser 1"); + end"#; + let expected = Statement::TestDef(Function { + name: "test_example".to_string(), + kind: Type::TVoid, + params: vec![], + body: Some(Box::new(Statement::Block(vec![ + Statement::Assignment("x".to_string(), Box::new(Expression::CInt(1))), + Statement::Assignment("y".to_string(), Box::new(Expression::CInt(2))), + Statement::Assert( + Box::new(Expression::EQ( + Box::new(Expression::Var("x".to_string())), + Box::new(Expression::CInt(1)), + )), + Box::new(Expression::CString("x deveria ser 1".to_string())), + ), + ]))), + }); + let parsed = parse_test_function_definition_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + + #[test] + fn test_parse_test_function_definition_statement_with_spaces() { + let input = "test test_spaces( ): x = 2; end"; + let expected = Statement::TestDef(Function { + name: "test_spaces".to_string(), + kind: Type::TVoid, + params: vec![], + body: Some(Box::new(Statement::Block(vec![Statement::Assignment( + "x".to_string(), + Box::new(Expression::CInt(2)), + )]))), + }); + let parsed = parse_test_function_definition_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + + #[test] + fn test_parse_test_function_definition_statement_args() { + let input = "test test_with_args(x: Int, y: Int): x = y; end"; + + // O parser deve falhar, pois funções de teste não podem ter argumentos. + let parsed = parse_test_function_definition_statement(input); + + assert!( + parsed.is_err(), + "Funções de teste com argumentos devem ser rejeitadas" + ); + } + + #[test] + fn test_parse_test_function_definition_statement_invalid_return() { + let input = "test test_with_invalid_return() -> Int: x = 2; end"; + + // O parser deve falhar, pois funções de teste não podem ter argumentos. + let parsed = parse_test_function_definition_statement(input); + + assert!( + parsed.is_err(), + "Funções de teste não devem especificar tipo de retorno" + ); + } + + #[test] + fn test_parse_test_function_definition_statement_valid_return_type() { + let input = "test test_with_valid_return() -> Boolean: x = 2; end"; + + let parsed = parse_test_function_definition_statement(input); + assert!( + parsed.is_err(), + "Funções de teste não devem especificar tipo de retorno" + ); + } } - - #[test] - fn test_parse_test_function_definition_statement_with_spaces() { - let input = "test test_spaces( ): x = 2; end"; - let expected = Statement::TestDef(Function { - name: "test_spaces".to_string(), - kind: Type::TVoid, - params: vec![], - body: Some(Box::new(Statement::Block(vec![Statement::Assignment( - "x".to_string(), + + //TODO: Apresentar Parser de Asserts (Testes) + mod assignment_tests { + use super::*; + + #[test] + fn test_parse_asserteq_statement() { + let input = "asserteq(1, 2, \"msg\")"; + let expected = Statement::AssertEQ( + Box::new(Expression::CInt(1)), Box::new(Expression::CInt(2)), - )]))), - }); - let parsed = parse_test_function_definition_statement(input).unwrap().1; - assert_eq!(parsed, expected); - } - - #[test] - fn test_parse_test_function_definition_statement_args() { - let input = "test test_with_args(x: Int, y: Int): x = y; end"; - - // O parser deve falhar, pois funções de teste não podem ter argumentos. - let parsed = parse_test_function_definition_statement(input); - - assert!( - parsed.is_err(), - "Funções de teste com argumentos devem ser rejeitadas" - ); + Box::new(Expression::CString("msg".to_string())), + ); + let parsed = parse_asserteq_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + + #[test] + fn test_parse_assertneq_statement() { + let input = "assertneq(3, 4, \"fail\")"; + let expected = Statement::AssertNEQ( + Box::new(Expression::CInt(3)), + Box::new(Expression::CInt(4)), + Box::new(Expression::CString("fail".to_string())), + ); + let parsed = parse_assertneq_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + + #[test] + fn test_parse_asserttrue_statement() { + let input = "asserttrue(True, \"should be true\")"; + let expected = Statement::AssertTrue( + Box::new(Expression::CTrue), + Box::new(Expression::CString("should be true".to_string())), + ); + let parsed = parse_asserttrue_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + + #[test] + fn test_parse_assertfalse_statement() { + let input = "assertfalse(False, \"should be false\")"; + let expected = Statement::AssertFalse( + Box::new(Expression::CFalse), + Box::new(Expression::CString("should be false".to_string())), + ); + let parsed = parse_assertfalse_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } } - - #[test] - fn test_parse_test_function_definition_statement_invalid_return() { - let input = "test test_with_invalid_return() -> Int: x = 2; end"; - - // O parser deve falhar, pois funções de teste não podem ter argumentos. - let parsed = parse_test_function_definition_statement(input); - - assert!( - parsed.is_err(), - "Funções de teste não devem especificar tipo de retorno" - ); - } - - #[test] - fn test_parse_test_function_definition_statement_valid_return_type() { - let input = "test test_with_valid_return() -> Boolean: x = 2; end"; - - let parsed = parse_test_function_definition_statement(input); - assert!( - parsed.is_err(), - "Funções de teste não devem especificar tipo de retorno" - ); - } - //TODO: Implementar testes para os ASSERTS - - #[test] - fn test_parse_asserteq_statement() { - let input = "asserteq(1, 2, \"msg\")"; - let expected = Statement::AssertEQ( - Box::new(Expression::CInt(1)), - Box::new(Expression::CInt(2)), - Box::new(Expression::CString("msg".to_string())), - ); - let parsed = parse_asserteq_statement(input).unwrap().1; - assert_eq!(parsed, expected); - } - - #[test] - fn test_parse_assertneq_statement() { - let input = "assertneq(3, 4, \"fail\")"; - let expected = Statement::AssertNEQ( - Box::new(Expression::CInt(3)), - Box::new(Expression::CInt(4)), - Box::new(Expression::CString("fail".to_string())), - ); - let parsed = parse_assertneq_statement(input).unwrap().1; - assert_eq!(parsed, expected); - } - - #[test] - fn test_parse_asserttrue_statement() { - let input = "asserttrue(True, \"should be true\")"; - let expected = Statement::AssertTrue( - Box::new(Expression::CTrue), - Box::new(Expression::CString("should be true".to_string())), - ); - let parsed = parse_asserttrue_statement(input).unwrap().1; - assert_eq!(parsed, expected); - } - - #[test] - fn test_parse_assertfalse_statement() { - let input = "assertfalse(False, \"should be false\")"; - let expected = Statement::AssertFalse( - Box::new(Expression::CFalse), - Box::new(Expression::CString("should be false".to_string())), - ); - let parsed = parse_assertfalse_statement(input).unwrap().1; - assert_eq!(parsed, expected); - } -} +} \ No newline at end of file diff --git a/src/type_checker/statement_type_checker.rs b/src/type_checker/statement_type_checker.rs index fc64271..0214647 100644 --- a/src/type_checker/statement_type_checker.rs +++ b/src/type_checker/statement_type_checker.rs @@ -809,286 +809,316 @@ mod tests { assert!(check_stmt(stmt, &env).is_ok()); } - #[test] - fn test_assert_bool_error() { - let env: Environment = Environment::new(); - let stmt = Statement::Assert( - Box::new(Expression::CInt(1)), // não booleano - Box::new(Expression::CString("msg".to_string())), // segundo argumento pode ser qualquer um válido - ); - assert!(check_stmt(stmt, &env).is_err()); - } + //TODO: Apresentar TypeChecker de Asserts (Testes) + mod assignment_tests { + use super::*; + + #[test] + fn test_assert_bool_error() { + let env: Environment = Environment::new(); + let stmt = Statement::Assert( + Box::new(Expression::CInt(1)), // não booleano + Box::new(Expression::CString("msg".to_string())), // segundo argumento pode ser qualquer um válido + ); + assert!(check_stmt(stmt, &env).is_err()); + } - #[test] - fn test_assert_true_ok() { - let env = Environment::new(); - let stmt = Statement::AssertTrue( - Box::new(Expression::CTrue), - Box::new(Expression::CString("ok".to_string())), - ); - assert!(check_stmt(stmt, &env).is_ok()); - } + #[test] + fn test_assert_true_ok() { + let env = Environment::new(); + let stmt = Statement::AssertTrue( + Box::new(Expression::CTrue), + Box::new(Expression::CString("ok".to_string())), + ); + assert!(check_stmt(stmt, &env).is_ok()); + } - #[test] - fn test_assert_false_ok() { - let env = Environment::new(); - let stmt = Statement::AssertFalse( - Box::new(Expression::CFalse), - Box::new(Expression::CString("false".to_string())), - ); - assert!(check_stmt(stmt, &env).is_ok()); - } + #[test] + fn test_assert_false_ok() { + let env = Environment::new(); + let stmt = Statement::AssertFalse( + Box::new(Expression::CFalse), + Box::new(Expression::CString("false".to_string())), + ); + assert!(check_stmt(stmt, &env).is_ok()); + } - #[test] - fn test_assert_eq_same_type() { - let env = Environment::new(); - let stmt = Statement::AssertEQ( - Box::new(Expression::CInt(1)), - Box::new(Expression::CInt(2)), - Box::new(Expression::CString("eq".to_string())), - ); - assert!(check_stmt(stmt, &env).is_ok()); - } + #[test] + fn test_assert_eq_same_type() { + let env = Environment::new(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CInt(2)), + Box::new(Expression::CString("eq".to_string())), + ); + assert!(check_stmt(stmt, &env).is_ok()); + } - #[test] - fn test_assert_eq_mismatch_type() { - let env = Environment::new(); - let stmt = Statement::AssertEQ( - Box::new(Expression::CInt(1)), - Box::new(Expression::CString("x".to_string())), - Box::new(Expression::CString("eq".to_string())), - ); - assert!(check_stmt(stmt, &env).is_err()); - } + #[test] + fn test_assert_eq_mismatch_type() { + let env = Environment::new(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CString("x".to_string())), + Box::new(Expression::CString("eq".to_string())), + ); + assert!(check_stmt(stmt, &env).is_err()); + } - #[test] - fn test_assert_neq_same_type() { - let env = Environment::new(); - let stmt = Statement::AssertNEQ( - Box::new(Expression::CInt(1)), - Box::new(Expression::CInt(2)), - Box::new(Expression::CString("neq".to_string())), - ); - assert!(check_stmt(stmt, &env).is_ok()); - } + #[test] + fn test_assert_neq_same_type() { + let env = Environment::new(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CInt(2)), + Box::new(Expression::CString("neq".to_string())), + ); + assert!(check_stmt(stmt, &env).is_ok()); + } - #[test] - fn test_assert_neq_mismatch_type() { - let env = Environment::new(); - let stmt = Statement::AssertNEQ( - Box::new(Expression::CTrue), - Box::new(Expression::CString("x".to_string())), - Box::new(Expression::CString("neq".to_string())), - ); - assert!(check_stmt(stmt, &env).is_err()); - } + #[test] + fn test_assert_neq_mismatch_type() { + let env = Environment::new(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CString("x".to_string())), + Box::new(Expression::CString("neq".to_string())), + ); + assert!(check_stmt(stmt, &env).is_err()); + } - #[test] - fn test_assert_error_msg_not_string() { - let env = Environment::new(); - let stmt = Statement::Assert( - Box::new(Expression::CTrue), - Box::new(Expression::CTrue), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_err()); - } + #[test] + fn test_assert_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); + } - #[test] - fn test_assert_true_error_msg_not_string() { - let env = Environment::new(); - let stmt = Statement::AssertTrue( - Box::new(Expression::CTrue), - Box::new(Expression::CTrue), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_err()); - } + #[test] + fn test_assert_true_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::AssertTrue( + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); + } - #[test] - fn test_assert_false_error_msg_not_string() { - let env = Environment::new(); - let stmt = Statement::AssertFalse( - Box::new(Expression::CFalse), - Box::new(Expression::CTrue), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_err()); - } + #[test] + fn test_assert_false_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::AssertFalse( + Box::new(Expression::CFalse), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); + } - #[test] - fn test_assert_eq_error_msg_not_string() { - let env = Environment::new(); - let stmt = Statement::AssertEQ( - Box::new(Expression::CTrue), - Box::new(Expression::CTrue), - Box::new(Expression::CTrue), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_err()); - } + #[test] + fn test_assert_eq_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); + } - #[test] - fn test_assert_neq_error_msg_not_string() { - let env = Environment::new(); - let stmt = Statement::AssertNEQ( - Box::new(Expression::CTrue), - Box::new(Expression::CFalse), - Box::new(Expression::CTrue), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_err()); - } + #[test] + fn test_assert_neq_error_msg_not_string() { + let env = Environment::new(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CFalse), + Box::new(Expression::CTrue), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_err()); + } - #[test] - fn test_assert_error_msg_string() { - let env = Environment::new(); - let stmt = Statement::Assert( - Box::new(Expression::CTrue), - Box::new(Expression::CString("assert".to_string())), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_ok()); - } + #[test] + fn test_assert_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::Assert( + Box::new(Expression::CTrue), + Box::new(Expression::CString("assert".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); + } - #[test] - fn test_assert_true_error_msg_string() { - let env = Environment::new(); - let stmt = Statement::AssertTrue( - Box::new(Expression::CTrue), - Box::new(Expression::CString("asserttrue".to_string())), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_ok()); - } + #[test] + fn test_assert_true_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::AssertTrue( + Box::new(Expression::CTrue), + Box::new(Expression::CString("asserttrue".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); + } - #[test] - fn test_assert_false_error_msg_string() { - let env = Environment::new(); - let stmt = Statement::AssertFalse( - Box::new(Expression::CFalse), - Box::new(Expression::CString("assertfalse".to_string())), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_ok()); - } + #[test] + fn test_assert_false_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::AssertFalse( + Box::new(Expression::CFalse), + Box::new(Expression::CString("assertfalse".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); + } - #[test] - fn test_assert_eq_error_msg_string() { - let env = Environment::new(); - let stmt = Statement::AssertEQ( - Box::new(Expression::CTrue), - Box::new(Expression::CTrue), - Box::new(Expression::CString("eq".to_string())), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_ok()); - } + #[test] + fn test_assert_eq_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::AssertEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CTrue), + Box::new(Expression::CString("eq".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); + } - #[test] - fn test_assert_neq_error_msg_string() { - let env = Environment::new(); - let stmt = Statement::AssertNEQ( - Box::new(Expression::CTrue), - Box::new(Expression::CFalse), - Box::new(Expression::CString("neq".to_string())), // Error message must be a string - ); - assert!(check_stmt(stmt, &env).is_ok()); + #[test] + fn test_assert_neq_error_msg_string() { + let env = Environment::new(); + let stmt = Statement::AssertNEQ( + Box::new(Expression::CTrue), + Box::new(Expression::CFalse), + Box::new(Expression::CString("neq".to_string())), // Error message must be a string + ); + assert!(check_stmt(stmt, &env).is_ok()); + } } - #[test] - fn test_check_valid_test_function() { - let env: Environment = Environment::new(); - let stmt = TestDef(Function { - name: "valid_function".to_string(), - kind: Type::TVoid, - params: vec![], - body: Some(Box::new(Block(vec![ - Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), - Statement::VarDeclaration("b".to_string(), Box::new(Expression::CInt(5))), - Statement::AssertEQ( - Box::new(Expression::Add( - Box::new(Expression::Var("a".to_string())), - Box::new(Expression::Var("b".to_string())) - )), - Box::new(Expression::CInt(15)), - Box::new(Expression::CString("A soma deveria ser 15".to_string())) - ) - ]))), - }); - assert!(check_stmt(stmt, &env).is_ok()); - } + //TODO: Apresentar TypeChecker de TestDef (Testes) + mod testdef_tests { + use super::*; - #[test] - fn test_check_test_function_with_params() { - let env: Environment = Environment::new(); - let stmt = TestDef(Function { - name: "invalid_function".to_string(), - kind: Type::TVoid, - params: vec![FormalArgument::new("param".to_string(), Type::TString)], // Must have no parameters - body: None, - }); + #[test] + fn test_check_valid_test_function() { + let env: Environment = Environment::new(); + let stmt = TestDef(Function { + name: "valid_function".to_string(), + kind: Type::TVoid, + params: vec![], + body: Some(Box::new(Block(vec![ + Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), + Statement::VarDeclaration("b".to_string(), Box::new(Expression::CInt(5))), + Statement::AssertEQ( + Box::new(Expression::Add( + Box::new(Expression::Var("a".to_string())), + Box::new(Expression::Var("b".to_string())) + )), + Box::new(Expression::CInt(15)), + Box::new(Expression::CString("A soma deveria ser 15".to_string())) + ) + ]))), + }); + assert!(check_stmt(stmt, &env).is_ok()); + } - assert!(check_stmt(stmt, &env).is_err()); - } + #[test] + fn test_check_test_function_with_params() { + let env: Environment = Environment::new(); + let stmt = TestDef(Function { + name: "invalid_function".to_string(), + kind: Type::TVoid, + params: vec![FormalArgument::new("param".to_string(), Type::TString)], // Must have no parameters + body: None, + }); + + assert!(check_stmt(stmt.clone(), &env).is_err()); + + let error = match check_stmt(stmt, &env){ + Ok(_) => "Expected an error, but got Ok".to_string(), + Err(error) => error, + }; + + assert_eq!(error,"[Type Error] Test functions must not have parameters.".to_string()); + } - #[test] - fn test_check_test_function_with_non_void_return() { - let env: Environment = Environment::new(); - let stmt = TestDef(Function { - name: "invalid_function".to_string(), - kind: Type::TInteger, // Must be TVoid! - params: vec![], - body: Some(Box::new(Block(vec![ - Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), - Statement::VarDeclaration("b".to_string(), Box::new(Expression::CInt(5))), - Statement::AssertEQ( - Box::new(Expression::Add( - Box::new(Expression::Var("a".to_string())), - Box::new(Expression::Var("b".to_string())) - )), - Box::new(Expression::CInt(15)), - Box::new(Expression::CString("A soma deveria ser 15".to_string())) - ) - ]))), - }); - - assert!(check_stmt(stmt, &env).is_err()); - } + #[test] + fn test_check_test_function_with_non_void_return() { + let env: Environment = Environment::new(); + let stmt = TestDef(Function { + name: "invalid_function".to_string(), + kind: Type::TInteger, // Must be TVoid! + params: vec![], + body: Some(Box::new(Block(vec![ + Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), + Statement::VarDeclaration("b".to_string(), Box::new(Expression::CInt(5))), + Statement::AssertEQ( + Box::new(Expression::Add( + Box::new(Expression::Var("a".to_string())), + Box::new(Expression::Var("b".to_string())) + )), + Box::new(Expression::CInt(15)), + Box::new(Expression::CString("A soma deveria ser 15".to_string())) + ) + ]))), + }); + + assert!(check_stmt(stmt.clone(), &env).is_err()); - #[test] - fn test_check_duplicate_test_function() { - let mut env: Environment = Environment::new(); - let first_func = TestDef(Function { - name: "duplicate".to_string(), - kind: Type::TVoid, - params: vec![], - body: Some(Box::new(Block(vec![ - Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), - Statement::VarDeclaration("b".to_string(), Box::new(Expression::CInt(5))), - Statement::AssertEQ( - Box::new(Expression::Add( - Box::new(Expression::Var("a".to_string())), - Box::new(Expression::Var("b".to_string())) - )), - Box::new(Expression::CInt(15)), - Box::new(Expression::CString("A soma deveria ser 15".to_string())) - ) - ]))), - }); - - env = check_stmt(first_func, &env).unwrap(); - - let stmt = TestDef(Function { - name: "duplicate".to_string(), - kind: Type::TVoid, - params: vec![], - body: Some(Box::new(Block(vec![ - Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), - Statement::VarDeclaration("b".to_string(), Box::new(Expression::CInt(5))), - Statement::AssertEQ( - Box::new(Expression::Add( - Box::new(Expression::Var("a".to_string())), - Box::new(Expression::Var("b".to_string())) - )), - Box::new(Expression::CInt(15)), - Box::new(Expression::CString("A soma deveria ser 15".to_string())) - ) - ]))), - }); - - assert!(check_stmt(stmt, &env).is_err()); - } + let error = match check_stmt(stmt, &env){ + Ok(_) => "Expected an error, but got Ok".to_string(), + Err(error) => error, + }; + + assert_eq!(error,"[Type Error] Test functions must return void.".to_string()); + } + #[test] + fn test_check_duplicate_test_function() { + let mut env: Environment = Environment::new(); + let first_func = TestDef(Function { + name: "duplicate".to_string(), + kind: Type::TVoid, + params: vec![], + body: Some(Box::new(Block(vec![ + Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), + Statement::VarDeclaration("b".to_string(), Box::new(Expression::CInt(5))), + Statement::AssertEQ( + Box::new(Expression::Add( + Box::new(Expression::Var("a".to_string())), + Box::new(Expression::Var("b".to_string())) + )), + Box::new(Expression::CInt(15)), + Box::new(Expression::CString("A soma deveria ser 15".to_string())) + ) + ]))), + }); + + env = check_stmt(first_func, &env).unwrap(); + + let stmt = TestDef(Function { + name: "duplicate".to_string(), + kind: Type::TVoid, + params: vec![], + body: Some(Box::new(Block(vec![ + Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), + Statement::VarDeclaration("b".to_string(), Box::new(Expression::CInt(5))), + Statement::AssertEQ( + Box::new(Expression::Add( + Box::new(Expression::Var("a".to_string())), + Box::new(Expression::Var("b".to_string())) + )), + Box::new(Expression::CInt(15)), + Box::new(Expression::CString("A soma deveria ser 15".to_string())) + ) + ]))), + }); + + assert!(check_stmt(stmt.clone(), &env).is_err()); + + let error = match check_stmt(stmt, &env){ + Ok(_) => "Expected an error, but got Ok".to_string(), + Err(error) => error, + }; + + assert_eq!(error,"[Type Error] Test function 'duplicate' already exists.".to_string()); + } + } } From 11e6eab4d820c74ef94fad072c0da8b6dd48640f Mon Sep 17 00:00:00 2001 From: Arthur Luiz <132619258+The3rdMega@users.noreply.github.com> Date: Wed, 2 Jul 2025 19:37:25 -0300 Subject: [PATCH 16/19] [Update] New Parser Assert Tests that check parse Error Also cleaned some TODO's --- src/parser/keywords.rs | 1 + src/parser/parser_common.rs | 1 + src/parser/parser_stmt.rs | 144 ++++++++++++++++++--- src/type_checker/statement_type_checker.rs | 2 +- 4 files changed, 128 insertions(+), 20 deletions(-) diff --git a/src/parser/keywords.rs b/src/parser/keywords.rs index 5d528d8..d4f02dd 100644 --- a/src/parser/keywords.rs +++ b/src/parser/keywords.rs @@ -21,4 +21,5 @@ pub const KEYWORDS: &[&str] = &[ "not", "True", "False", + "test", ]; diff --git a/src/parser/parser_common.rs b/src/parser/parser_common.rs index 1d239ea..bb739d3 100644 --- a/src/parser/parser_common.rs +++ b/src/parser/parser_common.rs @@ -40,6 +40,7 @@ pub const ASSERTFALSE_KEYWORD: &str = "assertfalse"; pub const VAR_KEYWORD: &str = "var"; pub const VAL_KEYWORD: &str = "val"; pub const DEF_KEYWORD: &str = "def"; +pub const TEST_KEYWORD: &str = "test"; // Operator and symbol constants pub const FUNCTION_ARROW: &str = "->"; diff --git a/src/parser/parser_stmt.rs b/src/parser/parser_stmt.rs index 8af4d75..9150951 100644 --- a/src/parser/parser_stmt.rs +++ b/src/parser/parser_stmt.rs @@ -33,7 +33,7 @@ pub fn parse_statement(input: &str) -> IResult<&str, Statement> { parse_assertneq_statement, parse_assertfalse_statement, parse_asserttrue_statement, - parse_test_function_definition_statement, // FIXME: Being Implemented + parse_test_function_definition_statement, parse_function_definition_statement, ))(input) } @@ -300,17 +300,16 @@ fn parse_function_definition_statement(input: &str) -> IResult<&str, Statement> )(input) } -// TODO: alterar, não há mais necessidade de checar se o nome é test, usaremos uma keyword "test" + fn parse_test_function_definition_statement(input: &str) -> IResult<&str, Statement> { map( tuple(( - //keyword(DEF_KEYWORD), + //keyword(TEST_KEYWORD), tag("test"), preceded(multispace1, identifier), - //identifier, delimited( char::<&str, Error<&str>>(LEFT_PAREN), - multispace0, // Permite `()` com espaços, mas sem argumentos + multispace0, char::<&str, Error<&str>>(RIGHT_PAREN), ), parse_block, @@ -420,19 +419,7 @@ mod tests { assert_eq!(parsed, expected); } - #[test] - fn test_parse_assert_statement() { - let input = "assert(1 == 2, \"expecting an error\")"; - let expected = Statement::Assert( - Box::new(Expression::EQ( - Box::new(Expression::CInt(1)), - Box::new(Expression::CInt(2)), - )), - Box::new(Expression::CString("expecting an error".to_string())), - ); - let parsed = parse_assert_statement(input).unwrap().1; - assert_eq!(parsed, expected); - } + #[test] #[ignore] @@ -591,9 +578,23 @@ mod tests { } //TODO: Apresentar Parser de Asserts (Testes) - mod assignment_tests { + mod assert_tests { use super::*; + #[test] + fn test_parse_assert_statement() { + let input = "assert(1 == 2, \"expecting an error\")"; + let expected = Statement::Assert( + Box::new(Expression::EQ( + Box::new(Expression::CInt(1)), + Box::new(Expression::CInt(2)), + )), + Box::new(Expression::CString("expecting an error".to_string())), + ); + let parsed = parse_assert_statement(input).unwrap().1; + assert_eq!(parsed, expected); + } + #[test] fn test_parse_asserteq_statement() { let input = "asserteq(1, 2, \"msg\")"; @@ -639,5 +640,110 @@ mod tests { let parsed = parse_assertfalse_statement(input).unwrap().1; assert_eq!(parsed, expected); } + + #[test] + fn test_parse_assert_statement_invalid_argnumber() { + let input = "assert(False, False, \"should be false\")"; + + let result = std::panic::catch_unwind(|| { + parse_assert_statement(input) + }); + + assert!(result.is_err(), "Expected panic for invalid number of arguments"); + + if let Err(err) = result { + if let Some(s) = err.downcast_ref::<&str>() { + assert_eq!(*s, "Assert statement requires exactly 2 arguments"); + } else if let Some(s) = err.downcast_ref::() { + assert_eq!(s, "Assert statement requires exactly 2 arguments"); + } else { + panic!("Panic occurred, but message is not a string"); + } + } + } + + #[test] + fn test_parse_asserteq_statement_invalid_argnumber() { + let input = "asserteq(1, 2, 3, \"msg\")"; + + let result = std::panic::catch_unwind(|| { + parse_asserteq_statement(input) + }); + + assert!(result.is_err(), "Expected panic for invalid number of arguments"); + + if let Err(err) = result { + if let Some(s) = err.downcast_ref::<&str>() { + assert_eq!(*s, "AssertEQ statement requires exactly 3 arguments"); + } else if let Some(s) = err.downcast_ref::() { + assert_eq!(s, "AssertEQ statement requires exactly 3 arguments"); + } else { + panic!("Panic occurred, but message is not a string"); + } + } + } + + #[test] + fn test_parse_assertneq_statement_invalid_argnumber() { + let input = "assertneq(3, 4, 5, \"fail\")"; + + let result = std::panic::catch_unwind(|| { + parse_assertneq_statement(input) + }); + + assert!(result.is_err(), "Expected panic for invalid number of arguments"); + + if let Err(err) = result { + if let Some(s) = err.downcast_ref::<&str>() { + assert_eq!(*s, "AssertNEQ statement requires exactly 3 arguments"); + } else if let Some(s) = err.downcast_ref::() { + assert_eq!(s, "AssertNEQ statement requires exactly 3 arguments"); + } else { + panic!("Panic occurred, but message is not a string"); + } + } + } + + #[test] + fn test_parse_asserttrue_statement_invalid_argnumber() { + let input = "asserttrue(True, True, \"should be true\")"; + + let result = std::panic::catch_unwind(|| { + parse_asserttrue_statement(input) + }); + + assert!(result.is_err(), "Expected panic for invalid number of arguments"); + + if let Err(err) = result { + if let Some(s) = err.downcast_ref::<&str>() { + assert_eq!(*s, "AssertTrue statement requires exactly 2 arguments"); + } else if let Some(s) = err.downcast_ref::() { + assert_eq!(s, "AssertTrue statement requires exactly 2 arguments"); + } else { + panic!("Panic occurred, but message is not a string"); + } + } + } + + #[test] + fn test_parse_assertfalse_statement_invalid_argnumber() { + let input = "assertfalse(False, False, \"should be false\")"; + + let result = std::panic::catch_unwind(|| { + parse_assertfalse_statement(input) + }); + + assert!(result.is_err(), "Expected panic for invalid number of arguments"); + if let Err(err) = result { + if let Some(s) = err.downcast_ref::<&str>() { + assert_eq!(*s, "AssertFalse statement requires exactly 2 arguments"); + } else if let Some(s) = err.downcast_ref::() { + assert_eq!(s, "AssertFalse statement requires exactly 2 arguments"); + } else { + panic!("Panic occurred, but message is not a string"); + } + } + } + } } \ No newline at end of file diff --git a/src/type_checker/statement_type_checker.rs b/src/type_checker/statement_type_checker.rs index 0214647..a69dbdc 100644 --- a/src/type_checker/statement_type_checker.rs +++ b/src/type_checker/statement_type_checker.rs @@ -810,7 +810,7 @@ mod tests { } //TODO: Apresentar TypeChecker de Asserts (Testes) - mod assignment_tests { + mod assert_tests { use super::*; #[test] From a8d0f5bff8be6213c8af5feda284d980852b155e Mon Sep 17 00:00:00 2001 From: Arthur Luiz <132619258+The3rdMega@users.noreply.github.com> Date: Wed, 2 Jul 2025 19:49:13 -0300 Subject: [PATCH 17/19] =?UTF-8?q?[Update]=20Mais=20TODO's=20de=20Apresenta?= =?UTF-8?q?=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interpreter/statement_execute.rs | 4 +++- src/parser/parser_stmt.rs | 3 ++- src/type_checker/statement_type_checker.rs | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/interpreter/statement_execute.rs b/src/interpreter/statement_execute.rs index a1ce894..bae4e66 100644 --- a/src/interpreter/statement_execute.rs +++ b/src/interpreter/statement_execute.rs @@ -33,6 +33,7 @@ pub fn run( } } +//TODO: Apresentar RunTests pub fn run_tests(stmt: &Statement) -> Result, String> { let env = match run(stmt.clone(), &Environment::new()) { Ok(env) => env, @@ -85,7 +86,7 @@ pub fn execute(stmt: Statement, env: &Environment) -> Result { let value = match eval(*exp, &new_env)? { ExpressionResult::Value(expr) => expr, @@ -359,6 +360,7 @@ pub fn execute(stmt: Statement, env: &Environment) -> Result { new_env.map_test(teste.clone()); Ok(Computation::Continue(new_env)) diff --git a/src/parser/parser_stmt.rs b/src/parser/parser_stmt.rs index 9150951..ec02911 100644 --- a/src/parser/parser_stmt.rs +++ b/src/parser/parser_stmt.rs @@ -130,6 +130,7 @@ fn parse_for_statement(input: &str) -> IResult<&str, Statement> { )(input) } +//TODO: Apresentar Asserts fn parse_assert_statement(input: &str) -> IResult<&str, Statement> { map( tuple(( @@ -300,7 +301,7 @@ fn parse_function_definition_statement(input: &str) -> IResult<&str, Statement> )(input) } - +//TODO: Apresentar TestDef fn parse_test_function_definition_statement(input: &str) -> IResult<&str, Statement> { map( tuple(( diff --git a/src/type_checker/statement_type_checker.rs b/src/type_checker/statement_type_checker.rs index a69dbdc..3bd8a19 100644 --- a/src/type_checker/statement_type_checker.rs +++ b/src/type_checker/statement_type_checker.rs @@ -256,7 +256,7 @@ fn check_return_stmt( } } } - +//TODO: Apresentar Asserts fn check_assert( expr1: Box, expr2: Box, @@ -348,6 +348,7 @@ fn check_assert_neq( } } +//TODO: Apresentar TestDef fn check_test_function_stmt( function: Function, env: &Environment, From 3ecc4ebcbfbc4b77acf27627ac7c186da87f65f3 Mon Sep 17 00:00:00 2001 From: Arthur Luiz <132619258+The3rdMega@users.noreply.github.com> Date: Fri, 4 Jul 2025 10:32:26 -0300 Subject: [PATCH 18/19] Made cargo fmt --- src/interpreter/statement_execute.rs | 2 +- src/parser/parser_stmt.rs | 68 ++++++++++---------- src/type_checker/statement_type_checker.rs | 74 +++++++++++++--------- 3 files changed, 79 insertions(+), 65 deletions(-) diff --git a/src/interpreter/statement_execute.rs b/src/interpreter/statement_execute.rs index bae4e66..d2ffc66 100644 --- a/src/interpreter/statement_execute.rs +++ b/src/interpreter/statement_execute.rs @@ -1145,7 +1145,7 @@ mod tests { assert_eq!(computation, "assertfalse fail".to_string()); } } - + //TODO: Apresentar Interpretador TestDef (Tests) mod testdef_statement_tests { use super::*; diff --git a/src/parser/parser_stmt.rs b/src/parser/parser_stmt.rs index ec02911..264412a 100644 --- a/src/parser/parser_stmt.rs +++ b/src/parser/parser_stmt.rs @@ -33,7 +33,7 @@ pub fn parse_statement(input: &str) -> IResult<&str, Statement> { parse_assertneq_statement, parse_assertfalse_statement, parse_asserttrue_statement, - parse_test_function_definition_statement, + parse_test_function_definition_statement, parse_function_definition_statement, ))(input) } @@ -310,7 +310,7 @@ fn parse_test_function_definition_statement(input: &str) -> IResult<&str, Statem preceded(multispace1, identifier), delimited( char::<&str, Error<&str>>(LEFT_PAREN), - multispace0, + multispace0, char::<&str, Error<&str>>(RIGHT_PAREN), ), parse_block, @@ -420,8 +420,6 @@ mod tests { assert_eq!(parsed, expected); } - - #[test] #[ignore] fn test_parse_function_definition_statement() { @@ -480,7 +478,7 @@ mod tests { //TODO: Apresentar Parser de TestDef (Testes) mod testdef_tests { use super::*; - + #[test] fn test_parse_test_function_definition_statement_valid() { let input = "test test_example(): x = 1; end"; @@ -577,7 +575,7 @@ mod tests { ); } } - + //TODO: Apresentar Parser de Asserts (Testes) mod assert_tests { use super::*; @@ -645,12 +643,13 @@ mod tests { #[test] fn test_parse_assert_statement_invalid_argnumber() { let input = "assert(False, False, \"should be false\")"; - - let result = std::panic::catch_unwind(|| { - parse_assert_statement(input) - }); - assert!(result.is_err(), "Expected panic for invalid number of arguments"); + let result = std::panic::catch_unwind(|| parse_assert_statement(input)); + + assert!( + result.is_err(), + "Expected panic for invalid number of arguments" + ); if let Err(err) = result { if let Some(s) = err.downcast_ref::<&str>() { @@ -666,12 +665,13 @@ mod tests { #[test] fn test_parse_asserteq_statement_invalid_argnumber() { let input = "asserteq(1, 2, 3, \"msg\")"; - - let result = std::panic::catch_unwind(|| { - parse_asserteq_statement(input) - }); - assert!(result.is_err(), "Expected panic for invalid number of arguments"); + let result = std::panic::catch_unwind(|| parse_asserteq_statement(input)); + + assert!( + result.is_err(), + "Expected panic for invalid number of arguments" + ); if let Err(err) = result { if let Some(s) = err.downcast_ref::<&str>() { @@ -687,12 +687,13 @@ mod tests { #[test] fn test_parse_assertneq_statement_invalid_argnumber() { let input = "assertneq(3, 4, 5, \"fail\")"; - - let result = std::panic::catch_unwind(|| { - parse_assertneq_statement(input) - }); - assert!(result.is_err(), "Expected panic for invalid number of arguments"); + let result = std::panic::catch_unwind(|| parse_assertneq_statement(input)); + + assert!( + result.is_err(), + "Expected panic for invalid number of arguments" + ); if let Err(err) = result { if let Some(s) = err.downcast_ref::<&str>() { @@ -708,12 +709,13 @@ mod tests { #[test] fn test_parse_asserttrue_statement_invalid_argnumber() { let input = "asserttrue(True, True, \"should be true\")"; - - let result = std::panic::catch_unwind(|| { - parse_asserttrue_statement(input) - }); - assert!(result.is_err(), "Expected panic for invalid number of arguments"); + let result = std::panic::catch_unwind(|| parse_asserttrue_statement(input)); + + assert!( + result.is_err(), + "Expected panic for invalid number of arguments" + ); if let Err(err) = result { if let Some(s) = err.downcast_ref::<&str>() { @@ -729,12 +731,13 @@ mod tests { #[test] fn test_parse_assertfalse_statement_invalid_argnumber() { let input = "assertfalse(False, False, \"should be false\")"; - - let result = std::panic::catch_unwind(|| { - parse_assertfalse_statement(input) - }); - assert!(result.is_err(), "Expected panic for invalid number of arguments"); + let result = std::panic::catch_unwind(|| parse_assertfalse_statement(input)); + + assert!( + result.is_err(), + "Expected panic for invalid number of arguments" + ); if let Err(err) = result { if let Some(s) = err.downcast_ref::<&str>() { assert_eq!(*s, "AssertFalse statement requires exactly 2 arguments"); @@ -745,6 +748,5 @@ mod tests { } } } - } -} \ No newline at end of file +} diff --git a/src/type_checker/statement_type_checker.rs b/src/type_checker/statement_type_checker.rs index 3bd8a19..a428a82 100644 --- a/src/type_checker/statement_type_checker.rs +++ b/src/type_checker/statement_type_checker.rs @@ -41,13 +41,13 @@ pub fn check_block( Statement::Block(stmts) => { let mut block_env = env.clone(); block_env.push(); - + for s in stmts { block_env = check_stmt(s, &block_env)?; - } - block_env.pop(); + } + block_env.pop(); Ok(block_env) - } + } _ => Err("Expected a block statement".to_string()), } } @@ -354,7 +354,10 @@ fn check_test_function_stmt( env: &Environment, ) -> Result, ErrorMessage> { if env.lookup_test(&function.name).is_some() { - return Err(format!("[Type Error] Test function '{}' already exists.", function.name)); + return Err(format!( + "[Type Error] Test function '{}' already exists.", + function.name + )); } if !function.params.is_empty() { return Err("[Type Error] Test functions must not have parameters.".into()); @@ -408,7 +411,7 @@ fn merge_environments( } } } - + //TODO: should we merge ADTs and functions? Ok(merged) @@ -996,7 +999,7 @@ mod tests { //TODO: Apresentar TypeChecker de TestDef (Testes) mod testdef_tests { use super::*; - + #[test] fn test_check_valid_test_function() { let env: Environment = Environment::new(); @@ -1010,16 +1013,16 @@ mod tests { Statement::AssertEQ( Box::new(Expression::Add( Box::new(Expression::Var("a".to_string())), - Box::new(Expression::Var("b".to_string())) + Box::new(Expression::Var("b".to_string())), )), Box::new(Expression::CInt(15)), - Box::new(Expression::CString("A soma deveria ser 15".to_string())) - ) + Box::new(Expression::CString("A soma deveria ser 15".to_string())), + ), ]))), }); assert!(check_stmt(stmt, &env).is_ok()); } - + #[test] fn test_check_test_function_with_params() { let env: Environment = Environment::new(); @@ -1032,12 +1035,15 @@ mod tests { assert!(check_stmt(stmt.clone(), &env).is_err()); - let error = match check_stmt(stmt, &env){ + let error = match check_stmt(stmt, &env) { Ok(_) => "Expected an error, but got Ok".to_string(), Err(error) => error, }; - assert_eq!(error,"[Type Error] Test functions must not have parameters.".to_string()); + assert_eq!( + error, + "[Type Error] Test functions must not have parameters.".to_string() + ); } #[test] @@ -1045,7 +1051,7 @@ mod tests { let env: Environment = Environment::new(); let stmt = TestDef(Function { name: "invalid_function".to_string(), - kind: Type::TInteger, // Must be TVoid! + kind: Type::TInteger, // Must be TVoid! params: vec![], body: Some(Box::new(Block(vec![ Statement::VarDeclaration("a".to_string(), Box::new(Expression::CInt(10))), @@ -1053,22 +1059,25 @@ mod tests { Statement::AssertEQ( Box::new(Expression::Add( Box::new(Expression::Var("a".to_string())), - Box::new(Expression::Var("b".to_string())) + Box::new(Expression::Var("b".to_string())), )), Box::new(Expression::CInt(15)), - Box::new(Expression::CString("A soma deveria ser 15".to_string())) - ) + Box::new(Expression::CString("A soma deveria ser 15".to_string())), + ), ]))), }); - + assert!(check_stmt(stmt.clone(), &env).is_err()); - let error = match check_stmt(stmt, &env){ + let error = match check_stmt(stmt, &env) { Ok(_) => "Expected an error, but got Ok".to_string(), Err(error) => error, }; - assert_eq!(error,"[Type Error] Test functions must return void.".to_string()); + assert_eq!( + error, + "[Type Error] Test functions must return void.".to_string() + ); } #[test] @@ -1084,16 +1093,16 @@ mod tests { Statement::AssertEQ( Box::new(Expression::Add( Box::new(Expression::Var("a".to_string())), - Box::new(Expression::Var("b".to_string())) + Box::new(Expression::Var("b".to_string())), )), Box::new(Expression::CInt(15)), - Box::new(Expression::CString("A soma deveria ser 15".to_string())) - ) + Box::new(Expression::CString("A soma deveria ser 15".to_string())), + ), ]))), }); - + env = check_stmt(first_func, &env).unwrap(); - + let stmt = TestDef(Function { name: "duplicate".to_string(), kind: Type::TVoid, @@ -1104,22 +1113,25 @@ mod tests { Statement::AssertEQ( Box::new(Expression::Add( Box::new(Expression::Var("a".to_string())), - Box::new(Expression::Var("b".to_string())) + Box::new(Expression::Var("b".to_string())), )), Box::new(Expression::CInt(15)), - Box::new(Expression::CString("A soma deveria ser 15".to_string())) - ) + Box::new(Expression::CString("A soma deveria ser 15".to_string())), + ), ]))), }); - + assert!(check_stmt(stmt.clone(), &env).is_err()); - let error = match check_stmt(stmt, &env){ + let error = match check_stmt(stmt, &env) { Ok(_) => "Expected an error, but got Ok".to_string(), Err(error) => error, }; - assert_eq!(error,"[Type Error] Test function 'duplicate' already exists.".to_string()); + assert_eq!( + error, + "[Type Error] Test function 'duplicate' already exists.".to_string() + ); } } } From 7a5671e3b74e2504a846dcf715d52072cd8da77a Mon Sep 17 00:00:00 2001 From: Arthur Luiz <132619258+The3rdMega@users.noreply.github.com> Date: Fri, 4 Jul 2025 11:40:37 -0300 Subject: [PATCH 19/19] =?UTF-8?q?Altera=C3=A7=C3=B5es=20do=20Bonif=C3=A1ci?= =?UTF-8?q?o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/environment/environment.rs | 2 +- src/interpreter/statement_execute.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/environment/environment.rs b/src/environment/environment.rs index 165e121..2c7c64e 100644 --- a/src/environment/environment.rs +++ b/src/environment/environment.rs @@ -131,7 +131,7 @@ impl Environment { self.globals.lookup_test(name) } - pub fn scrape_tests(&self) -> Vec { + pub fn get_all_tests(&self) -> Vec { let mut tests = Vec::new(); for scope in self.stack.iter() { for test in scope.tests.values() { diff --git a/src/interpreter/statement_execute.rs b/src/interpreter/statement_execute.rs index d2ffc66..068def6 100644 --- a/src/interpreter/statement_execute.rs +++ b/src/interpreter/statement_execute.rs @@ -42,7 +42,7 @@ pub fn run_tests(stmt: &Statement) -> Result, String> { let mut results = Vec::new(); - for test in env.scrape_tests() { + for test in env.get_all_tests() { let mut test_env = env.clone(); test_env.push();