Skip to content

Project 3 — Switch Statement - #18

Open
PauloV1 wants to merge 23 commits into
rbonifacio:mainfrom
PauloV1:refactor/switch
Open

Project 3 — Switch Statement#18
PauloV1 wants to merge 23 commits into
rbonifacio:mainfrom
PauloV1:refactor/switch

Conversation

@PauloV1

@PauloV1 PauloV1 commented Jul 9, 2026

Copy link
Copy Markdown

Summary

This PR implements the changes suggested by the professor during the presentation.

Statement::Switch used to carry two separate fields — cases: Vec<(Literal, Vec<StatementD<Ty>>)> and default: Vec<StatementD<Ty>> — which forced every downstream stage (parser, type checker, interpreter, codegen) to handle the default arm as a special, duplicated code path. This PR collapses both into a single Vec<(MatchCase, Box<StatementD<Ty>>)>, where MatchCase is CaseLiteral(Literal) | CaseDefault, and propagates that shape through the whole pipeline.

What changed

  • ast/parser: introduce MatchCase and switch Statement::Switch to a single cases vector. The parser parses zero or more case arms followed by an optional default arm via a shared case_body helper, wrapping each arm body in a Block so it stays a single StatementD instead of a Vec<StatementD>.
  • type-checker: type_check_stmt consumes the unified representation and enforces at most one default case.
  • interpreter: executes the first matching arm's Block directly instead of looping over a flat statement list. Drops the now-redundant duplicate-label check and manual scope cleanup, since the type checker already rejects duplicates and Block handles its own scope.
  • codegen: only literal arms get their own label; the default arm (if present) reuses label_default, and a synthetic label_default is emitted when no default exists so the fallback jump still has a landing spot. TAC tests updated to build cases as (MatchCase, Box<CheckedStmt>) pairs.

Why

The split cases/default representation meant "is this the default arm?" was answered differently in four places, and the default body being a bare Vec<StatementD> (vs. a Block for cases) was an inconsistency with no real benefit. Unifying on MatchCase removes that duplication and lets each stage treat every arm uniformly.

PauloV1 and others added 23 commits April 16, 2026 23:37
- Add scripts/minic wrapper script for convenient access to mini_c binary
- Add .envrc configuration for automatic direnv activation
- Allows using 'minic' command as documented in README
Introduce the  variant in  to represent switch-case control flow, including a target expression, literal cases with their bodies, and a default branch.
Parse switch statements in statements.rs, including the target expression, one or more case branches (integer and boolean literals), and a mandatory default block. Ensure each case and default contains at least one statement.
Add todo!() stubs for Statement::Switch in both type checking and evaluation phases to satisfy exhaustive pattern matching and keep the project compiling.
Replaced the mutable closures case_parse and default_parse with declarative nom combinators (tuple and map) inside the switch parser. This removes the need for mutable state in sub-parsers and makes the parsing logic more idiomatic and cohesive.
Implement type checker and interpreter for Switch statement
- Implement semantic check to reject duplicate cases in a switch block
- fix: make literal pattern matching exhaustive by returning a TypeError for unsupported types
- Implement runtime/evaluation check for duplicate labels in switch cases
- fix: import  from AST module to resolve scope compilation error
Replace the separate cases/default fields on Statement::Switch with a
single Vec<(MatchCase, Box<StatementD<Ty>>)>, where MatchCase is
CaseLiteral(Literal) or CaseDefault. The parser now parses zero or more
case arms followed by an optional default arm via a shared case_body
helper, appending it to the case list; each arm body is wrapped in a
Block so it stays a single StatementD instead of a Vec<StatementD>.
Update type_check_stmt to consume the unified MatchCase::CaseLiteral/
CaseDefault representation instead of a separate cases/default split,
enforcing at most one default case.
Consume the unified MatchCase::CaseLiteral/CaseDefault cases instead of
the separate cases/default fields, executing the first matching arm's
Block directly rather than looping over a flat statement list. Drops
the now-redundant duplicate-label check and manual scope cleanup, since
the type checker already rejects duplicates and Block handles its own
scope.
Consume the unified MatchCase::CaseLiteral/CaseDefault cases: only
literal arms get their own label, the default arm (if present) reuses
label_default, and a synthetic label_default is emitted when no default
exists so the fallback jump still has a landing spot. Update TAC tests
to build cases as (MatchCase, Box<CheckedStmt>) pairs.
Add interpreter_switch.minic (matched case, matched default, and
no-match-with-no-default fallthrough) and cli_switch_type_mismatch.minic
(non-Int/Bool target), exercised via new --check/--run scenarios in
check.test and run.test. Closes the gap where switch was covered at
the unit/integration level but never through the actual mini_c binary.
Add a Switch section to the language reference (01), the MatchCase AST
shape (03), the type-checker's target/case/default rules (05), an
exec_stmt table row (06), and the new CLI fixture scenario (08).
@PauloV1 PauloV1 changed the title Project 3 -- Switch Statement Project 3 — Switch Statement Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants