Skip to content

NitroPascal v0.4.0

Latest

Choose a tag to compare

@jarroddavis68 jarroddavis68 released this 20 Oct 00:01

🚀 NitroPascal v0.4.0: Real-World Usage

TL;DR

NitroPascal v0.4.0 proves real-world viability with function pointers, structured RTL modules (Types, SysUtils, StrUtils, DateUtils), static library linking (raylib example included), performance benchmarks showing competitive results against Delphi, and a dramatically expanded C++ runtime. You can now build, benchmark, and ship production applications with confidence.

🎯 The Real-World Milestone

This release isn't about adding more features - it's about proving NitroPascal works in production. You can now:

Use function pointers - Callbacks, event handlers, higher-order functions
Link static libraries - Demonstrated with raylib game development library
Structure with RTL modules - Types, SysUtils, StrUtils, DateUtils
Benchmark performance - Comprehensive NitroPascal vs Delphi comparison
Ship single executables - No DLL dependencies with static linking
Build real apps - Complete toolkit for production software

🔥 What's New in v0.4.0

Function Pointers - Callbacks and Events

The big feature request is here. Function pointers (procedural types) enable callbacks, event-driven programming, and functional patterns:

type
  TCompareFunc = function(const A, B: Integer): Integer;
  TNotifyProc = procedure(const Msg: String);

procedure QuickSort(var A: array of Integer; Compare: TCompareFunc);
var
  I, J, Pivot: Integer;
begin
  if Length(A) <= 1 then Exit;
  
  Pivot := A[Length(A) div 2];
  I := 0;
  J := High(A);
  
  while I <= J do
  begin
    while Compare(A[I], Pivot) < 0 do Inc(I);
    while Compare(A[J], Pivot) > 0 do Dec(J);
    if I <= J then
    begin
      Swap(A[I], A[J]);
      Inc(I);
      Dec(J);
    end;
  end;
  
  if J > 0 then QuickSort(Copy(A, 0, J + 1), Compare);
  if I < High(A) then QuickSort(Copy(A, I, Length(A) - I), Compare);
end;

function Ascending(const A, B: Integer): Integer;
begin
  Result := A - B;
end;

function Descending(const A, B: Integer): Integer;
begin
  Result := B - A;
end;

var
  Numbers: array of Integer;
begin
  SetLength(Numbers, 5);
  Numbers[0] := 5; Numbers[1] := 2; Numbers[2] := 8;
  Numbers[3] := 1; Numbers[4] := 9;
  
  // Sort ascending
  QuickSort(Numbers, @Ascending);
  
  // Sort descending
  QuickSort(Numbers, @Descending);
end.

Use cases:

  • Event handlers in GUI frameworks
  • Callbacks in async operations
  • Sorting with custom comparisons
  • Plugin architectures
  • Functional programming patterns
  • Framework integration hooks

Static Library Linking - Ship Single Executables

Want to distribute a single .exe with no dependencies? Static linking is here, demonstrated with raylib:

program RaylibGame;

{$link raylib}
{$library_path './lib'}

// Raylib functions via static linking
procedure InitWindow(Width, Height: Integer; Title: PAnsiChar); 
  cdecl; external 'raylib.lib';
procedure CloseWindow(); cdecl; external 'raylib.lib';
function WindowShouldClose(): Boolean; cdecl; external 'raylib.lib';
procedure BeginDrawing(); cdecl; external 'raylib.lib';
procedure EndDrawing(); cdecl; external 'raylib.lib';
procedure ClearBackground(Color: Integer); cdecl; external 'raylib.lib';
procedure DrawText(Text: PAnsiChar; X, Y, Size, Color: Integer); 
  cdecl; external 'raylib.lib';

begin
  InitWindow(800, 450, 'NitroPascal + Raylib!');
  
  while not WindowShouldClose() do
  begin
    BeginDrawing();
    ClearBackground(RAYWHITE);
    DrawText('Congrats! You created your first window!', 190, 200, 20, DARKGRAY);
    EndDrawing();
  end;
  
  CloseWindow();
end.

This is a complete, working game window that compiles to a single executable with raylib statically linked. No DLLs to ship. Just one .exe file.

Benefits:

  • Single-file deployment
  • No DLL versioning issues
  • Simplified distribution
  • Faster load times
  • Reduced attack surface

Structured RTL Modules - Professional Organization

The Runtime Library is now organized into standard Pascal modules for discoverability and maintainability:

Types - Base type declarations:

uses Types;
// Foundation types and utilities

SysUtils - System utilities and conversions:

uses SysUtils;

var
  S: String;
  I: Integer;
  F: Double;
