The Pax Programming Language is a Pascal/Oberon-inspired systems programming language targeting Win64. It compiles to C via TinyCC with a completely self-contained toolchain - no external dependencies required. Memory is automatically managed via the Boehm-Demers-Weiser Garbage Collector. Configuration is handled through TOML files powered by DX.TOML, providing a clean, readable format for build settings, C header import definitions, and general-purpose application configuration.
module exe HelloWorld;
routine printf(const fmt: pointer to char; ...): int32; external 'msvcrt.dll';
var
name: string;
begin
name := 'Pax';
printf('Hello from %s!\n', pointer to char(name));
printf('GC heap: %lld bytes\n', gc_heapsize());
end.
- π― Minimal by design - Clean syntax, no redundancy, just what you need
- π Pascal heritage - Readable, structured code inspired by Pascal and Oberon
- π§Ή Automatic memory - Boehm GC handles allocation and cleanup
- π¦ Self-contained - Embedded TinyCC toolchain, single executable distribution
- πͺ Windows native - Call any DLL directly, full Windows API access
- ποΈ Multiple outputs - Build executables, DLLs, or static libraries
- β‘ JIT compilation - Compile and execute code in memory without generating files
- 𧬠Type extension - Record inheritance without class complexity
- ποΈ Classes - Methods, inheritance, and virtual dispatch
- π Union types - C-compatible unions with anonymous nesting
- π Dynamic arrays - setlength/len with automatic memory management
- π Managed strings - UTF-8 and UTF-16 string types with emoji support
- π Varargs support - Full C interop with printf-style functions
- π§ͺ Built-in testing - Integrated unit test framework
β οΈ Exception handling - try/except/finally with OS exception support- π·οΈ Version info - Embed metadata and icons in executables
- π C header import - Convert C headers to Pax modules automatically
- βοΈ TOML configuration - General-purpose configuration file support
| Type | Size | Description |
|---|---|---|
| int8, int16, int32, int64 | 1-8 bytes | Signed integers |
| uint8, uint16, uint32, uint64 | 1-8 bytes | Unsigned integers |
| float32, float64 | 4-8 bytes | Floating point |
| boolean | 1 byte | true / false |
| char, uchar | 1 byte | Signed/unsigned characters |
| wchar, uwchar | 2 bytes | Wide characters (UTF-16) |
| string, wstring | 8 bytes | Managed strings (UTF-8 / UTF-16) |
| pointer, pointer to T | 8 bytes | Untyped / typed pointers |
| Type | Description |
|---|---|
| module exe Name | Executable program |
| module lib Name | Static library (.a) |
| module dll Name | Dynamic library (.dll) |
| module jit Name | JIT compilation (compile and run in memory) |
+ - * / // Arithmetic
div mod // Integer division and modulo
= <> < > <= >= // Comparison
and or not // Logical
in // Set membership
var
n: int32;
s: string;
days: set of 0..6;
begin
n := 10;
n += 5; // n = n + 5
n -= 3; // n = n - 3
n *= 2; // n = n * 2
n /= 4; // n = n / 4 (integer division)
s := 'Hello';
s += ' World'; // String concatenation
days := {1, 2};
days += {3}; // Set union
days -= {1}; // Set difference
end.
type
THandle = uint64;
TSize = int64;
TFileHandle = THandle; // Alias of alias
var
h: THandle;
fh: TFileHandle;
begin
h := 12345;
fh := h; // Compatible assignment
end.
type
TPoint = record
x: int32;
y: int32;
end;
TColorPoint = record(TPoint) // Inherits from TPoint
color: uint32;
end;
var
p: TColorPoint;
begin
p.x := 100; // Inherited from TPoint
p.y := 200; // Inherited from TPoint
p.color := $FF0000;
end.
Classes provide object-oriented programming with methods and virtual dispatch:
type
TCounter = class
Count: int32;
Name: string;
method Reset();
begin
Self.Count := 0;
end;
method Increment();
begin
Self.Count := Self.Count + 1;
end;
method GetCount(): int32;
begin
return Self.Count;
end;
end;
var
counter: pointer to TCounter;
begin
new(counter);
counter.Reset();
counter.Increment();
printf('Count: %d\n', counter.GetCount()); // Count: 1
dispose(counter);
end.
Classes support single inheritance with method overriding:
type
TAnimal = class
method Speak();
begin
printf('Animal speaks\n');
end;
end;
TDog = class(TAnimal)
method Speak();
begin
parent.Speak(); // Call parent's implementation
printf('Dog barks!\n');
end;
end;
var
dog: pointer to TDog;
begin
new(dog);
dog.Speak(); // Prints: Animal speaks / Dog barks!
end.
Key features:
Selfreferences the current instanceparentcalls the parent class's implementation (non-virtual dispatch)- Method calls on instances use virtual dispatch via automatic vtable
- Fields are inherited from parent classes
type
// Packed record - no padding between fields
THeader = record packed
magic: uint16;
version: uint8;
flags: uint8;
end;
// Explicit alignment for SIMD or cache optimization
TAlignedData = record align(16)
values: array[0..3] of float32;
end;
// Bit fields for compact storage
TFlags = record packed
enabled: uint8 : 1; // 1 bit
priority: uint8 : 3; // 3 bits
reserved: uint8 : 4; // 4 bits
end;
type
// All fields share the same memory location
TValue = union
asInt: int64;
asFloat: float64;
asPtr: pointer;
end;
// Record with anonymous union (C-style variant)
TVariant = record
kind: int32;
union
intVal: int64;
floatVal: float64;
strVal: pointer to char;
end;
end;
var
numbers: array of int32;
i: int32;
begin
setlength(numbers, 10);
for i := 0 to len(numbers) - 1 do
numbers[i] := i * 2;
end;
end.
Routines can accept dynamic arrays as parameters:
// Const array parameter (read-only)
routine SumArray(const arr: array of int32): int32;
var
i: int32;
total: int32;
begin
total := 0;
for i := 0 to len(arr) - 1 do
total := total + arr[i];
end;
return total;
end;
// Var array parameter (can modify)
routine FillArray(var arr: array of int32; const value: int32);
var
i: int32;
begin
for i := 0 to len(arr) - 1 do
arr[i] := value;
end;
end;
var
nums: array of int32;
sum: int32;
begin
setlength(nums, 5);
FillArray(nums, 10); // Fill with 10s
sum := SumArray(nums); // sum = 50
end.
type
TDays = set of 0..6;
var
weekdays: TDays;
weekend: TDays;
alldays: TDays;
today: int32;
begin
weekdays := {1, 2, 3, 4, 5}; // Mon-Fri
weekend := {0, 6}; // Sun, Sat
alldays := weekdays + weekend; // Union
weekdays := alldays - weekend; // Difference
today := 3;
if today in weekdays then
// It's a weekday
end;
end.
type
TColor = (
clRed,
clGreen,
clBlue
);
TErrorCode = (
Success = 0,
NotFound = 404,
ServerError = 500
);
TPriority = (
Low, // 0 (auto)
Medium = 5, // 5 (explicit)
High, // 6 (auto continues)
Critical = 10 // 10 (explicit)
);
var
color: TColor;
err: TErrorCode;
priority: TPriority;
val: int32;
begin
color := clGreen;
err := NotFound;
// Enum comparison
if priority > Low then
// Higher priority
end;
// Enum in case statement
case err of
Success:
printf('OK\n');
NotFound:
printf('Not found\n');
ServerError:
printf('Server error\n');
end;
// Enum to integer cast
val := int32(color); // val = 1 (clGreen)
// Integer to enum cast
color := TColor(2); // color = clBlue
end.
var
i: int32;
sum: int32;
n: int32;
ch: char;
begin
// If-then-else
if n > 10 then
sum := 1;
else
sum := 0;
end;
// While loop
while i < 10 do
i := i + 1;
end;
// For loop (ascending)
for i := 1 to 10 do
sum := sum + i;
end;
// For loop (descending)
for i := 10 downto 1 do
sum := sum + i;
end;
// Repeat-until
repeat
i := i + 1;
until i >= 10;
// Case statement with ranges
case n of
0: sum := 0;
1..10: sum := 1;
11..100: sum := 2;
else
sum := -1;
end;
// Case with character ranges
case ch of
'a'..'z': sum := 1;
'A'..'Z': sum := 2;
'0'..'9': sum := 3;
else
sum := 0;
end;
end.
Pax provides structured exception handling using try/except/finally blocks. Both software exceptions (raised by your code) and OS exceptions (access violations, divide by zero) are caught.
var
result: int32;
begin
// Basic try-except
try
raiseexception('Something went wrong');
result := 1; // Never reached
except
result := 0; // Exception caught
end;
// Try-finally (cleanup always runs)
try
result := DoSomething();
finally
Cleanup(); // Always executes
end;
// Try-except-finally combined
try
riskyOperation();
except
handleError();
finally
cleanup(); // Runs whether exception occurred or not
end;
end.
begin
// Raise with message (code defaults to 1)
raiseexception('File not found');
// Raise with custom code and message
raiseexceptioncode(404, 'Resource not found');
end.
begin
try
raiseexceptioncode(42, 'Custom error');
except
printf('Code: %d\n', getexceptioncode()); // 42
printf('Message: %s\n', pointer to char(getexceptionmessage())); // Custom error
end;
end.
OS-level exceptions are automatically caught via Windows Vectored Exception Handling:
var
p: pointer to int32;
a, b, c: int32;
begin
// Null pointer dereference
try
p := nil;
p^ := 42; // Access violation!
except
printf('Caught: 0x%08X\n', getexceptioncode()); // 0xC0000005
end;
// Integer divide by zero
try
a := 10;
b := 0;
c := a div b; // Divide by zero!
except
printf('Caught: 0x%08X\n', getexceptioncode()); // 0xC0000094
end;
end.
| Code | Description |
|---|---|
| 1 | Default software exception |
| 0xC0000005 | Access violation (null pointer, invalid memory) |
| 0xC0000094 | Integer divide by zero |
| 0xC0000095 | Integer overflow |
| 0xC00000FD | Stack overflow |
type
TIntFunc = routine(const a: int32; const b: int32): int32;
TCallback = routine(const x: int32): int32;
routine Add(const a: int32; const b: int32): int32;
begin
return a + b;
end;
routine Multiply(const a: int32; const b: int32): int32;
begin
return a * b;
end;
// Higher-order function
routine Apply(const fn: TIntFunc; const x: int32; const y: int32): int32;
begin
return fn(x, y);
end;
var
mathOp: TIntFunc;
result: int32;
begin
mathOp := Add;
result := mathOp(10, 5); // 15
mathOp := Multiply;
result := mathOp(10, 5); // 50
result := Apply(Add, 3, 4); // 7
end.
var
path: string;
msg: wstring;
begin
// Normal strings - backslashes are escape characters
path := 'C:\\Users\\Name\\file.txt';
// Raw strings - no escape processing (great for paths)
path := @'C:\Users\Name\file.txt';
// Wide strings for Windows API (UTF-16)
msg := L'Hello World!';
// Raw wide strings
msg := @L'C:\Path\To\File';
end.
String literals can be concatenated with the + operator:
var
path: string;
wide: wstring;
begin
// Raw string + regular string
path := @'C:\Users\' + 'Admin';
// Wide string concatenation
wide := L'Hello' + L'World';
// Raw wide + regular wide
wide := @L'C:\Path\' + L'File';
end.
routine wprintf(const fmt: pointer to const wchar; ...): int32; external 'msvcrt.dll';
var
value: int32;
ptr: pointer to int32;
constPtr: pointer to const char;
begin
value := 42;
ptr := address of value; // Get pointer to value
ptr^ := 100; // Dereference and assign
// value is now 100
end.
Call Windows DLL functions with simple declarations - no binding libraries required:
module exe WinAPI;
routine MessageBoxW(hwnd: pointer; text: pointer to wchar;
caption: pointer to wchar; utype: uint32): int32; external 'user32.dll';
begin
MessageBoxW(nil, L'Hello from Pax!', L'Pax', 0);
end.
For custom DLLs or when linking multiple functions from the same library, use #library:
module exe CustomDLL;
#library 'mylib'
// Routines declared without DLL name - linked via #library
routine MyFunction(const value: int32): int32; external;
routine MyOtherFunction(const msg: pointer to char); external;
begin
MyOtherFunction('Hello');
end.
begin
// Force garbage collection
gc_collect();
// Query heap statistics
printf('Heap size: %lld bytes\n', gc_heapsize());
printf('Used: %lld bytes\n', gc_usedsize());
printf('GC cycles: %lld\n', gc_collectcount());
// Dump detailed GC stats (debug builds)
gc_dump();
end.
var
i: int32;
arg: string;
begin
printf('Arguments: %d\n', paramcount());
for i := 0 to paramcount() do
arg := paramstr(i);
printf(' [%d] %s\n', i, pointer to char(arg));
end;
end.
#define DEBUG
#define VERSION 100
begin
#ifdef DEBUG
printf('Debug mode enabled\n');
#endif
#ifndef RELEASE
printf('Not a release build\n');
#endif
#if VERSION >= 100
printf('Version 100 or higher\n');
#elif VERSION >= 50
printf('Version 50-99\n');
#else
printf('Old version\n');
#endif
#undef DEBUG
end.
begin
// Available as both preprocessor macros and runtime constants
printf('Pax version: %s\n', PAX_VERSION_STR);
printf('Major: %d\n', PAX_MAJOR_VERSION);
printf('Minor: %d\n', PAX_MINOR_VERSION);
printf('Patch: %d\n', PAX_PATCH_VERSION);
printf('Combined: %d\n', PAX_VERSION);
// Preprocessor detection
#ifdef PAX
printf('Compiled with Pax\n');
#endif
end.
module exe MyTests;
#unittestmode on
routine Add(const a: int32; const b: int32): int32;
begin
return a + b;
end;
routine IsPositive(const a: int32): boolean;
begin
return a > 0;
end;
begin
// Normal entry point (skipped in test mode)
end.
test 'Addition works correctly'
begin
TestAssertEqualInt(5, Add(2, 3));
TestAssertEqualInt(0, Add(-1, 1));
end;
test 'Boolean assertions'
begin
TestAssertTrue(IsPositive(5));
TestAssertFalse(IsPositive(-5));
end;
test 'Pointer and allocation assertions'
var
p: pointer to int32;
begin
p := nil;
TestAssertNil(p);
new(p);
TestAssertNotNil(p);
p^ := 42;
TestAssertEqualInt(42, p^);
end;
module exe MyApp;
#subsystem gui
#addverinfo yes
#vimajor 1
#viminor 0
#vipatch 0
#viproductname 'My Application'
#videscription 'A sample Pax application'
#vicompanyname 'My Company'
#vicopyright 'Copyright 2025'
#exeicon @'assets\app.ico'
begin
// Application code
end.
JIT modules compile and execute directly in memory without generating executable files. This is useful for scripting, rapid prototyping, or embedding Pax as a scripting language in applications.
module jit Calculator;
routine printf(const fmt: pointer to char; ...): int32; external 'msvcrt.dll';
var
x: int32;
y: int32;
begin
x := 10;
y := 20;
printf('=== JIT Module ===\n');
printf('x = %d, y = %d\n', x, y);
printf('x + y = %d\n', x + y);
printf('x * y = %d\n', x * y);
end.
JIT modules use the same language features as regular executables but run immediately after compilation. The paxrtl.dll runtime must be available in the host application's working directory.
| Directive | Description |
|---|---|
| #subsystem console/gui | PE subsystem (default: console) |
| #library 'name' | Link a library |
| #librarypath 'path' | Add library search path |
| #includepath 'path' | Add C include path |
| #addfile 'file' | Add .c, .obj, .lib, .dll to link |
| #modulepath 'path' | Add module search path |
| #outputpath 'path' | Set output directory |
| #generatedpath 'path' | Set generated C files directory |
| Directive | Description |
|---|---|
| #define SYM [value] | Define preprocessor symbol |
| #undef SYM | Undefine symbol |
| #ifdef SYM | Conditional if defined |
| #ifndef SYM | Conditional if not defined |
| #if expr | Conditional expression |
| #elif expr | Else if |
| #else | Else branch |
| #endif | End conditional |
| Directive | Description |
|---|---|
| #debug | Enable debug info (STABS format) |
| #unittestmode on/off | Enable unit test mode |
| #maxerrors N | Max errors before stopping |
| #option 'flag' | Pass raw TCC option |
| Directive | Description |
|---|---|
| #addverinfo yes/no | Enable version info embedding |
| #vimajor N | Major version number |
| #viminor N | Minor version number |
| #vipatch N | Patch version number |
| #viproductname 'name' | Product name |
| #videscription 'desc' | File description |
| #vifilename 'name' | Original filename |
| #vicompanyname 'name' | Company name |
| #vicopyright 'text' | Copyright notice |
| #exeicon 'path' | Executable icon |
Pax includes a built-in C header importer that converts C headers into Pax module source code. It uses TCC for preprocessing (expanding macros, includes) then parses the result to generate Pax declarations.
LImporter := TPaxCImporter.Create();
try
LImporter.SetModuleName('sdl3'); // Output module name (sdl3.pax)
LImporter.SetDllName('sdl3.dll'); // DLL to link against
LImporter.SetOutputPath('libs/sdl3/src'); // Where to write output
LImporter.AddIncludePath('libs/sdl3/include');
LImporter.AddExcludedType('va_list');
LImporter.SetHeader('libs/sdl3/include/sdl3/sdl.h');
LImporter.Process();
finally
LImporter.Free();
end;The importer supports TOML configuration files for repeatable builds:
// Save current settings to config file
LImporter.SaveConfig('libs/sdl3/sdl3.toml');
// Load settings from config file
LImporter.LoadConfig('libs/sdl3/sdl3.toml');
LImporter.Process();[cimporter]
header = "libs/sdl3/include/sdl3/sdl.h"
module_name = "sdl3"
dll_name = "sdl3.dll"
output_path = "libs/sdl3/src"
include_paths = ["libs/sdl3/include"]
library_paths = ["libs/sdl3/bin"]
copy_dlls = ["libs/sdl3/bin/sdl3.dll"]
excluded_types = ["va_list", "__builtin_va_list", "MSG", "XEvent"]
excluded_functions = ["alloca"]
[[cimporter.insertions]]
target = "(* Forward declarations (opaque types) *)"
file = "libs/sdl3/src/sdl3_constants.txt"
position = "before"
occurrence = 1| Method | Description |
|---|---|
| SetHeader(filename) | Set the C header file to import |
| SetModuleName(name) | Set output module name (defaults to header name) |
| SetDllName(name) | Set DLL name for external declarations |
| SetOutputPath(path) | Set output directory for generated .pax file |
| AddIncludePath(path) | Add C include search path |
| AddLibraryPath(path) | Add library search path |
| AddCopyDLL(path) | Add DLL to copy to output |
| AddExcludedType(name) | Skip type during import |
| AddExcludedFunction(name) | Skip function during import |
| InsertTextBefore(target, text) | Insert text before target line |
| InsertTextAfter(target, text) | Insert text after target line |
| InsertFileBefore(target, file) | Insert file content before target line |
| InsertFileAfter(target, file) | Insert file content after target line |
| LoadConfig(filename) | Load settings from TOML file |
| SaveConfig(filename) | Save settings to TOML file |
| Process() | Run the import process |
| GetLastError() | Get error message if Process() failed |
| Clear() | Reset all settings |
- Structs, unions, and typedefs
- Function declarations with varargs
- Enumerations with explicit values
- Pointer types and arrays
- Forward declarations for opaque types
- Packed records, alignment, and bit fields
- Constants from #define (integers, floats, strings)
- Typed constants from compound literals (e.g., raylib Color constants)
When a C library depends on another (e.g., SDL3_image uses SDL3 types), the importer can generate proper module imports and qualified type references.
Example: Importing SDL3_image (depends on SDL3)
LImporter := TPaxCImporter.Create();
try
// Basic settings
LImporter.SetModuleName('sdl3_image');
LImporter.SetDllName('sdl3_image.dll');
LImporter.SetOutputPath('libs/sdl3_image/src');
// Generate "import sdl3;" in output
LImporter.AddModuleImport('sdl3');
// Generate "#modulepath 'libs/sdl3/src'" so compiler can find sdl3.pax
LImporter.AddModulePath('libs/sdl3/src');
// Include paths - TCC needs both for preprocessing
LImporter.AddIncludePath('libs/sdl3_image/include');
LImporter.AddIncludePath('libs/sdl3/include', 'sdl3'); // Associate with sdl3 module
// Source path - only generate output from SDL3_image headers
LImporter.AddSourcePath('libs/sdl3_image/include');
LImporter.SetHeader('libs/sdl3_image/include/sdl3_image/sdl_image.h');
LImporter.Process();
finally
LImporter.Free();
end;How It Works:
- AddModuleImport('sdl3') - Generates
import sdl3;at the top of the output module - AddModulePath('libs/sdl3/src') - Generates
#modulepathdirective so the Pax compiler can locate sdl3.pax - AddIncludePath(path, 'sdl3') - Associates the SDL3 include path with the sdl3 module name. Types from this path are automatically qualified (e.g.,
SDL_Surfacebecomessdl3.SDL_Surface) - AddSourcePath - Only headers in this path produce output. SDL3 headers are parsed for type information but not re-exported
Generated Output:
module lib sdl3_image;
import sdl3;
#modulepath 'libs/sdl3/src'
public type
IMG_Animation = record
frames: pointer to pointer to sdl3.SDL_Surface; // Qualified!
// ...
end;
public routine IMG_LoadTexture(const Arenderer: pointer to sdl3.SDL_Renderer;
const Afile: pointer to const char): pointer to sdl3.SDL_Texture; external 'sdl3_image.dll';
| Limitation | Workaround |
|---|---|
| Function-like macros | Manually add equivalent Pax routines |
| Expression macros (arithmetic/bitwise) | Use InsertFileBefore() to inject constants from a text file |
| va_list types cause errors | Use AddExcludedType('va_list') and similar |
| Platform-specific types (MSG, XEvent) | Use AddExcludedType() to skip them |
| Inline functions | Manually translate to Pax routines |
| Complex preprocessor conditionals | Import may include/exclude based on TCC's platform detection |
Pax includes a general-purpose TOML configuration class that can be used for any configuration needs. It wraps the DX.TOML library with a simple, type-safe API.
LConfig := TPaxConfig.Create();
try
if LConfig.LoadFromFile('settings.toml') then
begin
// Read values with defaults
LHost := LConfig.GetString('database.host', 'localhost');
LPort := LConfig.GetInteger('database.port', 5432);
LEnabled := LConfig.GetBoolean('database.enabled', True);
// Read arrays
LPaths := LConfig.GetStringArray('search.paths');
// Check if key exists
if LConfig.HasKey('database.timeout') then
LTimeout := LConfig.GetInteger('database.timeout');
// Modify and save
LConfig.SetString('database.host', 'newhost');
LConfig.SetInteger('database.port', 3306);
LConfig.SaveToFile('settings.toml');
end;
finally
LConfig.Free();
end;| Method | Description |
|---|---|
| GetString / SetString | String values |
| GetInteger / SetInteger | Int64 values |
| GetFloat / SetFloat | Double values |
| GetBoolean / SetBoolean | Boolean values |
| GetDateTime / SetDateTime | TDateTime values |
| GetStringArray / SetStringArray | Array of strings |
| GetIntegerArray / SetIntegerArray | Array of Int64 |
| GetFloatArray / SetFloatArray | Array of Double |
For TOML arrays of tables (e.g., [[items]]):
// Get count of table entries
LCount := LConfig.GetTableCount('items');
// Read fields from each table
for LI := 0 to LCount - 1 do
begin
LName := LConfig.GetTableString('items', LI, 'name', '');
LValue := LConfig.GetTableInteger('items', LI, 'value', 0);
LWeight := LConfig.GetTableFloat('items', LI, 'weight', 0.0);
LActive := LConfig.GetTableBoolean('items', LI, 'active', False);
end;Keys use dot notation to access nested tables:
[database]
host = "localhost"
port = 5432
[database.connection]
timeout = 30
retries = 3LHost := LConfig.GetString('database.host');
LTimeout := LConfig.GetInteger('database.connection.timeout');The compiler is built in Delphi with a clean pipeline architecture:
| Unit | Purpose |
|---|---|
| Pax.Lexer | Tokenization with full Unicode support |
| Pax.AST | Abstract syntax tree node definitions |
| Pax.Parser | Recursive descent parser with error recovery |
| Pax.Types | Type registry and type system management |
| Pax.Symbols | Symbol table with scope management |
| Pax.Checker | Semantic analysis and type checking |
| Pax.CodeGen | C99 code generation |
| Pax.Compiler | Build orchestration and TinyCC integration |
| Unit | Purpose |
|---|---|
| Pax.ArArchive | Native AR archive creation for static libraries |
| Pax.ZipVFS | Virtual file system for embedded toolchain |
| Pax.IATHook | IAT hooking for transparent file redirection |
| Pax.LibTCC | TinyCC (libtcc) integration |
| Pax.ModuleLoader | Module dependency resolution |
| Pax.Errors | Error types, codes, and diagnostic formatting |
| Pax.CImporter | C header to Pax module converter |
| Pax.Config | General-purpose TOML configuration management |
| Pax.Utils | Utility functions and console helpers |
| Pax.Resources | Localized error messages and resource strings |
Under active development.
The core compiler is functional and can produce working executables, DLLs, and static libraries. All major language features are implemented:
- Records with inheritance
- Classes with methods and virtual dispatch
- Unions (named and anonymous)
- Packed records and alignment
- Bit fields
- Dynamic arrays
- Sets with ranges and operations
- Managed strings (UTF-8/UTF-16)
- External DLL calls with varargs
- Module system with imports
- Unit testing framework
- GC intrinsics
- Version info embedding
- Icon embedding
- Type aliases
- Routine types (function pointers)
- Compound assignment operators
- Case statement ranges
- Conditional compilation
- PAX compiler constants
- Enumeration types
- Exception handling (try/except/finally)
- JIT compilation (compile and run in memory)
- C header importer with TOML config
- General-purpose TOML configuration
Option 1: Download ZIP
Option 2: Git Clone
git clone https://github.com/tinyBigGAMES/PaxLang.git- Open src\Pax Programming Language.groupproj in Delphi
- Build the project
That's it! Everything is included - TinyCC, Boehm GC, and runtime resources are already bundled in the repo. The compiled executable will be output to the bin folder.
The pax command line tool provides everything you need to create, build, and run Pax projects.
pax <COMMAND> [OPTIONS]
| Command | Description |
|---|---|
init <name> [type] |
Create a new Pax project |
build |
Compile the project |
run |
Build and execute the program |
clean |
Remove all generated files |
version |
Display version information |
help |
Display help message |
# Create an executable project (default)
pax init MyGame
# Create a static library
pax init MyLib lib
# Create a shared library (DLL)
pax init MyPlugin dllThis creates a project folder with the following structure:
MyGame/
βββ src/
β βββ MyGame.pax # Main source file
βββ pax.toml # Project configuration
| Type | Description | Output |
|---|---|---|
exe |
Executable program (default) | .exe |
lib |
Static library | .a |
dll |
Shared library | .dll |
# Navigate to project folder
cd MyGame
# Build the project
pax build
# Build and run
pax run
# Clean generated files
pax cleanThe pax.toml file stores all project settings:
[pax]
signature = "PAX-7F3A9B2C-E1D5-4A8F-B6C2-9D0E3F7A1B5C"
version = "1"
compiler = "0.1.0"
last_build = "2025-01-01T12:00:00.000Z"
[build]
mainsourcefile = "src\\MyGame.pax"
outputpath = "."
modulename = "MyGame"
buildtype = "exe"
subsystem = "console"
unittestmode = false
debugmode = false
verbose = false
maxerrors = 100
[paths]
modulepaths = []
includepaths = []
librarypaths = []
[tccoptions]
options = []
[versioninfo]
enabled = false
major = 0
minor = 0
patch = 0
productname = ""
description = ""
filename = ""
companyname = ""
copyright = ""
[resources]
exeicon = ""After building, the project folder contains:
MyGame/
βββ src/
β βββ MyGame.pax
βββ generated/ # Generated C code
β βββ MyGame.c
β βββ MyGame.h
βββ out/
β βββ bin/ # Compiled output
β βββ MyGame.exe
β βββ paxrtl.dll # Runtime library
βββ pax.toml
# Create a new project
pax init HelloWorld
# Edit the source (src/HelloWorld.pax)
cd HelloWorld
# Build and run
pax run
# Output:
# Hello from Pax!(*
HelloWorld - Pax EXE
*)
module exe HelloWorld;
routine printf(const fmt: pointer to const char; ...): int32; external 'msvcrt.dll';
begin
printf('Hello from Pax!\n');
end.
| Minimum | Tested | |
|---|---|---|
| Platform | Windows 10 x64 | Windows 11 25H2 x64 |
| Build | Delphi 11 (Alexandria) | Delphi 12 (Athens) |
Dependencies: None for end users - TinyCC and Boehm GC are embedded.
Contributions are welcome! Join our Discord to discuss development.
Pax is licensed under the Apache License 2.0. See LICENSE for details.
Paxβ’ Programming Language.
Copyright Β© 2025-present tinyBigGAMESβ’ LLC
All Rights Reserved.
