iToverDose/Software· 4 JUNE 2026 · 12:03

CrabPascal’s dual-mode build: interpreter vs native for faster development

How CrabPascal balances speed and precision by offering both interpreted and native execution paths from the same Pascal source code, optimizing daily workflows and release builds.

DEV Community4 min read0 Comments

CrabPascal’s latest architecture update introduces a pragmatic dual-mode build system that lets developers switch between interpreted and native execution without rewriting a single line of code. This design emerged from challenges documented in Post 039 and was finalized in the v2.17.0 release, ensuring developers can iterate quickly during daily workflows while still producing optimized native binaries for deployment or benchmarks.

One source code, two execution paths

The system maintains a shared frontend for lexing, parsing, preprocessing, and semantic analysis while diverging only at the code generation or direct execution stage. When developers run crab-pascal run, the interpreter executes Pascal code immediately, ideal for rapid feedback during development. For deployment or performance-critical scenarios, the crab-pascal build-exe command generates C code, compiles it via gcc or clang, and produces a standalone native executable.

Both modes leverage the same compiler infrastructure, so changes to the source reflect immediately in either workflow. The interpreter path skips compilation entirely, delivering near-instant results for testing and debugging, while the native path compiles to machine code for maximum performance and distribution flexibility.

Interpreter mode: speed without toolchain overhead

Developers can execute Pascal files directly using:

crab-pascal run examples/crud/crud.dpr

The interpreter walks the abstract syntax tree, manages scopes and heap memory, and maintains virtual method tables exactly as described in Post 038. This setup keeps long-running Horse servers operational because the event loop runs within Pascal units loaded via ProjectConfig.

  • Pros: No dependency on a C toolchain, immediate error feedback with rich context, and seamless alignment with sprint QA test fixtures.
  • Cons: Output is not a standalone binary, so end-users need CrabPascal installed to run the program.

This mode excels in daily development, particularly when writing Horse web routes, testing APIs with Postman, or teaching Pascal concepts where students benefit from seeing behavior in real time.

Native mode: performance and distribution-ready binaries

To compile a Pascal file into a native executable, developers use:

crab-pascal build-exe examples/hello.dpr

The code generator produces C output that calls runtime helpers defined in stubs.c, handling strings, objects, and SysUtils bridges. The compiler then invokes gcc or clang to produce the final binary, which can be distributed independently.

  • Pros: Delivers native execution speed, produces standalone binaries, and avoids embedding the Rust runtime.
  • Cons: Some language features, such as exception handling and certain generics edge cases, may show parity gaps that are actively tracked and addressed per sprint.

This path is ideal for distributing CLI tools to environments without CrabPascal, benchmarking performance against Free Pascal or Delphi baselines, or verifying code generation for sprint deliverables like string handling or exception support.

Hardening parity with honesty rules in v2.17.0

Before v2.17.0, missing C toolchains could silently produce misleading results. Now, the build system enforces strict honesty rules:

  • C source files are always generated, allowing developers to inspect codegen differences in pull requests.
  • The link step fails explicitly if no compiler is found, preventing false positives.
  • Undefined variables now trigger consistent semantic errors across both modes.

To validate local builds, developers can run:

cargo test --test run_build_parity
cargo test --lib

These tests compare standard output and exit codes to ensure parity claims hold true across both execution paths. Skipping these checks risks shipping code that behaves differently in production versus development.

Choosing the right mode for the right task

Use `run` when:

  • Writing Horse web routes or Postman test suites for APIs.
  • Practicing test-driven development on parser or semantic fixtures.
  • Teaching Pascal to students who benefit from immediate feedback.

Use `build-exe` when:

  • Distributing a CLI tool to machines without CrabPascal installed.
  • Performing performance benchmarks against FPC or Delphi outputs.
  • Verifying code generation quality for sprint milestones like string handling or exception support.

Use `check` when:

  • Integrating with IDEs for static analysis without execution.

The crabpascal.toml configuration file centralizes settings that affect both modes, including default backend hints, search paths, and conditional compilation directives after preprocessing. On Windows, the build system supports MSVC, MinGW, or clang—whichever is found first—and developers should document the CI environment image to ensure reproducibility.

Tracking parity: known gaps and sprint priorities

A snapshot of the current parity matrix highlights areas where both execution modes align and where gaps remain:

  • Green: Hello world, arithmetic operations, and record types.
  • Mostly green: Generics collections, post Sprint 7 improvements.
  • Yellow: Try/except handling in build-exe, expected to stabilize by Sprint 13.
  • Varied: Long-running Horse servers primarily tested in run mode; native behavior may differ.

Before committing to a native build for clients, always review the project status page to confirm current parity coverage. This transparency prevents overpromising on features that may still be in active development.

CI pipeline example: validating both modes automatically

A minimal Bitbucket pipeline can exercise both execution paths to catch regressions early:

pipelines:
  default:
    - step:
        script:
          - cargo test
          - ./target/release/crab-pascal check examples/hello.dpr
          - ./target/release/crab-pascal run examples/hello.dpr
          - ./target/release/crab-pascal build-exe examples/hello.dpr

If the gcc toolchain bloats the Rust image, consider splitting jobs—but never omit the check step. As documented in the v2.17.0 release notes, faking native success caused more long-term pain than a failing build badge ever could.

Looking ahead: closing the architecture loop

The next installment, Post 041 — Sprint 1 Review: Real Diagnostic Spans, will revisit the architecture arc by examining how accurate check output became the foundation for IDE integration, CI reliability, and honest error reporting across both modes. This retrospective closes the loop on the strategic decisions that made dual-mode development possible from day one.

AI summary

Learn how CrabPascal’s dual-mode build system delivers faster development with interpreted execution and optimized native binaries from the same Pascal source code.

Comments

00
LEAVE A COMMENT
ID #KP8OKG

0 / 1200 CHARACTERS

Human check

6 + 2 = ?

Will appear after editor review

Moderation · Spam protection active

No approved comments yet. Be first.