Pascal elegance. Full C ABI interop.
A systems programming language -- batteries included -- .cpas to native binary
.cpas
➜
Lexer
➜
Parser
➜
Semantics
➜
Codegen
➜
C++23
➜
Zig/Clang
➜
binary
Under active development
Win64 + Linux64
Batteries included
Apache 2.0
Write clean code. Get native binaries.

A complete CPaskal program compiles to C++23, then to a native x86_64 executable via Zig/Clang. Everything you need ships in one ZIP.

demo.cpas
module exe demo;

@target x86_64_windows;

// Bind to C library
routine clink c_abs(const n: int32): int32;
  external "c" name "abs";

type
  Point = record
    x: float64;
    y: float64;
  end;

routine distance(a: Point; b: Point): float64;
var dx: float64; dy: float64;
begin
  dx := a.x - b.x;
  dy := a.y - b.y;
  return cpp("std::sqrt(dx*dx + dy*dy)");
end;

begin
  var a: Point = Point(x: 0.0, y: 0.0);
  var b: Point = Point(x: 3.0, y: 4.0);
  println("Distance: {}", distance(a, b));
  println("abs(-42) = {}", c_abs(-42));
end.
Terminal
$ cpas demo -r

CPaskal CLI v0.1.0
Parsing demo.cpas...
Analyzing...
Generating C++23...
Building demo...
Build succeeded.
Running demo.exe...

Distance: 5
abs(-42) = 42
Systems programming, the Pascal way

Familiar syntax, explicit types, full control. These examples show CPaskal's core features.

// Explicit-width types -- no ambiguous "integer"
var count: int32 = 0;
var big:   int64 = 9999999999;
var byte:  uint8 = 255;
var pi:    float64 = 3.14159;
var flag:  boolean = true;
var name:  string = "CPaskal";
var wname: wstring = w"Unicode";

// Pointers with typed syntax
var p: pointer to int32 = address of count;
println("value = {}", p^);  // dereference

// Choices (enums) with explicit values
type
  Color = choices(red, green, blue = 5, alpha);
var c: Color = Color.blue;

// Routine types (function pointers)
type
  BinOp = routine(x: int32; y: int32): int32;
var op: BinOp = add;
println("result = {}", op(3, 4));
// Records with inheritance, packing, alignment
type
  Base = record
    x: int32;
    y: int32;
  end;

  Derived = record(Base)
    z: int32;
  end;

  Packed = record packed
    a: int8;
    b: int32;
  end;

// Overlays (C unions)
  Value = overlay
    asInt: int64;
    asFloat: float64;
  end;

// Bit fields for hardware/protocol work
  StatusReg = record
    ready: uint32 : 1;
    mode:  uint32 : 3;
    data:  uint32 : 8;
  end;

// Record literals -- named initialization
var pt: Base = Base(x: 42, y: 99);
// if/then/else with end
if x > 0 then
  println("positive");
else
  println("non-positive");
end;

// for with to/downto
for i := 1 to 10 do
  total += i;
end;

// while loop
while count < 100 do
  count += 1;
end;

// repeat..until
repeat
  count += 1;
until count >= 10;

// match with ranges and multi-labels
match value of
  0:        println("zero");
  1, 2, 3: println("low");
  4..10:   println("mid");
else
  println("high");
end;
// Four module kinds
module exe  myapp;      // executable
module dll  mylib;      // shared library (DLL/SO)
module lib  mystaticlib;// static library
module unit myutils;    // importable unit

// Import and use with full qualification
import myutils;
println("version = %d", myutils.UTIL_VERSION);
println("add = %d", myutils.util_add(3, 4));

// Public/private visibility
public const VERSION: int32 = 1;
const SECRET: int32 = 42; // private

public routine add(a: int32; b: int32): int32;
begin
  return a + b;
end;

// Module lifecycle
initialize
  println("starting up...");
end;

finalize
  println("shutting down...");
end;
// guard/except/finally -- catches software AND hardware exceptions
guard
  result := a div b;
except
  println("error: code={}, msg={}", exccode(), excmsg());
finally
  cleanup();
end;

// Throw with message or code
throw("something went wrong");
throwcode(404, "resource not found");

// Hardware exceptions caught automatically
guard
  x := 10 div 0;  // divide by zero
except
  println("caught: {}", excmsg());
  // prints: "Divide by zero"
end;

// Exception propagation across modules
guard
  myutils.dangerous_operation();
except
  println("caught from unit: {}", excmsg());
end;
// External clause -- bind to any C library
routine clink abs_val(const n: int32): int32;
  external "c" name "abs";

// Windows API
routine clink GetTick(): uint64;
  external "kernel32" name "GetTickCount64";

// Third-party library via const
const RAYLIB: string = "raylib";
routine clink InitWindow(w: int32; h: int32;
  title: pointer); external RAYLIB;

// Raw C++ injection
cppstart header
#include <cmath>
cppend

// Inline C++ expressions
var sq: float64 = cpp("std::sqrt(2.0)");

// Conditional compilation for platform code
@ifdef TARGET_WIN64
  println("tick: {}", GetTick());
@endif
// Built-in unit testing framework
module exe mathlib;

@unittestmode on;

routine add(a: int32; b: int32): int32;
begin
  return a + b;
end;

end.

// Test blocks appear after end.
test "add returns correct sum"
begin
  asserteq(5, add(2, 3));
  asserteq(-2, add(-5, 3));
end;

test "add handles zero"
begin
  asserteq(0, add(0, 0));
  asserttrue(add(1, -1) = 0);
end;
Everything a systems language needs. Nothing you don't.

Clean syntax, explicit types, full control over memory and ABI. No hidden complexity.

📦
Batteries Included
Unzip and compile. The compiler, Zig/Clang toolchain, C++23 runtime, and build system ship in one portable package. No external SDKs or package managers required.
🔗
Bidirectional C Interop
Consume any C library via external clause. Produce C-compatible DLLs and static libraries that Python, Rust, Go, C# and any other language can call.
⚡
C++23 Backend
Compiles to C++23 via Zig/Clang, giving you LLVM-level optimization. Drop into raw C++ with cppstart/cppend blocks and cpp() expressions.
🌍
Cross-Platform
Target Win64 and Linux64 from a single installation. Cross-compile Linux binaries on Windows and run them via WSL2 -- no separate toolchain needed.
🧠
Rich Type System
Explicit-width integers, IEEE 754 floats, managed strings (UTF-8/UTF-16), records with inheritance, overlays (unions), bit fields, choices (enums), sets, and routine types.
🛡
Exception Handling
guard/except/finally catches both software exceptions and hardware faults like divide-by-zero and access violations. Propagates across module boundaries.
⚙
Module System
Four module kinds: exe, dll, lib, unit. Public/private visibility. Full module qualification eliminates name collisions. Initialize/finalize lifecycle hooks.
🔧
CImporter Tool
Auto-generates CPaskal import units from C header files. Point it at raylib.h, SDL3.h, or any C header -- get a ready-to-import .cpas module.
💻
Developer Tooling
Full LSP for editor integration (completion, hover, diagnostics, signature help). Native Win64 debugger. Built-in test framework with assertions.
Five clean stages. One enriched AST.

The AST is the single source of truth. Each stage enriches it, codegen emits C++23, and Zig/Clang compiles to native.

01
Lexer
Tokenizes .cpas source. Keywords and all 16 primitive types are dynamically registered with their C++23 mappings. Nothing hardcoded.
02
Parser
Recursive descent for statements and declarations. Pratt parser (top-down operator precedence) for expressions. Produces a fully detailed AST preserving every syntactic detail.
03
Semantic Analysis
Walks and enriches the AST. Resolves all types, symbols, cross-module references. After this pass, every expression has a resolved type, every call points to its target. Exhaustive validation.
04
Code Generation
A pure AST walker. Reads enriched nodes, emits C++23. Zero lookups, zero resolution. If codegen needs to figure something out, that is a bug in the semantic pass.
05
Zig/Clang Native Compilation
The generated C++23 is compiled to native x86_64 binaries by Zig/Clang -- zig.exe bundles Clang and its own build system into a single tool. It compiles the C++23 output to the target platform (Win64 or Linux64) entirely from Windows. No separate toolchain installation required.
Consume C libraries. Produce C-compatible binaries.

The C boundary is paper-thin. Bind to any C library with a single declaration. Produce DLLs and static libraries the rest of the world can call.

Consuming C libraries
// Bind to libc
routine clink toupper(const c: int32): int32;
  external "c" name "toupper";

// Bind to raylib via CImporter-generated unit
import raylib;
raylib.InitWindow(800, 600, cstr("Hello"));

// Bind to Windows API
@ifdef TARGET_WIN64
routine clink GetTick(): uint64;
  external "kernel32" name "GetTickCount64";
@endif

// Bind to Linux API
@ifdef TARGET_LINUX64
routine clink getpid(): int32;
  external "c" name "getpid";
@endif
Producing C-compatible output
// Build a DLL any language can call
module dll mathlib;

public routine clink add(
  a: int32; b: int32): int32;
begin
  return a + b;
end;

public routine clink factorial(
  n: int32): int32;
var result: int32 = 1;
    i: int32;
begin
  for i := 1 to n do
    result *= i;
  end;
  return result;
end;

end.

// Result: mathlib.dll with standard C ABI
// Callable from Python, Rust, C#, Java...

The CImporter tool reads C header files and auto-generates CPaskal import units. It uses tinycc for preprocessing, then translates types, constants, and function declarations to .cpas syntax. Binding to raylib, SDL3, SQLite, or any C library is one command away.

Built for people who build systems

CPaskal is a systems programming language. It belongs in the same conversation as C, C++, Rust, and Zig.

Systems Programmers
Direct access to hardware, operating systems, and low-level APIs with Pascal's readability. Produce native libraries, tools, and services with full control over memory and ABI.
Game Developers
Bind to raylib, SDL3, OpenGL, or Vulkan with a single declaration. Write game logic in clean, readable code while getting native performance from LLVM optimization.
Library Authors
Write code in CPaskal, produce standard C-ABI DLLs and static libraries. Every language in the world can call your code -- Python, Rust, Go, C#, Java, and more.
Pascal Veterans
Keep the syntax you know and love. Gain the C interop that Delphi never delivered cleanly. Modern type system, module system, and cross-platform compilation without leaving the Pascal family.
See CPaskal in action

Infographic, walkthroughs, and a deep-dive podcast into the language design.

CPaskal Infographic
🎵
Deep Dive -- Language Design
Use the expand button to view full size
Three steps to your first binary

No SDK, no package manager, no setup wizard. Unzip and go.

01
Unzip CPaskal
Download the release ZIP and extract it anywhere. Everything ships inside -- the cpas compiler, the Zig/Clang toolchain, and the C++23 runtime. No installers, no SDKs, no setup. You're ready to compile.
02
Write hello.cpas
module exe hello; begin println("Hello, {}!", "CPaskal"); end.
03
Compile and run
$ cpas hello -r Hello, CPaskal! // Cross-compile for Linux: $ cpas hello -r -t x86_64_linux