Skip to content

Commit d79be09

Browse files
Merge pull request #65 from UnBCIC-TP2/develop-Rafael
Integrating PR #51 and #52 into main.
2 parents fd0917a + fa5f2ac commit d79be09

File tree

12 files changed

+564
-51
lines changed

12 files changed

+564
-51
lines changed

docs/PARSER.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ The parser supports various types of statements:
6060
- Assert statements
6161
- ADT (Algebraic Data Type) declarations
6262

63+
#### For statement semantics (iterables and scoping)
64+
65+
- Syntax: `for <identifier> in <expression>: <block> end`
66+
- Supported iterables at parse time (resolved at runtime/type-check):
67+
- Lists: `[e1, e2, ...]`
68+
- Strings: "abc" (iterates over characters as 1-length strings)
69+
- Tuples: `(e1, e2, ...)` (see tuple literals below)
70+
- Scoping: the loop variable is bound in an inner scope created for the loop body. It is not visible outside the loop.
71+
- The variable is considered immutable by the type checker; each iteration rebinds it.
72+
6373
### Expression Parsing
6474
Handles different types of expressions:
6575
- Arithmetic expressions
@@ -69,6 +79,17 @@ Handles different types of expressions:
6979
- Literals (numbers, strings, booleans)
7080
- ADT constructors and pattern matching
7181

82+
#### Tuple literals and parenthesis grouping
83+
84+
The parser supports tuple literals and distinguishes them from parenthesized groupings:
85+
86+
- Empty tuple: `()` -> `Expression::Tuple([])`
87+
- Single-element tuple: `(x,)` -> `Expression::Tuple([x])`
88+
- Multi-element tuple: `(x, y, z)` -> `Expression::Tuple([x, y, z])`
89+
- Grouping (no comma): `(expr)` -> parsed as `expr` (not a tuple)
90+
91+
Tuples may be nested, e.g., `((1, 2), (3, 4))`.
92+
7293
### Type System
7394
Supports a rich type system including:
7495
- Basic types (Int, Real, Boolean, String, Unit, Any)

docs/type_checker.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,34 @@ Error: type mismatch
236236
```
237237

238238
### Statement Sequences
239+
### For Loops
240+
241+
Typing rules for `for` loops support the following iterable types:
242+
243+
- `TList(T)` iterates elements of type `T`.
244+
- `TString` iterates characters as `TString` (1-length strings).
245+
- `TTuple([T, T, ..., T])` iterates elements if and only if the tuple is homogeneous (all element types equal). Empty tuple is rejected.
246+
247+
Scoping and binding:
248+
249+
- The loop introduces an inner scope for the body using `push()`/`pop()`.
250+
- The iterator variable is bound as immutable to the element type in that inner scope.
251+
- The variable does not escape to the outer scope after the loop finishes.
252+
253+
Formally:
254+
255+
```
256+
Γ ⊢ e : TList(T) or Γ ⊢ e : TString or Γ ⊢ e : TTuple([T, ..., T]) (homogeneous, non-empty)
257+
Γ, x:T ⊢ s : Γ' (checked in an inner scope)
258+
———————————————————————————————————————————————————————————————————————————————
259+
Γ ⊢ For(x, e, s) : Γ
260+
```
261+
262+
Errors:
263+
264+
- Non-iterable type in `e``[Type Error] Type <...> is not iterable`.
265+
- Empty tuple type → `[Type Error] Cannot iterate over empty tuple type`.
266+
- Heterogeneous tuple → `[Type Error] Can only iterate over homogeneous tuples (all elements same type)`.
239267

240268
```rust
241269
// Sequential composition

src/environment/environment.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,21 @@ impl<A: Clone> Scope<A> {
4949
.map(|(mutable, value)| (*mutable, value.clone()))
5050
}
5151

