CPaskal is a systems programming language that combines Pascal's clarity with complete C ABI interoperability. It compiles .cpas source files to C++23, then builds native x86_64 binaries via a bundled Zig/Clang toolchain. Two targets are officially supported: Windows (Win64) and Linux (Linux64).
module exe hello;
begin
println("Hello from CPaskal!");
end.
The name is deliberate: "C" for C ABI compatibility, "Paskal" honoring Niklaus Wirth's Pascal. The "k" distinguishes it from the original while acknowledging its heritage.
CPaskal solves a problem that has existed in the Pascal world for decades: clean C interoperability.
If a library has a C API -- raylib, SDL3, SQLite, zlib, Vulkan -- CPaskal can call it directly. No binding generators, no wrapper layers. And when you compile a CPaskal DLL or static library, the output uses the C ABI. Any language that can call C -- which is virtually all of them -- can call your CPaskal code.
This bidirectional interop is what sets CPaskal apart. It is not just a consumer of the C ecosystem. It is a full participant.
CPaskal ships as a completely self-contained package. The compiler, the Zig/Clang toolchain, the C++23 runtime, the build system -- everything is in the box. There is nothing to install separately. You unzip, you compile, you get native binaries.
The same installation that builds Windows binaries can also produce Linux binaries. If WSL2 is available, you can run them immediately.
- Clean Pascal syntax with
begin..endblocks,:=assignment, strong static typing, and a module system - Full C ABI interop -- consume any C library, produce C-compatible binaries
- Native binaries -- real executables, shared libraries, and static libraries via Zig/Clang
- Four module kinds --
exe,dll,lib,unit - Explicit sized types --
int8throughint64,uint8throughuint64,float32,float64 - Records with inheritance, overlays (unions), packed/aligned records, bit fields
- Routine overloading with automatic linkage management
- Structured exceptions --
guard/except/finallycatches both software and hardware faults - Conditional compilation --
@ifdef/@ifndef/@else/@endifwith predefined platform symbols - C++ escape hatch --
cppstart/cppendblocks andcpp()inline expressions - Managed strings -- UTF-8
stringand UTF-16wstring - Sets, dynamic arrays, pointers, routine types -- the full systems programming toolkit
- Built-in unit testing --
testblocks with assertion intrinsics - Format-string output --
println("value = {}", x)with automatic type formatting
Every CPaskal program is a module. The module kind determines the output:
| Declaration | Output |
|---|---|
module exe name |
Native executable |
module dll name |
Shared library (.dll / .so) |
module lib name |
Static library |
module unit name |
Importable source module |
A minimal program:
module exe hello;
routine add(const a: int32; const b: int32): int32;
begin
return a + b;
end;
begin
println("Hello from CPaskal!");
println("add(2, 3) = {}", add(2, 3));
end.
Compile and run:
cpas hello -rCPaskal has explicit-width primitive types essential for systems programming and C interop:
| Category | Types |
|---|---|
| Signed integers | int8, int16, int32, int64 |
| Unsigned integers | uint8, uint16, uint32, uint64 |
| Floating point | float32, float64 |
| Text | char, wchar, string, wstring |
| Other | boolean, pointer, pointer to T |
Records are CPaskal's structured data type. They support single inheritance, packing, alignment, nested overlays (unions), and bit fields:
type
Point = record
x: int32;
y: int32;
end;
Derived = record(Point)
z: int32;
end;
Tagged = record
tag: int32;
overlay
iVal: int64;
fVal: float64;
end;
end;
Record literals use named initialization:
var pt: Point = Point(x: 42, y: 99);
Functions and procedures are unified under routine. Parameters are const by default:
routine distance(a: Point; b: Point): float64;
var dx: float64;
var dy: float64;
begin
dx := a.x - b.x;
dy := a.y - b.y;
return cpp("std::sqrt(dx*dx + dy*dy)");
end;
Routines are first-class values via routine types:
type BinaryOp = routine(x: int32; y: int32): int32;
var op: BinaryOp = add;
println("result = {}", op(3, 4));
if x > 0 then
println("positive");
end;
while count < 10 do
count += 1;
end;
for i := 1 to 10 do
total += i;
end;
match value of
0: println("zero");
1..5: println("low");
else
println("high");
end;
CPaskal catches both software exceptions and hardware faults (divide-by-zero, access violations):
guard
result := a div b;
except
println("error: {}", excmsg());
finally
cleanup();
end;
All access to imported symbols requires full module qualification:
import myutils;
begin
println("{}", myutils.util_add(3, 4));
end.
Symbols are private by default. The public keyword exports them:
public const VERSION: int32 = 1;
public routine add(const a: int32; const b: int32): int32;
Bind CPaskal routines to C library functions:
routine clink c_abs(const n: int32): int32; external "c" name "abs";
routine clink InitWindow(w: int32; h: int32; title: pointer); external "raylib";
Inject C++ directly into the generated output:
cppstart header
#include <cmath>
cppend
cppstart source
double my_helper() { return 3.14; }
cppend
var result: int32 = cpp("my_helper_function()");
Auto-generate CPaskal import units from C header files. CImporter is driven by .cis scripts that specify which headers to process, what binding mode to use, and how to handle library dependencies:
cpas cimport raylib.cis
Production bindings for raylib, SDL3, SDL3_mixer, and SDL3_image have all been generated this way.
All module kinds support optional initialize and finalize blocks:
module dll mylib;
public routine clink compute(const x: int32): int32;
begin
return x * x;
end;
initialize
println("library loaded");
end;
finalize
println("library unloading");
end;
end.
exe modules have a begin..end. entry point. dll, lib, and unit modules do not.
Test blocks appear after the module's end. and run when @unittestmode is active:
module exe mathlib;
@unittestmode on;
routine add(const a: int32; const b: int32): int32;
begin
return a + b;
end;
end.
test "add returns correct sum"
begin
asserteq(5, add(2, 3));
end;
@ifdef TARGET_WIN64
routine clink GetTickCount64(): uint64; external "kernel32" name "GetTickCount64";
@endif
@ifdef TARGET_LINUX64
routine clink getpid(): int32; external "c" name "getpid";
@endif
Predefined symbols include CPASKAL, TARGET_WIN64, TARGET_LINUX64, CPUX64, BUILD_EXE, BUILD_DLL, BUILD_LIB, DEBUG, and RELEASE.
The full language reference, directive list, BNF grammar, debugging guide, and common task recipes are in a single document:
| Document | Description |
|---|---|
| CPaskal | Complete tour: types, routines, records, choices, sets, arrays, strings, control flow, exceptions, memory, pointers, overlays, variadics, modules, C++ interop, directives, intrinsics, test blocks, the full BNF grammar, debugging, code style conventions, and common task recipes. |
A release is what you need to use CPaskal. Cloning the repository is not enough -- the toolchain and the compiled binaries are not in it. A release bundles everything required to go from a .cpas file to a native binary: the compiler, the Zig/Clang build backend, the C++ runtime, the standard library, the vendor bindings, and the debug adapter. It is pre-built and ready to run.
Unzip it, add bin\ to your PATH. That is the complete installation.
CPaskal/
bin/
cpas.exe <- compiler
res/
runtime/ <- CPaskal C++ runtime
libs/std/ <- standard library modules
libs/vendor/ <- vendor bindings (raylib, SDL3)
tests/ <- test suite (.cpas)
zig/ <- bundled Zig/Clang toolchain
cpas <source> [options]
cpas cimport <script> [options]| Flag | Description |
|---|---|
-r |
Compile and run |
-d |
Build with debug info and launch debugger (Windows) |
-t <target> |
Set target (x86_64_windows, x86_64_linux) |
-o <path> |
Set output directory |
-opt <level> |
Optimization (debug, release_safe, release_fast, release_small) |
-sub <type> |
Subsystem (console, gui) |
cpas hello # compile
cpas hello -r # compile and run
cpas hello -r -t x86_64_linux # cross-compile for Linux and run (via WSL2)- Lexer -- tokenizes
.cpassource with dynamically registered keywords - Parser -- recursive descent + Pratt parser produces a detailed AST
- Semantic Analysis -- enriches the AST with resolved types, symbols, and cross-module references
- Code Generation -- pure AST walker emits C++23
- Zig/Clang -- compiles C++23 to native binary with full LLVM optimization
The compiler is a single executable (cpas.exe) that drives the entire pipeline.
| Requirement | |
|---|---|
| Host OS | Windows 10/11 x64 |
| Linux auto-run | WSL2 + Ubuntu |
| External toolchain | None. Zig/Clang is bundled. |
The compiler is implemented in Delphi (Object Pascal).
| Requirement | |
|---|---|
| Host OS | Windows x64 |
| Compiler | Delphi 12 Athens or higher |
| Release | Required. Supplies bin\res\ (toolchain, runtime). |
- Open the project group in Delphi
- Build the
CPASproject (Win64 Release) - Output is
bin\cpas.exe
CPaskal is an open project and contributions are welcome:
- Report bugs -- open an issue with a minimal
.cpasreproduction case - Suggest features -- describe the use case first, then the syntax
- Submit pull requests -- bug fixes, documentation, test cases
Join the Discord to discuss development or share what you are building.
- Star the repo -- helps others find the project
- Spread the word -- write a post, mention it on social media
- Join us on Discord
- Become a sponsor via GitHub Sponsors
CPaskal is licensed under the Apache License 2.0. See LICENSE for details.
CPaskalβ’ - Pascal elegance. C interop. Native binaries.
Copyright Β© 2026-present tinyBigGAMESβ’ LLC All Rights Reserved.

