pcre2 clib Text / Regular Expressions

PCRE2 Perl-compatible regular expressions for PascalAI. Named groups, lookahead/behind, non-greedy quantifiers, Unicode property escapes, JIT compilation, atomic groups, and recursive patterns — the full Perl 5 regex engine as a native library call.

ppm install pcre2

Overview

pcre2 wraps libpcre2 — the industry-standard Perl-compatible regular expression library used by PHP, Python, R, Julia, and many others. It provides the complete Perl 5 regex syntax including features that are deliberately excluded from simpler engines like Go's RE2 or POSIX regex.

Compile a pattern once with Compile or CompileEx, then call Match / MatchAll / Replace / Split as many times as needed. For hot loops, call JITCompile after compilation to get a 10–100× speed boost from the PCRE2 JIT engine.

CLib package

Requires libpcre2-dev. Ubuntu/Debian: sudo apt install libpcre2-dev

Key Features

FeatureSyntaxDescription
Named groups(?P<name>...) or (?<name>...)Access captures by name with GetNamedGroup
Lookahead(?=...) / (?!...)Positive and negative lookahead assertions
Lookbehind(?<=...) / (?<!...)Positive and negative lookbehind assertions
Non-greedy*? +? ??Lazy quantifiers; or flip all with Ungreedy flag
Unicode\p{Lu} \p{Script=Latin}Full UTF-8 and Unicode property escapes with Unicode+UnicodeProps
JIT compilationJITCompile(R)10–100× faster repeated matching via native code generation
Atomic groups(?>...)Prevent backtracking into a subexpression once it has matched
Recursion(?R) (?1)Match nested structures such as balanced parentheses

Comparison

EngineLookahead/behindNamed groupsJITUnicode propsNotes
pcre2YesYesYesYesFull Perl 5 syntax
Go regexp (RE2)NoYesNoPartialGuaranteed linear time; no backtracking
Python reYesYesNoYesSame syntax; PCRE2 JIT faster for hot paths
POSIX (ERE)NoNoNoNoAvailable everywhere; far more limited

Quick Start

Compile and match

uses pcre2;

{ Compile once, reuse many times }
var R := Compile('\b\d{4}-\d{2}-\d{2}\b');

var mr := Match(R, 'Invoice date: 2024-07-15, due 2024-08-01');
if mr.Matched then
  WriteLn('First date: ' + mr.FullMatch);

{ Find all dates }
var all := MatchAll(R, 'Invoice date: 2024-07-15, due 2024-08-01');
for var i := 0 to Length(all) - 1 do
  WriteLn(all[i].FullMatch);

FreeRegex(R);

Named capture groups

uses pcre2;

var R := Compile('(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})');
var mr := Match(R, 'Published: 2025-11-03');

if mr.Matched then
begin
  WriteLn('Year : '  + GetNamedGroup(mr, 'year'));
  WriteLn('Month: ' + GetNamedGroup(mr, 'month'));
  WriteLn('Day  : '  + GetNamedGroup(mr, 'day'));
end;

FreeRegex(R);

Replace all matches

uses pcre2;

{ Redact credit card numbers — use $1 backreference to keep prefix }
var R := Compile('\b(\d{4})[- ]?\d{4}[- ]?\d{4}[- ]?(\d{4})\b');
var result := ReplaceAll(R, 'Card: 4111 1111 1111 1234',
                              '$1-****-****-$2');
WriteLn(result);  { Card: 4111-****-****-1234 }

FreeRegex(R);

JIT compilation for hot paths

uses pcre2;

if JITSupported then
begin
  var R := Compile('^\s*#|^\s*$');  { skip comments and blank lines }
  JITCompile(R);  { one-time JIT compilation }

  var lines := Split(R, logText, 0);  { now runs at native speed }
  WriteLn(IntToStr(Length(lines)) + ' non-comment lines');

  FreeRegex(R);
end;

Types

TPCRE2Regex

