home *** CD-ROM | disk | FTP | other *** search
/ Freelog 22 / freelog 22.iso / Prog / Djgpp / GPC2952B.ZIP / lib / gcc-lib / djgpp / 2.952 / units / regex.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2001-02-08  |  16.0 KB  |  372 lines

  1. {$nested-comments}
  2. {
  3. Regular expression matching and replacement
  4.  
  5. The RegEx unit provides routines to match strings against regular
  6. expressions and perform substitutions using matched subexpressions.
  7.  
  8. To use the RegEx unit, you will need the rx library which can be
  9. found in ftp://agnes.dida.physik.uni-essen.de/gnu-pascal/libs/ .
  10.  
  11. Regular expressions are strings with some characters having special
  12. meanings. They describe (match) a class of strings. They are similar
  13. to wild cards used in file name matching, but much more powerful.
  14.  
  15. There are two kinds of regular expressions supported by this unit,
  16. basic and extended regular expressions. The difference between them
  17. is not functionality, but only syntax. The following is a short
  18. overview of regular expressions. For a more thorough explanation see
  19. the literature, or the documentation of the rx library, or man pages
  20. of programs like grep(1) and sed(1).
  21.  
  22. Basic           Extended        Meaning
  23. `.'             `.'             matches any single character
  24. `[aei-z]'       `[aei-z]'       matches either `a', `e', or any
  25.                                 character from `i' to `z'
  26. `[^aei-z]'      `[^aei-z]'      matches any character but `a', `e',
  27.                                 or `i' .. `z'
  28.                                 To include in such a list the the
  29.                                 characters `]', `^', or `-', put
  30.                                 them first, anywhere but first, or
  31.                                 first or last, resp.
  32. `[[:alnum:]]'   `[[:alnum:]]'   matches any alphanumeric character
  33. `[^[:digit:]]'  `[^[:digit:]]'  matches anything but a digit
  34. `[a[:space:]]'  `[a[:space:]]'  matches the letter `a' or a space
  35.                                 character (space, tab)
  36. ...                             (there are more classes available)
  37. `\w'            `\w'            = [[:alnum:]]
  38. `\W'            `\W'            = [^[:alnum:]]
  39. `^'             `^'             matches the empty string at the
  40.                                 beginning of a line
  41. `$'             `$'             matches the empty string at the end
  42.                                 of a line
  43. `*'             `*'             matches zero or more occurences of
  44.                                 the preceding expression
  45. `\+'            `+'             matches one or more occurences of
  46.                                 the preceding expression
  47. `\?'            `?'             matches zero or one occurence of the
  48.                                 preceding expression
  49. `\{N\}'         `{N}'           matches exactly N occurences of the
  50.                                 preceding expression (N is an
  51.                                 integer number)
  52. `\{M,N\}'       `{M,N}'         matches M to N occurences of the
  53.                                 preceding expression (M and N are
  54.                                 integer numbers, M <= N)
  55. `A B'           `A B'           matches A followed by B (A and B are
  56.                                 regular expressions)
  57. `A\|B'          `A|B'           matches A or B (A and B are regular
  58.                                 expressions)
  59. `\( \)'         `( )'           forms a subexpression, to override
  60.                                 precedence, and for subexpression
  61.                                 references
  62. `\7'            `\7'            matches the 7'th parenthesized
  63.                                 subexpression (counted by their
  64.                                 start in the regex), where 7 is a
  65.                                 number from 1 to 9 ;-).
  66.                                 *Please note:* using this feature
  67.                                 can be *very* slow or take very much
  68.                                 memory (exponential time and space
  69.                                 in the worst case, if you know what
  70.                                 that means...).
  71. `\'             `\'             quotes the following character if
  72.                                 it's special (i.e. listed above)
  73. rest            rest            any other character matches itself
  74.  
  75. Precedence, from highest to lowest:
  76. * parentheses (`()')
  77. * repetition (`*', `+', `?', `{}')
  78. * concatenation
  79. * alternation (`|')
  80.  
  81. When performing substitutions using matched subexpressions of a
  82. regular expression (see `ReplaceSubExpressionReferences'), the
  83. replacement string can reference the whole matched expression with
  84. `&' or `\0', the 7th subexpression with `\7' (just like in the regex
  85. itself, but using it in replacements is not slow), and the 7th
  86. subexpression converted to upper/lower case with `\u7' or `\l7',
  87. resp. (which also works for the whole matched expression with `\u0'
  88. or `\l0'). A verbatim `&' or `\' can be specified with `\&' or `\\',
  89. resp.
  90.  
  91. Copyright (C) 1998-2001 Free Software Foundation, Inc.
  92.  
  93. Author: Frank Heckenbach <frank@pascal.gnu.de>
  94.  
  95. This file is part of GNU Pascal.
  96.  
  97. GNU Pascal is free software; you can redistribute it and/or modify
  98. it under the terms of the GNU General Public License as published by
  99. the Free Software Foundation; either version 2, or (at your option)
  100. any later version.
  101.  
  102. GNU Pascal is distributed in the hope that it will be useful,
  103. but WITHOUT ANY WARRANTY; without even the implied warranty of
  104. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  105. GNU General Public License for more details.
  106.  
  107. You should have received a copy of the GNU General Public License
  108. along with GNU Pascal; see the file COPYING. If not, write to the
  109. Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  110. 02111-1307, USA.
  111.  
  112. As a special exception, if you link this file with files compiled
  113. with a GNU compiler to produce an executable, this does not cause
  114. the resulting executable to be covered by the GNU General Public
  115. License. This exception does not however invalidate any other
  116. reasons why the executable file might be covered by the GNU General
  117. Public License.
  118.  
  119. Please also note the license of the rx library.
  120. }
  121.  
  122. {$gnu-pascal,B-,I-}
  123. {$if __GPC_RELEASE__ < 20000412}
  124. {$error This unit requires GPC release 20000412 or newer.}
  125. {$endif}
  126.  
  127. unit RegEx;
  128.  
  129. interface
  130.  
  131. uses GPC;
  132.  
  133. const
  134.   { `BasicRegExSpecialChars' contains all characters that have
  135.     special meanings in basic regular expressions.
  136.     `ExtRegExSpecialChars' contains those that have special meanings
  137.     in extended regular expressions. }
  138.   BasicRegExSpecialChars = ['.', '[', ']', '^', '$', '*', '\'];
  139.   ExtRegExSpecialChars   = ['.', '[', ']', '^', '$', '*', '+', '?', '{', '}', '|', '(', ')', '\'];
  140.  
  141. type
  142.   { The type used by the routines of the `RegEx' unit to store
  143.     regular expressions in an internal format. The fields RegEx,
  144.     RegMatch, ErrorInternal, From and Length are only used
  145.     internally. SubExpressions can be read after `NewRegEx' and will
  146.     contain the number of parenthesized subexpressions. Error should
  147.     be checked after `NewRegEx'. It will be `nil' when it succeeded,
  148.     and contain an error message otherwise. }
  149.   RegExType = record
  150.     RegEx, RegMatch : Pointer;  { Internal }
  151.     ErrorInternal : CString;    { Internal }
  152.     From, Length : Integer;     { Internal }
  153.     SubExpressions : Integer;
  154.     Error : PString
  155.   end;
  156.  
  157. { Simple interface to regular expression mathing. Matches a regular
  158.   expression against a string starting from a specified position.
  159.   Returns the position of the first match, or 0 if it does not
  160.   match, or the regular expression is invalid. }
  161. function  RegExPosFrom (const Expression : String; ExtendedRegEx, CaseInsensitive : Boolean; const s : String; From : Integer) : Integer; asmname '_p_regexposfrom';
  162.  
  163. { Creates the internal format of a regular expression. If
  164.   ExtendedRegEx is True, Expression is assumed to denote an extended
  165.   regular expression, otherwise a basic regular expression.
  166.   CaseInsensitive determines if the case of letters will be ignored
  167.   when mathing the expression. If NewLines is True, `NewLine'
  168.   characters in a string matched against the expression will be
  169.   treated as dividing the string in multiple lines, so that `$' can
  170.   match before the NewLine and `^' can match after. Also, `.' and
  171.   `[^...]' will not match a NewLine then.}
  172. procedure NewRegEx (var RegEx : RegExType; const Expression : String; ExtendedRegEx, CaseInsensitive, NewLines : Boolean); asmname '_p_newregex';
  173.  
  174. { Disposes of a regular expression created with `NewRegEx'. *Must*
  175.   be used after `NewRegEx' before the RegEx variable becomes invalid
  176.   (i.e., goes out of scope or a pointer pointing to it is Dispose'd
  177.   of). }
  178. procedure DisposeRegEx (var RegEx : RegExType); asmname '_p_dispose_regex';
  179.  
  180. { Matches a regular expression created with `NewRegEx' against a
  181.   string. }
  182. function  MatchRegEx (var RegEx : RegExType; const s : String; NotBeginningOfLine, NotEndOfLine : Boolean) : Boolean; asmname '_p_matchregex';
  183.  
  184. { Matches a regular expression created with `NewRegEx' against a
  185.   string, starting from a specified position. }
  186. function  MatchRegExFrom (var RegEx : RegExType; const s : String; NotBeginningOfLine, NotEndOfLine : Boolean; From : Integer) : Boolean; asmname '_p_matchregexfrom';
  187.  
  188. { Finds out where the regular expression matched, if `MatchRegEx' or
  189.   `MatchRegExFrom' were successful. If n = 0, it returns the
  190.   position of the whole match, otherwise the position of the n'th
  191.   parenthesized subexpression. MatchPosition and MatchLength will
  192.   contain the position (counted from 1) and length of the match, or
  193.   0 if it didn't match. (Note: MatchLength can also be 0 for a
  194.   successful empty match, so check MatchPosition for 0 to find out
  195.   if it matched at all.) MatchPosition or MatchLength may be null
  196.   and is ignored then. }
  197. procedure GetMatchRegEx (var RegEx : RegExType; n : Integer; var MatchPosition, MatchLength : Integer); asmname '_p_getmatch_regex';
  198.  
  199. { Checks if the string s contains any quoted characters or
  200.   (sub)expression references to the regular expression RegEx created
  201.   with `NewRegEx'. These are `&' or `\0' for the whole matched
  202.   expression (if OnlySub is not set) and `\1' .. `\9' for the n'th
  203.   parenthesized subexpression. Returns 0 if it does not contain any,
  204.   and the number of references and quoted characters if it does. If
  205.   an invalid reference (i.e. a number bigger than the number of
  206.   subexpressions in RegEx) is found, it returns the negative value
  207.   of the (first) invalid reference. }
  208. function  FindSubExpressionReferences (var RegEx : RegExType; const s : String; OnlySub : Boolean) : Integer; asmname '_p_find_subexpressionreferences_regex';
  209.  
  210. { Replaces (sub)expression references in ReplaceStr by the actual
  211.   (sub)expressions and unquotes quoted characters. To be used after
  212.   the regular expression RegEx created with `NewRegEx' was matched
  213.   against s successfully with `MatchRegEx' or `MatchRegExFrom'. }
  214. function  ReplaceSubExpressionReferences (var RegEx : RegExType; const s, ReplaceStr : String) : TString; asmname '_p_replace_subexpressionreferences_regex';
  215.  
  216. { Returns the string for a regular expression that matches exactly
  217.   one character out of the given set. It can be combined with the
  218.   usual operators to form more complex expressions. }
  219. function  CharSet2RegEx (const Characters : CharSet) : TString; asmname '_p_CharSet2RegEx';
  220.  
  221. implementation
  222.  
  223. {$L rx, regexc.c}
  224.  
  225. procedure CNewRegEx       (var RegEx : RegExType; Expression : CString; ExpressionLength : Integer; ExtendedRegEx, CaseInsensitive, NewLines : Boolean); asmname '_p_new_regex';
  226. function  CMatchRegExFrom (var RegEx : RegExType; aString : CString; StrLength : Integer; NotBeginningOfLine, NotEndOfLine : Boolean; From : Integer) : Boolean; asmname '_p_match_regex_from';
  227.  
  228. procedure NewRegEx (var RegEx : RegExType; const Expression : String; ExtendedRegEx, CaseInsensitive, NewLines : Boolean);
  229. begin
  230.   CNewRegEx (RegEx, Expression, Length (Expression), ExtendedRegEx, CaseInsensitive, NewLines);
  231.   if RegEx.ErrorInternal = nil then
  232.     RegEx.Error := nil
  233.   else
  234.     RegEx.Error := NewString (CString2String (RegEx.ErrorInternal))
  235. end;
  236.  
  237. function MatchRegEx (var RegEx : RegExType; const s : String; NotBeginningOfLine, NotEndOfLine : Boolean) : Boolean;
  238. begin
  239.   MatchRegEx := CMatchRegExFrom (RegEx, s, Length (s), NotBeginningOfLine, NotEndOfLine, 1)
  240. end;
  241.  
  242. function MatchRegExFrom (var RegEx : RegExType; const s : String; NotBeginningOfLine, NotEndOfLine : Boolean; From : Integer) : Boolean;
  243. begin
  244.   MatchRegExFrom := CMatchRegExFrom (RegEx, s, Length (s), (From <> 1) or NotBeginningOfLine, NotEndOfLine, From)
  245. end;
  246.  
  247. function RegExPosFrom (const Expression : String; ExtendedRegEx, CaseInsensitive : Boolean; const s : String; From : Integer) = MatchPosition : Integer;
  248. var RegEx : RegExType;
  249. begin
  250.   MatchPosition := 0;
  251.   NewRegEx (RegEx, Expression, ExtendedRegEx, CaseInsensitive, False);
  252.   if (RegEx.Error = nil) and MatchRegExFrom (RegEx, s, False, False, From) then
  253.     GetMatchRegEx (RegEx, 0, MatchPosition, null);
  254.   DisposeRegEx (RegEx)
  255. end;
  256.  
  257. function FindSubExpressionReferences (var RegEx : RegExType; const s : String; OnlySub : Boolean) : Integer;
  258. var
  259.   i, j : Integer;
  260.   ch : Char;
  261. begin
  262.   j := 0;
  263.   i := 1;
  264.   while i <= Length (s) do
  265.     begin
  266.       case s [i] of
  267.         '&' : if not OnlySub then Inc (j);
  268.         '\' : if i = Length (s) then
  269.                 Inc (j)
  270.               else
  271.                 begin
  272.                   ch := s [i + 1];
  273.                   if (ch in ['u', 'l']) and (i + 1 < Length (s)) then
  274.                     begin
  275.                       Inc (i);
  276.                       ch := s [i + 1]
  277.                     end;
  278.                   if (ch in ['0' .. '9']) and
  279.                     (Ord (ch) - Ord ('0') > RegEx.SubExpressions) then
  280.                     return - (Ord (ch) - Ord ('0'))
  281.                   else
  282.                     begin
  283.                       if not OnlySub or (ch <> '0') then Inc (j);
  284.                       Inc (i)
  285.                     end
  286.                 end;
  287.       end;
  288.       Inc (i)
  289.     end;
  290.   FindSubExpressionReferences := j
  291. end;
  292.  
  293. function ReplaceSubExpressionReferences (var RegEx : RegExType; const s, ReplaceStr : String) = Res : TString;
  294. var i : Integer;
  295.  
  296.   procedure DoReplace (l, n : Integer; CaseMode : Char);
  297.   var
  298.     MatchPosition, MatchLength : Integer;
  299.     st : TString;
  300.   begin
  301.     GetMatchRegEx (RegEx, n, MatchPosition, MatchLength);
  302.     Delete (Res, i, l);
  303.     if (n <= RegEx.SubExpressions) and (MatchPosition > 0) (*@@rx-1.5 bug*)and(MatchPosition + MatchLength - 1 <= Length (s)) then
  304.       begin
  305.         st := Copy (s, MatchPosition, MatchLength);
  306.         case CaseMode of
  307.           'u' : UpCaseString (st);
  308.           'l' : LoCaseString (st);
  309.         end;
  310.         Insert (st, Res, i);
  311.         Inc (i, MatchLength)
  312.       end;
  313.     Dec (i)
  314.   end;
  315.  
  316. begin
  317.   Res := ReplaceStr;
  318.   i := 1;
  319.   while i <= Length (Res) do
  320.     begin
  321.       case Res [i] of
  322.         '&' : DoReplace (1, 0, #0);
  323.         '\' : if (i < Length (Res)) and (Res [i + 1] in ['0' .. '9']) then
  324.                 DoReplace (2, Ord (Res [i + 1]) - Ord ('0'), #0)
  325.               else if (i + 1 < Length (Res)) and (Res [i + 1] in ['u', 'l']) and (Res [i + 2] in ['0' .. '9']) then
  326.                 DoReplace (3, Ord (Res [i + 2]) - Ord ('0'), Res [i + 1])
  327.               else
  328.                 Delete (Res, i, 1);
  329.       end;
  330.       Inc (i)
  331.     end
  332. end;
  333.  
  334. function CharSet2RegEx (const Characters : CharSet) = s : TString;
  335. var
  336.   i : Integer;
  337.   c, c2 : Char;
  338.   s2, s3 : String (1) = '';
  339. begin
  340.   if Characters = [] then return '[^' + Low (Char) + '-' + High (Char) + ']';
  341.   if Characters = ['^'] then return '\^'; { A `^' alone cannot be handled within `[]'! }
  342.   if Characters = ['^', '-'] then return '[-^]';
  343.   s := '';
  344.   i := Ord (Low (Char));
  345.   { we cannot use a Char for the loop, because it would overflow }
  346.   while i <= Ord (High (Char)) do
  347.     begin
  348.       c := Chr (i);
  349.       if c in Characters then
  350.         case c of
  351.           ']' : s := c + s; { `]' must come first }
  352.           '^' : s2 := c; { `^' must not come first }
  353.           '-' : s3 := c; { `-' must come last (or first) }
  354.           else
  355.             c2 := c;
  356.             while (c2 < High (c)) and (Succ (c2) in Characters) do Inc (c2);
  357.             if c2 = ']' then Dec (c2); { `x-]' would be interpreted as a closing bracket }
  358.             if c2 <= Succ (c) then
  359.               s := s + c
  360.             else
  361.               begin
  362.                 s := s + c + '-' + c2;
  363.                 c := c2
  364.               end
  365.         end;
  366.       i := Ord (c) + 1
  367.     end;
  368.   s := '[' + s + s2 + s3 + ']'
  369. end;
  370.  
  371. end.
  372.