1365 lines
47 KiB
ObjectPascal
1365 lines
47 KiB
ObjectPascal
{******************************************************************************}
|
|
{ }
|
|
{ Library: Fundamentals 5.00 }
|
|
{ File name: flcStringPatternMatcher.pas }
|
|
{ File version: 5.06 }
|
|
{ Description: String pattern matchers. }
|
|
{ }
|
|
{ Copyright: Copyright (c) 2000-2018, David J Butler }
|
|
{ All rights reserved. }
|
|
{ Redistribution and use in source and binary forms, with }
|
|
{ or without modification, are permitted provided that }
|
|
{ the following conditions are met: }
|
|
{ Redistributions of source code must retain the above }
|
|
{ copyright notice, this list of conditions and the }
|
|
{ following disclaimer. }
|
|
{ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND }
|
|
{ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED }
|
|
{ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED }
|
|
{ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A }
|
|
{ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL }
|
|
{ THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, }
|
|
{ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR }
|
|
{ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
|
|
{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF }
|
|
{ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) }
|
|
{ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER }
|
|
{ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING }
|
|
{ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE }
|
|
{ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE }
|
|
{ POSSIBILITY OF SUCH DAMAGE. }
|
|
{ }
|
|
{ Github: https://github.com/fundamentalslib }
|
|
{ E-mail: fundamentals.library at gmail.com }
|
|
{ }
|
|
{ Revision history: }
|
|
{ }
|
|
{ 2000/09/04 1.01 Added MatchFileMask. }
|
|
{ 2001/05/13 1.02 Added simple regular expression matching. }
|
|
{ 2002/02/14 2.03 Added MatchPattern. }
|
|
{ 2016/04/13 5.04 Change pattern match functions to RawByteString. }
|
|
{ 2017/10/07 5.05 Move from flcStrings unit. }
|
|
{ 2018/08/12 5.06 String type changes. }
|
|
{ }
|
|
{ Supported compilers: }
|
|
{ }
|
|
{ Delphi 10 Win32 5.05 2016/01/09 }
|
|
{ Delphi 10 Win64 5.05 2016/01/09 }
|
|
{ }
|
|
{******************************************************************************}
|
|
|
|
{$INCLUDE ..\flcInclude.inc}
|
|
|
|
{$IFDEF FREEPASCAL}
|
|
{$WARNINGS OFF}
|
|
{$HINTS OFF}
|
|
{$ENDIF}
|
|
|
|
{$IFDEF DEBUG}
|
|
{$IFDEF TEST}
|
|
{$DEFINE STRING_PAT_MATCH_TEST}
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
unit flcStringPatternMatcher;
|
|
|
|
interface
|
|
|
|
uses
|
|
{ Fundamentals }
|
|
flcStdTypes;
|
|
|
|
|
|
|
|
{ }
|
|
{ Pattern Matcher }
|
|
{ }
|
|
{ Matches a subset of regular expressions (* . ? and []) }
|
|
{ '*' Matches zero or more of any character }
|
|
{ '.' Matches exactly one character }
|
|
{ '?' Matches zero or one character }
|
|
{ '[' <char set> ']' Matches character from <char set> }
|
|
{ '[^' <char set> ']' Matches character not in <char set> }
|
|
{ <char set> can include multiple ranges and escaped characters }
|
|
{ '\n' matches NewLine (#10) }
|
|
{ '\r' matches Return (#13) }
|
|
{ '\\' matches a slash ('\') }
|
|
{ StrZMatchPattern returns the number of matched characters, }
|
|
{ or < 0 if no match. }
|
|
{ StrPosPattern returns the index of matched pattern (F) in string S, }
|
|
{ or 0 if not found. Len is the length of the matched pattern. }
|
|
{ }
|
|
type
|
|
TMatchPatternGreed = (
|
|
mpgLazy,
|
|
mpgGreedy,
|
|
mpgGreedyNoBacktrack);
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
function StrZMatchPatternA(M, S: PAnsiChar; const G: TMatchPatternGreed = mpgLazy): Integer;
|
|
{$ENDIF}
|
|
function StrZMatchPatternW(M, S: PWideChar; const G: TMatchPatternGreed = mpgLazy): Integer;
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
function StrEqualPatternA(const M, S: RawByteString; const G: TMatchPatternGreed = mpgLazy): Boolean;
|
|
{$ENDIF}
|
|
function StrEqualPatternU(const M, S: UnicodeString; const G: TMatchPatternGreed = mpgLazy): Boolean;
|
|
function StrEqualPattern(const M, S: String; const G: TMatchPatternGreed = mpgLazy): Boolean;
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
function StrPosPatternA(const F, S: RawByteString; var Len: Integer;
|
|
const StartIndex: Integer = 1; const G: TMatchPatternGreed = mpgLazy): Integer;
|
|
{$ENDIF}
|
|
function StrPosPatternU(const F, S: UnicodeString; var Len: Integer;
|
|
const StartIndex: Integer = 1; const G: TMatchPatternGreed = mpgLazy): Integer;
|
|
|
|
|
|
|
|
{ }
|
|
{ File Mask Matcher }
|
|
{ }
|
|
{ Matches classic file mask type regular expressions. }
|
|
{ ? = matches one character (or zero if at end of mask) }
|
|
{ * = matches zero or more characters }
|
|
{ }
|
|
{$IFDEF SupportAnsiString}
|
|
function MatchFileMaskB(const Mask, Key: RawByteString;
|
|
const AsciiCaseSensitive: Boolean = False): Boolean;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ Fast abbreviated regular expression matcher }
|
|
{ }
|
|
{ Matches regular expressions of the form: (<charset><quant>)* }
|
|
{ where <charset> is a character set and <quant> is one of the quantifiers }
|
|
{ (mnOnce, mnOptional = ?, mnAny = *, mnLeastOnce = +). }
|
|
{ }
|
|
{ Supports deterministic/non-deterministic, greedy/non-greedy matching. }
|
|
{ Returns first MatchPos (as opposed to longest). }
|
|
{ Uses an NFA (Non-deterministic Finite Automata). }
|
|
{ }
|
|
{ For example: }
|
|
{ I := 1 }
|
|
{ S := 'a123' }
|
|
{ MatchQuantSeq(I, [['a'..'z'], ['0'..9']], [mqOnce, mqAny], S) = True }
|
|
{ }
|
|
{ is the same as matching the regular expression [a-z][0-9]* }
|
|
{ }
|
|
type
|
|
TMatchQuantifier = (
|
|
mqOnce,
|
|
mqAny,
|
|
mqLeastOnce,
|
|
mqOptional);
|
|
TMatchQuantSeqOptions = Set of (
|
|
moDeterministic,
|
|
moNonGreedy);
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
function MatchQuantSeqB(var MatchPos: Integer;
|
|
const MatchSeq: array of ByteCharSet; const Quant: array of TMatchQuantifier;
|
|
const S: RawByteString; const MatchOptions: TMatchQuantSeqOptions = [];
|
|
const StartIndex: Integer = 1; const StopIndex: Integer = -1): Boolean;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ Tests }
|
|
{ }
|
|
{$IFDEF STRING_PAT_MATCH_TEST}
|
|
procedure Test;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
{ Fundamentals }
|
|
flcUtils,
|
|
flcASCII,
|
|
flcStrings;
|
|
|
|
|
|
|
|
{ }
|
|
{ MatchPattern }
|
|
{ }
|
|
{$IFDEF SupportAnsiString}
|
|
function StrZMatchPatternA(M, S: PAnsiChar; const G: TMatchPatternGreed): Integer;
|
|
|
|
function EscapedChar(const C: AnsiChar): AnsiChar;
|
|
begin
|
|
case C of
|
|
'b' : Result := AsciiBS;
|
|
'e' : Result := AsciiESC;
|
|
'f' : Result := AsciiFF;
|
|
'n' : Result := AsciiLF;
|
|
'r' : Result := AsciiCR;
|
|
't' : Result := AsciiHT;
|
|
'v' : Result := AsciiVT;
|
|
else Result := C;
|
|
end;
|
|
end;
|
|
|
|
var A, C, D : AnsiChar;
|
|
N, R : Boolean;
|
|
L, I : Integer;
|
|
begin
|
|
Result := 0;
|
|
repeat
|
|
case M^ of
|
|
#0 : // end of pattern
|
|
exit;
|
|
'.' : // match exactly one
|
|
if S^ = #0 then
|
|
begin
|
|
Result := -1; // no match
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
Inc(M);
|
|
Inc(S);
|
|
Inc(Result);
|
|
end;
|
|
'\' : // escaped character
|
|
if S^ = #0 then
|
|
begin
|
|
Result := -1; // no match
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
Inc(M);
|
|
C := M^;
|
|
if C = #0 then
|
|
begin
|
|
Result := -2; // format error
|
|
exit;
|
|
end;
|
|
C := EscapedChar(C);
|
|
if S^ <> C then
|
|
begin
|
|
Result := -1; // no match
|
|
exit;
|
|
end else
|
|
begin
|
|
Inc(M);
|
|
Inc(S);
|
|
Inc(Result);
|
|
end;
|
|
end;
|
|
'?' : // match zero or one
|
|
begin
|
|
Inc(M);
|
|
if M^ = #0 then
|
|
begin
|
|
if G in [mpgGreedy, mpgGreedyNoBacktrack] then
|
|
if S^ <> #0 then
|
|
Inc(Result);
|
|
break;
|
|
end;
|
|
if S^ <> #0 then
|
|
begin
|
|
if G = mpgGreedyNoBacktrack then
|
|
begin
|
|
Inc(S);
|
|
Inc(Result);
|
|
end
|
|
else
|
|
if G = mpgGreedy then
|
|
begin
|
|
Inc(S);
|
|
if S^ = #0 then
|
|
L := -1
|
|
else
|
|
begin
|
|
L := StrZMatchPatternA(M, S, G); // try one
|
|
if L >= 0 then
|
|
Inc(Result, L + 1);
|
|
end;
|
|
if L < 0 then
|
|
begin
|
|
Dec(S);
|
|
L := StrZMatchPatternA(M, S, G); // try zero
|
|
if L > 0 then
|
|
Inc(Result, L);
|
|
end;
|
|
if L < 0 then
|
|
Result := -1; // no match
|
|
exit;
|
|
end
|
|
else
|
|
begin // lazy
|
|
L := StrZMatchPatternA(M, S, G); // try zero
|
|
if L >= 0 then
|
|
Inc(Result, L)
|
|
else
|
|
begin
|
|
Inc(S);
|
|
if S <> #0 then
|
|
begin
|
|
L := StrZMatchPatternA(M, S, G); // try one
|
|
if L >= 0 then
|
|
Inc(Result, L + 1);
|
|
end;
|
|
end;
|
|
if L < 0 then
|
|
Result := -1; // no match
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
'*' :
|
|
begin
|
|
Inc(M);
|
|
if M^ = #0 then // always match at end of mask
|
|
begin
|
|
if G in [mpgGreedy, mpgGreedyNoBacktrack] then
|
|
while S^ <> #0 do
|
|
begin
|
|
Inc(S);
|
|
Inc(Result);
|
|
end;
|
|
break;
|
|
end
|
|
else
|
|
if S^ <> #0 then
|
|
if G = mpgGreedyNoBacktrack then
|
|
while S^ <> #0 do
|
|
begin
|
|
Inc(S);
|
|
Inc(Result);
|
|
end
|
|
else
|
|
if G = mpgGreedy then
|
|
begin
|
|
// move to end
|
|
I := 0;
|
|
while S^ <> #0 do
|
|
begin
|
|
Inc(S);
|
|
Inc(Result);
|
|
Inc(I);
|
|
end;
|
|
// match from back
|
|
L := 0;
|
|
while I >= 0 do
|
|
begin
|
|
L := StrZMatchPatternA(M, S, G);
|
|
if L >= 0 then
|
|
break
|
|
else
|
|
begin
|
|
Dec(S);
|
|
Dec(Result);
|
|
Dec(I);
|
|
end;
|
|
end;
|
|
if L < 0 then
|
|
Result := -1 // no match
|
|
else
|
|
Inc(Result, L);
|
|
exit;
|
|
end
|
|
else
|
|
// lazy
|
|
begin
|
|
// match from front
|
|
L := 0;
|
|
while S^ <> #0 do
|
|
begin
|
|
L := StrZMatchPatternA(M, S, G);
|
|
if L >= 0 then
|
|
break
|
|
else
|
|
begin
|
|
Inc(S);
|
|
Inc(Result);
|
|
end;
|
|
end;
|
|
if L < 0 then
|
|
Result := -1 // no match
|
|
else
|
|
Inc(Result, L);
|
|
exit;
|
|
end;
|
|
end;
|
|
'[' : // character class
|
|
begin
|
|
A := S^;
|
|
if A = #0 then
|
|
begin
|
|
Result := -1; // no match
|
|
exit;
|
|
end;
|
|
Inc(M);
|
|
C := M^;
|
|
N := C = '^';
|
|
if N then
|
|
begin
|
|
Inc(M);
|
|
C := M^;
|
|
end;
|
|
R := False;
|
|
while C <> ']' do
|
|
begin
|
|
if C = #0 then
|
|
begin
|
|
Result := -2; // format error
|
|
exit;
|
|
end;
|
|
Inc(M);
|
|
if C = '\' then // escaped character
|
|
begin
|
|
C := M^;
|
|
if C = #0 then
|
|
begin
|
|
Result := -2; // format error
|
|
exit;
|
|
end;
|
|
C := EscapedChar(C);
|
|
Inc(M);
|
|
end;
|
|
D := M^;
|
|
if D = '-' then // match range
|
|
begin
|
|
Inc(M);
|
|
D := M^;
|
|
if D = #0 then
|
|
begin
|
|
Result := -2; // format error
|
|
exit;
|
|
end;
|
|
if D = '\' then // escaped character
|
|
begin
|
|
Inc(M);
|
|
D := M^;
|
|
if D = #0 then
|
|
begin
|
|
Result := -2; // format error
|
|
exit;
|
|
end;
|
|
D := EscapedChar(D);
|
|
Inc(M);
|
|
end;
|
|
if (A >= C) and (A <= D) then
|
|
begin
|
|
R := True;
|
|
break;
|
|
end;
|
|
Inc(M);
|
|
C := M^;
|
|
end
|
|
else
|
|
begin // match single character
|
|
if A = C then
|
|
begin
|
|
R := True;
|
|
break;
|
|
end;
|
|
C := D;
|
|
end;
|
|
end;
|
|
if (N and R) or
|
|
(not N and not R) then
|
|
begin
|
|
Result := -1; // no match
|
|
exit;
|
|
end;
|
|
Inc(Result);
|
|
Inc(S);
|
|
// locate closing bracket
|
|
while M^ <> ']' do
|
|
if M^ = #0 then
|
|
begin
|
|
Result := -2; // format error
|
|
exit;
|
|
end
|
|
else
|
|
Inc(M);
|
|
Inc(M);
|
|
end;
|
|
else // single character match
|
|
if M^ <> S^ then
|
|
begin
|
|
Result := -1; // no match
|
|
exit;
|
|
end else
|
|
begin
|
|
Inc(M);
|
|
Inc(S);
|
|
Inc(Result);
|
|
end;
|
|
end;
|
|
until False;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function StrZMatchPatternW(M, S: PWideChar; const G: TMatchPatternGreed): Integer;
|
|
|
|
function EscapedChar(const C: WideChar): WideChar;
|
|
begin
|
|
case C of
|
|
'b' : Result := WideBS;
|
|
'e' : Result := WideESC;
|
|
'f' : Result := WideFF;
|
|
'n' : Result := WideLF;
|
|
'r' : Result := WideCR;
|
|
't' : Result := WideHT;
|
|
'v' : Result := WideVT;
|
|
else Result := C;
|
|
end;
|
|
end;
|
|
|
|
var A, C, D : WideChar;
|
|
N, R : Boolean;
|
|
L, I : Integer;
|
|
begin
|
|
Result := 0;
|
|
repeat
|
|
case M^ of
|
|
#0 : // end of pattern
|
|
exit;
|
|
'.' : // match exactly one
|
|
if S^ = #0 then
|
|
begin
|
|
Result := -1; // no match
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
Inc(M);
|
|
Inc(S);
|
|
Inc(Result);
|
|
end;
|
|
'\' : // escaped character
|
|
if S^ = #0 then
|
|
begin
|
|
Result := -1; // no match
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
Inc(M);
|
|
C := M^;
|
|
if C = #0 then
|
|
begin
|
|
Result := -2; // format error
|
|
exit;
|
|
end;
|
|
C := EscapedChar(C);
|
|
if S^ <> C then
|
|
begin
|
|
Result := -1; // no match
|
|
exit;
|
|
end else
|
|
begin
|
|
Inc(M);
|
|
Inc(S);
|
|
Inc(Result);
|
|
end;
|
|
end;
|
|
'?' : // match zero or one
|
|
begin
|
|
Inc(M);
|
|
if M^ = #0 then
|
|
begin
|
|
if G in [mpgGreedy, mpgGreedyNoBacktrack] then
|
|
if S^ <> #0 then
|
|
Inc(Result);
|
|
break;
|
|
end;
|
|
if S^ <> #0 then
|
|
begin
|
|
if G = mpgGreedyNoBacktrack then
|
|
begin
|
|
Inc(S);
|
|
Inc(Result);
|
|
end
|
|
else
|
|
if G = mpgGreedy then
|
|
begin
|
|
Inc(S);
|
|
if S^ = #0 then
|
|
L := -1
|
|
else
|
|
begin
|
|
L := StrZMatchPatternW(M, S, G); // try one
|
|
if L >= 0 then
|
|
Inc(Result, L + 1);
|
|
end;
|
|
if L < 0 then
|
|
begin
|
|
Dec(S);
|
|
L := StrZMatchPatternW(M, S, G); // try zero
|
|
if L > 0 then
|
|
Inc(Result, L);
|
|
end;
|
|
if L < 0 then
|
|
Result := -1; // no match
|
|
exit;
|
|
end
|
|
else
|
|
begin // lazy
|
|
L := StrZMatchPatternW(M, S, G); // try zero
|
|
if L >= 0 then
|
|
Inc(Result, L)
|
|
else
|
|
begin
|
|
Inc(S);
|
|
if S^ <> #0 then
|
|
begin
|
|
L := StrZMatchPatternW(M, S, G); // try one
|
|
if L >= 0 then
|
|
Inc(Result, L + 1);
|
|
end;
|
|
end;
|
|
if L < 0 then
|
|
Result := -1; // no match
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
'*' :
|
|
begin
|
|
Inc(M);
|
|
if M^ = #0 then // always match at end of mask
|
|
begin
|
|
if G in [mpgGreedy, mpgGreedyNoBacktrack] then
|
|
while S^ <> #0 do
|
|
begin
|
|
Inc(S);
|
|
Inc(Result);
|
|
end;
|
|
break;
|
|
end
|
|
else
|
|
if S^ <> #0 then
|
|
if G = mpgGreedyNoBacktrack then
|
|
while S^ <> #0 do
|
|
begin
|
|
Inc(S);
|
|
Inc(Result);
|
|
end
|
|
else
|
|
if G = mpgGreedy then
|
|
begin
|
|
// move to end
|
|
I := 0;
|
|
while S^ <> #0 do
|
|
begin
|
|
Inc(S);
|
|
Inc(Result);
|
|
Inc(I);
|
|
end;
|
|
// match from back
|
|
L := 0;
|
|
while I >= 0 do
|
|
begin
|
|
L := StrZMatchPatternW(M, S, G);
|
|
if L >= 0 then
|
|
break
|
|
else
|
|
begin
|
|
Dec(S);
|
|
Dec(Result);
|
|
Dec(I);
|
|
end;
|
|
end;
|
|
if L < 0 then
|
|
Result := -1 // no match
|
|
else
|
|
Inc(Result, L);
|
|
exit;
|
|
end
|
|
else
|
|
// lazy
|
|
begin
|
|
// match from front
|
|
L := 0;
|
|
while S^ <> #0 do
|
|
begin
|
|
L := StrZMatchPatternW(M, S, G);
|
|
if L >= 0 then
|
|
break
|
|
else
|
|
begin
|
|
Inc(S);
|
|
Inc(Result);
|
|
end;
|
|
end;
|
|
if L < 0 then
|
|
Result := -1 // no match
|
|
else
|
|
Inc(Result, L);
|
|
exit;
|
|
end;
|
|
end;
|
|
'[' : // character class
|
|
begin
|
|
A := S^;
|
|
if A = #0 then
|
|
begin
|
|
Result := -1; // no match
|
|
exit;
|
|
end;
|
|
Inc(M);
|
|
C := M^;
|
|
N := C = '^';
|
|
if N then
|
|
begin
|
|
Inc(M);
|
|
C := M^;
|
|
end;
|
|
R := False;
|
|
while C <> ']' do
|
|
begin
|
|
if C = #0 then
|
|
begin
|
|
Result := -2; // format error
|
|
exit;
|
|
end;
|
|
Inc(M);
|
|
if C = '\' then // escaped character
|
|
begin
|
|
C := M^;
|
|
if C = #0 then
|
|
begin
|
|
Result := -2; // format error
|
|
exit;
|
|
end;
|
|
C := EscapedChar(C);
|
|
Inc(M);
|
|
end;
|
|
D := M^;
|
|
if D = '-' then // match range
|
|
begin
|
|
Inc(M);
|
|
D := M^;
|
|
if D = #0 then
|
|
begin
|
|
Result := -2; // format error
|
|
exit;
|
|
end;
|
|
if D = '\' then // escaped character
|
|
begin
|
|
Inc(M);
|
|
D := M^;
|
|
if D = #0 then
|
|
begin
|
|
Result := -2; // format error
|
|
exit;
|
|
end;
|
|
D := EscapedChar(D);
|
|
Inc(M);
|
|
end;
|
|
if (A >= C) and (A <= D) then
|
|
begin
|
|
R := True;
|
|
break;
|
|
end;
|
|
Inc(M);
|
|
C := M^;
|
|
end
|
|
else
|
|
begin // match single character
|
|
if A = C then
|
|
begin
|
|
R := True;
|
|
break;
|
|
end;
|
|
C := D;
|
|
end;
|
|
end;
|
|
if (N and R) or
|
|
(not N and not R) then
|
|
begin
|
|
Result := -1; // no match
|
|
exit;
|
|
end;
|
|
Inc(Result);
|
|
Inc(S);
|
|
// locate closing bracket
|
|
while M^ <> ']' do
|
|
if M^ = #0 then
|
|
begin
|
|
Result := -2; // format error
|
|
exit;
|
|
end
|
|
else
|
|
Inc(M);
|
|
Inc(M);
|
|
end;
|
|
else // single character match
|
|
if M^ <> S^ then
|
|
begin
|
|
Result := -1; // no match
|
|
exit;
|
|
end else
|
|
begin
|
|
Inc(M);
|
|
Inc(S);
|
|
Inc(Result);
|
|
end;
|
|
end;
|
|
until False;
|
|
end;
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
function StrEqualPatternA(const M, S: RawByteString; const G: TMatchPatternGreed): Boolean;
|
|
begin
|
|
Result := StrZMatchPatternA(PAnsiChar(M), PAnsiChar(S), G) = Length(S);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function StrEqualPatternU(const M, S: UnicodeString; const G: TMatchPatternGreed): Boolean;
|
|
begin
|
|
Result := StrZMatchPatternW(PWideChar(M), PWideChar(S), G) = Length(S);
|
|
end;
|
|
|
|
function StrEqualPattern(const M, S: String; const G: TMatchPatternGreed): Boolean;
|
|
begin
|
|
{$IFDEF StringIsUnicode}
|
|
Result := StrZMatchPatternW(PWideChar(M), PWideChar(S), G) = Length(S);
|
|
{$ELSE}
|
|
Result := StrZMatchPatternA(PAnsiChar(M), PAnsiChar(S), G) = Length(S);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
function StrPosPatternA(const F, S: RawByteString; var Len: Integer; const StartIndex: Integer; const G: TMatchPatternGreed): Integer;
|
|
var P : PAnsiChar;
|
|
M : PAnsiChar;
|
|
I : Integer;
|
|
begin
|
|
P := PAnsiChar(S);
|
|
M := PAnsiChar(F);
|
|
for I := MaxInt(1, StartIndex) to Length(S) do
|
|
begin
|
|
Len := StrZMatchPatternA(M, P, G);
|
|
if Len >= 0 then
|
|
begin
|
|
Result := I;
|
|
exit;
|
|
end
|
|
else
|
|
Inc(P);
|
|
end;
|
|
Len := 0;
|
|
Result := 0;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function StrPosPatternU(const F, S: UnicodeString; var Len: Integer; const StartIndex: Integer; const G: TMatchPatternGreed): Integer;
|
|
var P : PWideChar;
|
|
M : PWideChar;
|
|
I : Integer;
|
|
begin
|
|
P := PWideChar(S);
|
|
M := PWideChar(F);
|
|
for I := MaxInt(1, StartIndex) to Length(S) do
|
|
begin
|
|
Len := StrZMatchPatternW(M, P, G);
|
|
if Len >= 0 then
|
|
begin
|
|
Result := I;
|
|
exit;
|
|
end
|
|
else
|
|
Inc(P);
|
|
end;
|
|
Len := 0;
|
|
Result := 0;
|
|
end;
|
|
|
|
|
|
|
|
{ }
|
|
{ MatchFileMask }
|
|
{ }
|
|
{$IFDEF SupportAnsiString}
|
|
function MatchFileMaskB(const Mask, Key: RawByteString; const AsciiCaseSensitive: Boolean): Boolean;
|
|
var ML, KL : Integer;
|
|
|
|
function MatchAt(MaskPos, KeyPos: Integer): Boolean;
|
|
begin
|
|
while (MaskPos <= ML) and (KeyPos <= KL) do
|
|
case Mask[MaskPos] of
|
|
'?' :
|
|
begin
|
|
Inc(MaskPos);
|
|
Inc(KeyPos);
|
|
end;
|
|
'*' :
|
|
begin
|
|
while (MaskPos <= ML) and (Mask[MaskPos] = '*') do
|
|
Inc(MaskPos);
|
|
if MaskPos > ML then
|
|
begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
repeat
|
|
if MatchAt(MaskPos, KeyPos) then
|
|
begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
Inc(KeyPos);
|
|
until KeyPos > KL;
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
else
|
|
if not CharMatchB(Mask[MaskPos], Key[KeyPos], AsciiCaseSensitive) then
|
|
begin
|
|
Result := False;
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
Inc(MaskPos);
|
|
Inc(KeyPos);
|
|
end;
|
|
end;
|
|
while (MaskPos <= ML) and (Mask[MaskPos] in ['?', '*']) do
|
|
Inc(MaskPos);
|
|
if (MaskPos <= ML) or (KeyPos <= KL) then
|
|
begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
begin
|
|
ML := Length(Mask);
|
|
if ML = 0 then
|
|
begin
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
KL := Length(Key);
|
|
Result := MatchAt(1, 1);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ Abbreviated regular expression matcher }
|
|
{ }
|
|
{$IFDEF SupportAnsiString}
|
|
function MatchQuantSeqB(var MatchPos: Integer; const MatchSeq: array of ByteCharSet;
|
|
const Quant: array of TMatchQuantifier; const S: RawByteString;
|
|
const MatchOptions: TMatchQuantSeqOptions;
|
|
const StartIndex: Integer; const StopIndex: Integer): Boolean;
|
|
|
|
var Stop : Integer;
|
|
Deterministic : Boolean;
|
|
NonGreedy : Boolean;
|
|
|
|
function MatchAt(MPos, SPos: Integer; var MatchPos: Integer): Boolean;
|
|
|
|
function MatchAndAdvance: Boolean;
|
|
var I : Integer;
|
|
begin
|
|
I := SPos;
|
|
Result := S[I] in MatchSeq[MPos];
|
|
if Result then
|
|
begin
|
|
MatchPos := I;
|
|
Inc(SPos);
|
|
end;
|
|
end;
|
|
|
|
function MatchAndSetResult(var Res: Boolean): Boolean;
|
|
begin
|
|
Result := MatchAndAdvance;
|
|
Res := Result;
|
|
if not Result then
|
|
MatchPos := 0;
|
|
end;
|
|
|
|
function MatchAny: Boolean;
|
|
var I, L : Integer;
|
|
P : PAnsiChar;
|
|
begin
|
|
L := Stop;
|
|
if Deterministic then
|
|
begin
|
|
while (SPos <= L) and MatchAndAdvance do ;
|
|
Result := False;
|
|
end
|
|
else
|
|
if NonGreedy then
|
|
repeat
|
|
Result := MatchAt(MPos + 1, SPos, MatchPos);
|
|
if Result or not MatchAndAdvance then
|
|
exit;
|
|
until SPos > L
|
|
else
|
|
begin
|
|
I := SPos;
|
|
P := Pointer(S);
|
|
Inc(P, I - 1);
|
|
while (I <= L) and (P^ in MatchSeq[MPos]) do
|
|
begin
|
|
Inc(I);
|
|
Inc(P);
|
|
end;
|
|
repeat
|
|
MatchPos := I - 1;
|
|
Result := MatchAt(MPos + 1, I, MatchPos);
|
|
if Result then
|
|
exit;
|
|
Dec(I);
|
|
until SPos > I;
|
|
end;
|
|
end;
|
|
|
|
var Q : TMatchQuantifier;
|
|
L, M : Integer;
|
|
begin
|
|
L := Length(MatchSeq);
|
|
M := Stop;
|
|
while (MPos < L) and (SPos <= M) do
|
|
begin
|
|
Q := Quant[MPos];
|
|
if Q in [mqOnce, mqLeastOnce] then
|
|
if not MatchAndSetResult(Result) then
|
|
exit;
|
|
if (Q = mqAny) or ((Q = mqLeastOnce) and (SPos <= M)) then
|
|
begin
|
|
Result := MatchAny;
|
|
if Result then
|
|
exit;
|
|
end
|
|
else
|
|
if Q = mqOptional then
|
|
if Deterministic then
|
|
MatchAndAdvance
|
|
else
|
|
begin
|
|
if NonGreedy then
|
|
begin
|
|
Result := MatchAt(MPos + 1, SPos, MatchPos);
|
|
if Result or not MatchAndSetResult(Result) then
|
|
exit;
|
|
end
|
|
else
|
|
begin
|
|
Result := (MatchAndAdvance and MatchAt(MPos + 1, SPos, MatchPos)) or
|
|
MatchAt(MPos + 1, SPos, MatchPos);
|
|
exit;
|
|
end;
|
|
end;
|
|
Inc(MPos);
|
|
end;
|
|
while (MPos < L) and (Quant[MPos] in [mqAny, mqOptional]) do
|
|
Inc(MPos);
|
|
Result := MPos = L;
|
|
if not Result then
|
|
MatchPos := 0;
|
|
end;
|
|
|
|
begin
|
|
Assert(Length(MatchSeq) = Length(Quant));
|
|
if StopIndex < 0 then
|
|
Stop := Length(S)
|
|
else
|
|
Stop := MinInt(StopIndex, Length(S));
|
|
MatchPos := 0;
|
|
if (Length(MatchSeq) = 0) or (StartIndex > Stop) or (StartIndex <= 0) then
|
|
begin
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
NonGreedy := moNonGreedy in MatchOptions;
|
|
Deterministic := moDeterministic in MatchOptions;
|
|
Result := MatchAt(0, StartIndex, MatchPos);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
{ }
|
|
{ Tests }
|
|
{ }
|
|
{$IFDEF STRING_PAT_MATCH_TEST}
|
|
procedure Test_MatchFileMask;
|
|
begin
|
|
{$IFDEF SupportAnsiString}
|
|
Assert(MatchFileMaskB('*', 'A'), 'MatchFileMask');
|
|
Assert(MatchFileMaskB('?', 'A'), 'MatchFileMask');
|
|
Assert(MatchFileMaskB('', 'A'), 'MatchFileMask');
|
|
Assert(MatchFileMaskB('', ''), 'MatchFileMask');
|
|
Assert(not MatchFileMaskB('X', ''), 'MatchFileMask');
|
|
Assert(MatchFileMaskB('A?', 'A'), 'MatchFileMask');
|
|
Assert(MatchFileMaskB('A?', 'AB'), 'MatchFileMask');
|
|
Assert(MatchFileMaskB('A*B*C', 'ACBDC'), 'MatchFileMask');
|
|
Assert(MatchFileMaskB('A*B*?', 'ACBDC'), 'MatchFileMask');
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure Test_MatchPattern;
|
|
var
|
|
I : Integer;
|
|
begin
|
|
{ MatchPattern }
|
|
{$IFDEF SupportAnsiString}
|
|
Assert(StrZMatchPatternA('', '', mpgLazy) = 0);
|
|
Assert(StrZMatchPatternA('', '', mpgGreedy) = 0);
|
|
Assert(StrZMatchPatternA('', '', mpgGreedyNoBacktrack) = 0);
|
|
Assert(StrZMatchPatternA('', 'a', mpgLazy) = 0);
|
|
{$ENDIF}
|
|
|
|
Assert(StrZMatchPatternW('', '', mpgLazy) = 0);
|
|
|
|
{$IFDEF SupportAnsiString}
|
|
Assert(StrZMatchPatternA('a', '', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a', 'a', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('a', 'b', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a', 'aa', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('aa', '', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('aa', 'a', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('aa', 'aa', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('aa', 'aaa', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('aa', 'ab', mpgLazy) < 0);
|
|
|
|
Assert(StrZMatchPatternA('.', '', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('.', 'a', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('.', 'aa', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('a.', 'a', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a.', 'aa', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a.', 'aaa', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('.a', 'a', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('.a', 'aa', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('.a', 'ab', mpgLazy) < 0);
|
|
|
|
Assert(StrZMatchPatternA('?', '', mpgLazy) = 0);
|
|
Assert(StrZMatchPatternA('?', 'a', mpgLazy) = 0);
|
|
Assert(StrZMatchPatternA('?', 'aa', mpgLazy) = 0);
|
|
Assert(StrZMatchPatternA('a?', 'a', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('a?', 'aa', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('a?', 'aaa', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('?a', 'a', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('?a', 'aa', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('?a', 'ab', mpgLazy) = 1);
|
|
|
|
Assert(StrZMatchPatternA('?', '', mpgGreedy) = 0);
|
|
Assert(StrZMatchPatternA('?', 'a', mpgGreedy) = 1);
|
|
Assert(StrZMatchPatternA('?', 'aa', mpgGreedy) = 1);
|
|
Assert(StrZMatchPatternA('a?', 'a', mpgGreedy) = 1);
|
|
Assert(StrZMatchPatternA('a?', 'aa', mpgGreedy) = 2);
|
|
Assert(StrZMatchPatternA('a?', 'aaa', mpgGreedy) = 2);
|
|
Assert(StrZMatchPatternA('?a', 'a', mpgGreedy) = 1);
|
|
Assert(StrZMatchPatternA('?a', 'aa', mpgGreedy) = 2);
|
|
Assert(StrZMatchPatternA('?a', 'ab', mpgGreedy) = 1);
|
|
|
|
Assert(StrZMatchPatternA('?', '', mpgGreedyNoBacktrack) = 0);
|
|
Assert(StrZMatchPatternA('?', 'a', mpgGreedyNoBacktrack) = 1);
|
|
Assert(StrZMatchPatternA('?', 'aa', mpgGreedyNoBacktrack) = 1);
|
|
Assert(StrZMatchPatternA('a?', 'a', mpgGreedyNoBacktrack) = 1);
|
|
Assert(StrZMatchPatternA('a?', 'aa', mpgGreedyNoBacktrack) = 2);
|
|
Assert(StrZMatchPatternA('a?', 'aaa', mpgGreedyNoBacktrack) = 2);
|
|
Assert(StrZMatchPatternA('?a', 'a', mpgGreedyNoBacktrack) < 0);
|
|
Assert(StrZMatchPatternA('?a', 'aa', mpgGreedyNoBacktrack) = 2);
|
|
Assert(StrZMatchPatternA('?a', 'ab', mpgGreedyNoBacktrack) < 0);
|
|
|
|
Assert(StrZMatchPatternA('*', '', mpgLazy) = 0);
|
|
Assert(StrZMatchPatternA('*', 'a', mpgLazy) = 0);
|
|
Assert(StrZMatchPatternA('*', 'aa', mpgLazy) = 0);
|
|
Assert(StrZMatchPatternA('a*', '', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a*', 'a', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('a*', 'aa', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('a*', 'abc', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('a*b', 'a', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a*b', 'ab', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a*b', 'acb', mpgLazy) = 3);
|
|
Assert(StrZMatchPatternA('a*b', 'acbd', mpgLazy) = 3);
|
|
Assert(StrZMatchPatternA('a*b', 'acccbd', mpgLazy) = 5);
|
|
Assert(StrZMatchPatternA('a*b', 'acbcb', mpgLazy) = 3);
|
|
Assert(StrZMatchPatternA('a*b', 'acbcbd', mpgLazy) = 3);
|
|
Assert(StrZMatchPatternA('a*b', 'bb', mpgLazy) < 0);
|
|
|
|
Assert(StrZMatchPatternA('*', '', mpgGreedy) = 0);
|
|
Assert(StrZMatchPatternA('*', 'a', mpgGreedy) = 1);
|
|
Assert(StrZMatchPatternA('*', 'aa', mpgGreedy) = 2);
|
|
Assert(StrZMatchPatternA('a*', '', mpgGreedy) < 0);
|
|
Assert(StrZMatchPatternA('a*', 'a', mpgGreedy) = 1);
|
|
Assert(StrZMatchPatternA('a*', 'aa', mpgGreedy) = 2);
|
|
Assert(StrZMatchPatternA('a*', 'abc', mpgGreedy) = 3);
|
|
Assert(StrZMatchPatternA('a*b', 'a', mpgGreedy) < 0);
|
|
Assert(StrZMatchPatternA('a*b', 'ab', mpgGreedy) = 2);
|
|
Assert(StrZMatchPatternA('a*b', 'acb', mpgGreedy) = 3);
|
|
Assert(StrZMatchPatternA('a*b', 'acbd', mpgGreedy) = 3);
|
|
Assert(StrZMatchPatternA('a*b', 'acccbd', mpgGreedy) = 5);
|
|
Assert(StrZMatchPatternA('a*b', 'acbcb', mpgGreedy) = 5);
|
|
Assert(StrZMatchPatternA('a*b', 'acbcbd', mpgGreedy) = 5);
|
|
Assert(StrZMatchPatternA('a*b', 'bb', mpgGreedy) < 0);
|
|
|
|
Assert(StrZMatchPatternA('*', '', mpgGreedyNoBacktrack) = 0);
|
|
Assert(StrZMatchPatternA('*', 'a', mpgGreedyNoBacktrack) = 1);
|
|
Assert(StrZMatchPatternA('*', 'aa', mpgGreedyNoBacktrack) = 2);
|
|
Assert(StrZMatchPatternA('a*', '', mpgGreedyNoBacktrack) < 0);
|
|
Assert(StrZMatchPatternA('a*', 'a', mpgGreedyNoBacktrack) = 1);
|
|
Assert(StrZMatchPatternA('a*', 'aa', mpgGreedyNoBacktrack) = 2);
|
|
Assert(StrZMatchPatternA('a*', 'abc', mpgGreedyNoBacktrack) = 3);
|
|
Assert(StrZMatchPatternA('a*b', 'a', mpgGreedyNoBacktrack) < 0);
|
|
Assert(StrZMatchPatternA('a*b', 'ab', mpgGreedyNoBacktrack) < 0);
|
|
Assert(StrZMatchPatternA('a*b', 'acb', mpgGreedyNoBacktrack) < 0);
|
|
Assert(StrZMatchPatternA('a*b', 'acbd', mpgGreedyNoBacktrack) < 0);
|
|
Assert(StrZMatchPatternA('a*b', 'acccbd', mpgGreedyNoBacktrack) < 0);
|
|
Assert(StrZMatchPatternA('a*b', 'acbcb', mpgGreedyNoBacktrack) < 0);
|
|
Assert(StrZMatchPatternA('a*b', 'acbcbd', mpgGreedyNoBacktrack) < 0);
|
|
Assert(StrZMatchPatternA('a*b', 'bb', mpgGreedyNoBacktrack) < 0);
|
|
|
|
Assert(StrZMatchPatternA('a[b]', 'a', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a[b]', 'ab', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a[b]', 'abb', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a[b]', 'aa', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a[^a]', 'a', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a[^a]', 'ab', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a[^a]', 'abb', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a[^a]', 'aa', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a[0-9]', 'a', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a[0-9]', 'a0', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a[0-9]', 'aa', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a[0-9]', 'a99', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a[^0-9]', 'a', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a[^0-9]', 'a0', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a[^0-9]', 'aa', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a[^0-9]', 'aaa', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a[0-9a-z]', 'a', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a[0-9a-z]', 'aa', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a[0-9a-z]', 'az', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a[0-9a-z]', 'a0', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a[0-9a-z]', 'a9', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a[0-9a-z]', 'a00', mpgLazy) = 2);
|
|
Assert(StrZMatchPatternA('a[0-9a-z]', 'aA', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a[0-9a-z]', 'aA0', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a[0-9a-z]a', 'aaa', mpgLazy) = 3);
|
|
Assert(StrZMatchPatternA('a[0-9a-z]a', 'aa', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a[0-9a-z]a', 'aab', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('a[0-9a-z][AB]', 'aaA', mpgLazy) = 3);
|
|
Assert(StrZMatchPatternA('a[0-9a-z][AB]', 'aaC', mpgLazy) < 0);
|
|
|
|
Assert(StrZMatchPatternA('\r', #13, mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('\\', '\', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('\.', '.', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('[\r]', #13, mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('[\\]', '\', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('[\-]', '-', mpgLazy) = 1);
|
|
Assert(StrZMatchPatternA('[\-]', '\', mpgLazy) < 0);
|
|
Assert(StrZMatchPatternA('[\-\.]', '.', mpgLazy) = 1);
|
|
|
|
Assert(StrPosPatternA('', '', I, 1, mpgGreedy) = 0); Assert(I = 0);
|
|
Assert(StrPosPatternA('', 'a', I, 1, mpgGreedy) = 1); Assert(I = 0);
|
|
Assert(StrPosPatternA('a', '', I, 1, mpgGreedy) = 0); Assert(I = 0);
|
|
Assert(StrPosPatternA('a*b', 'xacb', I, 1, mpgGreedy) = 2); Assert(I = 3);
|
|
Assert(StrPosPatternA('a*b', 'xaccbd', I, 1, mpgGreedy) = 2); Assert(I = 4);
|
|
Assert(StrPosPatternA('a*b', 'xa', I, 1, mpgGreedy) = 0); Assert(I = 0);
|
|
{$ENDIF}
|
|
|
|
Assert(StrPosPatternU('', '', I, 1, mpgGreedy) = 0); Assert(I = 0);
|
|
Assert(StrPosPatternU('', 'a', I, 1, mpgGreedy) = 1); Assert(I = 0);
|
|
Assert(StrPosPatternU('a', '', I, 1, mpgGreedy) = 0); Assert(I = 0);
|
|
Assert(StrPosPatternU('a*b', 'xacb', I, 1, mpgGreedy) = 2); Assert(I = 3);
|
|
Assert(StrPosPatternU('a*b', 'xaccbd', I, 1, mpgGreedy) = 2); Assert(I = 4);
|
|
Assert(StrPosPatternU('a*b', 'xa', I, 1, mpgGreedy) = 0); Assert(I = 0);
|
|
end;
|
|
|
|
procedure Test_MatchQuantSeq;
|
|
var I : Integer;
|
|
begin
|
|
{$IFDEF SupportAnsiString}
|
|
Assert(MatchQuantSeqB(I, [csAlpha], [mqOnce], 'a', []));
|
|
Assert(I = 1);
|
|
Assert(MatchQuantSeqB(I, [csAlpha], [mqAny], 'a', []));
|
|
Assert(I = 1);
|
|
Assert(MatchQuantSeqB(I, [csAlpha], [mqLeastOnce], 'a', []));
|
|
Assert(I = 1);
|
|
Assert(MatchQuantSeqB(I, [csAlpha], [mqOptional], 'a', []));
|
|
Assert(I = 1);
|
|
Assert(MatchQuantSeqB(I, [csAlpha], [mqOnce], 'ab', []));
|
|
Assert(I = 1);
|
|
Assert(MatchQuantSeqB(I, [csAlpha], [mqAny], 'ab', []));
|
|
Assert(I = 2);
|
|
Assert(MatchQuantSeqB(I, [csAlpha], [mqLeastOnce], 'ab', []));
|
|
Assert(I = 2);
|
|
Assert(MatchQuantSeqB(I, [csAlpha], [mqOptional], 'ab', []));
|
|
Assert(I = 1);
|
|
Assert(MatchQuantSeqB(I, [csAlpha], [mqOnce], 'abc', []));
|
|
Assert(I = 1);
|
|
Assert(MatchQuantSeqB(I, [csAlpha], [mqAny], 'abc', []));
|
|
Assert(I = 3);
|
|
Assert(MatchQuantSeqB(I, [csAlpha], [mqLeastOnce], 'abc', []));
|
|
Assert(I = 3);
|
|
Assert(MatchQuantSeqB(I, [csAlpha], [mqOptional], 'abc', []));
|
|
Assert(I = 1);
|
|
Assert(not MatchQuantSeqB(I, [csAlpha, csNumeric], [mqOnce, mqOnce], 'ab12', []));
|
|
Assert(I = 0);
|
|
Assert(MatchQuantSeqB(I, [csAlpha, csNumeric], [mqAny, mqOnce], 'abc123', []));
|
|
Assert(I = 4);
|
|
Assert(not MatchQuantSeqB(I, [csAlpha, csNumeric], [mqLeastOnce, mqAny], '123', []));
|
|
Assert(I = 0);
|
|
Assert(MatchQuantSeqB(I, [csAlpha, csNumeric], [mqOptional, mqAny], '123abc', []));
|
|
Assert(I = 3);
|
|
Assert(MatchQuantSeqB(I, [csAlpha, csNumeric], [mqOnce, mqAny], 'a123', []));
|
|
Assert(I = 4);
|
|
Assert(MatchQuantSeqB(I, [csAlpha, csNumeric], [mqAny, mqAny], 'abc123', []));
|
|
Assert(I = 6);
|
|
Assert(MatchQuantSeqB(I, [csAlpha, csNumeric], [mqLeastOnce, mqOnce], 'ab123', []));
|
|
Assert(I = 3);
|
|
Assert(MatchQuantSeqB(I, [csAlpha, csNumeric], [mqOptional, mqOptional], '1', []));
|
|
Assert(I = 1);
|
|
Assert(MatchQuantSeqB(I, [csAlpha, csNumeric], [mqOptional, mqOptional], 'a', []));
|
|
Assert(I = 1);
|
|
Assert(MatchQuantSeqB(I, [csAlpha, csNumeric], [mqOnce, mqOptional], 'ab', []));
|
|
Assert(I = 1);
|
|
Assert(not MatchQuantSeqB(I, [csAlpha, csNumeric], [mqOptional, mqOnce], 'ab', []));
|
|
Assert(I = 0);
|
|
Assert(MatchQuantSeqB(I, [csAlphaNumeric, csNumeric, csAlpha, csNumeric],
|
|
[mqLeastOnce, mqAny, mqOptional, mqOnce], 'a1b2', []));
|
|
Assert(I = 4);
|
|
Assert(MatchQuantSeqB(I, [csAlphaNumeric, csNumeric, csAlpha, csNumeric],
|
|
[mqAny, mqOnce, mqOptional, mqOnce], 'a1b2cd3efg4', []));
|
|
Assert(I = 4);
|
|
Assert(MatchQuantSeqB(I, [csAlphaNumeric, csNumeric], [mqAny, mqOptional], 'a1', [moDeterministic]));
|
|
Assert(I = 2);
|
|
Assert(not MatchQuantSeqB(I, [csAlphaNumeric, csNumeric], [mqAny, mqOnce], 'a1', [moDeterministic]));
|
|
Assert(I = 0);
|
|
Assert(MatchQuantSeqB(I, [csAlpha, csNumeric, csAlpha, csAlphaNumeric],
|
|
[mqAny, mqOnce, mqAny, mqLeastOnce], 'a1b2cd3efg4', [moDeterministic]));
|
|
Assert(I = 11);
|
|
Assert(MatchQuantSeqB(I, [csAlphaNumeric, csNumeric], [mqAny, mqOptional], 'a1', [moNonGreedy]));
|
|
Assert(I = 0);
|
|
Assert(MatchQuantSeqB(I, [csAlphaNumeric, csNumeric], [mqAny, mqLeastOnce], 'a1', [moNonGreedy]));
|
|
Assert(I = 2);
|
|
Assert(not MatchQuantSeqB(I, [csAlphaNumeric, csNumeric], [mqAny, mqOnce], 'abc', [moNonGreedy]));
|
|
Assert(I = 0);
|
|
Assert(MatchQuantSeqB(I, [csAlphaNumeric, csNumeric, csAlpha, csNumeric],
|
|
[mqAny, mqOnce, mqOnce, mqLeastOnce], 'a1bc2de3g4', [moNonGreedy]));
|
|
Assert(I = 10);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure Test;
|
|
begin
|
|
Test_MatchFileMask;
|
|
Test_MatchPattern;
|
|
Test_MatchQuantSeq;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
|
|
|
|
end.
|
|
|