begin
  // String conversions
  S := IntToStr(42);
  I := StrToInt('123');
  F := StrToFloat('3.14');
  
  // String manipulation
  S := UpperCase('hello');
  S := Trim('  spaces  ');
  S := StringReplace('test', 't', 'T', [rfReplaceAll]);
  
  // File system
  if FileExists('config.ini') then
    ProcessConfig();
end.

StrUtils - Advanced string manipulation:

uses StrUtils;

var
  S: String;
begin
  S := StringOfChar('*', 40);  // '****************************************'
  S := LeftStr('Hello', 3);    // 'Hel'
  S := RightStr('World', 3);   // 'rld'
end.

DateUtils - Date/time functions (foundation):

uses DateUtils;
// Date/time operations (initial implementation)

This modular organization makes the API discoverable, maintainable, and familiar to Delphi developers.

Performance Benchmarks - Competitive Results

The included benchmark suite provides quantitative comparison between NitroPascal and Delphi:

Test scenarios:

  • String operations (concatenation, manipulation, conversions)
  • Array operations (creation, access, iteration)
  • Mathematical computations (loops, calculations)

Results show NitroPascal delivers competitive performance thanks to:

  • LLVM optimization pipeline
  • Efficient C++ code generation
  • Optimized RTL implementation
  • Native compilation via Zig

Run the benchmarks yourself (bin folder):

NPBench_Delphi.exe (compiled in Delphi)
NPBench.exe (compiled in NitroPascal)

Enhanced String Library - Complete Unicode Support

New string functions for advanced manipulation and wide character support:

var
  S: String;
  Ch: Char;
  Buffer: array[0..255] of WideChar;
begin
  // Create repeated character strings
  S := StringOfChar('=', 80);  // '===============....'
  
  // Character case conversion
  Ch := UpCase('a');  // 'A'

  // Numeric to string (legacy format)
  Str(42:4, S);      // '  42' (width 4)
  Str(3.14:6:2, S);  // '  3.14' (width 6, precision 2)
  
  // Wide character operations
  WideCharToString(Buffer);
  StringToWideChar(S, Buffer, 256);

  // Direct buffer to string conversion
  SetString(S, @Buffer[0], WideCharLen(Buffer));
end.

Expanded Math Library - Scientific Computing

Complete mathematical toolkit for scientific and engineering applications:

var
  X, Y, Result: Double;
begin
  // Logarithms
  Result := Ln(2.718);      // Natural log
  Result := Log10(100);     // Base-10 log (2.0)
  Result := Log2(8);        // Base-2 log (3.0)
  Result := LogN(3, 27);    // Custom base log (3.0)
  
  // Exponential and power
  Result := Exp(1.0);       // e^1 ≈ 2.718
  Result := Power(2, 10);   // 2^10 = 1024
  
  // Integer and fractional parts
  Result := Int(3.7);       // 3.0
  Result := Frac(3.7);      // 0.7
  
  // Hyperbolic functions
  Result := Sinh(1.0);      // Hyperbolic sine
  Result := Cosh(1.0);      // Hyperbolic cosine
  Result := Tanh(1.0);      // Hyperbolic tangent
  Result := ArcSinh(0.5);   // Inverse hyperbolic sine
  Result := ArcCosh(1.5);   // Inverse hyperbolic cosine
  Result := ArcTanh(0.5);   // Inverse hyperbolic tangent
  
  // Angle calculations
  Result := ArcTan2(1.0, 1.0);  // Proper quadrant-aware arctangent
  
  // Mathematical constants
  Result := Pi;             // 3.14159265358979323846...
end.

Enhanced File I/O - Complete Delphi Compatibility

New file operations for complete Delphi text file compatibility:

var
  F: TextFile;
  Ch: Char;
  Line: String;
  ErrorCode: Integer;
begin
  Assign(F, 'data.txt');
  Reset(F);
  try
    // Character-by-character reading
    while not Eof(F) do
    begin
      Read(F, Ch);
      if Eoln(F) then
        WriteLn('End of line reached');
    end;
    
    // Skip whitespace and test for end
    Reset(F);
    SeekEof(F);   // Skip whitespace, check EOF
    SeekEoln(F);  // Skip whitespace, check EOL
    
    // Force write
    Flush(F);
    
    // Truncate at current position
    Truncate(F);
    
    Close(F);
    
    // File operations on closed file
    Erase(F);                    // Delete file
    Rename(F, 'newname.txt');    // Rename file
    
    // Check I/O errors
    ErrorCode := IOResult;
    if ErrorCode <> 0 then
      WriteLn('I/O Error: ', ErrorCode);
  except
    Close(F);
  end;
end.

