From dc6880417b161ef7c0641b3fac9d8d3276422f84 Mon Sep 17 00:00:00 2001 From: rafael-pf Date: Thu, 16 Apr 2026 20:49:07 -0300 Subject: [PATCH 01/26] add examples --- examples/hello.minic | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 examples/hello.minic diff --git a/examples/hello.minic b/examples/hello.minic new file mode 100644 index 0000000..456ac27 --- /dev/null +++ b/examples/hello.minic @@ -0,0 +1,4 @@ +void main() { + str name = "Alice"; + print(name); +} \ No newline at end of file From 9b98fc03d4f0ee8af7179fb2e62efcd35f1e444c Mon Sep 17 00:00:00 2001 From: Marcelo Arcoverde Date: Thu, 16 Apr 2026 21:02:26 -0300 Subject: [PATCH 02/26] add relative path to nix shell --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index fd81e57..755cce4 100644 --- a/flake.nix +++ b/flake.nix @@ -23,7 +23,7 @@ ]; shellHook = '' - export PS1="MiniC ❄️ > " + export PS1="MiniC ❄️ \[\033[01;34m\]\w\[\033[00m\] > " ''; }; } From fa116a95b9d3cd2c25fbe99202417d30a3d1f45b Mon Sep 17 00:00:00 2001 From: rafael-pf Date: Sun, 19 Apr 2026 15:13:45 -0300 Subject: [PATCH 03/26] feat: add T* support in AST and parser --- src/ir/ast.rs | 1 + src/parser/functions.rs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 5f57b24..2022884 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -58,6 +58,7 @@ pub enum Type { Str, Array(Box), Fun(Vec, Box), + Pointer(Box), /// Matches any type. Only used as a parameter type in native stdlib registrations. Any, } diff --git a/src/parser/functions.rs b/src/parser/functions.rs index 845a59c..235c6f0 100644 --- a/src/parser/functions.rs +++ b/src/parser/functions.rs @@ -48,6 +48,10 @@ pub fn type_name(input: &str) -> IResult<&str, Type> { map(tag("float[]"), |_| Type::Array(Box::new(Type::Float))), map(tag("bool[]"), |_| Type::Array(Box::new(Type::Bool))), map(tag("str[]"), |_| Type::Array(Box::new(Type::Str))), + map(tag("int*"), |_| Type::Pointer(Box::new(Type::Int))), + map(tag("float*"), |_| Type::Pointer(Box::new(Type::Float))), + map(tag("bool*"), |_| Type::Pointer(Box::new(Type::Bool))), + map(tag("str*"), |_| Type::Pointer(Box::new(Type::Str))), map(tag("int"), |_| Type::Int), map(tag("float"), |_| Type::Float), map(tag("bool"), |_| Type::Bool), From f3ecbbe2a27323c6d7aa76497f5a1b8f4fca1ed1 Mon Sep 17 00:00:00 2001 From: rafael-pf Date: Sun, 19 Apr 2026 15:55:06 -0300 Subject: [PATCH 04/26] feat: add & and * support --- src/ir/ast.rs | 3 +++ src/parser/expressions.rs | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 2022884..197298d 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -111,6 +111,9 @@ pub enum Expr { base: Box>, index: Box>, }, + /// Pointer operations + AddrOf(Box>), + Deref(Box>), } /// Statement with type decoration. diff --git a/src/parser/expressions.rs b/src/parser/expressions.rs index 8cfcab4..b6f8b1f 100644 --- a/src/parser/expressions.rs +++ b/src/parser/expressions.rs @@ -119,6 +119,12 @@ fn unary(input: &str) -> IResult<&str, UncheckedExpr> { map(pair(preceded(multispace0, tag("-")), unary), |(_, e)| { wrap(Expr::Neg(Box::new(e))) }), + map(pair(preceded(multispace0, tag("&")), unary), |(_, e)| { + wrap(Expr::AddrOf(Box::new(e))) + }), + map(pair(preceded(multispace0, tag("*")), unary), |(_, e)| { + wrap(Expr::Deref(Box::new(e))) + }), primary, ))(input) } From b48b0327b646cad6c447d68c1847db01499a2cd0 Mon Sep 17 00:00:00 2001 From: rafael-pf Date: Sun, 19 Apr 2026 15:55:48 -0300 Subject: [PATCH 05/26] adress and deref todo in next phases --- src/interpreter/eval_expr.rs | 4 ++++ src/semantic/type_checker.rs | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/interpreter/eval_expr.rs b/src/interpreter/eval_expr.rs index 49fcbef..0592c8e 100644 --- a/src/interpreter/eval_expr.rs +++ b/src/interpreter/eval_expr.rs @@ -147,6 +147,10 @@ pub fn eval_expr(expr: &CheckedExpr, env: &mut Environment) -> Result Err(RuntimeError::new( + "address-of and dereference are not implemented in the interpreter yet", + )), } } diff --git a/src/semantic/type_checker.rs b/src/semantic/type_checker.rs index 46681cf..7ac3169 100644 --- a/src/semantic/type_checker.rs +++ b/src/semantic/type_checker.rs @@ -416,6 +416,9 @@ fn type_check_expr_inner( base: Box::new(type_check_expr_to_typed(base, env)?), index: Box::new(type_check_expr_to_typed(index, env)?), }), + Expr::AddrOf(_) | Expr::Deref(_) => Err(TypeError::new( + "address-of and dereference are not implemented in the type checker yet", + )), } } @@ -543,6 +546,9 @@ fn type_check_expr( Err(TypeError::new("indexed expression must be array")) } } + Expr::AddrOf(_) | Expr::Deref(_) => Err(TypeError::new( + "address-of and dereference are not implemented in the type checker yet", + )), } } From 74b492bff0985c07c16d69981c07935a7b078ef1 Mon Sep 17 00:00:00 2001 From: rafael-pf Date: Sun, 19 Apr 2026 15:56:30 -0300 Subject: [PATCH 06/26] tests: add tests for T*, &n and *p --- tests/parser.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/parser.rs b/tests/parser.rs index eca6640..2c26d0f 100644 --- a/tests/parser.rs +++ b/tests/parser.rs @@ -9,6 +9,7 @@ use mini_c::parser::{ }, statement, }; +use mini_c::parser::functions::type_name; // --- Literals --- @@ -672,3 +673,55 @@ fn test_array_in_expression() { assert!(matches!(result.exp, Expr::Index { ref base, ref index } if matches!(base.exp, Expr::ArrayLit(_)) && index.exp == Expr::Literal(Literal::Int(0)))); } + +// --- Pointers (`Type::Pointer` + `type_name`: int*, float*, …) --- + +#[test] +fn test_pointer_type_name() { + assert_eq!( + type_name("int*"), + Ok(("", Type::Pointer(Box::new(Type::Int)))) + ); + assert_eq!( + type_name("float*"), + Ok(("", Type::Pointer(Box::new(Type::Float)))) + ); + assert_eq!( + type_name("bool*"), + Ok(("", Type::Pointer(Box::new(Type::Bool)))) + ); + assert_eq!( + type_name("str*"), + Ok(("", Type::Pointer(Box::new(Type::Str)))) + ); + // `int*` deve ser reconhecido antes de `int` (caso contrário vira só Int). + assert_eq!(type_name("int"), Ok(("", Type::Int))); +} + +#[test] +fn test_pointer_variable_declaration() { + let result = statement("int* ptr = q;").unwrap().1; + assert!(matches!( + result.stmt, + Statement::Decl { + ref name, + ref ty, + .. + } if name == "ptr" && ty == &Type::Pointer(Box::new(Type::Int)) + )); + if let Statement::Decl { ref init, .. } = result.stmt { + assert!(matches!(init.exp, Expr::Ident(ref s) if s == "q")); + } +} + +#[test] +fn test_pointer_address_of() { + let result = expression("&x").unwrap().1; + assert!(matches!(result.exp, Expr::AddrOf(ref target) if matches!(target.exp, Expr::Ident(ref s) if s == "x"))); +} + +#[test] +fn test_pointer_dereference() { + let result = expression("*p").unwrap().1; + assert!(matches!(result.exp, Expr::Deref(ref target) if matches!(target.exp, Expr::Ident(ref s) if s == "p"))); +} \ No newline at end of file From 792792a0e0f94111d90a3a01214df97e9b6d1101 Mon Sep 17 00:00:00 2001 From: vlsmcin Date: Mon, 20 Apr 2026 00:43:24 -0300 Subject: [PATCH 07/26] refactor: Deleted unused folder --- examples/hello.minic | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 examples/hello.minic diff --git a/examples/hello.minic b/examples/hello.minic deleted file mode 100644 index 456ac27..0000000 --- a/examples/hello.minic +++ /dev/null @@ -1,4 +0,0 @@ -void main() { - str name = "Alice"; - print(name); -} \ No newline at end of file From 076d52e60d7ebd4bfd1072cf7a65f274ff22dea1 Mon Sep 17 00:00:00 2001 From: vlsmcin Date: Mon, 20 Apr 2026 00:44:46 -0300 Subject: [PATCH 08/26] feat: Create statement pointer tratative --- src/parser/statements.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/parser/statements.rs b/src/parser/statements.rs index 9dcfef5..5a3c228 100644 --- a/src/parser/statements.rs +++ b/src/parser/statements.rs @@ -162,11 +162,22 @@ fn while_statement(input: &str) -> IResult<&str, UncheckedStmt> { /// Parse an lvalue: identifier followed by zero or more `[ expr ]` suffixes. fn lvalue(input: &str) -> IResult<&str, UncheckedExpr> { - let (mut rest, id) = preceded(multispace0, identifier)(input)?; - let mut acc = ExprD { - exp: Expr::Ident(id.to_string()), - ty: (), - }; + let (mut rest, mut acc) = preceded( + multispace0, + alt(( + map(preceded(tag("*"), identifier), |id| ExprD { + exp: Expr::Deref(Box::new(ExprD { + exp: Expr::Ident(id.to_string()), + ty: (), + })), + ty: (), + }), + map(identifier, |id| ExprD { + exp: Expr::Ident(id.to_string()), + ty: (), + }), + )), + )(input)?; loop { let index_parse = delimited( preceded(multispace0, char('[')), @@ -206,4 +217,4 @@ pub fn assignment(input: &str) -> IResult<&str, UncheckedStmt> { }) }, )(input) -} +} \ No newline at end of file From 3205cc9ee29e56730448a7ed7f970230857d7246 Mon Sep 17 00:00:00 2001 From: vlsmcin Date: Mon, 20 Apr 2026 00:45:11 -0300 Subject: [PATCH 09/26] tests: Create pointer statements unit tests --- tests/parser.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/parser.rs b/tests/parser.rs index 2c26d0f..9869bbb 100644 --- a/tests/parser.rs +++ b/tests/parser.rs @@ -724,4 +724,35 @@ fn test_pointer_address_of() { fn test_pointer_dereference() { let result = expression("*p").unwrap().1; assert!(matches!(result.exp, Expr::Deref(ref target) if matches!(target.exp, Expr::Ident(ref s) if s == "p"))); +} + +#[test] +fn test_pointer_params_function() { + let result = fun_decl("void foo(int* p) { *p = 42; }").unwrap().1; + assert_eq!(result.name, "foo"); + assert_eq!( + result.params, + vec![("p".to_string(), Type::Pointer(Box::new(Type::Int)))] + ); + assert!(matches!(result.body.stmt, Statement::Block { ref seq } if seq.len() == 1)); + if let Statement::Block { ref seq } = &result.body.stmt { + assert!(matches!(seq[0].stmt, Statement::Assign { ref target, ref value } + if matches!(target.exp, Expr::Deref(ref t) if matches!(t.exp, Expr::Ident(ref s) if s == "p")) + && value.exp == Expr::Literal(Literal::Int(42)))); + } +} + +#[test] +fn test_pointer_type_function() { + let result = fun_decl("int* changeRef(int* x, int* y) { x = y; return x; }") + .unwrap() + .1; + assert_eq!(result.name, "changeRef"); + assert_eq!( + result.params, + vec![ + ("x".to_string(), Type::Pointer(Box::new(Type::Int))), + ("y".to_string(), Type::Pointer(Box::new(Type::Int))) + ] + ); } \ No newline at end of file From 9533d5c0d2db4a0070046f0e8dd2017a0679f793 Mon Sep 17 00:00:00 2001 From: vlsmcin Date: Mon, 20 Apr 2026 00:45:34 -0300 Subject: [PATCH 10/26] tests: Create integration tests --- tests/fixtures/pointer_feature.minic | 10 ++++ tests/fixtures/pointer_function.minic | 14 +++++ tests/fixtures/pointer_init.minic | 11 ++++ tests/program.rs | 74 +++++++++++++++++++++++++++ 4 files changed, 109 insertions(+) create mode 100644 tests/fixtures/pointer_feature.minic create mode 100644 tests/fixtures/pointer_function.minic create mode 100644 tests/fixtures/pointer_init.minic diff --git a/tests/fixtures/pointer_feature.minic b/tests/fixtures/pointer_feature.minic new file mode 100644 index 0000000..579146a --- /dev/null +++ b/tests/fixtures/pointer_feature.minic @@ -0,0 +1,10 @@ +void increment(int* p) { + *p = *p + 1; +} + +void main() { + int x = 10; + int* y = &x; + increment(&x); + print(x); +} \ No newline at end of file diff --git a/tests/fixtures/pointer_function.minic b/tests/fixtures/pointer_function.minic new file mode 100644 index 0000000..89b5387 --- /dev/null +++ b/tests/fixtures/pointer_function.minic @@ -0,0 +1,14 @@ +int* changeRef (int* x, int* y){ + x = y; + return x; +} + +void main() { + int x = 10; + int y = 5; + + int* x_ref = &x; + int* y_ref = &y; + + changeRef(x_ref, y_ref); +} \ No newline at end of file diff --git a/tests/fixtures/pointer_init.minic b/tests/fixtures/pointer_init.minic new file mode 100644 index 0000000..7c8032d --- /dev/null +++ b/tests/fixtures/pointer_init.minic @@ -0,0 +1,11 @@ +void main() { + float a = 10.0; + int b = 5; + str c = "teste"; + bool d = true; + + float* a_ref = &a; + int* b_ref = &b; + str* c_ref = &c; + bool* d_ref = &d; +} \ No newline at end of file diff --git a/tests/program.rs b/tests/program.rs index dd8160b..800b858 100644 --- a/tests/program.rs +++ b/tests/program.rs @@ -91,3 +91,77 @@ fn test_parse_top_level_statements_fail() { let result = parse_program_file("top_level_statements.minic"); assert!(result.is_err(), "top-level statements without def should fail to parse"); } + +#[test] +fn test_parse_pointer_feature_program() { + let prog = parse_program_file("pointer_feature.minic").expect("pointer feature should parse"); + assert_eq!(prog.functions.len(), 2); + assert_eq!(prog.functions[0].name, "increment"); + assert_eq!(prog.functions[1].name, "main"); + assert_eq!(prog.functions[0].params, vec![("p".to_string(), Type::Pointer(Box::new(Type::Int)))]); + + if let Statement::Block { ref seq } = prog.functions[1].body.stmt { + assert_eq!(seq.len(), 4); + assert!(matches!(seq[0].stmt, Statement::Decl { ref name, .. } if name == "x")); + assert!(matches!(seq[1].stmt, Statement::Decl { ref name, .. } if name == "y")); + assert!(matches!(seq[2].stmt, Statement::Call { ref name, .. } if name == "increment")); + assert!(matches!(seq[3].stmt, Statement::Call { ref name, .. } if name == "print")); + } else { + panic!("expected main to have block body"); + } +} + +#[test] +fn test_parse_pointer_function_program() { + let prog = parse_program_file("pointer_function.minic").expect("pointer function should parse"); + assert_eq!(prog.functions.len(), 2); + assert_eq!(prog.functions[0].name, "changeRef"); + assert_eq!(prog.functions[1].name, "main"); + assert_eq!( + prog.functions[0].params, + vec![ + ("x".to_string(), Type::Pointer(Box::new(Type::Int))), + ("y".to_string(), Type::Pointer(Box::new(Type::Int))) + ] + ); + + if let Statement::Block { ref seq } = prog.functions[0].body.stmt { + assert_eq!(seq.len(), 2); + assert!(matches!(seq[0].stmt, Statement::Assign { .. })); + assert!(matches!(seq[1].stmt, Statement::Return(_))); + } else { + panic!("expected changeRef to have block body"); + } + + if let Statement::Block { ref seq } = prog.functions[1].body.stmt { + assert_eq!(seq.len(), 5); + assert!(matches!(seq[0].stmt, Statement::Decl { ref name, .. } if name == "x")); + assert!(matches!(seq[1].stmt, Statement::Decl { ref name, .. } if name == "y")); + assert!(matches!(seq[2].stmt, Statement::Decl { ref name, .. } if name == "x_ref")); + assert!(matches!(seq[3].stmt, Statement::Decl { ref name, .. } if name == "y_ref")); + assert!(matches!(seq[4].stmt, Statement::Call { ref name, .. } if name == "changeRef")); + } else { + panic!("expected main to have block body"); + } +} + +#[test] +fn test_parse_pointer_init_program() { + let prog = parse_program_file("pointer_init.minic").expect("pointer init should parse"); + assert_eq!(prog.functions.len(), 1); + assert_eq!(prog.functions[0].name, "main"); + + if let Statement::Block { ref seq } = prog.functions[0].body.stmt { + assert_eq!(seq.len(), 8); + assert!(matches!(seq[0].stmt, Statement::Decl { ref name, .. } if name == "a")); + assert!(matches!(seq[1].stmt, Statement::Decl { ref name, .. } if name == "b")); + assert!(matches!(seq[2].stmt, Statement::Decl { ref name, .. } if name == "c")); + assert!(matches!(seq[3].stmt, Statement::Decl { ref name, .. } if name == "d")); + assert!(matches!(seq[4].stmt, Statement::Decl { ref name, .. } if name == "a_ref")); + assert!(matches!(seq[5].stmt, Statement::Decl { ref name, .. } if name == "b_ref")); + assert!(matches!(seq[6].stmt, Statement::Decl { ref name, .. } if name == "c_ref")); + assert!(matches!(seq[7].stmt, Statement::Decl { ref name, .. } if name == "d_ref")); + } else { + panic!("expected main to have block body"); + } +} \ No newline at end of file From 23f7fe0f6e37fca22c2b8ca8bebbb177ceb308ee Mon Sep 17 00:00:00 2001 From: rafael-pf Date: Mon, 20 Apr 2026 20:02:24 -0300 Subject: [PATCH 11/26] docs: add pointer feature spec --- openspec/specs/pointers/spec.md | 109 ++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 openspec/specs/pointers/spec.md diff --git a/openspec/specs/pointers/spec.md b/openspec/specs/pointers/spec.md new file mode 100644 index 0000000..2768c58 --- /dev/null +++ b/openspec/specs/pointers/spec.md @@ -0,0 +1,109 @@ +# Pointers + +## Purpose + +Document MiniC **pointer syntax** and **intended usage**: pointer types (`T*`), +address-of (`&expr`), dereference (`*expr`), assignment through a dereference +(`*expr = …`), and pointer-typed parameters and return values. + +## Requirements + +### Requirement: Pointer type notation + +The parser SHALL recognise pointer types written as a scalar type name +immediately followed by `*`, with no space between them: `int*`, `float*`, +`bool*`, `str*`. Function parameters, return types, and local declarations +SHALL use this same spelling (as in the three fixture programs). + +#### Scenario: Declaration with pointer type + +- **WHEN** the source contains `int* y = &x` or `float* a_ref = &a` +- **THEN** the parser SHALL succeed and associate the declared name with + `Type::Pointer` to the corresponding scalar type + +#### Scenario: Function signature with pointers + +- **WHEN** the source contains `void increment(int* p)` or + `int* changeRef(int* x, int* y)` +- **THEN** the parser SHALL succeed with each pointer parameter typed as + `Pointer` to the inner scalar type + +--- + +### Requirement: Address-of expression + +The parser SHALL recognise the unary prefix `&` applied to an expression, +producing `Expr::AddrOf`. The fixtures use `&` only on simple identifiers +(`&x`, `&a`, …), which is the intended teaching subset. + +#### Scenario: Initialisation from address of variable + +- **WHEN** the input is `int* y = &x` or `bool* d_ref = &d` +- **THEN** the parser SHALL succeed with the initialiser as `AddrOf` wrapping + the identifier expression + +#### Scenario: Address passed to a function + +- **WHEN** the input is `increment(&x)` as in `pointer_feature.minic` +- **THEN** the parser SHALL succeed with the call argument as `AddrOf` + +--- + +### Requirement: Dereference expression + +The parser SHALL recognise the unary prefix `*` as dereference (same lexical +token as multiplication, resolved in the unary layer), producing `Expr::Deref`. + +#### Scenario: Dereference on the right-hand side + +- **WHEN** the input is `*p + 1` as in `pointer_feature.minic` +- **THEN** the parser SHALL succeed with `Deref` applied to the operand of `+` + as appropriate for unary-before-additive precedence + +#### Scenario: Nested dereference in assignment target + +- **WHEN** the input is `*p = *p + 1` +- **THEN** the parser SHALL succeed with assignment whose target expression is + `Deref` and whose value expression uses `Deref` on the same pointer + +--- + +### Requirement: Assignment to a dereference + +The parser SHALL accept assignment statements whose target is a dereference +expression `*expr`, as in `*p = *p + 1`. + +#### Scenario: Mutate through pointer parameter + +- **WHEN** the statement is `*p = *p + 1` inside `increment` in + `pointer_feature.minic` +- **THEN** the parser SHALL produce `Stmt::Assign` with a `Deref` target + +--- + +### Requirement: Return type and return value as pointer + +The parser SHALL allow a function to declare a pointer return type and +`return` an expression of pointer type, as in `changeRef` in +`pointer_function.minic`. + +#### Scenario: Return pointer from function + +- **WHEN** the function is `int* changeRef(int* x, int* y) { … return x; }` +- **THEN** the parser SHALL succeed with return type `Pointer(Int)` and a + return statement carrying the pointer expression + +--- + +### Requirement: Assignment between pointer variables + +The parser SHALL allow assignment where both sides are pointer-typed +expressions (e.g. `x = y` when `x` and `y` are `int*`), as in the body of +`changeRef` in `pointer_function.minic`. + +#### Scenario: Rebind pointer parameter + +- **WHEN** the statement is `x = y` with `x` and `y` declared as `int*` + parameters +- **THEN** the parser SHALL succeed with `Stmt::Assign` and identifier + expressions for `x` and `y` \ No newline at end of file From c276f4cb5ea9dea5c19f213709045ed9718a7cf1 Mon Sep 17 00:00:00 2001 From: rbonifacio Date: Tue, 26 May 2026 07:22:13 -0300 Subject: [PATCH 12/26] Definition of the second milestone. --- docs/09-projects01.md | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/docs/09-projects01.md b/docs/09-projects01.md index 53fb56f..9e1cebf 100644 --- a/docs/09-projects01.md +++ b/docs/09-projects01.md @@ -13,19 +13,14 @@ The allocation of projects to groups was performed randomly, using a reproducibl | Project | Group | |-------------|--------| -| Projeto 10 | Daniel Silvestre de França e Souza
João Pedro Barbosa Marins
Marcelo Arcoverde Neves Britto de Rezende
Rafael Paz Fernandes
Vinícius Lima Sá de Melo | -| Projeto 7 | Enzo Gurgel Bissoli
Shellyda de Fatima Silva Barbosa
Rodrigo Santos Batista
Juliana Serafim da Silva
Anderson Vitor Leoncio de Lima | -| Projeto 8 | Alberto Guevara de Araujo Franca
Davi Gonzaga Guerreiro Barboza
Fábio Pereira de Miranda
Felipe Torres de Macedo | +| Projeto 1 | Valter Sanches, Artur Vinicius
Victor Silva, Miguel Gomes, Matheus Ayres| | Projeto 2 | Ian Medeiros Melo
Victor Mendonça Aguiar
Rafael Alves de Azevedo Silva
João Victor Fellows Rabelo
Guilherme Montenegro de Albuquerque | -| Projeto 5 | Álvaro Cavalcante Negromonte
Gabriel Valença Mayerhofer
Henrique César Higino Holanda Cordeiro
João Victor Nascimento Lima
Vinicius de Souza Rodrigues | +| Projeto 3 | Joana D'Arc, Juliana Silva
Leandro Luiz, Paulo Vitor, Thiago Henrique| | Projeto 4 | Bruno Antonio dos Santos Bezerra
Luan de Oliveira Romancini Leite
Leônidas Dantas de Castro Netto
Pedro Gabriel Alves da Silva | - -The following projects were not assigned. - - * Project 1 - * Project 3 - * Project 6 - * Project 9 +| Projeto 5 | Álvaro Cavalcante Negromonte
Gabriel Valença Mayerhofer
Henrique César Higino Holanda Cordeiro
João Victor Nascimento Lima
Vinicius de Souza Rodrigues | +| Projeto 7 | Enzo Gurgel Bissoli
Shellyda de Fatima Silva Barbosa
Rodrigo Santos Batista
Juliana Serafim da Silva
Anderson Vitor Leoncio de Lima | +| Projeto 8 | Alberto Guevara de Araujo Franca
Davi Gonzaga Guerreiro Barboza
Fábio Pereira de Miranda
Felipe Torres de Macedo | +| Projeto 10 | Daniel Silvestre de França e Souza
João Pedro Barbosa Marins
Marcelo Arcoverde Neves Britto de Rezende
Rafael Paz Fernandes
Vinícius Lima Sá de Melo | --- @@ -391,7 +386,7 @@ A good starting point for any of them is: ### Second Milestone: * Review the implementation of the type checker and interpreter - * Deadline: 11/05 + * Deadline: 07/06 ### Third Milestone: From ff732602f4f93b167345b63cbf66b1bd18ce2a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Pedro=20Marins?= <136388066+joaopbmarins@users.noreply.github.com> Date: Wed, 27 May 2026 20:22:46 -0300 Subject: [PATCH 13/26] feat: implement typechecker --- src/semantic/type_checker.rs | 67 ++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/src/semantic/type_checker.rs b/src/semantic/type_checker.rs index 7ac3169..ceaee0f 100644 --- a/src/semantic/type_checker.rs +++ b/src/semantic/type_checker.rs @@ -314,6 +314,43 @@ fn type_check_assign_target( } Ok(()) } + Expr::Deref(inner) => { + let inner_ty = type_check_expr(inner, env)?; + match inner_ty { + Type::Pointer(base_ty) => { + if !types_compatible(value_ty, &base_ty) { + return Err(TypeError::new(format!( + "assignment through pointer: expected {:?}, got {:?}", + base_ty, + value_ty + ))); + } + Ok(()) + } + other => Err(TypeError::new(format!( + "cannot dereference non-pointer type: {:?}", + other + ))), + } + }, + Expr::AddrOf(inner) => { + let inner_ty = type_check_expr(inner, env)?; + match &inner.exp { + Expr::Ident(_) => { + if !types_compatible(&Type::Pointer(Box::new(inner_ty.clone())), value_ty) { + return Err(TypeError::new(format!( + "assignment to address-of: expected {:?}, got {:?}", + Type::Pointer(Box::new(inner_ty)), + value_ty + ))); + } + Ok(()) + } + _ => Err(TypeError::new( + "can only take address of variables" + )), + } + }, Expr::Index { base, index } => { let index_ty = type_check_expr(index, env)?; if index_ty != Type::Int { @@ -416,9 +453,8 @@ fn type_check_expr_inner( base: Box::new(type_check_expr_to_typed(base, env)?), index: Box::new(type_check_expr_to_typed(index, env)?), }), - Expr::AddrOf(_) | Expr::Deref(_) => Err(TypeError::new( - "address-of and dereference are not implemented in the type checker yet", - )), + Expr::AddrOf(elem) => Ok(Expr::AddrOf(Box::new(type_check_expr_to_typed(elem, env)?))), + Expr::Deref(elem) => Ok(Expr::Deref(Box::new(type_check_expr_to_typed(elem, env)?))), } } @@ -546,9 +582,27 @@ fn type_check_expr( Err(TypeError::new("indexed expression must be array")) } } - Expr::AddrOf(_) | Expr::Deref(_) => Err(TypeError::new( - "address-of and dereference are not implemented in the type checker yet", - )), + Expr::AddrOf(elem) => { + let inner_ty = type_check_expr(elem, env)?; + match &elem.exp { + Expr::Ident(_) => { + Ok(Type::Pointer(Box::new(inner_ty))) + } + _ => Err(TypeError::new( + "can only take address of variables" + )), + } + }, + Expr::Deref(elem) => { + let inner_ty = type_check_expr(elem, env)?; + match inner_ty { + Type::Pointer(base_ty) => Ok(*base_ty), + other => Err(TypeError::new(format!( + "cannot dereference non-pointer type: {:?}", + other + ))), + } + }, } } @@ -585,6 +639,7 @@ fn types_compatible(a: &Type, b: &Type) -> bool { | (Type::Str, Type::Str) | (Type::Unit, Type::Unit) => true, (Type::Int, Type::Float) | (Type::Float, Type::Int) => true, + (Type::Pointer(a), Type::Pointer(b)) => {types_compatible(a, b)}, (Type::Array(a), Type::Array(b)) => types_compatible(a, b), _ => false, } From 31876578abc4bd04e73c39bd07a55be43304f421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Pedro=20Marins?= <136388066+joaopbmarins@users.noreply.github.com> Date: Tue, 2 Jun 2026 19:46:49 -0300 Subject: [PATCH 14/26] add pointer_bindings to environment --- src/environment/env.rs | 22 ++++++++++++++++++++++ src/interpreter/eval_expr.rs | 8 +++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/environment/env.rs b/src/environment/env.rs index dd83ba1..6342b0c 100644 --- a/src/environment/env.rs +++ b/src/environment/env.rs @@ -59,12 +59,14 @@ use std::collections::{HashMap, HashSet}; /// Both variable bindings and function bindings are stored in the same map. pub struct Environment { bindings: HashMap, + pointer_bindings: HashMap, } impl Environment { pub fn new() -> Self { Self { bindings: HashMap::new(), + pointer_bindings: HashMap::new(), } } @@ -107,6 +109,26 @@ impl Environment { pub fn remove_new(&mut self, outer: &HashSet) { self.bindings.retain(|k, _| outer.contains(k)); } + + //bind a pointer name to an address + pub fn declare_pointer(&mut self, name: impl Into, address: String) { + self.pointer_bindings.insert(name.into(), address); + } + + //look up a pointer binding by name + pub fn get_pointer(&self, name: &str) -> Option<&String> { + self.pointer_bindings.get(name) + } + + //update an existing pointer binding. Returns `false` if the name is not bound. + pub fn set_pointer(&mut self, name: &str, address: String) -> bool { + if self.pointer_bindings.contains_key(name) { + self.pointer_bindings.insert(name.to_string(), address); + true + } else { + false + } + } } impl Default for Environment { diff --git a/src/interpreter/eval_expr.rs b/src/interpreter/eval_expr.rs index 0592c8e..509121d 100644 --- a/src/interpreter/eval_expr.rs +++ b/src/interpreter/eval_expr.rs @@ -147,9 +147,11 @@ pub fn eval_expr(expr: &CheckedExpr, env: &mut Environment) -> Result Err(RuntimeError::new( - "address-of and dereference are not implemented in the interpreter yet", + Expr::AddrOf(elem) => Err(RuntimeError::new( + "address-of is not implemented in the interpreter yet", + )), + Expr::Deref(_elem) => Err(RuntimeError::new( + "dereference is not implemented in the interpreter yet", )), } } From a5e7257e6eb6d2f6f5230c512403be3a5a1b0e24 Mon Sep 17 00:00:00 2001 From: rafael-pf Date: Tue, 2 Jun 2026 20:40:23 -0300 Subject: [PATCH 15/26] feat: add pointer interpreter --- src/environment/env.rs | 22 -------------- src/interpreter/eval_expr.rs | 57 ++++++++++++++++++++++++++++++++---- src/interpreter/exec_stmt.rs | 6 +++- src/interpreter/value.rs | 4 +++ tests/interpreter.rs | 56 +++++++++++++++++++++++++++++++++++ 5 files changed, 116 insertions(+), 29 deletions(-) diff --git a/src/environment/env.rs b/src/environment/env.rs index 6342b0c..dd83ba1 100644 --- a/src/environment/env.rs +++ b/src/environment/env.rs @@ -59,14 +59,12 @@ use std::collections::{HashMap, HashSet}; /// Both variable bindings and function bindings are stored in the same map. pub struct Environment { bindings: HashMap, - pointer_bindings: HashMap, } impl Environment { pub fn new() -> Self { Self { bindings: HashMap::new(), - pointer_bindings: HashMap::new(), } } @@ -109,26 +107,6 @@ impl Environment { pub fn remove_new(&mut self, outer: &HashSet) { self.bindings.retain(|k, _| outer.contains(k)); } - - //bind a pointer name to an address - pub fn declare_pointer(&mut self, name: impl Into, address: String) { - self.pointer_bindings.insert(name.into(), address); - } - - //look up a pointer binding by name - pub fn get_pointer(&self, name: &str) -> Option<&String> { - self.pointer_bindings.get(name) - } - - //update an existing pointer binding. Returns `false` if the name is not bound. - pub fn set_pointer(&mut self, name: &str, address: String) -> bool { - if self.pointer_bindings.contains_key(name) { - self.pointer_bindings.insert(name.to_string(), address); - true - } else { - false - } - } } impl Default for Environment { diff --git a/src/interpreter/eval_expr.rs b/src/interpreter/eval_expr.rs index 509121d..656f114 100644 --- a/src/interpreter/eval_expr.rs +++ b/src/interpreter/eval_expr.rs @@ -147,12 +147,56 @@ pub fn eval_expr(expr: &CheckedExpr, env: &mut Environment) -> Result Err(RuntimeError::new( - "address-of is not implemented in the interpreter yet", - )), - Expr::Deref(_elem) => Err(RuntimeError::new( - "dereference is not implemented in the interpreter yet", - )), + Expr::AddrOf(elem) => eval_addr_of(elem, env), + Expr::Deref(elem) => eval_deref(elem, env), + } +} + +fn eval_addr_of(elem: &CheckedExpr, env: &Environment) -> Result { + match &elem.exp { + Expr::Ident(name) => { + if env.get(name).is_none() { + return Err(RuntimeError::new(format!("undefined variable '{}'", name))); + } + Ok(Value::Ptr(name.clone())) + } + _ => Err(RuntimeError::new("can only take address of variables")), + } +} + +fn eval_deref(elem: &CheckedExpr, env: &mut Environment) -> Result { + let target = ptr_target(eval_expr(elem, env)?)?; + read_through(env, &target) +} + +/// Extract the target variable name from a pointer value. +pub(crate) fn ptr_target(val: Value) -> Result { + match val { + Value::Ptr(target) => Ok(target), + v => Err(RuntimeError::new(format!("expected pointer, got: {}", v))), + } +} + +/// Read the value stored at the variable named by a pointer. +pub(crate) fn read_through(env: &Environment, target: &str) -> Result { + env.get(target) + .cloned() + .ok_or_else(|| RuntimeError::new(format!("dereference of invalid target '{}'", target))) +} + +/// Write `val` into the variable named by a pointer. +pub(crate) fn write_through( + env: &mut Environment, + target: &str, + val: Value, +) -> Result<(), RuntimeError> { + if env.set(target, val) { + Ok(()) + } else { + Err(RuntimeError::new(format!( + "dereference of invalid target '{}'", + target + ))) } } @@ -241,6 +285,7 @@ fn values_equal(a: &Value, b: &Value) -> bool { (Value::Float(x), Value::Int(y)) => *x == (*y as f64), (Value::Bool(x), Value::Bool(y)) => x == y, (Value::Str(x), Value::Str(y)) => x == y, + (Value::Ptr(x), Value::Ptr(y)) => x == y, _ => false, } } diff --git a/src/interpreter/exec_stmt.rs b/src/interpreter/exec_stmt.rs index ceeda2c..689813c 100644 --- a/src/interpreter/exec_stmt.rs +++ b/src/interpreter/exec_stmt.rs @@ -36,7 +36,7 @@ use crate::environment::Environment; use crate::ir::ast::{CheckedExpr, CheckedStmt, Expr, Statement}; -use super::eval_expr::{eval_call, eval_expr}; +use super::eval_expr::{eval_call, eval_expr, ptr_target, write_through}; use super::value::{RuntimeError, Value}; /// `None` = normal fall-through; `Some(v)` = early return with value. @@ -158,6 +158,10 @@ fn assign_lvalue( }; assign_index(base, idx, val, env) } + Expr::Deref(inner) => { + let target = ptr_target(eval_expr(inner, env)?)?; + write_through(env, &target, val) + } _ => Err(RuntimeError::new("invalid assignment target".to_string())), } } diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index 2abdc6e..d8b27b8 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -28,6 +28,7 @@ //! Value::Array([...]) — a list of Values //! Value::Void — no value (returned by void functions) //! Value::Fn(FnValue::...) — a callable function +//! Value::Ptr("x") — pointer to variable `x` (name-based indirection) //! ``` //! //! This is the idiomatic Rust approach to *tagged unions* — a value that can @@ -103,6 +104,8 @@ pub enum Value { Array(Vec), Void, Fn(FnValue), + /// Pointer: holds the name of the target variable in the environment. + Ptr(String), } impl fmt::Display for Value { @@ -124,6 +127,7 @@ impl fmt::Display for Value { write!(f, "]") } Value::Fn(_) => write!(f, ""), + Value::Ptr(target) => write!(f, "&{}", target), } } } diff --git a/tests/interpreter.rs b/tests/interpreter.rs index 51696c9..56ed101 100644 --- a/tests/interpreter.rs +++ b/tests/interpreter.rs @@ -256,3 +256,59 @@ fn test_stdlib_pow_float_args() { "#; assert!(run(src).is_ok(), "{}", run(src).unwrap_err()); } + +// --------------------------------------------------------------------------- +// Pointers (Value::Ptr in a single bindings map) +// --------------------------------------------------------------------------- +#[test] +fn test_pointer_addr_of_and_deref_read() { + let src = r#" + void main() { + int x = 10; + int* p = &x; + int y = *p; + } + "#; + assert!(run(src).is_ok(), "{}", run(src).unwrap_err()); +} + +#[test] +fn test_pointer_deref_assign() { + let src = r#" + void increment(int* p) { + *p = *p + 1; + } + void main() { + int x = 10; + increment(&x); + } + "#; + assert!(run(src).is_ok(), "{}", run(src).unwrap_err()); +} + +#[test] +fn test_pointer_rebind() { + let src = r#" + void main() { + int x = 10; + int y = 5; + int* x_ref = &x; + int* y_ref = &y; + x_ref = y_ref; + } + "#; + assert!(run(src).is_ok(), "{}", run(src).unwrap_err()); +} + +#[test] +fn test_pointer_return() { + let src = r#" + int* pick(int* a, int* b) { return a; } + void main() { + int x = 1; + int y = 2; + int* p = pick(&x, &y); + } + "#; + assert!(run(src).is_ok(), "{}", run(src).unwrap_err()); +} From 56b1c373c25c75b9a228e295cc7b24e2fae2bf7a Mon Sep 17 00:00:00 2001 From: rafael-pf Date: Tue, 2 Jun 2026 20:50:52 -0300 Subject: [PATCH 16/26] test: add type checker tests --- tests/type_checker.rs | 154 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/tests/type_checker.rs b/tests/type_checker.rs index 3357161..f323b18 100644 --- a/tests/type_checker.rs +++ b/tests/type_checker.rs @@ -1,7 +1,9 @@ //! Integration tests for the MiniC type checker. +use std::path::Path; + use nom::combinator::all_consuming; -use mini_c::ir::ast::{CheckedProgram, Type}; +use mini_c::ir::ast::{CheckedProgram, Expr, Statement, Type}; use mini_c::parser::program; use mini_c::semantic::type_check; @@ -14,6 +16,14 @@ fn parse_and_type_check(src: &str) -> Result Result { + let path = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures") + .join(name); + let src = std::fs::read_to_string(&path).expect("fixture file should exist"); + parse_and_type_check(src.trim()) +} + #[test] fn test_type_check_simple_assign() { let result = parse_and_type_check("void main() int x = 1;"); @@ -202,3 +212,145 @@ fn test_type_check_print_wrong_arity() { let result = parse_and_type_check("void main() { print(1, 2); }"); assert!(result.is_err(), "expected arity error for print(1, 2)"); } + +// --------------------------------------------------------------------------- +// Pointers +// --------------------------------------------------------------------------- +#[test] +fn test_type_check_pointer_init_fixture() { + assert!( + parse_and_type_check_fixture("pointer_init.minic").is_ok(), + "{}", + parse_and_type_check_fixture("pointer_init.minic") + .unwrap_err() + .message + ); +} + +#[test] +fn test_type_check_pointer_feature_fixture() { + assert!(parse_and_type_check_fixture("pointer_feature.minic").is_ok()); +} + +#[test] +fn test_type_check_pointer_function_fixture() { + assert!(parse_and_type_check_fixture("pointer_function.minic").is_ok()); +} + +#[test] +fn test_type_check_addr_of_and_deref_types() { + let prog = parse_and_type_check( + "void main() { int x = 1; int* p = &x; int y = *p; }", + ) + .expect("pointer decl and deref should type-check"); + + let main_fn = prog.functions.iter().find(|f| f.name == "main").unwrap(); + let Statement::Block { seq } = &main_fn.body.stmt else { + panic!("expected block body"); + }; + + let p_init = match &seq[1].stmt { + Statement::Decl { init, .. } => init, + _ => panic!("expected int* p = &x decl"), + }; + assert_eq!(p_init.ty, Type::Pointer(Box::new(Type::Int))); + assert!(matches!(p_init.exp, Expr::AddrOf(_))); + + let y_init = match &seq[2].stmt { + Statement::Decl { init, .. } => init, + _ => panic!("expected int y = *p decl"), + }; + assert_eq!(y_init.ty, Type::Int); + assert!(matches!(y_init.exp, Expr::Deref(_))); +} + +#[test] +fn test_type_check_pointer_param_and_deref_assign() { + let prog = parse_and_type_check( + "void bump(int* p) { *p = *p + 1; }\nvoid main() { int x = 0; bump(&x); }", + ) + .expect("pointer param and assignment through deref"); + + let bump = prog.functions.iter().find(|f| f.name == "bump").unwrap(); + assert_eq!( + bump.params, + vec![("p".to_string(), Type::Pointer(Box::new(Type::Int)))] + ); +} + +#[test] +fn test_type_check_pointer_return_type() { + let prog = parse_and_type_check( + "int* id(int* p) { return p; }\nvoid main() { int x = 1; int* q = id(&x); }", + ) + .expect("pointer return"); + + let id_fn = prog.functions.iter().find(|f| f.name == "id").unwrap(); + assert_eq!(id_fn.return_type, Type::Pointer(Box::new(Type::Int))); +} + +#[test] +fn test_type_check_pointer_rebind() { + assert!(parse_and_type_check( + "void main() { int x = 1; int y = 2; int* a = &x; int* b = &y; a = b; }", + ) + .is_ok()); +} + +#[test] +fn test_type_check_deref_non_pointer() { + let result = parse_and_type_check("void main() { int x = 1; int y = *x; }"); + assert!(result.is_err()); + assert!( + result.unwrap_err().message.contains("cannot dereference non-pointer") + ); +} + +#[test] +fn test_type_check_addr_of_non_variable() { + let result = parse_and_type_check("void main() { int x = 1; int* p = &(x + 1); }"); + assert!(result.is_err()); + assert!( + result + .unwrap_err() + .message + .contains("can only take address of variables") + ); +} + +#[test] +fn test_type_check_deref_assign_type_mismatch() { + let result = parse_and_type_check( + "void main() { int x = 1; int* p = &x; *p = true; }", + ); + assert!(result.is_err()); + assert!( + result + .unwrap_err() + .message + .contains("assignment through pointer") + ); +} + +#[test] +fn test_type_check_call_expects_pointer_arg() { + let result = parse_and_type_check( + "void take(int* p) { *p = 1; }\nvoid main() { int x = 0; take(x); }", + ); + assert!(result.is_err()); + assert!(result.unwrap_err().message.contains("argument")); +} + +#[test] +fn test_type_check_assign_int_from_pointer() { + let result = parse_and_type_check( + "void main() { int x = 1; int* p = &x; int y = p; }", + ); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert!( + err.message.contains("declaration"), + "unexpected error: {}", + err.message + ); +} From 4789736564fe22b3ea5c4d9a88f366b0f1aab8ca Mon Sep 17 00:00:00 2001 From: Marcelo Arcoverde Date: Mon, 29 Jun 2026 17:43:43 -0300 Subject: [PATCH 17/26] feat: add all tac logic --- src/codegen/tac_code_gen.rs | 42 ++++++++++++++++++++++++++---------- src/interpreter/eval_expr.rs | 14 +++++++++++- src/ir/tac.rs | 3 +++ tests/tac_gen.rs | 40 ++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 12 deletions(-) diff --git a/src/codegen/tac_code_gen.rs b/src/codegen/tac_code_gen.rs index 2f5e6ee..4ecc981 100644 --- a/src/codegen/tac_code_gen.rs +++ b/src/codegen/tac_code_gen.rs @@ -55,16 +55,24 @@ pub fn translate_statement(statement: CheckedStmt, env: &mut Environment) -> Vec seq.into_iter().flat_map(|s| translate_statement(s, env)).collect::>() }, Statement::Assign { target, value } => { - if let Expr::Ident(name) = &target.exp { - let var_type = target.ty.clone(); - let var_address = Address::Variable(name.to_string(), var_type); - let (expression_address, instructions) = translate_expression(*value, env); - res.extend(instructions); - res.push(Instruction::CopyAssignment(var_address, expression_address)); - res - } - else { - todo!() + match target.exp { + Expr::Ident(name) => { + let var_addr = Address::Variable(name.to_string(), target.ty); + let (val_addr, instrs) = translate_expression(*value, env); + res.extend(instrs); + res.push(Instruction::CopyAssignment(var_addr, val_addr)); + res + }, + Expr::Deref(inner) => { + let (ptr_addr, ptr_instrs) = translate_expression(*inner, env); + let (val_addr, val_instrs) = translate_expression(*value, env); + res.extend(ptr_instrs); + res.extend(val_instrs); + res.push(Instruction::DerefWrite(ptr_addr, val_addr)); + res + }, + + _ => todo!() } }, Statement::Call{name, args} => { @@ -164,7 +172,19 @@ fn translate_expression(expression: CheckedExpr, env: &mut Environment) -> (Addr let temp = Address::Temporary(env.new_temporary(), expression.ty); instructions.push(Instruction::BinaryAssignment(Operator::Add, temp.clone(), l_addr, r_addr)); (temp, instructions) - } + }, + Expr::AddrOf(inner) => { + let (inner_addr, mut instructions) = translate_expression(*inner, env); + let temp = Address::Temporary(env.new_temporary(), expression.ty); + instructions.push(Instruction::AddressOf(temp.clone(), inner_addr)); + (temp, instructions) + }, + Expr::Deref(inner) => { + let (inner_addr, mut instructions) = translate_expression(*inner, env); + let temp = Address::Temporary(env.new_temporary(), expression.ty); + instructions.push(Instruction::DerefRead(temp.clone(), inner_addr)); + (temp, instructions) + }, _ => todo!() } } diff --git a/src/interpreter/eval_expr.rs b/src/interpreter/eval_expr.rs index 656f114..43b7fc8 100644 --- a/src/interpreter/eval_expr.rs +++ b/src/interpreter/eval_expr.rs @@ -217,12 +217,24 @@ pub fn eval_call( args.len() ))); } + let snapshot = env.snapshot(); + let outer_keys = env.names(); + for ((param_name, _), val) in decl.params.iter().zip(args.into_iter()) { env.declare(param_name.clone(), val); } + let result = exec_stmt(&decl.body, env)?; - env.restore(snapshot); + + env.remove_new(&outer_keys); + + for (param_name, _) in decl.params.iter() { + if let Some(old_val) = snapshot.get(param_name) { + env.declare(param_name.clone(), old_val.clone()); + } + } + Ok(result.unwrap_or(Value::Void)) } Some(_) => Err(RuntimeError::new(format!("'{}' is not a function", name))), diff --git a/src/ir/tac.rs b/src/ir/tac.rs index 92e9623..908586c 100644 --- a/src/ir/tac.rs +++ b/src/ir/tac.rs @@ -26,6 +26,9 @@ pub enum Instruction { Store(Address, Address, Address), // x[i] = y Load(Address, Address, Address), // x = y[i] Return(Option
), + AddressOf(Address, Address), // x = &y + DerefRead(Address, Address), // x = *y + DerefWrite(Address, Address), // *x = y } #[derive(Debug, Clone, PartialEq)] diff --git a/tests/tac_gen.rs b/tests/tac_gen.rs index f602e15..5689696 100644 --- a/tests/tac_gen.rs +++ b/tests/tac_gen.rs @@ -67,3 +67,43 @@ fn test_if_else_with_relational_condition() { Instruction::Label("Label2:".to_string()), ]); } + +#[test] +fn test_pointer_assignment() { + + let p_deref = ExprD { + exp: Expr::Deref(Box::new(ExprD { exp: Expr::Ident("p".to_string()), ty: Type::Pointer(Box::new(Type::Int)) })), + ty: Type::Int, + }; + + let one = ExprD { + exp: Expr::Literal(Literal::Int(1)), + ty: Type::Int, + }; + + let add_expr = ExprD { + exp: Expr::Add(Box::new(p_deref.clone()), Box::new(one)), + ty: Type::Int, + }; + + let stmt = StatementD { + stmt: Statement::Assign { + target: Box::new(p_deref), + value: Box::new(add_expr), + }, + ty: Type::Unit, + }; + + let mut env = Environment::new(); + let instructions = translate_statement(stmt, &mut env); + + let p = Address::Variable("p".to_string(), Type::Pointer(Box::new(Type::Int))); + let temp1 = Address::Temporary("temp1".to_string(), Type::Int); + let temp2 = Address::Temporary("temp2".to_string(), Type::Int); + + assert_eq!(instructions, vec![ + Instruction::DerefRead(temp1.clone(), p.clone()), + Instruction::BinaryAssignment(Operator::Add, temp2.clone(), temp1, Address::Constant(Literal::Int(1), Type::Int)), + Instruction::DerefWrite(p, temp2), + ]); +} From d7fcc20e7efff85b424d1ff65644d725212db539 Mon Sep 17 00:00:00 2001 From: Marcelo Arcoverde Date: Mon, 29 Jun 2026 19:43:52 -0300 Subject: [PATCH 18/26] test: make stronger interpreter tests --- tests/interpreter.rs | 181 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 180 insertions(+), 1 deletion(-) diff --git a/tests/interpreter.rs b/tests/interpreter.rs index 56ed101..3f1c824 100644 --- a/tests/interpreter.rs +++ b/tests/interpreter.rs @@ -1,4 +1,16 @@ -use mini_c::{interpreter::interpret, parser::program, semantic::type_check}; +use mini_c::{ + environment::Environment, + interpreter::{ + eval_expr::eval_call, + exec_stmt::exec_stmt, + value::{FnValue, Value}, + }, + ir::ast::{CheckedProgram, CheckedStmt, Statement}, + parser::program, + semantic::type_check, + stdlib::NativeRegistry, +}; +use mini_c::interpreter::interpret; /// Parse, type-check, and interpret a MiniC source string. fn run(src: &str) -> Result<(), String> { @@ -9,6 +21,52 @@ fn run(src: &str) -> Result<(), String> { interpret(&checked).map_err(|e| format!("runtime error: {}", e.message)) } +fn checked_program(src: &str) -> Result { + let unchecked = program(src) + .map_err(|e| format!("parse error: {:?}", e)) + .map(|(_, p)| p)?; + type_check(&unchecked).map_err(|e| format!("type error: {}", e.message)) +} + +fn exec_stmt_sequence(seq: &[CheckedStmt], env: &mut Environment) -> Result<(), String> { + for stmt in seq { + match exec_stmt(stmt, env).map_err(|e| format!("runtime error: {}", e.message))? { + Some(Value::Void) => continue, + Some(v) => return Err(format!("unexpected return value: {:?}", v)), + None => continue, + } + } + Ok(()) +} + +fn main_body_sequence(program: &CheckedProgram) -> Result<&[CheckedStmt], String> { + let main = program + .main_function() + .ok_or_else(|| "missing main function".to_string())?; + match &main.body.stmt { + Statement::Block { seq } => Ok(seq), + _ => Err("main body is not a block".to_string()), + } +} + +fn build_program_env(program: &CheckedProgram) -> Environment { + let mut env = Environment::new(); + + // Register stdlib native functions like the interpreter does. + let registry = NativeRegistry::default(); + for (name, entry) in registry.iter() { + env.declare(name.clone(), Value::Fn(FnValue::Native(entry.func))); + } + + for fun in &program.functions { + env.declare( + fun.name.clone(), + Value::Fn(FnValue::UserDefined(fun.clone())), + ); + } + env +} + // --------------------------------------------------------------------------- // 7.2 Empty main // --------------------------------------------------------------------------- @@ -312,3 +370,124 @@ fn test_pointer_return() { "#; assert!(run(src).is_ok(), "{}", run(src).unwrap_err()); } + +#[test] +fn test_pointer_deref_returns_original_value() { + let program = checked_program( + r#" + void main() { + int x = 10; + int* p = &x; + int y = *p; + } + "#, + ) + .unwrap(); + + let mut env = Environment::new(); + exec_stmt_sequence(main_body_sequence(&program).unwrap(), &mut env).unwrap(); + + assert_eq!(env.get("y"), Some(&Value::Int(10))); +} + +#[test] +fn test_pointer_deref_assign_updates_target() { + let program = checked_program( + r#" + void main() { + int x = 10; + int* p = &x; + *p = *p + 1; + } + "#, + ) + .unwrap(); + + let mut env = Environment::new(); + exec_stmt_sequence(main_body_sequence(&program).unwrap(), &mut env).unwrap(); + + assert_eq!(env.get("x"), Some(&Value::Int(11))); +} + +#[test] +fn test_pointer_rebind_and_deref_new_target() { + let program = checked_program( + r#" + void main() { + int x = 10; + int y = 5; + int* p = &x; + p = &y; + *p = 99; + } + "#, + ) + .unwrap(); + + let mut env = Environment::new(); + exec_stmt_sequence(main_body_sequence(&program).unwrap(), &mut env).unwrap(); + + assert_eq!(env.get("x"), Some(&Value::Int(10))); + assert_eq!(env.get("y"), Some(&Value::Int(99))); +} + +#[test] +fn test_pointer_function_return_and_deref() { + let program = checked_program( + r#" + int* pick(int* a, int* b) { return b; } + void main() { + int x = 1; + int y = 2; + int* p = pick(&x, &y); + int z = *p; + } + "#, + ) + .unwrap(); + + let mut env = build_program_env(&program); + exec_stmt_sequence(main_body_sequence(&program).unwrap(), &mut env).unwrap(); + + assert_eq!(env.get("z"), Some(&Value::Int(2))); +} + +#[test] +fn test_pointer_parameter_aliasing_updates_caller_variable() { + let program = checked_program( + r#" + void increment(int* p) { + *p = *p + 1; + } + void main() { + int x = 10; + increment(&x); + } + "#, + ) + .unwrap(); + + let mut env = build_program_env(&program); + env.declare("x".to_string(), Value::Int(10)); + eval_call("increment", vec![Value::Ptr("x".to_string())], &mut env) + .expect("pointer function call failed"); + + assert_eq!(env.get("x"), Some(&Value::Int(11))); +} + +#[test] +fn test_pointer_dangling_after_return_is_runtime_error() { + let src = r#" + int* leak(int x) { + return &x; + } + void main() { + int* p = leak(10); + int y = *p; + } + "#; + + let result = run(src); + assert!(result.is_err(), "expected dangling pointer runtime error"); + assert!(result.unwrap_err().contains("dereference of invalid target")); +} From 52a56b50d642591f570735910078ec3b30cdbdba Mon Sep 17 00:00:00 2001 From: Marcelo Arcoverde Date: Tue, 30 Jun 2026 07:41:22 -0300 Subject: [PATCH 19/26] test: add AddrOf test coverage --- tests/tac_gen.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/tac_gen.rs b/tests/tac_gen.rs index 5689696..81fab8b 100644 --- a/tests/tac_gen.rs +++ b/tests/tac_gen.rs @@ -107,3 +107,38 @@ fn test_pointer_assignment() { Instruction::DerefWrite(p, temp2), ]); } + +#[test] +fn test_pointer_address_of() { + + let addr_of_expr = ExprD { + exp: Expr::AddrOf(Box::new(ExprD { + exp: Expr::Ident("x".to_string()), + ty: Type::Int + })), + ty: Type::Pointer(Box::new(Type::Int)), + }; + + let stmt = StatementD { + stmt: Statement::Assign { + target: Box::new(ExprD { + exp: Expr::Ident("y".to_string()), + ty: Type::Pointer(Box::new(Type::Int)) + }), + value: Box::new(addr_of_expr), + }, + ty: Type::Unit, + }; + + let mut env = Environment::new(); + let instructions = translate_statement(stmt, &mut env); + + let x = Address::Variable("x".to_string(), Type::Int); + let y = Address::Variable("y".to_string(), Type::Pointer(Box::new(Type::Int))); + let temp1 = Address::Temporary("temp1".to_string(), Type::Pointer(Box::new(Type::Int))); + + assert_eq!(instructions, vec![ + Instruction::AddressOf(temp1.clone(), x), + Instruction::CopyAssignment(y, temp1), + ]); +} \ No newline at end of file From 42bf74f64454479cb37f323f4faa004309e3d7de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Silvestre=20de=20Fran=C3=A7a=20e=20Souza?= <136166261+dsfs10@users.noreply.github.com> Date: Tue, 30 Jun 2026 11:08:03 -0300 Subject: [PATCH 20/26] test: fix function return pointer --- tests/parser.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/parser.rs b/tests/parser.rs index 9869bbb..de4d3c1 100644 --- a/tests/parser.rs +++ b/tests/parser.rs @@ -748,6 +748,12 @@ fn test_pointer_type_function() { .unwrap() .1; assert_eq!(result.name, "changeRef"); + + assert_eq!( + result.return_type, + Type::Pointer(Box::new(Type::Int)) + ); + assert_eq!( result.params, vec![ @@ -755,4 +761,4 @@ fn test_pointer_type_function() { ("y".to_string(), Type::Pointer(Box::new(Type::Int))) ] ); -} \ No newline at end of file +} From 01a15e8d454124dfc8231e921a689c91c7556573 Mon Sep 17 00:00:00 2001 From: Marcelo Arcoverde Date: Thu, 2 Jul 2026 13:05:10 -0300 Subject: [PATCH 21/26] refactor(environment): separate lexical scope from memory store --- src/environment/env.rs | 66 +++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/src/environment/env.rs b/src/environment/env.rs index dd83ba1..efbd104 100644 --- a/src/environment/env.rs +++ b/src/environment/env.rs @@ -53,59 +53,77 @@ //! language. The cost — cloning the entire map on each function call — is //! acceptable at MiniC's scale. -use std::collections::{HashMap, HashSet}; +use std::collections::{HashMap}; -/// Unified parametric environment: maps names to values of type `V`. -/// Both variable bindings and function bindings are stored in the same map. +/// Unified parametric environment representing program state. +/// It separates lexical bindings (`scopes`) from physical memory (`store`). pub struct Environment { - bindings: HashMap, + scopes: HashMap, + store: HashMap, + next_address: usize, } impl Environment { pub fn new() -> Self { Self { - bindings: HashMap::new(), + scopes: HashMap::new(), + store: HashMap::new(), + next_address: 1, } } - /// Bind `name` to `value`, overwriting any existing binding. + /// Allocates a new address in the store for `value`, and binds `name` to this address. pub fn declare(&mut self, name: impl Into, value: V) { - self.bindings.insert(name.into(), value); + let addr = self.next_address; + self.next_address += 1; + self.store.insert(addr, value); + self.scopes.insert(name.into(), addr); } - /// Look up a binding by name. + /// Looks up the value currently bound to `name` by resolving its physical address. pub fn get(&self, name: &str) -> Option<&V> { - self.bindings.get(name) + let addr = self.scopes.get(name)?; + self.store.get(addr) } - /// Update an existing binding. Returns `false` if the name is not bound. + /// Updates the value at the address currently bound to `name`. Returns `false` if the name is not found. pub fn set(&mut self, name: &str, value: V) -> bool { - if self.bindings.contains_key(name) { - self.bindings.insert(name.to_string(), value); + if let Some(&addr) = self.scopes.get(name) { + self.store.insert(addr, value); true } else { false } } - /// Capture a full clone of the current bindings (for function call scoping). - pub fn snapshot(&self) -> HashMap { - self.bindings.clone() + /// Returns the physical address (usize) bound to `name`. + pub fn get_address(&self, name: &str) -> Option { + self.scopes.get(name).copied() } - /// Replace all bindings with the given snapshot (for function call scoping). - pub fn restore(&mut self, snapshot: HashMap) { - self.bindings = snapshot; + /// Reads a value directly from the memory store using its physical address. + pub fn read_store(&self, addr: usize) -> Option<&V> { + self.store.get(&addr) } - /// Return the set of currently bound names (for block-entry capture). - pub fn names(&self) -> HashSet { - self.bindings.keys().cloned().collect() + /// Writes a value directly to the memory store at the given physical address. + pub fn write_store(&mut self, addr: usize, value: V) -> bool { + if self.store.contains_key(&addr) { + self.store.insert(addr, value); + true + } else { + false + } + } + + /// Captures a clone of the current lexical scope (names to addresses). + pub fn snapshot(&self) -> HashMap { + self.scopes.clone() } - /// Remove any binding whose name is not in `outer` (for block-exit cleanup). - pub fn remove_new(&mut self, outer: &HashSet) { - self.bindings.retain(|k, _| outer.contains(k)); + /// Replaces the current lexical scope with a previously saved snapshot. + pub fn restore(&mut self, snapshot: HashMap) { + self.scopes = snapshot; } } From 5d0f480c9c8e10eb06aebe83131acac0a78feb80 Mon Sep 17 00:00:00 2001 From: Marcelo Arcoverde Date: Thu, 2 Jul 2026 13:05:51 -0300 Subject: [PATCH 22/26] refactor(interpreter): update pointer evaluation to use numeric addresses --- src/interpreter/eval_expr.rs | 55 ++++++++---------------------------- 1 file changed, 12 insertions(+), 43 deletions(-) diff --git a/src/interpreter/eval_expr.rs b/src/interpreter/eval_expr.rs index 43b7fc8..627576c 100644 --- a/src/interpreter/eval_expr.rs +++ b/src/interpreter/eval_expr.rs @@ -155,48 +155,24 @@ pub fn eval_expr(expr: &CheckedExpr, env: &mut Environment) -> Result) -> Result { match &elem.exp { Expr::Ident(name) => { - if env.get(name).is_none() { - return Err(RuntimeError::new(format!("undefined variable '{}'", name))); + if let Some(addr) = env.get_address(name) { + Ok(Value::Ptr(addr)) + } else { + Err(RuntimeError::new(format!("undefined variable '{}'", name))) } - Ok(Value::Ptr(name.clone())) } _ => Err(RuntimeError::new("can only take address of variables")), } } fn eval_deref(elem: &CheckedExpr, env: &mut Environment) -> Result { - let target = ptr_target(eval_expr(elem, env)?)?; - read_through(env, &target) -} - -/// Extract the target variable name from a pointer value. -pub(crate) fn ptr_target(val: Value) -> Result { - match val { - Value::Ptr(target) => Ok(target), - v => Err(RuntimeError::new(format!("expected pointer, got: {}", v))), - } -} - -/// Read the value stored at the variable named by a pointer. -pub(crate) fn read_through(env: &Environment, target: &str) -> Result { - env.get(target) - .cloned() - .ok_or_else(|| RuntimeError::new(format!("dereference of invalid target '{}'", target))) -} - -/// Write `val` into the variable named by a pointer. -pub(crate) fn write_through( - env: &mut Environment, - target: &str, - val: Value, -) -> Result<(), RuntimeError> { - if env.set(target, val) { - Ok(()) + let ptr_val = eval_expr(elem, env)?; + if let Value::Ptr(addr) = ptr_val { + env.read_store(addr).cloned().ok_or_else(|| { + RuntimeError::new(format!("dereference of invalid address '{}'", addr)) + }) } else { - Err(RuntimeError::new(format!( - "dereference of invalid target '{}'", - target - ))) + Err(RuntimeError::new("cannot dereference non-pointer value")) } } @@ -219,21 +195,14 @@ pub fn eval_call( } let snapshot = env.snapshot(); - let outer_keys = env.names(); - + for ((param_name, _), val) in decl.params.iter().zip(args.into_iter()) { env.declare(param_name.clone(), val); } let result = exec_stmt(&decl.body, env)?; - env.remove_new(&outer_keys); - - for (param_name, _) in decl.params.iter() { - if let Some(old_val) = snapshot.get(param_name) { - env.declare(param_name.clone(), old_val.clone()); - } - } + env.restore(snapshot); Ok(result.unwrap_or(Value::Void)) } From 8323613fe7321ef8d1c4c090731a7c9eb7733d71 Mon Sep 17 00:00:00 2001 From: Marcelo Arcoverde Date: Thu, 2 Jul 2026 13:06:09 -0300 Subject: [PATCH 23/26] refactor(interpreter): streamline block execution and pointer assignment --- src/interpreter/exec_stmt.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/interpreter/exec_stmt.rs b/src/interpreter/exec_stmt.rs index 689813c..62932eb 100644 --- a/src/interpreter/exec_stmt.rs +++ b/src/interpreter/exec_stmt.rs @@ -36,7 +36,7 @@ use crate::environment::Environment; use crate::ir::ast::{CheckedExpr, CheckedStmt, Expr, Statement}; -use super::eval_expr::{eval_call, eval_expr, ptr_target, write_through}; +use super::eval_expr::{eval_call, eval_expr}; use super::value::{RuntimeError, Value}; /// `None` = normal fall-through; `Some(v)` = early return with value. @@ -63,14 +63,16 @@ pub fn exec_stmt(stmt: &CheckedStmt, env: &mut Environment) -> ExecResult // Only remove variables declared inside the block on exit. // Assignments to outer-scope variables must persist (e.g., loop counters). Statement::Block { seq } => { - let outer_keys = env.names(); - for s in seq { - if let Some(ret) = exec_stmt(s, env)? { - env.remove_new(&outer_keys); - return Ok(Some(ret)); + let snapshot = env.snapshot(); + + for stmt in seq { + if let Some(val) = exec_stmt(stmt, env)? { + env.restore(snapshot.clone()); + return Ok(Some(val)); } } - env.remove_new(&outer_keys); + + env.restore(snapshot); Ok(None) } @@ -159,8 +161,16 @@ fn assign_lvalue( assign_index(base, idx, val, env) } Expr::Deref(inner) => { - let target = ptr_target(eval_expr(inner, env)?)?; - write_through(env, &target, val) + let ptr_val = eval_expr(inner, env)?; + if let Value::Ptr(addr) = ptr_val { + if env.write_store(addr, val) { + Ok(()) + } else { + Err(RuntimeError::new(format!("invalid memory address '{}'", addr))) + } + } else { + Err(RuntimeError::new("cannot assign to dereference of non-pointer")) + } } _ => Err(RuntimeError::new("invalid assignment target".to_string())), } From 0493aabfd135a5d9a336b840bd548e5c3539850f Mon Sep 17 00:00:00 2001 From: Marcelo Arcoverde Date: Thu, 2 Jul 2026 13:06:39 -0300 Subject: [PATCH 24/26] fix(semantic): align type checker snapshot with new environment signature --- src/semantic/type_checker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semantic/type_checker.rs b/src/semantic/type_checker.rs index 3ed9216..6380ce7 100644 --- a/src/semantic/type_checker.rs +++ b/src/semantic/type_checker.rs @@ -123,7 +123,7 @@ pub fn type_check(program: &UncheckedProgram) -> Result, - fn_snapshot: &HashMap, + fn_snapshot: &HashMap, ) -> Result { // Restore to clean function-only state, then add parameters. env.restore(fn_snapshot.clone()); From b3c1aa429863ffc631e47265570e91267cae636e Mon Sep 17 00:00:00 2001 From: Marcelo Arcoverde Date: Thu, 2 Jul 2026 13:07:05 -0300 Subject: [PATCH 25/26] test(interpreter): update tests to validate stack and heap separation --- tests/interpreter.rs | 59 +++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/tests/interpreter.rs b/tests/interpreter.rs index 3f1c824..ee9bc6d 100644 --- a/tests/interpreter.rs +++ b/tests/interpreter.rs @@ -469,25 +469,56 @@ fn test_pointer_parameter_aliasing_updates_caller_variable() { let mut env = build_program_env(&program); env.declare("x".to_string(), Value::Int(10)); - eval_call("increment", vec![Value::Ptr("x".to_string())], &mut env) + + let addr_x = env.get_address("x").expect("x deveria ter um endereço"); + eval_call("increment", vec![Value::Ptr(addr_x)], &mut env) .expect("pointer function call failed"); assert_eq!(env.get("x"), Some(&Value::Int(11))); } #[test] -fn test_pointer_dangling_after_return_is_runtime_error() { - let src = r#" - int* leak(int x) { - return &x; - } - void main() { - int* p = leak(10); - int y = *p; - } - "#; +fn test_pointer_escaping_local_scope_survives_in_store() { + let program = checked_program( + r#" + int* leak(int x) { + return &x; + } + void main() { + int* p = leak(10); + int y = *p; + } + "#, + ) + .unwrap(); - let result = run(src); - assert!(result.is_err(), "expected dangling pointer runtime error"); - assert!(result.unwrap_err().contains("dereference of invalid target")); + let mut env = build_program_env(&program); + exec_stmt_sequence(main_body_sequence(&program).unwrap(), &mut env).unwrap(); + + assert_eq!(env.get("y"), Some(&Value::Int(10))); +} + +#[test] +fn test_traditional_stack_and_heap_separation() { + let mut env = Environment::new(); + + env.declare("x".to_string(), Value::Int(10)); + let addr_x = env.get_address("x").unwrap(); + + let snapshot = env.snapshot(); + + env.declare("p".to_string(), Value::Ptr(addr_x)); + + if let Some(&Value::Ptr(target_addr)) = env.get("p") { + env.write_store(target_addr, Value::Int(11)); + } + + env.restore(snapshot); + + assert!(env.get("p").is_none(), "O isolamento de escopo falhou, 'p' vazou."); + assert_eq!( + env.get("x"), + Some(&Value::Int(11)), + "A mutação de memória foi perdida! A Store foi indevidamente revertida." + ); } From 48c99b2502ab535fb89f559c762470bb43804875 Mon Sep 17 00:00:00 2001 From: Marcelo Arcoverde Date: Thu, 2 Jul 2026 13:07:36 -0300 Subject: [PATCH 26/26] minor changes --- src/codegen/tac_code_gen.rs | 2 ++ src/interpreter/value.rs | 2 +- tests/parser.rs | 2 +- tests/tac_gen.rs | 10 ++++++---- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/codegen/tac_code_gen.rs b/src/codegen/tac_code_gen.rs index 4ecc981..a7b1171 100644 --- a/src/codegen/tac_code_gen.rs +++ b/src/codegen/tac_code_gen.rs @@ -27,6 +27,7 @@ impl Environment { } } +#[allow(dead_code)] fn translate_program(program: CheckedProgram, env: &mut Environment) -> TACProgram { let main_fn = program.main_function(); match main_fn { @@ -36,6 +37,7 @@ fn translate_program(program: CheckedProgram, env: &mut Environment) -> TACProgr } +#[allow(dead_code)] fn translate_function(function: CheckedFunDecl, env: &mut Environment) -> TACProgram { let mut instructions = if let Statement::Block { seq : stmts } = function.body.stmt { diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index d8b27b8..5dcda9d 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -105,7 +105,7 @@ pub enum Value { Void, Fn(FnValue), /// Pointer: holds the name of the target variable in the environment. - Ptr(String), + Ptr(usize), } impl fmt::Display for Value { diff --git a/tests/parser.rs b/tests/parser.rs index de4d3c1..82eba6b 100644 --- a/tests/parser.rs +++ b/tests/parser.rs @@ -659,7 +659,7 @@ fn test_multidimensional_indexed_assignment() { #[test] fn test_nested_index() { let result = expression("arr[i][j]").unwrap().1; - assert!(matches!(result.exp, Expr::Index { ref base, ref index } + assert!(matches!(result.exp, Expr::Index { base: _, ref index } if matches!(index.exp, Expr::Ident(ref s) if s == "j"))); if let Expr::Index { ref base, .. } = result.exp { assert!(matches!(base.exp, Expr::Index { ref base, ref index } diff --git a/tests/tac_gen.rs b/tests/tac_gen.rs index 81fab8b..f726e94 100644 --- a/tests/tac_gen.rs +++ b/tests/tac_gen.rs @@ -58,13 +58,15 @@ fn test_if_else_with_relational_condition() { let temp = Address::Temporary("temp1".to_string(), Type::Int); assert_eq!(instructions, vec![ - Instruction::ConditionalJMPRelational(Operator::GTE, x.clone(), y.clone(), "Label1:".to_string()), - Instruction::BinaryAssignment(Operator::Add, temp.clone(), x.clone(), y.clone()), - Instruction::CopyAssignment(z.clone(), temp), + Instruction::ConditionalJMPRelational(Operator::LT, x.clone(), y.clone(), "Label1:".to_string()), Instruction::JMP("Label2:".to_string()), Instruction::Label("Label1:".to_string()), - Instruction::CopyAssignment(z, x), + Instruction::BinaryAssignment(Operator::Add, temp.clone(), x.clone(), y.clone()), + Instruction::CopyAssignment(z.clone(), temp), + Instruction::JMP("Label3:".to_string()), Instruction::Label("Label2:".to_string()), + Instruction::CopyAssignment(z, x), + Instruction::Label("Label3:".to_string()), ]); }