Pascal — Reference

Source: https://www.freepascal.org/docs.html

Pascal

  • Created: 1970 by Niklaus Wirth at ETH Zürich. Originally as a teaching language; Turbo Pascal (Borland, 1983) made it mainstream; Object Pascal / Delphi (Borland, 1995) industrialized it; Free Pascal Compiler (FPC) + Lazarus (open source) keep it alive today.
  • Latest stable: Free Pascal 3.2.2 (released 2021-05-20). 3.2.4 release candidate June 2025; trunk dev 3.3.1. Embarcadero Delphi 12 Athens is the leading commercial Object Pascal IDE/compiler.
  • Paradigms: Imperative, procedural, modular, object-oriented (Object Pascal), generic, with structured exception handling and (in Delphi/Free Pascal) anonymous methods + closures.
  • Typing: Strong, static, structural for arrays/records but nominal for declared types. Subrange types (type Hour = 0..23;) are first-class. Strict by default; FPC’s mode switches relax some rules.
  • Memory: No GC for the compiler-managed code. Manual New/Dispose, automatic stack for value types. Reference-counted for managed types (AnsiString/UnicodeString, dynamic arrays, interfaces). try..finally for deterministic cleanup; ARC mode optional in mobile/Linux Delphi (deprecated in newer versions).
  • Compilation: Ahead-of-time native code, no VM/runtime. Free Pascal Compiler (FPC): Linux/Win/macOS/BSD, ARM/AArch64/RISC-V/MIPS/AVR/PowerPC/SPARC/Z80, even WebAssembly + JVM backends. Embarcadero Delphi: Windows/macOS/iOS/Android/Linux x86_64. GNU Pascal (gpc) is unmaintained.
  • Primary domains: Cross-platform desktop (Lazarus apps), embedded (FPC ARM/AVR), scientific/engineering, retail point-of-sale (legacy Delphi), older Windows business apps, education.
  • Official docs: https://www.freepascal.org/docs.html (FPC), https://wiki.lazarus.freepascal.org/ (Lazarus IDE), https://docwiki.embarcadero.com/RADStudio/ (Delphi).

At a glance

Pascal split into two living branches: standardized Pascal (ISO 7185:1990 / ISO 10206 Extended Pascal — both effectively dead in industry but supported in mode iso for academic work) and Object Pascal, which is what people use. Object Pascal is what runs in Delphi and what FPC’s default mode objfpc and mode delphi accept. The FPC + Lazarus stack delivers a true write-once compile-everywhere native desktop GUI experience that’s hard to match — one Lazarus codebase compiles to Win32/Win64, Linux GTK/Qt, macOS Cocoa, and ARM with no source changes for typical CRUD apps.

Getting started

Install FPC + Lazarus:

  • Easy path: install Lazarus which bundles FPC. Windows: installer from https://www.lazarus-ide.org/. macOS: brew install --cask lazarus or DMG. Linux: sudo apt install lazarus (often older; better to use fpcupdeluxe).
  • fpcupdeluxe: GUI tool to install/maintain multiple FPC + Lazarus versions side-by-side; the de-facto version manager. https://github.com/LongDirtyAnimAlf/fpcupdeluxe.
  • Just FPC: sudo apt install fpc then fpc --version.

Hello world (hello.pas):

program Hello;
{$mode objfpc}{$H+}
begin
  WriteLn('Hello, world!');
end.
  • {$mode objfpc} selects Free Pascal’s Object Pascal dialect; alternatives: delphi, tp (Turbo Pascal 7), iso, mac, extpas (ISO Extended Pascal).
  • {$H+} makes string map to AnsiString/UnicodeString instead of fixed ShortString.

Compile + run: fpc hello.pas && ./hello (Unix) or fpc hello.pas && hello.exe (Windows).

Project layout (Lazarus app):

myapp/
  myapp.lpi        -- Lazarus project info (XML)
  myapp.lpr        -- main program file
  unit1.pas        -- form unit (code)
  unit1.lfm        -- form layout (Lazarus Form Module)

Lazarus generates and manages .lpi/.lfm. Build via IDE or lazbuild myapp.lpi.

Package/build tool. fpmake (Pascal-based, like Rake) — declarative build via fpmake.pp. fppkg for online package fetching (https://gitlab.com/freepascal.org/fpc/source). For Lazarus, OPM (Online Package Manager) ships in the IDE; many community packages on https://wiki.lazarus.freepascal.org/Online_Package_Manager.

REPL. None native. Some workaround tools (e.g. InstantFPC lets you run .pas files like scripts: instantfpc script.pas).

Basics

Types & literals. Numbers: Integer (native size: 32 or 64), Int8/Int16/Int32/Int64/UInt8UInt64, Single (32-bit float), Double (64-bit), Extended (80-bit on x86). Booleans: Boolean/ByteBool/WordBool. Characters: Char (8-bit AnsiChar), WideChar (UTF-16 unit), UnicodeChar. Strings: ShortString (max 255 bytes, length in byte 0), AnsiString (heap, ref-counted, code-page aware), UnicodeString (heap, ref-counted, UTF-16), WideString (Windows BSTR). Literals: 42, $FF (hex), &777 (octal), %1010 (binary), 1.0e10, 'hello', #13#10 (concatenated char literals = CRLF).

Variables/scoping. Declared in var blocks, scoped to the enclosing routine/unit. Constants: const Pi = 3.14; (typed: const X: Integer = 5;). Subrange types: type Hour = 0..23;. Enums: type Color = (Red, Green, Blue); — first-class with Ord(Red) = 0, Inc(C), etc.

Control flow. if cond then ... else ...;, case x of 1: ...; 2,3: ...; else ... end;, while cond do ...;, repeat ... until cond;, for i := 1 to N do ...; / for i := N downto 1 do ...;, for x in collection do ...; (since FPC 2.4 / Delphi 2005). break;, continue;, exit; (from procedure), exit(value); (return from function, Delphi-style).

Procedures & functions. procedure P(A: Integer; var B: string); and function F(X: Integer): Integer;. Parameter modes: by-value (default), var (by-reference, mutable), const (by-reference, read-only — compiler may inline), out (write-only, no init). Function result via Result := value; (mode objfpc/delphi) or F := value; (classic). Default parameter values: procedure P(A: Integer = 0);. Overloading: mark with overload directive.

Strings. Three live string types: ShortString (legacy, fixed max 255), AnsiString (preferred for byte strings), UnicodeString (preferred for text — UTF-16 internally, default string in Delphi 2009+). Length(s), Copy(s, start, len), Pos(sub, s), s := 'a' + 'b';. Indexing is 1-based: s[1] is the first char. With {$H+} directive, string is AnsiString/UnicodeString per platform default.

Collections. Static arrays: var A: array[1..10] of Integer;. Dynamic arrays: var A: array of Integer; SetLength(A, 100); — heap-allocated, ref-counted, 0-indexed (A[0]A[99]). Records: type TPoint = record X, Y: Integer; end;. Variant records (tagged unions): record case Tag: Integer of 0: (Int: Integer); 1: (Str: ShortString); end;. Sets: type TFlags = set of (fA, fB, fC);, then s := [fA, fC]; if fA in s then .... Generic containers (since 2009-ish): TList<T>, TDictionary<K,V> from Generics.Collections.

Intermediate

Type system depth. Object Pascal classes (type TFoo = class ... end) are heap-allocated, single-inheritance, with multiple-inheritance-of-interface via IFoo = interface. Methods marked virtual are dispatched dynamically; override with override; mark final with final. Properties with read/write accessors: property Count: Integer read FCount write SetCount;. Class methods (callable on the class itself): class procedure CreateAll;. Class variables (shared): class var FInstance: TFoo;. Generics (Delphi 2009+, FPC 2.4+): type TList<T> = class ... end;. Anonymous methods + closures: var Add: TFunc<Integer, Integer> := function(X: Integer): Integer begin Result := X + 1; end; (Delphi 2009+, FPC has {$modeswitch functionreferences}).

Modules. Units are the modularity primitive. Each .pas is a unit:

unit MyUnit;
interface
  uses OtherUnit;
  type TFoo = class ... end;
  procedure DoSomething;
implementation
  uses HelperUnit;
  procedure DoSomething; begin ... end;
initialization
  // run at program start in dependency order
finalization
  // run at program shutdown in reverse order
end.

The interface section is the public API; implementation is private. initialization/finalization blocks let units do startup/shutdown work safely.

Error handling. Structured exceptions: try ... except on E: Exception do ... end; and try ... finally ... end;. Hierarchy rooted at Exception. raise EMyError.Create('msg');. raise; (bare) re-raises. Standard exceptions in SysUtils.

Concurrency primitives. TThread class wraps OS threads (Execute method overrides). Synchronization: TCriticalSection, TRTLCriticalSection, TMonitor (Delphi-style, Monitor.Enter(obj)), TMultiReadExclusiveWriteSynchronizer. TThreadPool for work queues. Parallel Programming Library (PPL) in Delphi: TParallel.&For, TTask.Run. Async/Await in modern Delphi via PPL futures. FPC has MTProcs for OpenMP-like parallel loops. Atomic ops in SyncObjs (InterlockedIncrement).

I/O. Classic Pascal: Assign(F, 'name'); Reset(F); ReadLn(F, x); CloseFile(F);. Modern: TFileStream, TStringList.LoadFromFile, TStreamReader. The VCL (Delphi) and LCL (Lazarus) provide TFile/TPath/TDirectory (Delphi IOUtils). Network: Indy (IdHTTP, IdTCPClient), Synapse, lNet. JSON: JsonTools, fpjson, System.JSON.

Stdlib highlights. RTL (Run-Time Library): SysUtils (strings, dates, files, exceptions), Classes (TStringList, TStream, TList, TComponent), Math, DateUtils, StrUtils, Variants, TypInfo (RTTI), Generics.Collections. FCL (Free Component Library): fpjson, fpxml, database connectors, web (fphttpserver), images. LCL (Lazarus Component Library): widget set abstraction (GTK2/3, Qt5/6, Cocoa, Win32, Carbon).

Advanced

Memory model. Class instances are heap pointers under the hood; var f: TFoo; is a reference, f := TFoo.Create; allocates, f.Free; deallocates (idiomatic: try ... finally f.Free; end; or FreeAndNil(f)). Records are value types, copied on assignment. Strings, dynamic arrays, interfaces are reference-counted with copy-on-write — assigning is cheap, mutating triggers a copy if refcount > 1. GetMem/FreeMem for raw heap. Custom memory managers via SetMemoryManager (you can swap in FastMM5, NexusMM, ScaleMM for performance).

Concurrency deep dive. TThread is the building block; you almost always wrap with a queue/pool. TThread.Synchronize and TThread.Queue marshal back to the main thread for GUI updates (this is mandatory — VCL/LCL are not thread-safe). PPL (TTask.Future, TTask.WaitForAll) is the modern Delphi pattern. MTProcs (FPC) for ProcThreadPool.DoParallelLocalProc. Anonymous threads via TThread.CreateAnonymousThread(procedure begin ... end).Start;. Lock-free atomics in SyncObjs. No language-level async/await — closures + futures are how it’s done.

FFI. Direct C interop via external directive:

function strlen(s: PChar): NativeUInt; cdecl; external 'libc' name 'strlen';

Calling conventions: cdecl, stdcall, register (FPC default — fastcall-like), pascal, safecall, ms_abi_default. Pointers: PInteger, PChar, Pointer, ^Integer. C strings: PAnsiChar/PWideChar. Header translation tools: h2pas, C-to-Pascal converters. COM/ActiveX on Windows via interfaces with {$M+} and IDispatch. Java/Android via JNI bindings (FPC’s Android target).

Reflection (RTTI). TypInfo unit + Rtti.pas (Delphi 2010+ / FPC 3.0+) provides TRttiContext, TRttiType, TRttiMethod, TRttiProperty — full enumeration and invocation. Compile classes with {$M+} (or inherit from TPersistent) to get extended RTTI. Used by JSON serializers, ORM (mORMot, TMS Aurelius), DI containers.

Performance tools. AQTime (commercial), Sampling Profiler (free, Delphi), valgrind/callgrind on Linux FPC binaries, perf for native, VTune if you go x86. FastMM4/5 for memory leak detection (replace memory manager, get full leak reports at shutdown). Lazarus IDE has built-in heap-trc backed leak reporting (-gh switch).

God mode

Object Pascal class system. Every class descends from TObject (RTL) or TInterfacedObject (for ref-counted COM-style objects). Class-of types: class of TFoo is a metaclass — var C: TPersistentClass; C := TStringList; obj := C.Create; lets you instantiate by class reference. Used heavily in VCL’s component streaming (form files store class names, IDE resolves via class registry).

Properties + RTTI + streaming. A published property is enumerable via RTTI. TStream.WriteComponent serializes any TComponent by walking published properties — that’s how .dfm/.lfm form files work. You can write custom property editors that hook the IDE.

Inline assembly. Both FPC and Delphi support asm ... end; blocks with platform-specific assembly:

function Add(a, b: Integer): Integer; assembler;
asm
  mov eax, a
  add eax, b
end;

Useful for SIMD intrinsics that the compiler hasn’t wrapped, or hand-tuned hot paths. FPC supports AT&T syntax via {$asmmode att}.

{$mode} and {$modeswitch} directives. {$mode delphi} enables Delphi-compatibility (operator overloading semantics, Result, exceptions on/off, etc.); {$mode objfpc} is FPC’s preferred Object Pascal mode. Modeswitches layer features: {$modeswitch advancedrecords} lets records have methods + properties; {$modeswitch functionreferences} enables anonymous-method types; {$modeswitch nestedprocvars} allows nested-procedure variables. Pin them at the top of every .pas you write.

Cross-compilation. FPC’s killer feature. fpc -Tlinux -Parm -CpARMV7A myapp.pas compiles a Linux ARM binary on a Windows x86_64 host (you need the matching cross-binutils). Targets include AVR microcontrollers, RISC-V, embedded ARM Cortex-M (with no OS), WebAssembly (-Twasi/-Twasm32), JVM bytecode (limited). fpcupdeluxe automates installing cross-toolchains.

Lazarus + LCL widgetset abstraction. TForm, TButton, etc. are interfaces; the LCL has multiple widgetset backends (win32, gtk2, gtk3, qt5, qt6, cocoa, carbon, customdrawn). Same source, recompile for the target widgetset. Performance is native (no Electron tax). Conditional defines ({$IFDEF LCLGTK2}) for backend-specific tweaks.

Custom packages with FPMake. fpmake.pp is a Pascal program that constructs a build description. Compile it (fpc fpmake.pp), run ./fpmake build install. Integrates with fppkg for installing/distributing packages from the central repo.

Delphi-specific god-mode. FireMonkey (FMX) GPU-accelerated cross-platform UI; LiveBindings expression engine; RTTI-driven property serialization; the form designer is itself a Pascal program editing other Pascal programs. {$EXTERNALSYM} and {$HPPEMIT} directives generate matching C++ headers for C++Builder consumption.

Idioms & style

  • Pin your {$mode} and {$H+} at the top of every unit. Mode determines language semantics; rely on no defaults.
  • Naming: Types prefixed with T (TPoint, TStringList); interfaces with I (IDisposable); pointers with P (PInteger). Methods/identifiers in PascalCase. Field names start with F (FCount). Enum members get a 2-letter prefix tied to the type (fkBold, fkItalic for a TFontKind).
  • try ... finally Free; end; around every manually-allocated object — non-negotiable.
  • FreeAndNil(obj) instead of obj.Free when the reference might be re-checked.
  • Strings are 1-indexed (s[1] is first char), dynamic arrays are 0-indexed (a[0]). Yes, both. Don’t slip up.
  • Use const for read-only reference parameters (const s: string) — avoids refcount churn.
  • Formatter/linter: JCF (Jedi Code Format) — built into Lazarus IDE. PtopX (older). Delphi has Delphi Formatter in the IDE. PasPascal + PascalAnalyzer for static analysis. ptop is FPC’s own pretty-printer.
  • Expert review focus: (1) Every Create paired with a Free in try..finally? (2) String types consistent ({$H+} set; not mixing ShortString and AnsiString)? (3) Ref-counted leaks via interface-circular references? (4) Thread access to TStringList/VCL/LCL only via Synchronize/Queue? (5) {$mode} and {$modeswitch} declared per file — no implicit defaults? (6) class procedure Free; not called on a nil instance (yes, nil.Free is a no-op in Object Pascal — surprisingly safe)? (7) Pointer arithmetic only with {$POINTERMATH ON}?

Ecosystem

  • GUI frameworks: VCL (Delphi, Windows-only — the classic), FMX/FireMonkey (Delphi, cross-platform GPU UI), LCL (Lazarus, cross-platform native widget abstraction).
  • Web: Brook, mORMot 2 (also a full SOA / ORM / database stack), Pas2JS (Pascal-to-JavaScript transpiler), fphttpserver (FCL).
  • Database: mORMot 2, TMS Aurelius (ORM), ZeosLib (multi-DB), SQLdb (FCL), FireDAC (Delphi).
  • Game dev: Castle Game Engine (3D, Pascal-native), SDL bindings.
  • Embedded: mikroPascal (commercial, MCU-focused), FPC AVR/ARM (open source, bare-metal). Lazarus/FPC build firmware for STM32, ESP32 (limited), AVR.
  • Testing: fpcunit (xUnit-style, ships with FPC), DUnit / DUnitX (Delphi).
  • Notable users: Skype (original Windows client was Delphi), Total Commander, Inno Setup (the installer maker — itself in Object Pascal), Beyond Compare, Embarcadero Delphi IDE itself (Delphi is written in Delphi), Lazarus IDE (in Lazarus/FPC), WinRAR’s GUI, R Studio Data Recovery, much enterprise CRUD in finance/insurance.

Gotchas

  • string type changes meaning by mode + directive. With {$H+} it’s AnsiString or UnicodeString depending on platform/dialect; without, it’s ShortString (max 255 bytes). Mixed code corrupts data. Pin {$H+} everywhere.
  • String indexing is 1-based; dynamic array indexing is 0-based. Mixing them up is the #1 off-by-one source.
  • AnsiString is platform-dependent UTF-8 in Lazarus, system codepage in Delphi. Cross-platform code should prefer UnicodeString (UTF-16) or explicitly UTF-8 (UTF8String).
  • No null-safety. A TFoo reference can be nil; calling a method on nil faults. nil.Free is the lone exception (safe by design in TObject.Free).
  • Global vars in units initialize in dependency order (per uses), but circular uses are detected only between interface sections. Move circular dependencies to implementation uses.
  • var parameter sharing. Passing the same variable to two var parameters of one call is undefined. Compiler doesn’t always warn.
  • Operator overloading semantics differ between modes. mode delphi uses class operators; mode objfpc uses unit-level operator declarations. Code isn’t portable across modes without rewriting.
  • TStringList.Sorted := True; silently sorts in place but also changes lookup semantics to binary search. Setting back to False doesn’t restore order. Hot bug source.
  • Reference cycles in interfaces. IFoo ref-counts; two interfaces holding each other never get freed. Break cycles with weak references ([weak] attribute, Delphi mobile/Linux only) or Pointer casts manually.
  • Form files (.dfm/.lfm) and class names must match. Renaming a form class without re-saving the form file breaks loading at runtime, not compile time.
  • Result shadows local Result only in mode delphi/objfpc. In classic Pascal, you assign to the function name (MyFunc := value). Mixing styles is confusing.

Citations