Enhanced Memory Functions - Efficient Operations

Type-specific fill operations for better performance:

var
  ByteArray: array[0..99] of Byte;
  WordArray: array[0..99] of Word;
  DWordArray: array[0..99] of Cardinal;
  P: Pointer;
begin
  // Zero-filled allocation
  P := AllocMem(1024);  // Allocate and zero-fill
  try
    // Work with memory
  finally
    FreeMem(P);
  end;
  
  // Type-specific fills (more efficient than FillChar)
  FillByte(ByteArray, Length(ByteArray), $FF);
  FillWord(WordArray, Length(WordArray), $FFFF);
  FillDWord(DWordArray, Length(DWordArray), $FFFFFFFF);
end.

Code Generation Improvements - Better Debugging

Source position tracking in generated C++ code:

// Original Pascal code at line 42
WriteLn('Hello');

Generates:

#line 42 "myprogram.pas"
np::WriteLn(u"Hello");

Benefits:

  • Error messages reference original Pascal source lines
  • Debugger shows Pascal source positions
  • Stack traces map to Pascal code
  • Better development experience

💪 Why This Matters

v0.3.0 asked: "Is NitroPascal production-ready?"
v0.4.0 answers: "Yes, and here's proof!"

This release provides quantifiable evidence of real-world viability:

  • 📊 Benchmarks - Measured performance comparison against Delphi
  • 🎮 Raylib integration - Real library, static linked, single executable
  • 🗂️ Structured RTL - Professional organization for production use
  • 🔧 Function pointers - Essential pattern for frameworks and callbacks
  • 🚀 Complete toolkit - Everything needed for production applications

🎁 The Growing RTL

The Runtime Library continues expanding with professional organization:

Organized Modules:

  • Types - Base type declarations
  • SysUtils - System utilities
  • StrUtils - String utilities
  • DateUtils - Date/time functions (initial)

Key Features:

  • np::String - Complete Unicode string support
  • np::DynArray - Dynamic arrays
  • np::Set - Set operations
  • File I/O wrappers - Full Delphi compatibility
  • Exception support - Robust error handling
  • Math functions - Scientific computing
  • Memory management - Efficient operations

Everything maintains true Delphi semantics while generating optimal C++ code that compiles to native machine code via Zig's LLVM backend.

📚 Documentation

Complete feature tracking:

Check the docs to see exactly what you can build today!

🚀 Getting Started

# Clone the repository
git clone https://github.com/tinyBigGAMES/NitroPascal
cd NitroPascal

# Build the raylib example
cd bin\projects\raylib\core_basic_window
nitro build
nitro run

# Run the benchmark suite
cd bin\projects\NPBench
nitro build
nitro run

# Create your own project
cd bin\projects
nitro init myproject -t program
cd myproject
nitro build
nitro run

Example: Function pointer callback pattern:

program CallbackDemo;

type
  TProcessCallback = procedure(const Item: String);

procedure ProcessList(Items: array of String; Callback: TProcessCallback);
var
  I: Integer;
begin
  for I := 0 to High(Items) do
    Callback(Items[I]);
end;

procedure PrintItem(const Item: String);
begin
  WriteLn('Processing: ', Item);
end;

procedure CountItem(const Item: String);
begin
  Inc(GlobalCount);
  WriteLn('Item #', GlobalCount, ': ', Item);
end;

var
  Files: array of String;
  GlobalCount: Integer;
begin
  SetLength(Files, 3);
  Files[0] := 'file1.txt';
  Files[1] := 'file2.txt';
  Files[2] := 'file3.txt';
  
  GlobalCount := 0;
  
  WriteLn('Using PrintItem callback:');
  ProcessList(Files, @PrintItem);
  
  WriteLn('Using CountItem callback:');
  ProcessList(Files, @CountItem);
end.

This is production-ready callback handling. Framework integration. Event systems. Plugin architectures. All possible now.

🌐 Community

🔮 What's Next?

With function pointers, static linking, structured RTL, and proven performance, NitroPascal has the foundation for any application type. What do YOU want to build? Let us know in discussions!

🎊 The Bottom Line

NitroPascal v0.4.0 proves real-world viability.

Function pointers enable callbacks and frameworks. Static linking ships single executables. Structured RTL provides professional organization. Benchmarks show competitive performance. The raylib example demonstrates real library integration.

Build games. Build utilities. Build applications.


Write pure Delphi. Link static libraries. Get native performance. Ship everywhere. 🚀

File Integrity

Files are signed with minisign using this public key:
RWTqBYfsUOCQscb6ZeknLC0On3cvWCVzMzlHamtgXNaDOO4bNs3WCSkV