52+
fn update_var(&mut self, var: &Name, value: A) -> bool {
53+
if let Some((mutable, slot)) = self.variables.get_mut(var) {
54+
*slot = value;
55+
// preserve existing mutability flag
56+
let _ = mutable; // silence unused warning if optimised out
57+
true
58+
} else {
59+
false
60+
}
61+
}
62+
63+
fn remove_var(&mut self, var: &Name) -> bool {
64+
self.variables.remove(var).is_some()
65+
}
66+
5267
fn lookup_function(&self, name: &Name) -> Option<&Function> {
5368
self.functions.get(name)
5469
}
@@ -113,6 +128,30 @@ impl<A: Clone> Environment<A> {
113128
self.globals.lookup_var(var)
114129
}
115130

131+
/// Update an existing variable in the nearest scope where it's defined.
132+
/// Returns true if the variable existed and was updated; false otherwise.
133+
pub fn update_existing_variable(&mut self, var: &Name, value: A) -> bool {
134+
// Search local scopes first (top-most first)
135+
for scope in self.stack.iter_mut() {
136+
if scope.update_var(var, value.clone()) {
137+
return true;
138+
}
139+
}
140+
// Fallback to globals
141+
self.globals.update_var(var, value)
142+
}
143+
144+
/// Remove a variable from the nearest scope where it's defined.
145+
/// Returns true if something was removed; false otherwise.
146+
pub fn remove_variable(&mut self, var: &Name) -> bool {
147+
for scope in self.stack.iter_mut() {
148+
if scope.remove_var(var) {
149+
return true;
150+
}
151+
}
152+
self.globals.remove_var(var)
153+
}
154+
116155
pub fn lookup_function(&self, name: &Name) -> Option<&Function> {
117156
for scope in self.stack.iter() {
118157
if let Some(func) = scope.lookup_function(name) {

src/interpreter/expression_eval.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub fn eval(exp: Expression, env: &Environment<Expression>) -> Result<Expression
3333
Expression::IsNothing(e) => eval_isnothing_expression(*e, env),
3434
Expression::FuncCall(name, args) => eval_function_call(name, args, env),
3535
Expression::ListValue(values) => eval_list_value(values, env),
36+
Expression::Tuple(values) => eval_tuple_value(values, env),
3637
_ if is_constant(exp.clone()) => Ok(ExpressionResult::Value(exp)),
3738
_ => Err(String::from("Not implemented yet.")),
3839
}
@@ -535,6 +536,20 @@ fn eval_list_value(
535536
Ok(ExpressionResult::Value(Expression::ListValue(values)))
536537
}
537538

539+
fn eval_tuple_value(
540+
sub_expressions: Vec<Expression>,
541+
env: &Environment<Expression>,
542+
) -> Result<ExpressionResult, String> {
543+
let mut values = Vec::new();
544+
for exp in sub_expressions {
545+
match eval(exp, env)? {
546+
ExpressionResult::Value(expr) => values.push(expr),
547+
ExpressionResult::Propagate(expr) => return Ok(ExpressionResult::Propagate(expr)),
548+
}
549+
}
550+
Ok(ExpressionResult::Value(Expression::Tuple(values)))
551+
}
552+
538553
#[cfg(test)]
539554
mod tests {
540555
use super::*;

src/interpreter/statement_execute.rs

Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,25 @@ pub fn execute(stmt: Statement, env: &Environment<Expression>) -> Result<Computa
248248
return Ok(Computation::PropagateError(expr, new_env))
249249
}
250250
};
251-
new_env.map_variable(name, true, value);
251+
// Respect existing mutability; if variable exists and is immutable, propagate error
252+
match new_env.lookup(&name) {
253+
Some((is_mut, _)) => {
254+
if !is_mut {
255+
return Ok(Computation::PropagateError(
256+
Expression::CString(format!(
257+
"Cannot assign to immutable variable '{}'",
258+
name
259+
)),
260+
new_env,
261+
));
262+
}
263+
let _ = new_env.update_existing_variable(&name, value);
264+
}
265+
None => {
266+
// If not previously declared, create as mutable (back-compat with tests)
267+
new_env.map_variable(name, true, value);
268+
}
269+
}
252270
Ok(Computation::Continue(new_env))
253271
}
254272

@@ -316,18 +334,68 @@ pub fn execute(stmt: Statement, env: &Environment<Expression>) -> Result<Computa
316334
}
317335
}
318336

319-
Statement::For(var, list, stmt) => {
320-
let values = match eval(*list.clone(), &new_env)? {
337+
Statement::For(var, expr, stmt) => {
338+
let coll = match eval(*expr.clone(), &new_env)? {
321339
ExpressionResult::Value(expr) => expr,
322340
ExpressionResult::Propagate(expr) => {
323341
return Ok(Computation::PropagateError(expr, new_env))
324342
}
325343
};
326344

327-
match values {
328-
Expression::ListValue(expressions) => {
329-
for exp in expressions {
330-
new_env.map_variable(var.clone(), false, exp);
345+
match coll {
346+
// List of values
347+
Expression::ListValue(items) => {
348+
for item in items {
349+
// Bind loop variable in a transient manner: shadow during iteration
350+
// Save previous binding (if any)
351+
let prev = new_env.lookup(&var.clone());
352+
new_env.map_variable(var.clone(), false, item);
353+
match execute(*stmt.clone(), &new_env)? {
354+
Computation::Continue(env) => new_env = env,
355+
Computation::Return(expr, env) => {
356+
return Ok(Computation::Return(expr, env))
357+
}
358+
Computation::PropagateError(expr, env) => {
359+
return Ok(Computation::PropagateError(expr, env))
360+
}
361+
}
362+
// Restore previous binding after each iteration
363+
let _ = new_env.remove_variable(&var.clone());
364+
if let Some((was_mut, old_val)) = prev {
365+
new_env.map_variable(var.clone(), was_mut, old_val);
366+
}
367+
}
368+
Ok(Computation::Continue(new_env))
369+
}
370+
371+
// String - itera sobre caracteres
372+
Expression::CString(s) => {
373+
for ch in s.chars() {
374+
let char_value = Expression::CString(ch.to_string());
375+
let prev = new_env.lookup(&var.clone());
376+
new_env.map_variable(var.clone(), false, char_value);
377+
match execute(*stmt.clone(), &new_env)? {
378+
Computation::Continue(env) => new_env = env,
379+
Computation::Return(expr, env) => {
380+
return Ok(Computation::Return(expr, env))
381+
}
382+
Computation::PropagateError(expr, env) => {
383+
return Ok(Computation::PropagateError(expr, env))
384+
}
385+
}
386+
let _ = new_env.remove_variable(&var.clone());
387+
if let Some((was_mut, old_val)) = prev {
388+
new_env.map_variable(var.clone(), was_mut, old_val);
389+
}
390+
}
391+
Ok(Computation::Continue(new_env))
392+
}
393+
394+
// Tupla (assumindo que você tem Expression::Tuple)
395+
Expression::Tuple(items) => {
396+
for item in items {
397+
let prev = new_env.lookup(&var.clone());
398+
new_env.map_variable(var.clone(), false, item);
331399
match execute(*stmt.clone(), &new_env)? {
332400
Computation::Continue(env) => new_env = env,
333401
Computation::Return(expr, env) => {
@@ -337,10 +405,38 @@ pub fn execute(stmt: Statement, env: &Environment<Expression>) -> Result<Computa
337405
return Ok(Computation::PropagateError(expr, env))
338406
}
339407
}
408+
let _ = new_env.remove_variable(&var.clone());
409+
if let Some((was_mut, old_val)) = prev {
410+
new_env.map_variable(var.clone(), was_mut, old_val);
411+
}
340412
}
341-
return Ok(Computation::Continue(new_env));
413+
Ok(Computation::Continue(new_env))
342414
}
343-
_ => unreachable!(),
415+
416+
// Constructor (já existia)
417+
Expression::Constructor(_, items) => {
418+
for item_expr in items {
419+
let item_value = *item_expr.clone();
420+
let prev = new_env.lookup(&var.clone());
421+
new_env.map_variable(var.clone(), false, item_value);
422+
match execute(*stmt.clone(), &new_env)? {
423+
Computation::Continue(env) => new_env = env,
424+
Computation::Return(expr, env) => {
425+
return Ok(Computation::Return(expr, env))
426+
}
427+
Computation::PropagateError(expr, env) => {
428+
return Ok(Computation::PropagateError(expr, env))
429+
}
430+
}
431+
let _ = new_env.remove_variable(&var.clone());
432+
if let Some((was_mut, old_val)) = prev {
433+
new_env.map_variable(var.clone(), was_mut, old_val);
434+
}
435+
}
436+
Ok(Computation::Continue(new_env))
437+
}
438+
439+
_ => Err(String::from("Cannot iterate over provided expression")),
344440
}
345441
}
346442

@@ -859,11 +955,9 @@ mod tests {
859955
let (_, result_expr) = result_value.unwrap();
860956
assert_eq!(result_expr, Expression::CInt(42));
861957

862-
// Check that loop variable i is still accessible with the last value
958+
// With isolated loop scope, the iterator variable should NOT leak outside the loop
863959
let i_value = final_env.lookup(&"i".to_string());
864-
assert!(i_value.is_some());
865-
let (_, i_expr) = i_value.unwrap();
866-
assert_eq!(i_expr, Expression::CInt(42));
960+
assert!(i_value.is_none());
867961
}
868962

869963
#[test]

src/ir/ast.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ pub enum Expression {
119119
// List value
120120
ListValue(Vec<Expression>),
121121

122+
// Tuple value
123+
Tuple(Vec<Expression>),
124+
122125
// Constructor
123126
Constructor(Name, Vec<Box<Expression>>),
124127
}
@@ -130,6 +133,10 @@ pub enum Statement {
130133
ValDeclaration(Name, Box<Expression>),
131134
Assignment(Name, Box<Expression>),
132135
IfThenElse(Box<Expression>, Box<Statement>, Option<Box<Statement>>),
136+
IfChain {
137+
branches: Vec<(Box<Expression>, Box<Statement>)>,
138+
else_branch: Option<Box<Statement>>,
139+
},
133140
While(Box<Expression>, Box<Statement>),
134141
For(Name, Box<Expression>, Box<Statement>),
135142
Block(Vec<Statement>),

src/parser/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub const KEYWORDS: &[&str] = &[
22
"if",
33
"in",
44
"else",
5+
"elif",
56
"def",
67
"while",
78
"for",

src/parser/parser_common.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub const END_KEYWORD: &str = "end";
2828

2929
// Statement keyword constants
3030
pub const IF_KEYWORD: &str = "if";
31+
pub const ELIF_KEYWORD: &str = "elif";
3132
pub const ELSE_KEYWORD: &str = "else";
3233
pub const WHILE_KEYWORD: &str = "while";
3334
pub const FOR_KEYWORD: &str = "for";
@@ -72,11 +73,16 @@ pub fn separator<'a>(sep: &'static str) -> impl FnMut(&'a str) -> IResult<&'a st
7273
}
7374

7475
/// Parses a reserved keyword (e.g., "if") surrounded by optional spaces
75-
/// Fails if followed by an identifier character
76+
/// A implementação da função keyword foi alterada para que seja garantida que a keyword seja uma palavra completa e seja separada por um espaço
7677
pub fn keyword<'a>(kw: &'static str) -> impl FnMut(&'a str) -> IResult<&'a str, &'a str> {
77-
terminated(
78-
delimited(multispace0, tag(kw), multispace0),
79-
not(peek(identifier_start_or_continue)),
78+
delimited(
79+
multispace0,
80+
terminated(
81+
tag(kw),
82+
// Ensure the keyword is not followed by an identifier character (letter, digit, or underscore)
83+
peek(not(identifier_start_or_continue)),
84+
),
85+
multispace0,
8086
)
8187
}
8288

0 commit comments

Comments
 (0)