English {#english}
Preprocessor: IFDEF and Conditional Compilation
Delphi projects rarely compile as a single flat source file. {$IFDEF}, {$DEFINE}, {$INCLUDE}, and friends select platform code, toggle debug logging, and stitch units together. CrabPascal ships a line-oriented preprocessor (since v1.5.2, refined through v2.x) integrated into check, run, and unit loading.
Why it matters
Without preprocessing, this compiles on Windows in Delphi but fails elsewhere — or worse, includes dead code:
{$DEFINE DEBUG}
{$IFDEF MSWINDOWS}
uses Winapi.Windows;
{$ELSE}
uses Posix.Unistd;
{$ENDIF}
procedure Log(const Msg: string);
begin
{$IFDEF DEBUG}
WriteLn('[DEBUG] ', Msg);
{$ENDIF}
end;
CrabPascal evaluates directives before lexing the main program, matching Delphi's compilation model.
Supported directives (high level)
From project changelog and src/preprocessor/:
-
{$DEFINE}/{$UNDEF}— symbol table -
{$IFDEF}/{$IFNDEF}/{$ELSE}/{$ENDIF}— conditional blocks -
{$IF}— numeric/symbolic conditions (basic support) -
{$INCLUDE}— text inclusion -
{$REGION}/{$ENDREGION}— editor folding (preserved/stripped)
Nested blocks work for typical patterns; deeply nested {$ELSEIF} chains remain an improvement area noted in architecture docs.
CLI usage
Inspect preprocessor output without running:
crab-pascal preproc MyUnit.pas
Symbols can come from source or config. Project TOML supports a [preprocessor] section:
[preprocessor]
enabled = true
# symbols = ["DEBUG", "MSWINDOWS"]
Disable when debugging parser issues in isolation:
[preprocessor]
enabled = false
Integration with unit loading
When the recursive loader reads a .pas file containing {$, it runs the preprocessor and parses processed source. That keeps conditional uses clauses consistent:
uses
System.SysUtils,
{$IFDEF USE_JSON}
System.JSON,
{$ENDIF}
Horse;
Practical tips
-
Prefer explicit
{$DEFINE}in project config for CI — same flags on Linux agents and Windows dev machines. -
Use
preprocto diff output when porting a large Delphi unit; surprises usually mean a symbol mismatch. -
Combine with
check— semantic errors after preprocessing are real errors, not lexer ghosts.
Limitations (honest)
CrabPascal is not FPC nor DCC; exotic directives ({$MESSAGE}, compiler-specific pragmas) may pass through or warn. Report minimal repros — preprocessor bugs are fixed with golden file tests.
Real-world pattern: feature flags
Libraries often gate experimental APIs behind symbols defined in the project file or CI:
{$DEFINE CRABPASCAL_EXPERIMENTAL}
{$IFDEF CRABPASCAL_EXPERIMENTAL}
procedure FastPath(const Data: TBytes);
{$ENDIF}
Define CRABPASCAL_EXPERIMENTAL in TOML for nightly builds; omit it for stable runs. The same source compiles both ways — exactly how Delphi teams ship gradual rollouts.
Debugging workflow
When a unit fails only on CI:
- Run
crab-pascal preproc FailingUnit.pas > local.out - Compare with developer machine output
- Diff symbol tables — missing
MSWINDOWSorDEBUGis the usual culprit
The preprocessor runs in src/preprocessor/mod.rs with line-by-line evaluation (rewritten in v2.x for predictability). Errors reference the original line numbers when possible, so IDE jump-to-error still works after expansion.
Next steps
Read usage/configuracao for full TOML keys. Follow @crabpascal for sprint updates when {$ELSEIF} nesting and FPC parity improve. The preprocessor is the door most Delphi code walks through first; getting it right multiplies everything downstream.
Português {#portugus}
Preprocessor: IFDEF e compilação condicional
Projetos Delphi raramente compilam como arquivo único. {$IFDEF}, {$DEFINE}, {$INCLUDE} e similares escolhem código por plataforma, ligam log de debug e montam units. O CrabPascal traz preprocessor linha a linha (desde v1.5.2, refinado na v2.x) integrado em check, run e carregamento de units.
Por que importa
Sem preprocessamento, isto compila no Windows em Delphi mas falha em outro lugar — ou pior, inclui código morto:
{$DEFINE DEBUG}
{$IFDEF MSWINDOWS}
uses Winapi.Windows;
{$ELSE}
uses Posix.Unistd;
{$ENDIF}
procedure Log(const Msg: string);
begin
{$IFDEF DEBUG}
WriteLn('[DEBUG] ', Msg);
{$ENDIF}
end;
O CrabPascal avalia diretivas antes de lexar o programa principal, alinhado ao modelo Delphi.
Diretivas suportadas (visão geral)
Do changelog e src/preprocessor/:
-
{$DEFINE}/{$UNDEF}— tabela de símbolos -
{$IFDEF}/{$IFNDEF}/{$ELSE}/{$ENDIF}— blocos condicionais -
{$IF}— condições numéricas/simbólicas (suporte básico) -
{$INCLUDE}— inclusão de texto -
{$REGION}/{$ENDREGION}— folding de editor (preservado/removido)
Blocos aninhados funcionam em padrões típicos; cadeias profundas de {$ELSEIF} seguem como área de melhoria na documentação de arquitetura.
Uso na CLI
Inspecione saída do preprocessor sem executar:
crab-pascal preproc MyUnit.pas
Símbolos vêm do fonte ou config. TOML do projeto tem seção [preprocessor]:
[preprocessor]
enabled = true
# symbols = ["DEBUG", "MSWINDOWS"]
Desabilite ao debugar parser isolado:
[preprocessor]
enabled = false
Integração com carregamento de units
Quando o loader recursivo lê .pas com {$, roda preprocessor e parseia fonte processada. Mantém cláusulas uses condicionais consistentes:
uses
System.SysUtils,
{$IFDEF USE_JSON}
System.JSON,
{$ENDIF}
Horse;
Dicas práticas
-
Prefira
{$DEFINE}explícito na config para CI — mesmas flags em agentes Linux e máquinas Windows. -
Use
preprocpara diff ao portar unit Delphi grande; surpresas costumam ser símbolo faltando. -
Combine com
check— erros semânticos pós-preprocessamento são erros reais, não fantasmas do lexer.
Limitações (honestas)
CrabPascal não é FPC nem DCC; diretivas exóticas ({$MESSAGE}, pragmas específicos) podem passar ou avisar. Reporte repro mínimo — bugs de preprocessor ganham testes golden.
Padrão real: feature flags
Bibliotecas costumam isolar APIs experimentais atrás de símbolos no projeto ou CI:
{$DEFINE CRABPASCAL_EXPERIMENTAL}
{$IFDEF CRABPASCAL_EXPERIMENTAL}
procedure FastPath(const Data: TBytes);
{$ENDIF}
Defina CRABPASCAL_EXPERIMENTAL no TOML para builds nightly; omita em runs estáveis. O mesmo fonte compila dos dois jeitos — como times Delphi fazem rollouts graduais.
Fluxo de debug
Quando uma unit falha só no CI:
- Rode
crab-pascal preproc UnitComFalha.pas > local.out - Compare com saída da máquina do dev
- Diff tabelas de símbolos — falta de
MSWINDOWSouDEBUGé culpado usual
O preprocessor roda em src/preprocessor/mod.rs com avaliação linha a linha (reescrito na v2.x para previsibilidade). Erros referenciam linhas originais quando possível, então jump-to-error na IDE continua após expansão.
Próximos passos
Leia usage/configuracao para chaves TOML completas. Siga @crabpascal para updates de sprint em aninhamento {$ELSEIF} e paridade FPC. O preprocessor é a porta por onde a maior parte do código Delphi entra; acertar aqui multiplica tudo downstream.
Published on dev.to/@crabpascal · Código em CrabPascal
Top comments (0)