diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..14f6d8d --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,105 @@ +# INSTALL — preparando o ambiente + +Guia para deixar o ambiente pronto para compilar e rodar o Crusty. + +## Pré-requisitos + +| Ferramenta | Versão mínima | Para quê | +|---|---|---| +| [Rust](https://rustup.rs/) (rustc + cargo) | 1.70+ | Compilar o próprio Crusty | +| `gcc` | qualquer versão recente | Montar (`as`) e linkar (`ld`) os executáveis ELF gerados pelo backend x86-64 | +| Linux x86-64 | — | O backend gera assembly x86-64 / System V ABI. Não há suporte a outras arquiteturas ou a Windows/macOS nativo | + +Sem `gcc` no `PATH`, o compilador ainda funciona até a emissão de assembly (`--emit=asm`), mas os testes de smoke e2e (`tests/exe_smoke_test.rs`, `tests/codegen_smoke.rs`) são automaticamente pulados (skip), e `--emit=obj`/`--emit=exe` falham. + +## 1. Instalar o Rust + +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +source "$HOME/.cargo/env" +rustup update stable +``` + +Verifique: + +```bash +rustc --version # esperado: 1.70 ou mais recente +cargo --version +``` + +## 2. Instalar o gcc (toolchain de montagem/link) + +**Debian/Ubuntu** +```bash +sudo apt update && sudo apt install -y gcc +``` + +**Arch/Manjaro** +```bash +sudo pacman -S gcc +``` + +**Fedora** +```bash +sudo dnf install gcc +``` + +Verifique: + +```bash +gcc --version +``` + +## 3. Obter o código + +```bash +git clone https://github.com/Bappoz/Crusty.git +cd Crusty +``` + +(Se você já está dentro do repositório, pule esta etapa.) + +## 4. Compilar o projeto + +```bash +cargo build --release +``` + +O binário fica em `target/release/crusty`. Para um build de desenvolvimento (mais rápido de compilar, binário mais lento): + +```bash +cargo build +# binário em target/debug/crusty +``` + +## 5. Verificar a instalação + +Rode o compilador sobre um exemplo incluso no repositório e execute o binário gerado: + +```bash +cargo run --release -- src/examples/hello_world.c -o /tmp/hello +/tmp/hello +``` + +Saída esperada: + +``` +Hello, World! +``` + +Se isso funcionou, o ambiente está pronto. Para confirmar que toda a suíte de testes passa no seu ambiente: + +```bash +cargo test --all +cargo clippy -- -D warnings +cargo fmt --check +``` + +Essas três checagens são exatamente as que o CI (`.github/workflows/`) roda em todo push/PR para `developer` e `master`. + +## Problemas comuns + +- **`error: linker 'cc' not found` ou falha ao montar/linkar** — `gcc` não está instalado ou não está no `PATH`. Repita o passo 2. +- **`cargo: command not found`** depois de instalar o Rust — rode `source "$HOME/.cargo/env"` ou abra um novo terminal. +- **Testes de smoke "pulando" silenciosamente** — esperado se `gcc` não estiver disponível; veja [TESTER.md](TESTER.md) para detalhes. +- **Programa de teste usa `float`/`double`** e falha com `error: code generation` — limitação conhecida atual do backend, ver [README.md](README.md#limitações-conhecidas) e [issue #172](https://github.com/Bappoz/Crusty/issues/172). diff --git a/README.md b/README.md index 86b5799..4eaded0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Crusty — Compilador C em Rust -Projeto da disciplina de Compiladores 1. Implementa um compilador para um subconjunto da linguagem C, escrito em Rust. +Projeto da disciplina de Compiladores 1. Implementa um compilador para um subconjunto da linguagem C, escrito em Rust, com backend nativo x86-64 (System V ABI, Linux). ## Estágio atual @@ -8,8 +8,18 @@ Projeto da disciplina de Compiladores 1. Implementa um compilador para um subcon |------|--------| | Análise léxica | Completo | | Análise sintática | Completo | -| Análise semântica | Em desenvolvimento | -| Geração de código | Não iniciado | +| Análise semântica | Completo | +| IR (TAC) | Completo | +| Otimizações (CSE, DCE, constant folding, copy propagation, LICM, inlining) | Completo | +| Geração de código x86-64 | Completo para tipos inteiros, ponteiros, structs, arrays e globais | + +### Limitações conhecidas + +- **`float`/`double` não têm codegen.** O analisador semântico aceita e tipa esses tipos, mas o backend x86-64 ainda não emite instruções de ponto flutuante (registradores XMM). Em desenvolvimento na [issue #172](https://github.com/Bappoz/Crusty/issues/172). Programas que usam `float`/`double` falham com `error: code generation` no estágio final. +- O modo REPL interativo (executar `crusty` sem argumentos) não está implementado. +- `--dump-ir` ainda não imprime a IR (placeholder). + +Fora isso, o pipeline completo (lexer → parser → análise semântica → IR → otimizações → assembly x86-64 → executável ELF via `gcc`) funciona ponta a ponta para um subconjunto relevante de C: tipos inteiros e `char`, ponteiros, structs, arrays de tamanho fixo, enums, typedefs, variáveis globais, todas as estruturas de controle (`if`/`while`/`do-while`/`for`/`switch`) e chamadas de função. ## Estrutura do projeto @@ -18,43 +28,54 @@ src/ ├── lexer/ Análise léxica — transforma código-fonte em tokens ├── parser/ Análise sintática — constrói a AST via Pratt parsing ├── analyser/ Análise semântica — tabela de símbolos, escopos, verificação de tipos -├── codegen/ Geração de código — esqueleto (não implementado) +├── ir/ Geração e lowering da IR intermediária (TAC) +├── codegen/ Geração de código — otimizações sobre TAC (inter/) e backend x86-64 (last/) ├── common/ Estruturas compartilhadas: AST, erros, spans, utilitários +├── examples/ Arquivos .c de exemplo usados em testes e demonstrações └── tests/ Testes unitários por módulo +tests/ Testes de integração e smoke tests (ponta a ponta, com gcc) +docs/ Documentação técnica de cada fase do compilador ``` -## Pré-requisitos +## Começando -- [Rust](https://rustup.rs/) 1.70+ +Instruções completas de instalação e configuração do ambiente (Rust, `gcc`, verificação de toolchain) estão em [INSTALL.md](INSTALL.md). + +Resumo rápido: ```bash rustup update stable +cargo build --release +cargo run --release -- src/examples/hello_world.c +./hello_world ``` -## Build +## Uso ```bash -cargo build +crusty [flags] ``` -## Uso +Principais flags (lista completa em `crusty` sem argumentos): -Rodar o compilador sobre um arquivo de entrada: - -```bash -cargo run -- -``` +| Flag | Efeito | +|---|---| +| `--dump-tokens` | Lista os tokens emitidos pelo lexer | +| `--dump-ast` | Imprime a AST | +| `--only-lex` / `--only-parse` / `--only-semantic` | Para o pipeline no estágio indicado | +| `-S`, `--emit-asm` / `--emit=asm` | Para após emitir o assembly x86-64 (`.s`) | +| `--emit=obj` | Para após montar o objeto (`.o`), sem linkar | +| `--emit=exe` | Monta e linka um executável ELF rodável (padrão) | +| `-o `, `--out-dir `, `--out-name ` | Controlam o caminho/nome de saída | +| `-O0`\|`-O1`\|`-O2`\|`-O3`, `--opt-level ` | Nível de otimização aplicado à IR | -Exemplo: +Exemplo gerando e executando um binário: ```bash -cargo run -- input.c +cargo run --release -- src/examples/simple.c -o /tmp/simple +/tmp/simple; echo "exit: $?" ``` -O compilador imprime os tokens reconhecidos, a AST e eventuais diagnósticos de erro. - -> O modo REPL interativo (sem argumentos) ainda não está implementado. - ## Funcionalidades implementadas **Lexer** @@ -81,60 +102,38 @@ O compilador imprime os tokens reconhecidos, a AST e eventuais diagnósticos de - Promoção numérica implícita (Double > Float > Long > Int) - Detecção de atribuição a `const` -## Testes +**IR e otimizações** +- Lowering de AST para TAC (Three-Address Code), incluindo arrays fixos, structs e globais +- Pipeline de otimização configurável por nível (`-O0`..`-O3`): constant folding, common subexpression elimination, dead code elimination, copy propagation, loop-invariant code motion, inlining -### Todos os testes unitários +**Backend x86-64** +- Convenção de chamada System V ABI (inteiros/ponteiros em `rdi`..`r9`/`rax`) +- Endereço de variáveis, indexação de array, acesso a membro de struct (`.`, `->`), address-of/deref +- `sizeof` em tempo de compilação +- Variáveis globais com acesso RIP-relative +- Peephole optimizer sobre o assembly emitido +- Emissão de `.s`, montagem de `.o` e link de executável via `gcc` -```bash -cargo test -``` +## Testes -### Filtrar por módulo +Cobertura completa (testes unitários e testes com arquivos `.c` reais, executados de ponta a ponta) está documentada em [TESTER.md](TESTER.md). -```bash -cargo test lexical # testes do scanner/lexer (21 casos) -cargo test parser_test # testes do parser / AST (76 casos) -cargo test semantic_test # testes do analisador semântico (21 casos) -cargo test symbol_test # testes da tabela de símbolos (11 casos) -cargo test analyzer_test # testes de integração do analisador (3 casos) -cargo test source # testes de SourceFile e spans (12 casos) -cargo test lexer_file # testes do scanner lendo arquivos (7 casos) -cargo test parser_file # testes do parser lendo arquivos (4 casos) -cargo test literals # testes de literais numéricos (4 casos) -cargo test ast_errors # testes de erros de AST (4 casos) -cargo test token # testes de tokens individuais (2 casos) -``` - -### Com saída detalhada +Resumo rápido: ```bash -cargo test -- --nocapture +cargo test --all # ~354 testes (unitários + integração + smoke e2e) +cargo clippy -- -D warnings +cargo fmt --check ``` -### Módulos de teste - -| Arquivo | Cobertura | Testes | -|---|---|---| -| `src/tests/lexical_test.rs` | Scanner: operadores, palavras-chave, literais | 21 | -| `src/tests/parser_test.rs` | Parser / construção de AST | 76 | -| `src/tests/semantic_test.rs` | Verificação de tipos, undefined vars, const | 21 | -| `src/tests/symbol_test.rs` | Tabela de símbolos, escopos, redeclaração | 11 | -| `src/tests/source_test.rs` | `SourceFile`, `ByteSpan`, posicionamento | 12 | -| `src/tests/lexer_file_test.rs` | Scanner sobre arquivos reais | 7 | -| `src/tests/parser_file_test.rs` | Parser sobre arquivos reais | 4 | -| `src/tests/literals_test.rs` | Literais inteiros, floats, strings | 4 | -| `src/tests/ast_errors.rs` | Diagnósticos e erros de AST | 4 | -| `src/tests/analyzer_test.rs` | Integração léxico → sintático → semântico | 3 | -| `src/tests/token_test.rs` | `Token` e `TokenKind` | 2 | - -**Total: 165 testes** - ## Documentação técnica - [Lexer](docs/lexer.md) — scanner, tokens, erros léxicos - [Parser](docs/parser.md) — Pratt parser, AST, recuperação de erros - [Analisador Semântico](docs/semantic.md) — tabela de símbolos, verificação de tipos - [Precedência de Operadores C](docs/c_operator_precedence.md) — tabela C11 e mapeamento para binding powers +- [INSTALL.md](INSTALL.md) — como preparar o ambiente e compilar o projeto +- [TESTER.md](TESTER.md) — como rodar e interpretar todos os testes ## Contribuidores diff --git a/TESTER.md b/TESTER.md new file mode 100644 index 0000000..ca503e4 --- /dev/null +++ b/TESTER.md @@ -0,0 +1,143 @@ +# TESTER — como testar o Crusty + +Este documento cobre todas as formas de testar o compilador: testes unitários, testes de integração com arquivos `.c` reais, e testes de smoke ponta a ponta que montam/linkam/executam o binário gerado via `gcc`. + +Pré-requisito: ambiente configurado conforme [INSTALL.md](INSTALL.md) (Rust + `gcc`). + +## Visão geral das suítes + +| Suíte | Localização | O que verifica | Testes | +|---|---|---|---| +| Testes unitários da lib | `src/tests/*.rs` | Lexer, parser, analisador semântico, codegen, em isolamento | 295 | +| Testes unitários do binário | `src/main.rs` (`#[cfg(test)]`) | Parsing de flags da CLI | 10 | +| `tests/integration_test.rs` | Pipeline lexer→parser→semântico sobre arquivos `.c` reais (válidos e inválidos) | 16 | +| `tests/codegen_smoke.rs` | TAC montado manualmente → assembly → `gcc` → execução, checando exit code | 5 | +| `tests/exe_smoke_test.rs` | Código-fonte C real → pipeline completo → executável ELF → execução | 26 | +| `tests/licm_test.rs` | Otimização de loop-invariant code motion | 2 | + +Total atual: **354 testes**, todos passando em `developer`. + +## Rodando tudo + +```bash +cargo test --all +``` + +Isso compila e roda todas as suítes acima, na ordem mostrada por `cargo`. + +## Testes unitários (por módulo) + +Os testes unitários vivem em `src/tests/` e cobrem cada fase do compilador isoladamente, sem precisar de arquivos externos nem de `gcc`. + +```bash +cargo test --lib # só a suíte unitária da lib (sem integração/smoke) +cargo test lexical # scanner/lexer: operadores, palavras-chave, literais +cargo test parser_test # parser / construção de AST +cargo test semantic_test # verificação de tipos, undefined vars, const +cargo test symbol_test # tabela de símbolos, escopos, redeclaração +cargo test source # SourceFile, ByteSpan, posicionamento +cargo test lexer_file # scanner sobre arquivos reais +cargo test parser_file # parser sobre arquivos reais +cargo test literals # literais inteiros, floats, strings +cargo test ast_errors # diagnósticos e erros de AST +cargo test analyzer_test # integração léxico → sintático → semântico +cargo test token # Token e TokenKind +cargo test codegen_test # geração de código (unitário) +cargo test peephole_test # otimizador de assembly (peephole) +cargo test unmap_safe_test # segurança de unmap/memmap do SourceFile +``` + +Saída detalhada (não suprime `println!`/`eprintln!` dos testes): + +```bash +cargo test -- --nocapture +``` + +Rodar um único teste pelo nome exato: + +```bash +cargo test test_licm_loop_with_invariant +``` + +## Testes com arquivos `.c` reais + +### `tests/integration_test.rs` — front-end completo, sem executar binário + +Roda lexer → parser → análise semântica sobre arquivos em `tests/integration/valid/` e `tests/integration/invalid/`, verificando que programas válidos não geram diagnósticos e que programas inválidos geram exatamente o erro esperado (variável não declarada, redeclaração, atribuição a `const`, mismatch de tipo, aridade de chamada, etc). + +```bash +cargo test --test integration_test +``` + +Para adicionar um novo caso: crie um `.c` em `tests/integration/valid/` (deve compilar sem erros) ou `tests/integration/invalid/` (deve falhar com o diagnóstico esperado) e adicione o caso correspondente em `tests/integration_test.rs`. + +### `tests/exe_smoke_test.rs` e `tests/codegen_smoke.rs` — ponta a ponta, com execução real + +Estes são os testes mais completos: compilam código C real até assembly x86-64, montam e linkam com `gcc` em um executável ELF, executam o binário e verificam o **exit code** (e, quando aplicável, a saída em stdout). + +```bash +cargo test --test exe_smoke_test +cargo test --test codegen_smoke +``` + +Se `gcc` não estiver disponível no `PATH`, esses testes são pulados (skip) automaticamente — verifique a saída de `cargo test -- --nocapture` por `gcc indisponivel: pulando teste de smoke` para confirmar. + +### Testando manualmente com os arquivos de `src/examples/` + +O diretório `src/examples/` contém programas `.c` de exemplo usados como referência/demonstração. Para testar manualmente o pipeline completo sobre um deles: + +```bash +# 1. compilar e linkar +cargo run --release -- src/examples/simple.c -o /tmp/simple + +# 2. executar e checar o resultado +/tmp/simple; echo "exit code: $?" +``` + +Para inspecionar estágios intermediários: + +```bash +cargo run -- src/examples/simple.c --dump-tokens # tokens do lexer +cargo run -- src/examples/simple.c --dump-ast # AST +cargo run -- src/examples/simple.c --emit=asm -o /tmp/simple.s # assembly x86-64 gerado +``` + +Exemplos disponíveis e seu status atual: + +| Arquivo | Compila e roda? | Observação | +|---|---|---| +| `hello_world.c` | Sim | Imprime `Hello, World!` | +| `simple.c` | Sim | | +| `demo_presentation.c` | Sim | Demo usada na apresentação da disciplina | +| `declarations.c` | Gera assembly, mas não tem `main` | Não é pensado para ser linkado/executado isoladamente | +| `full_code1.c` | Não | Usa `float`, sem codegen ainda (issue #172) | +| `operators.c` | Não — nem com `gcc` | Tem statements em escopo global, o que não é C válido (confirmado com `gcc -fsyntax-only`); não é um bug do compilador | + +### Testando com seus próprios arquivos `.c` + +Qualquer arquivo `.c` válido pode ser usado diretamente: + +```bash +cargo run --release -- caminho/para/arquivo.c -o /tmp/saida +/tmp/saida +``` + +Para depurar um erro de compilação, repita o comando com `--dump-ast` ou `--only-semantic` para isolar em qual fase o problema ocorre. + +## Checagens de qualidade (rodadas no CI) + +O CI (`.github/workflows/`) roda, nesta ordem, em todo push/PR para `developer` e `master`: + +```bash +cargo build --all +cargo test --all +cargo clippy -- -D warnings +cargo fmt --check +``` + +Rode as quatro localmente antes de abrir um PR — é exatamente o que será verificado automaticamente. + +## Cobertura conhecida e limitações dos testes + +- Não há testes automatizados para `float`/`double` em codegen, porque a feature não existe ainda (issue #172). Quando for implementada, a suíte `exe_smoke_test.rs`/`codegen_smoke.rs` é o lugar natural para os novos casos. +- Os smoke tests de execução (`exe_smoke_test.rs`, `codegen_smoke.rs`) dependem de Linux x86-64 + `gcc`; em outras plataformas eles são pulados, não falham.