Documentation/Reference

Regular expressions

On this page

Regex is built into the BASIL toolchain and implemented in ordinary BASIL. No import, package declaration, regex .blib, external regex archive, or Rust runtime is required. Compile a reusable pattern for repeated work:

pattern=CompileRegex("(?P<name>\p{L}+)(?P<number>\d+)?")
IF LET found=RegexFind(pattern,"🌍Ada42") THEN
    PRINT found.Text
    PRINT found.Start       ' Unicode-scalar position: 1
    PRINT found.ByteStart   ' UTF-8 byte offset: 4
END IF
PRINT RegexIsMatch("^ada$","ADA",Flags="i")
PRINT RegexReplace("(?P<word>\w+)","hello world","${word}!")
PRINT Join(RegexSplit("[,;]","a,b;c")," | ")

API

Function Successful result
CompileRegex(pattern,Flags="",Dialect=RegexDialect.Native,Limits=RegexLimits {}) Owned, noncopyable Regex
RegexIsMatch(pattern,text) BOOLEAN
RegexFind(pattern,text) OPTIONAL OF RegexMatch; first leftmost match
RegexFindAll(pattern,text) LIST OF RegexMatch; nonoverlapping matches
RegexReplace(pattern,text,replacement,all=TRUE) Independent STRING; FALSE replaces first match
RegexSplit(pattern,text) LIST OF STRING; separators omitted, empty fields retained

Every operation accepts a compiled Regex or a STRING pattern. STRING overloads also accept Flags, Dialect and Limits; Replace places these after all. A compiled pattern retains its dialect, flags and limits. All overloads declare FAILS RegexError, including compiled-pattern operations: a checked work or output limit can fail during a search. Catch failures locally or declare them in the enclosing function’s contract; ordinary calls already perform checked error routing. An unhandled failure at the application entry reports and exits.

RegexMatch contains Start/Finish (half-open, zero-based Unicode-scalar offsets), ByteStart/ByteFinish (UTF-8 byte offsets), Text, Groups and Named. Groups is LIST OF OPTIONAL OF STRING and includes group0 for the whole match. Named is DICT OF STRING TO OPTIONAL OF STRING. An unmatched capture is NONE; a matched empty capture is SOME(""). Results own independent strings and collections and remain valid after the subject or pattern is destroyed.

Dialects

The default RegexDialect.Native follows the pinned Rust regex1.13.1 syntax and matching baseline with Unicode16 data: alternation, greedy/lazy/counted repeats, captures, named captures, Unicode properties and simple folding, nested class set operations and inline flags. Flags include i/m/s/u/x/U/R and scoped disabling. Lookaround and backreferences reject. Byte-oriented constructs that could match invalid UTF-8 reject; this is a STRING API, not Rust’s arbitrary-byte API.

RegexDialect.EcmaScript selects the ECMAScript2025 Unicode-mode pattern profile with Unicode16 properties. It supports backreferences and positive or negative lookahead/lookbehind, Unicode property escapes, surrogate-pair escapes, named captures, and disjoint-alternative duplicate names. Matching is stateless; Flags supports i/m/s/u, with Unicode mode always active. Flags d/g/v/y reject explicitly. JavaScript RegExp object state, v-mode string sets and isolated UTF-16 surrogates are outside the profile. BASIL subjects are valid UTF-8.

PRINT RegexIsMatch("^([a-z]+)-\1$","blue-blue",Dialect=RegexDialect.EcmaScript)
PRINT RegexIsMatch("^(?!admin$)[a-z]+$","user",Dialect=RegexDialect.EcmaScript)

Dialect selects pattern grammar/matching. Both dialects use BASIL’s Rust-style replacement expansion: $1, $name, ${name}, $$; missing/unmatched groups expand to empty text. Use braces to separate a name from following identifier characters. Split omits capture separators. These functions do not implement JavaScript String.replace/split conventions. Empty-match iteration advances on Unicode-scalar boundaries and suppresses an empty match immediately following a nonempty match at the same ending position. Normalization and locale collation are separate facilities.

Limits and typed failures

RegexLimits field Default
MaxWork 100,000,000
MaxCompileWork 10,000,000
MaxMemoryBytes 67,108,864
MaxPatternBytes 1,048,576
MaxInstructions 262,144
MaxDepth 256
MaxResults 1,000,000

All limits must be positive. Work is logical engine work, not milliseconds. Memory accounting conservatively covers estimated engine-owned program, scratch and output storage, including temporary construction; it is not a process RSS ceiling. Immutable shared Unicode tables are toolchain data. Compile work accumulates through the parser/compiler; search work resets once per public operation, including all candidate starts, matches and assertions. An optional DFA optimization stops within its own bounded quota and falls back to the complete NFA. Allocation failure retains BASIL’s fatal policy.

RegexError carries Code, Message and Offset (a UTF-8 pattern byte offset, or -1 when unavailable). RegexSyntaxError identifies invalid syntax; RegexUnsupportedError identifies unsupported profile features (Code3). RegexLimitError has Code2, Limit AS RegexLimitKind and Maximum. The enum contains Work, Memory, Pattern, Instructions, Depth and Results. Invalid configuration uses the RegexError base with Code4; internal invariant diagnostics use Code5. A limit failure never becomes FALSE, NONE, or a partial successful result.

TRY
    limits=RegexLimits {MaxWork=10000}
    PRINT RegexIsMatch("^(a+)+\1b$",Repeat("a",100),Dialect=RegexDialect.EcmaScript,Limits=limits)
CATCH exhausted AS RegexLimitError
    PRINT exhausted.Message
END TRY

Ownership and scheduling

Regex is an opaque resource. Ordinary parameters borrow it; explicit TAKE transfers a named owner. Cleanup releases it automatically. Matches and captures own independent data. Compilation and matching participate in normal scheduling and cancellation, including calls through compiled BASIL libraries. No background thread or application polling protocol is required. C-ABI callbacks retain the usual nonsuspending foreign-call rules.

From the BASIL documentation. View source ↗

Find your way around BASIL

Type to search the manual.