DEV Community

CrabPascal
CrabPascal

Posted on

Inside CrabPascal: The Five-Phase Compiler Pipeline | Pipeline do compilador em cinco fases

Bilingual post · Post bilíngue

Jump to: English · Português


English {#english}

Inside CrabPascal: The Five-Phase Compiler Pipeline

Every compiler textbook describes the same pipeline. CrabPascal v2.22.0 implements it faithfully in Rust — from raw .pas text to executable behavior. Understanding these five phases explains what check, run, and build-exe actually do under the hood.

The big picture

 Source (.pas/.dpr)
       │
       ▼
 ┌─────────────┐
 │ 1. LEXER    │  Text → Tokens
 └──────┬──────┘
        ▼
 ┌─────────────┐
 │ 2. PARSER   │  Tokens → AST
 └──────┬──────┘
        ▼
 ┌─────────────┐
 │ 3. SEMANTIC │  AST → Validated AST + Symbols
 └──────┬──────┘
        ▼
 ┌─────────────┐
 │ 4. CODEGEN  │  AST → C code OR Runtime bytecode
 └──────┬──────┘
        ▼
 ┌─────────────┐
 │ 5. OPTIMIZE │  (Optional) Peephole / C compiler opts
 └──────┬──────┘
        ▼
   Executable / Interpreted result
Enter fullscreen mode Exit fullscreen mode

Phase 1: Lexical analysis

The lexer scans source character by character and emits tokens — keywords (var, begin), identifiers, literals, operators.

Input: var x: Integer;

Output: [VAR, IDENT("x"), COLON, IDENT("Integer"), SEMICOLON]

CrabPascal handles Delphi-style comments { }, //, and (* *), plus preprocessor directives when enabled.

Phase 2: Parsing

The parser consumes tokens and builds an Abstract Syntax Tree (AST). It validates grammatical structure — matching begin/end, proper declaration order, nested expressions.

A assignment like x := 10 + 5 becomes an Assignment node with a BinaryOp(Add) child expression. The AST is serialized in Rust with serde for debugging (save_ast = true in crabpascal.toml).

Phase 3: Semantic analysis

Grammar alone is not enough. The semantic analyzer:

  • Builds a symbol table (variables, types, procedures)
  • Resolves uses clauses and unit search paths from rtl/
  • Checks type compatibility (Integer vs String)
  • Validates OOP constructs (classes, inheritance, visibility)

This is where crab-pascal check stops — full validation, zero execution.

Phase 4: Code generation

CrabPascal splits here based on command:

run — Internal runtime walks the AST directly. Built-in functions (WriteLn, TJSONObject.Create, Horse routes) dispatch to Rust implementations.

build-exe — Codegen emits C source, then invokes gcc/clang. Example output for x := 10 + 5:

int x;
x = 10 + 5;
Enter fullscreen mode Exit fullscreen mode

Phase 5: Optimization

Optional and layered:

  • CrabPascal may apply basic AST simplifications
  • gcc/clang apply their own optimizations when using build-exe
  • The internal runtime prioritizes correctness over aggressive optimization (for now)

Which commands use which phases?

Command Phases executed
check 1 → 2 → 3
run 1 → 2 → 3 → 4 (runtime)
build-exe 1 → 2 → 3 → 4 (C) → 5 (gcc)
preproc Pre-phase 0 (text transform)

Why this matters for contributors

Module boundaries in the Rust codebase mirror the pipeline: lexer/, parser/, semantic/, codegen/, runtime/. Sprint work typically targets one phase — parser hardening in one release, RTL coverage in the next.

When you report a bug, identifying the phase narrows the search instantly. Syntax error? Parser. "Unknown identifier" at valid code? Semantic or RTL shim. Crash at runtime? Runtime dispatch.

CrabPascal is not magic — it is a textbook compiler built with modern tools. The five phases are the map. 🦀


Português {#portugus}

Por dentro do CrabPascal: pipeline em cinco fases

Todo livro de compiladores descreve o mesmo pipeline. O CrabPascal v2.22.0 implementa isso fielmente em Rust — do texto .pas bruto ao comportamento executável. Entender as cinco fases explica o que check, run e build-exe fazem por baixo dos panos.

Visão geral

 Fonte (.pas/.dpr)
       │
       ▼
 ┌─────────────┐
 │ 1. LEXER    │  Texto → Tokens
 └──────┬──────┘
        ▼
 ┌─────────────┐
 │ 2. PARSER   │  Tokens → AST
 └──────┬──────┘
        ▼
 ┌─────────────┐
 │ 3. SEMÂNTICA│  AST → AST validada + Símbolos
 └──────┬──────┘
        ▼
 ┌─────────────┐
 │ 4. CODEGEN  │  AST → C ou bytecode do runtime
 └──────┬──────┘
        ▼
 ┌─────────────┐
 │ 5. OTIMIZAÇÃO│ (Opcional) Peephole / opts do gcc
 └──────┬──────┘
        ▼
   Resultado executável / interpretado
Enter fullscreen mode Exit fullscreen mode

Fase 1: Análise léxica

O lexer percorre o fonte caractere a caractere e emite tokens — palavras-chave (var, begin), identificadores, literais, operadores.

Entrada: var x: Integer;

Saída: [VAR, IDENT("x"), COLON, IDENT("Integer"), SEMICOLON]

O CrabPascal trata comentários estilo Delphi { }, // e (* *), além de diretivas de pré-processador quando habilitadas.

Fase 2: Parsing

O parser consome tokens e constrói a Árvore de Sintaxe Abstrata (AST). Valida estrutura gramatical — begin/end pareados, ordem de declarações, expressões aninhadas.

Uma atribuição x := 10 + 5 vira nó Assignment com filho BinaryOp(Add). A AST é serializada em Rust com serde para debug (save_ast = true no crabpascal.toml).

Fase 3: Análise semântica

Gramática não basta. O analisador semântico:

  • Constrói tabela de símbolos (variáveis, tipos, procedures)
  • Resolve cláusulas uses e search paths de rtl/
  • Verifica compatibilidade de tipos (Integer vs String)
  • Valida OOP (classes, herança, visibilidade)

É aqui que crab-pascal check para — validação completa, zero execução.

Fase 4: Geração de código

O CrabPascal bifurca conforme o comando:

run — Runtime interno percorre a AST diretamente. Built-ins (WriteLn, TJSONObject.Create, rotas Horse) despacham para implementações Rust.

build-exe — Codegen emite C e invoca gcc/clang. Exemplo para x := 10 + 5:

int x;
x = 10 + 5;
Enter fullscreen mode Exit fullscreen mode

Fase 5: Otimização

Opcional e em camadas:

  • CrabPascal pode aplicar simplificações básicas na AST
  • gcc/clang aplicam otimizações próprias no build-exe
  • O runtime interno prioriza correção sobre otimização agressiva (por ora)

Quais comandos usam quais fases?

Comando Fases executadas
check 1 → 2 → 3
run 1 → 2 → 3 → 4 (runtime)
build-exe 1 → 2 → 3 → 4 (C) → 5 (gcc)
preproc Pré-fase 0 (transformação de texto)

Por que isso importa para contribuidores

Os módulos Rust espelham o pipeline: lexer/, parser/, semantic/, codegen/, runtime/. Trabalho de sprint costuma mirar uma fase — endurecimento do parser num release, cobertura RTL no seguinte.

Ao reportar bug, identificar a fase estreia a busca na hora. Erro de sintaxe? Parser. "Unknown identifier" em código válido? Semântica ou shim RTL. Crash em runtime? Dispatch do runtime.

CrabPascal não é mágica — é um compilador de livro-texto com ferramentas modernas. As cinco fases são o mapa. 🦀


Published on dev.to/@crabpascal · Código em CrabPascal

Top comments (0)