type TPCRE2Regex = Integer;  { opaque handle to a compiled pattern }

An opaque integer handle returned by Compile or CompileEx. Pass it to any matching or pattern-info function. Free with FreeRegex when done.

TPCRE2Match

type TPCRE2Match = Integer;  { opaque handle to a match data block }

Low-level match data block handle. Returned internally; most callers work with TPCRE2MatchResult directly.

TPCRE2Options

type
  TPCRE2Options = record
    CaseInsensitive : Boolean;  { PCRE2_CASELESS }
    Multiline       : Boolean;  { PCRE2_MULTILINE — ^ and $ match line boundaries }
    DotAll          : Boolean;  { PCRE2_DOTALL — dot matches newline }
    Extended        : Boolean;  { PCRE2_EXTENDED — ignore whitespace + # comments }
    Unicode         : Boolean;  { PCRE2_UTF — pattern and subject are UTF-8 }
    UnicodeProps    : Boolean;  { PCRE2_UCP — \d \w \s use Unicode properties }
    Ungreedy        : Boolean;  { PCRE2_UNGREEDY — swap greedy/lazy by default }
    UseJIT          : Boolean;  { compile with JIT }
  end;

TPCRE2CaptureGroup

type
  TPCRE2CaptureGroup = record
    Name   : string;   { empty if the group is unnamed }
    Index  : Integer;  { group number (1-based; 0 = full match) }
    Start  : Integer;  { byte offset of the group in the subject string }
    Length : Integer;  { byte length of the matched text }
    Value  : string;   { matched text }
  end;

TPCRE2MatchResult

type
  TPCRE2MatchResult = record
    Matched   : Boolean;                    { True if the pattern matched }
    FullMatch : string;                     { the entire matched substring }
    Start     : Integer;                    { byte offset of match start in subject }
    Length    : Integer;                    { byte length of the full match }
    Groups    : array of TPCRE2CaptureGroup; { all capture groups }
  end;

Compile API

Compile a pattern once and reuse the handle across many calls. Compiling is expensive; matching is fast.

FunctionDescription
Compile(Pattern)Compile Pattern with default options. Returns a TPCRE2Regex handle.
CompileEx(Pattern, Opts)Compile with a TPCRE2Options record for full control over flags.
FreeRegex(R)Release all memory held by the compiled pattern.
JITCompile(R)JIT-compile the already-compiled pattern. Subsequent matches run as native machine code. Check JITSupported first.

Matching API

All match functions return a TPCRE2MatchResult. Check .Matched before accessing .FullMatch or .Groups.

FunctionDescription
IsMatch(R, Subject)Returns True if the pattern matches anywhere in Subject. Fastest check when you don't need captures.
Match(R, Subject)Find the first match and return a full TPCRE2MatchResult with all capture groups.
MatchAt(R, Subject, Offset)Same as Match but start searching at byte Offset in the subject.
MatchAll(R, Subject)Return all non-overlapping matches as an array of TPCRE2MatchResult.

Capture Groups API

After a successful Match, retrieve individual groups by name or index.

FunctionDescription
GetNamedGroup(MR, Name)Return the value of the named capture group Name. Returns empty string if the group did not participate in the match.
GetGroup(MR, Index)Return the value of capture group by number. Index 0 is the full match; 1+ are groups in left-to-right order.
uses pcre2;

var R  := Compile('(?P<proto>https?)://(?P<host>[^/]+)(?P<path>/\S*)?');
var mr := Match(R, 'Visit https://example.com/docs for details');

if mr.Matched then
begin
  WriteLn(GetNamedGroup(mr, 'proto'));  { https }
  WriteLn(GetNamedGroup(mr, 'host'));   { example.com }
  WriteLn(GetNamedGroup(mr, 'path'));   { /docs }
  WriteLn(GetGroup(mr, 0));             { full match }
end;

FreeRegex(R);

Replace API

Replacement strings support backreferences: $1, $2 by index, or ${name} for named groups.

FunctionDescription
Replace(R, Subject, Replacement)Replace the first match. Returns the modified string.
ReplaceAll(R, Subject, Replacement)Replace all non-overlapping matches. Returns the fully substituted string.
uses pcre2;

{ Normalize whitespace sequences to a single space }
var R := Compile('\s+');
WriteLn(ReplaceAll(R, '  foo   bar  baz  ', ' '));  { ' foo bar baz ' }

{ Named backreference in replacement }
var R2 := Compile('(?P<last>\w+),\s*(?P<first>\w+)');
WriteLn(ReplaceAll(R2, 'Smith, John', '${first} ${last}'));  { John Smith }

FreeRegex(R);
FreeRegex(R2);

Split API

FunctionDescription
Split(R, Subject, Limit)Split Subject at every match of R. Limit=0 means unlimited splits; Limit=N produces at most N parts.
uses pcre2;

{ Split on any run of whitespace or commas }
var R    := Compile('[\s,]+');
var tags := Split(R, 'alpha, beta  gamma,delta', 0);
for var i := 0 to Length(tags) - 1 do
  WriteLn(tags[i]);  { alpha / beta / gamma / delta }

FreeRegex(R);

One-Shot Helpers

Convenience functions that compile, execute, and free the pattern in one call. Convenient for ad-hoc use; prefer pre-compiled patterns in loops.

FunctionDescription
QuickMatch(Pattern, Subject)Compile, match first occurrence, free. Returns TPCRE2MatchResult.
QuickTest(Pattern, Subject)Compile, test for any match, free. Returns Boolean.
QuickReplace(Pattern, Subject, Replacement)Compile, replace all matches, free. Returns result string.
uses pcre2;

{ One-liner email check }
if QuickTest('^[\w.+-]+@[\w-]+\.[a-z]{2,}$', email) then
  WriteLn('Valid email');

{ One-liner replace }
var slug := QuickReplace('[^a-z0-9]+',
                          LowerCase(title), '-');

Pattern Info API

FunctionDescription
CaptureCount(R)Returns the number of capture groups in the compiled pattern.
NamedGroups(R)Returns the names of all named capture groups as array of string.
JITSupportedReturns True if the PCRE2 JIT engine is available on this platform.
PCRE2VersionReturns the version string of the linked libpcre2, e.g. "10.42 2022-12-11".
uses pcre2;

var R := Compile('(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})');
WriteLn('Groups : ' + IntToStr(CaptureCount(R)));  { 3 }
var names := NamedGroups(R);
for var i := 0 to Length(names) - 1 do
  WriteLn(names[i]);  { year / month / day }
WriteLn('PCRE2 : ' + PCRE2Version);
FreeRegex(R);
Tip — compile once, match many

Calling Compile parses the pattern and builds an internal NFA/DFA structure. For any pattern used more than once — including inside loops — compile before the loop and free after. The Quick* helpers re-compile every call and are only suitable for infrequent use.

Package Info

Version1.0.0
Typeclib
C Librarylibpcre2-dev
Authorgustavo

System Requirements

libpcre2-dev

Ubuntu/Debian:
sudo apt install libpcre2-dev

TPCRE2Options Flags

CaseInsensitive — PCRE2_CASELESS
Multiline — PCRE2_MULTILINE
DotAll — PCRE2_DOTALL
Extended — PCRE2_EXTENDED
Unicode — PCRE2_UTF
UnicodeProps — PCRE2_UCP
Ungreedy — PCRE2_UNGREEDY
UseJIT — JIT on compile

Functions

  • Compile
  • CompileEx
  • FreeRegex
  • JITCompile
  • IsMatch
  • Match
  • MatchAt
  • MatchAll
  • GetNamedGroup
  • GetGroup
  • Replace
  • ReplaceAll
  • Split
  • QuickMatch
  • QuickTest
  • QuickReplace
  • CaptureCount
  • NamedGroups
  • JITSupported
  • PCRE2Version

See Also