------------------------------------------------------------------------------ -- -- -- GNAT LIBRARY COMPONENTS -- -- -- -- G N A T . S P I T B O L . P A T T E R N S -- -- -- -- S p e c -- -- -- -- Copyright (C) 1997-2024, AdaCore -- -- -- -- GNAT is free software; you can redistribute it and/or modify it under -- -- terms of the GNU General Public License as published by the Free Soft- -- -- ware Foundation; either version 3, or (at your option) any later ver- -- -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- -- or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- -- GNAT was originally developed by the GNAT team at New York University. -- -- Extensive contributions were provided by Ada Core Technologies Inc. -- -- -- ------------------------------------------------------------------------------ -- SPITBOL-like pattern construction and matching -- This child package of GNAT.SPITBOL provides a complete implementation -- of the SPITBOL-like pattern construction and matching operations. This -- package is based on Macro-SPITBOL created by Robert Dewar. ------------------------------------------------------------ -- Summary of Pattern Matching Packages in GNAT Hierarchy -- ------------------------------------------------------------ -- There are three related packages that perform pattern matching functions. -- the following is an outline of these packages, to help you determine -- which is best for your needs. -- GNAT.Regexp (files g-regexp.ads/g-regexp.adb) -- This is a simple package providing Unix-style regular expression -- matching with the restriction that it matches entire strings. It -- is particularly useful for file name matching, and in particular -- it provides "globbing patterns" that are useful in implementing -- unix or DOS style wildcard matching for file names. -- GNAT.Regpat (files g-regpat.ads/g-regpat.adb) -- This is a more complete implementation of Unix-style regular -- expressions, copied from the original V7 style regular expression -- library written in C by Henry Spencer. It is functionally the -- same as this library, and uses the same internal data structures -- stored in a binary compatible manner. -- GNAT.Spitbol.Patterns (files g-spipat.ads/g-spipat.adb) -- This is a completely general pattern matching package based on the -- pattern language of SNOBOL4, as implemented in SPITBOL. The pattern -- language is modeled on context free grammars, with context sensitive -- extensions that provide full (type 0) computational capabilities. with Ada.Strings.Maps; use Ada.Strings.Maps; with Ada.Text_IO; use Ada.Text_IO; package GNAT.Spitbol.Patterns is pragma Elaborate_Body; ------------------------------- -- Pattern Matching Tutorial -- ------------------------------- -- A pattern matching operation (a call to one of the Match subprograms) -- takes a subject string and a pattern, and optionally a replacement -- string. The replacement string option is only allowed if the subject -- is a variable. -- The pattern is matched against the subject string, and either the -- match fails, or it succeeds matching a contiguous substring. If a -- replacement string is specified, then the subject string is modified -- by replacing the matched substring with the given replacement. -- Concatenation and Alternation -- ============================= -- A pattern consists of a series of pattern elements. The pattern is -- built up using either the concatenation operator: -- A & B -- which means match A followed immediately by matching B, or the -- alternation operator: -- A or B -- which means first attempt to match A, and then if that does not -- succeed, match B. -- There is full backtracking, which means that if a given pattern -- element fails to match, then previous alternatives are matched. -- For example if we have the pattern: -- (A or B) & (C or D) & (E or F) -- First we attempt to match A, if that succeeds, then we go on to try -- to match C, and if that succeeds, we go on to try to match E. If E -- fails, then we try F. If F fails, then we go back and try matching -- D instead of C. Let's make this explicit using a specific example, -- and introducing the simplest kind of pattern element, which is a -- literal string. The meaning of this pattern element is simply to -- match the characters that correspond to the string characters. Now -- let's rewrite the above pattern form with specific string literals -- as the pattern elements: -- ("ABC" or "AB") & ("DEF" or "CDE") & ("GH" or "IJ") -- The following strings will be attempted in sequence: -- ABC . DEF . GH -- ABC . DEF . IJ -- ABC . CDE . GH -- ABC . CDE . IJ -- AB . DEF . GH -- AB . DEF . IJ -- AB . CDE . GH -- AB . CDE . IJ -- Here we use the dot simply to separate the pieces of the string -- matched by the three separate elements. -- Moving the Start Point -- ====================== -- A pattern is not required to match starting at the first character -- of the string, and is not required to match to the end of the string. -- The first attempt does indeed attempt to match starting at the first -- character of the string, trying all the possible alternatives. But -- if all alternatives fail, then the starting point of the match is -- moved one character, and all possible alternatives are attempted at -- the new anchor point. -- The entire match fails only when every possible starting point has -- been attempted. As an example, suppose that we had the subject -- string -- "ABABCDEIJKL" -- matched using the pattern in the previous example: -- ("ABC" or "AB") & ("DEF" or "CDE") & ("GH" or "IJ") -- would succeed, after two anchor point moves: -- "ABABCDEIJKL" -- ^^^^^^^ -- matched -- section -- This mode of pattern matching is called the unanchored mode. It is -- also possible to put the pattern matcher into anchored mode by -- setting the global variable Anchored_Mode to True. This will cause -- all subsequent matches to be performed in anchored mode, where the -- match is required to start at the first character. -- We will also see later how the effect of an anchored match can be -- obtained for a single specified anchor point if this is desired. -- Other Pattern Elements -- ====================== -- In addition to strings (or single characters), there are many special -- pattern elements that correspond to special predefined alternations: -- Arb Matches any string. First it matches the null string, and -- then on a subsequent failure, matches one character, and -- then two characters, and so on. It only fails if the -- entire remaining string is matched. -- Bal Matches a non-empty string that is parentheses balanced -- with respect to ordinary () characters. Examples of -- balanced strings are "ABC", "A((B)C)", and "A(B)C(D)E". -- Bal matches the shortest possible balanced string on the -- first attempt, and if there is a subsequent failure, -- attempts to extend the string. -- Cancel Immediately aborts the entire pattern match, signalling -- failure. This is a specialized pattern element, which is -- useful in conjunction with some of the special pattern -- elements that have side effects. -- Fail The null alternation. Matches no possible strings, so it -- always signals failure. This is a specialized pattern -- element, which is useful in conjunction with some of the -- special pattern elements that have side effects. -- Fence Matches the null string at first, and then if a failure -- causes alternatives to be sought, aborts the match (like -- a Cancel). Note that using Fence at the start of a pattern -- has the same effect as matching in anchored mode. -- Rest Matches from the current point to the last character in -- the string. This is a specialized pattern element, which -- is useful in conjunction with some of the special pattern -- elements that have side effects. -- Succeed Repeatedly matches the null string (it is equivalent to -- the alternation ("" or "" or "" ....). This is a special -- pattern element, which is useful in conjunction with some -- of the special pattern elements that have side effects. -- Pattern Construction Functions -- ============================== -- The following functions construct additional pattern elements -- Any(S) Where S is a string, matches a single character that is -- any one of the characters in S. Fails if the current -- character is not one of the given set of characters. -- Arbno(P) Where P is any pattern, matches any number of instances -- of the pattern, starting with zero occurrences. It is -- thus equivalent to ("" or (P & ("" or (P & ("" ....)))). -- The pattern P may contain any number of pattern elements -- including the use of alternation and concatenation. -- Break(S) Where S is a string, matches a string of zero or more -- characters up to but not including a break character -- that is one of the characters given in the string S. -- Can match the null string, but cannot match the last -- character in the string, since a break character is -- required to be present. -- BreakX(S) Where S is a string, behaves exactly like Break(S) when -- it first matches, but if a string is successfully matched, -- then a subsequent failure causes an attempt to extend the -- matched string. -- Fence(P) Where P is a pattern, attempts to match the pattern P -- including trying all possible alternatives of P. If none -- of these alternatives succeeds, then the Fence pattern -- fails. If one alternative succeeds, then the pattern -- match proceeds, but on a subsequent failure, no attempt -- is made to search for alternative matches of P. The -- pattern P may contain any number of pattern elements -- including the use of alternation and concatenation. -- Len(N) Where N is a natural number, matches the given number of -- characters. For example, Len(10) matches any string that -- is exactly ten characters long. -- NotAny(S) Where S is a string, matches a single character that is -- not one of the characters of S. Fails if the current -- character is one of the given set of characters. -- NSpan(S) Where S is a string, matches a string of zero or more -- characters that is among the characters given in the -- string. Always matches the longest possible such string. -- Always succeeds, since it can match the null string. -- Pos(N) Where N is a natural number, matches the null string -- if exactly N characters have been matched so far, and -- otherwise fails. -- Rpos(N) Where N is a natural number, matches the null string -- if exactly N characters remain to be matched, and -- otherwise fails. -- Rtab(N) Where N is a natural number, matches characters from -- the current position until exactly N characters remain -- to be matched in the string. Fails if fewer than N -- unmatched characters remain in the string. -- Tab(N) Where N is a natural number, matches characters from -- the current position until exactly N characters have -- been matched in all. Fails if more than N characters -- have already been matched. -- Span(S) Where S is a string, matches a string of one or more -- characters that is among the characters given in the -- string. Always matches the longest possible such string. -- Fails if the current character is not one of the given -- set of characters. -- Recursive Pattern Matching -- ========================== -- The plus operator (+P) where P is a pattern variable, creates -- a recursive pattern that will, at pattern matching time, follow -- the pointer to obtain the referenced pattern, and then match this -- pattern. This may be used to construct recursive patterns. Consider -- for example: -- P := ("A" or ("B" & (+P))) -- On the first attempt, this pattern attempts to match the string "A". -- If this fails, then the alternative matches a "B", followed by an -- attempt to match P again. This second attempt first attempts to -- match "A", and so on. The result is a pattern that will match a -- string of B's followed by a single A. -- This particular example could simply be written as NSpan('B') & 'A', -- but the use of recursive patterns in the general case can construct -- complex patterns which could not otherwise be built. -- Pattern Assignment Operations -- ============================= -- In addition to the overall result of a pattern match, which indicates -- success or failure, it is often useful to be able to keep track of -- the pieces of the subject string that are matched by individual -- pattern elements, or subsections of the pattern. -- The pattern assignment operators allow this capability. The first -- form is the immediate assignment: -- P * S -- Here P is an arbitrary pattern, and S is a variable of type VString -- that will be set to the substring matched by P. This assignment -- happens during pattern matching, so if P matches more than once, -- then the assignment happens more than once. -- The deferred assignment operation: -- P ** S -- avoids these multiple assignments by deferring the assignment to the -- end of the match. If the entire match is successful, and if the -- pattern P was part of the successful match, then at the end of the -- matching operation the assignment to S of the string matching P is -- performed. -- The cursor assignment operation: -- Setcur(N'Access) -- assigns the current cursor position to the natural variable N. The -- cursor position is defined as the count of characters that have been -- matched so far (including any start point moves). -- Finally the operations * and ** may be used with values of type -- Text_IO.File_Access. The effect is to do a Put_Line operation of -- the matched substring. These are particularly useful in debugging -- pattern matches. -- Deferred Matching -- ================= -- The pattern construction functions (such as Len and Any) all permit -- the use of pointers to natural or string values, or functions that -- return natural or string values. These forms cause the actual value -- to be obtained at pattern matching time. This allows interesting -- possibilities for constructing dynamic patterns as illustrated in -- the examples section. -- In addition the (+S) operator may be used where S is a pointer to -- string or function returning string, with a similar deferred effect. -- A special use of deferred matching is the construction of predicate -- functions. The element (+P) where P is an access to a function that -- returns a Boolean value, causes the function to be called at the -- time the element is matched. If the function returns True, then the -- null string is matched, if the function returns False, then failure -- is signalled and previous alternatives are sought. -- Deferred Replacement -- ==================== -- The simple model given for pattern replacement (where the matched -- substring is replaced by the string given as the third argument to -- Match) works fine in simple cases, but this approach does not work -- in the case where the expression used as the replacement string is -- dependent on values set by the match. -- For example, suppose we want to find an instance of a parenthesized -- character, and replace the parentheses with square brackets. At first -- glance it would seem that: -- Match (Subject, '(' & Len (1) * Char & ')', '[' & Char & ']'); -- would do the trick, but that does not work, because the third -- argument to Match gets evaluated too early, before the call to -- Match, and before the pattern match has had a chance to set Char. -- To solve this problem we provide the deferred replacement capability. -- With this approach, which of course is only needed if the pattern -- involved has side effects, is to do the match in two stages. The -- call to Match sets a pattern result in a variable of the private -- type Match_Result, and then a subsequent Replace operation uses -- this Match_Result object to perform the required replacement. -- Using this approach, we can now write the above operation properly -- in a manner that will work: -- M : Match_Result; -- ... -- Match (Subject, '(' & Len (1) * Char & ')', M); -- Replace (M, '[' & Char & ']'); -- As with other Match cases, there is a function and procedure form -- of this match call. A call to Replace after a failed match has no -- effect. Note that Subject should not be modified between the calls. -- Examples of Pattern Matching -- ============================ -- First a simple example of the use of pattern replacement to remove -- a line number from the start of a string. We assume that the line -- number has the form of a string of decimal digits followed by a -- period, followed by one or more spaces. -- Digs : constant Pattern := Span("0123456789"); -- Lnum : constant Pattern := Pos(0) & Digs & '.' & Span(' '); -- Now to use this pattern we simply do a match with a replacement: -- Match (Line, Lnum, ""); -- which replaces the line number by the null string. Note that it is -- also possible to use an Ada.Strings.Maps.Character_Set value as an -- argument to Span and similar functions, and in particular all the -- useful constants 'in Ada.Strings.Maps.Constants are available. This -- means that we could define Digs as: -- Digs : constant Pattern := Span(Decimal_Digit_Set); -- The style we use here, of defining constant patterns and then using -- them is typical. It is possible to build up patterns dynamically, -- but it is usually more efficient to build them in pieces in advance -- using constant declarations. Note in particular that although it is -- possible to construct a pattern directly as an argument for the -- Match routine, it is much more efficient to preconstruct the pattern -- as we did in this example. -- Now let's look at the use of pattern assignment to break a -- string into sections. Suppose that the input string has two -- unsigned decimal integers, separated by spaces or a comma, -- with spaces allowed anywhere. Then we can isolate the two -- numbers with the following pattern: -- Num1, Num2 : aliased VString; -- B : constant Pattern := NSpan(' '); -- N : constant Pattern := Span("0123456789"); -- T : constant Pattern := -- NSpan(' ') & N * Num1 & Span(" ,") & N * Num2; -- The match operation Match (" 124, 257 ", T) would assign the -- string 124 to Num1 and the string 257 to Num2. -- Now let's see how more complex elements can be built from the -- set of primitive elements. The following pattern matches strings -- that have the syntax of Ada 95 based literals: -- Digs : constant Pattern := Span(Decimal_Digit_Set); -- UDigs : constant Pattern := Digs & Arbno('_' & Digs); -- Edig : constant Pattern := Span(Hexadecimal_Digit_Set); -- UEdig : constant Pattern := Edig & Arbno('_' & Edig); -- Bnum : constant Pattern := Udigs & '#' & UEdig & '#'; -- A match against Bnum will now match the desired strings, e.g. -- it will match 16#123_abc#, but not a#b#. However, this pattern -- is not quite complete, since it does not allow colons to replace -- the pound signs. The following is more complete: -- Bchar : constant Pattern := Any("#:"); -- Bnum : constant Pattern := Udigs & Bchar & UEdig & Bchar; -- but that is still not quite right, since it allows # and : to be -- mixed, and they are supposed to be used consistently. We solve -- this by using a deferred match. -- Temp : aliased VString; -- Bnum : constant Pattern := -- Udigs & Bchar * Temp & UEdig & (+Temp) -- Here the first instance of the base character is stored in Temp, and -- then later in the pattern we rematch the value that was assigned. -- For an example of a recursive pattern, let's define a pattern -- that is like the built in Bal, but the string matched is balanced -- with respect to square brackets or curly brackets. -- The language for such strings might be defined in extended BNF as -- ELEMENT ::= -- | '[' BALANCED_STRING ']' -- | '{' BALANCED_STRING '}' -- BALANCED_STRING ::= ELEMENT {ELEMENT} -- Here we use {} to indicate zero or more occurrences of a term, as -- is common practice in extended BNF. Now we can translate the above -- BNF into recursive patterns as follows: -- Element, Balanced_String : aliased Pattern; -- . -- . -- . -- Element := NotAny ("[]{}") -- or -- ('[' & (+Balanced_String) & ']') -- or -- ('{' & (+Balanced_String) & '}'); -- Balanced_String := Element & Arbno (Element); -- Note the important use of + here to refer to a pattern not yet -- defined. Note also that we use assignments precisely because we -- cannot refer to as yet undeclared variables in initializations. -- Now that this pattern is constructed, we can use it as though it -- were a new primitive pattern element, and for example, the match: -- Match ("xy[ab{cd}]", Balanced_String * Current_Output & Fail); -- will generate the output: -- x -- xy -- xy[ab{cd}] -- y -- y[ab{cd}] -- [ab{cd}] -- a -- ab -- ab{cd} -- b -- b{cd} -- {cd} -- c -- cd -- d -- Note that the function of the fail here is simply to force the -- pattern Balanced_String to match all possible alternatives. Studying -- the operation of this pattern in detail is highly instructive. -- Finally we give a rather elaborate example of the use of deferred -- matching. The following declarations build up a pattern which will -- find the longest string of decimal digits in the subject string. -- Max, Cur : VString; -- Loc : Natural; -- function GtS return Boolean is -- begin -- return Length (Cur) > Length (Max); -- end GtS; -- Digit : constant Character_Set := Decimal_Digit_Set; -- Digs : constant Pattern := Span(Digit); -- Find : constant Pattern := -- "" * Max & Fence & -- initialize Max to null -- BreakX (Digit) & -- scan looking for digits -- ((Span(Digit) * Cur & -- assign next string to Cur -- (+GtS'Unrestricted_Access) & -- check size(Cur) > Size(Max) -- Setcur(Loc'Access)) -- if so, save location -- * Max) & -- and assign to Max -- Fail; -- seek all alternatives -- As we see from the comments here, complex patterns like this take -- on aspects of sequential programs. In fact they are sequential -- programs with general backtracking. In this pattern, we first use -- a pattern assignment that matches null and assigns it to Max, so -- that it is initialized for the new match. Now BreakX scans to the -- next digit. Arb would do here, but BreakX will be more efficient. -- Once we have found a digit, we scan out the longest string of -- digits with Span, and assign it to Cur. The deferred call to GtS -- tests if the string we assigned to Cur is the longest so far. If -- not, then failure is signalled, and we seek alternatives (this -- means that BreakX will extend and look for the next digit string). -- If the call to GtS succeeds then the matched string is assigned -- as the largest string so far into Max and its location is saved -- in Loc. Finally Fail forces the match to fail and seek alternatives, -- so that the entire string is searched. -- If the pattern Find is matched against a string, the variable Max -- at the end of the pattern will have the longest string of digits, -- and Loc will be the starting character location of the string. For -- example, Match("ab123cd4657ef23", Find) will assign "4657" to Max -- and 11 to Loc (indicating that the string ends with the eleventh -- character of the string). -- Note: the use of Unrestricted_Access to reference GtS will not -- be needed if GtS is defined at the outer level, but definitely -- will be necessary if GtS is a nested function (in which case of -- course the scope of the pattern Find will be restricted to this -- nested scope, and this cannot be checked, i.e. use of the pattern -- outside this scope is erroneous). Generally it is a good idea to -- define patterns and the functions they call at the outer level -- where possible, to avoid such problems. -- Correspondence with Pattern Matching in SPITBOL -- =============================================== -- Generally the Ada syntax and names correspond closely to SPITBOL -- syntax for pattern matching construction. -- The basic pattern construction operators are renamed as follows: -- Spitbol Ada -- (space) & -- | or -- $ * -- . ** -- The Ada operators were chosen so that the relative precedences of -- these operators corresponds to that of the Spitbol operators, but -- as always, the use of parentheses is advisable to clarify. -- The pattern construction operators all have similar names except for -- Spitbol Ada -- Abort Cancel -- Rem Rest -- where we have clashes with Ada reserved names -- Ada requires the use of 'Access to refer to functions used in the -- pattern match, and often the use of 'Unrestricted_Access may be -- necessary to get around the scope restrictions if the functions -- are not declared at the outer level. -- The actual pattern matching syntax is modified in Ada as follows: -- Spitbol Ada -- X Y Match (X, Y); -- X Y = Z Match (X, Y, Z); -- and pattern failure is indicated by returning a Boolean result from -- the Match function (True for success, False for failure). ----------------------- -- Type Declarations -- ----------------------- type Pattern is private; -- Type representing a pattern. This package provides a complete set of -- operations for constructing patterns that can be used in the pattern -- matching operations provided. type Boolean_Func is access function return Boolean with Favor_Top_Level; -- General Boolean function type. When this type is used as a formal -- parameter type in this package, it indicates a deferred predicate -- pattern. The function will be called when the pattern element is -- matched and failure signalled if False is returned. type Natural_Func is access function return Natural with Favor_Top_Level; -- General Natural function type. When this type is used as a formal -- parameter type in this package, it indicates a deferred pattern. -- The function will be called when the pattern element is matched -- to obtain the currently referenced Natural value. type VString_Func is access function return VString with Favor_Top_Level; -- General VString function type. When this type is used as a formal -- parameter type in this package, it indicates a deferred pattern. -- The function will be called when the pattern element is matched -- to obtain the currently referenced string value. subtype PString is String; -- This subtype is used in the remainder of the package to indicate a -- formal parameter that is converted to its corresponding pattern, -- i.e. a pattern that matches the characters of the string. subtype PChar is Character; -- Similarly, this subtype is used in the remainder of the package to -- indicate a formal parameter that is converted to its corresponding -- pattern, i.e. a pattern that matches this one character. subtype VString_Var is VString; subtype Pattern_Var is Pattern; -- These synonyms are used as formal parameter types to a function where, -- if the language allowed, we would use in out parameters, but we are -- not allowed to have in out parameters for functions. Instead we pass -- actuals which must be variables, and with a bit of trickery in the -- body, manage to interpret them properly as though they were indeed -- in out parameters. pragma Warnings (Off, VString_Var); pragma Warnings (Off, Pattern_Var); -- We turn off warnings for these two types so that when variables are used -- as arguments in this context, warnings about them not being assigned in -- the source program will be suppressed. -------------------------------- -- Basic Pattern Construction -- -------------------------------- function "&" (L : Pattern; R : Pattern) return Pattern; function "&" (L : PString; R : Pattern) return Pattern; function "&" (L : Pattern; R : PString) return Pattern; function "&" (L : PChar; R : Pattern) return Pattern; function "&" (L : Pattern; R : PChar) return Pattern; -- Pattern concatenation. Matches L followed by R function "or" (L : Pattern; R : Pattern) return Pattern; function "or" (L : PString; R : Pattern) return Pattern; function "or" (L : Pattern; R : PString) return Pattern; function "or" (L : PString; R : PString) return Pattern; function "or" (L : PChar; R : Pattern) return Pattern; function "or" (L : Pattern; R : PChar) return Pattern; function "or" (L : PChar; R : PChar) return Pattern; function "or" (L : PString; R : PChar) return Pattern; function "or" (L : PChar; R : PString) return Pattern; -- Pattern alternation. Creates a pattern that will first try to match -- L and then on a subsequent failure, attempts to match R instead. ---------------------------------- -- Pattern Assignment Functions -- ---------------------------------- function "*" (P : Pattern; Var : VString_Var) return Pattern; function "*" (P : PString; Var : VString_Var) return Pattern; function "*" (P : PChar; Var : VString_Var) return Pattern; -- Matches P, and if the match succeeds, assigns the matched substring -- to the given VString variable Var. This assignment happens as soon as -- the substring is matched, and if the pattern P1 is matched more than -- once during the course of the match, then the assignment will occur -- more than once. function "**" (P : Pattern; Var : VString_Var) return Pattern; function "**" (P : PString; Var : VString_Var) return Pattern; function "**" (P : PChar; Var : VString_Var) return Pattern; -- Like "*" above, except that the assignment happens at most once -- after the entire match is completed successfully. If the match -- fails, then no assignment takes place. ---------------------------------- -- Deferred Matching Operations -- ---------------------------------- function "+" (Str : VString_Var) return Pattern; -- Here Str must be a VString variable. This function constructs a -- pattern which at pattern matching time will access the current -- value of this variable, and match against these characters. function "+" (Str : VString_Func) return Pattern; -- Constructs a pattern which at pattern matching time calls the given -- function, and then matches against the string or character value -- that is returned by the call. function "+" (P : Pattern_Var) return Pattern; -- Here P must be a Pattern variable. This function constructs a -- pattern which at pattern matching time will access the current -- value of this variable, and match against the pattern value. function "+" (P : Boolean_Func) return Pattern; -- Constructs a predicate pattern function that at pattern matching time -- calls the given function. If True is returned, then the pattern matches. -- If False is returned, then failure is signalled. -------------------------------- -- Pattern Building Functions -- -------------------------------- function Arb return Pattern; -- Constructs a pattern that will match any string. On the first attempt, -- the pattern matches a null string, then on each successive failure, it -- matches one more character, and only fails if matching the entire rest -- of the string. function Arbno (P : Pattern) return Pattern; function Arbno (P : PString) return Pattern; function Arbno (P : PChar) return Pattern; -- Pattern repetition. First matches null, then on a subsequent failure -- attempts to match an additional instance of the given pattern. -- Equivalent to (but more efficient than) P & ("" or (P & ("" or ... function Any (Str : String) return Pattern; function Any (Str : VString) return Pattern; function Any (Str : Character) return Pattern; function Any (Str : Character_Set) return Pattern; function Any (Str : not null access VString) return Pattern; function Any (Str : VString_Func) return Pattern; -- Constructs a pattern that matches a single character that is one of -- the characters in the given argument. The pattern fails if the current -- character is not in Str. function Bal return Pattern; -- Constructs a pattern that will match any non-empty string that is -- parentheses balanced with respect to the normal parentheses characters. -- Attempts to extend the string if a subsequent failure occurs. function Break (Str : String) return Pattern; function Break (Str : VString) return Pattern; function Break (Str : Character) return Pattern; function Break (Str : Character_Set) return Pattern; function Break (Str : not null access VString) return Pattern; function Break (Str : VString_Func) return Pattern; -- Constructs a pattern that matches a (possibly null) string which -- is immediately followed by a character in the given argument. This -- character is not part of the matched string. The pattern fails if -- the remaining characters to be matched do not include any of the -- characters in Str. function BreakX (Str : String) return Pattern; function BreakX (Str : VString) return Pattern; function BreakX (Str : Character) return Pattern; function BreakX (Str : Character_Set) return Pattern; function BreakX (Str : not null access VString) return Pattern; function BreakX (Str : VString_Func) return Pattern; -- Like Break, but the pattern attempts to extend on a failure to find -- the next occurrence of a character in Str, and only fails when the -- last such instance causes a failure. function Cancel return Pattern; -- Constructs a pattern that immediately aborts the entire match function Fail return Pattern; -- Constructs a pattern that always fails function Fence return Pattern; -- Constructs a pattern that matches null on the first attempt, and then -- causes the entire match to be aborted if a subsequent failure occurs. function Fence (P : Pattern) return Pattern; -- Constructs a pattern that first matches P. If P fails, then the -- constructed pattern fails. If P succeeds, then the match proceeds, -- but if subsequent failure occurs, alternatives in P are not sought. -- The idea of Fence is that each time the pattern is matched, just -- one attempt is made to match P, without trying alternatives. function Len (Count : Natural) return Pattern; function Len (Count : not null access Natural) return Pattern; function Len (Count : Natural_Func) return Pattern; -- Constructs a pattern that matches exactly the given number of -- characters. The pattern fails if fewer than this number of characters -- remain to be matched in the string. function NotAny (Str : String) return Pattern; function NotAny (Str : VString) return Pattern; function NotAny (Str : Character) return Pattern; function NotAny (Str : Character_Set) return Pattern; function NotAny (Str : not null access VString) return Pattern; function NotAny (Str : VString_Func) return Pattern; -- Constructs a pattern that matches a single character that is not -- one of the characters in the given argument. The pattern Fails if -- the current character is in Str. function NSpan (Str : String) return Pattern; function NSpan (Str : VString) return Pattern; function NSpan (Str : Character) return Pattern; function NSpan (Str : Character_Set) return Pattern; function NSpan (Str : not null access VString) return Pattern; function NSpan (Str : VString_Func) return Pattern; -- Constructs a pattern that matches the longest possible string -- consisting entirely of characters from the given argument. The -- string may be empty, so this pattern always succeeds. function Pos (Count : Natural) return Pattern; function Pos (Count : not null access Natural) return Pattern; function Pos (Count : Natural_Func) return Pattern; -- Constructs a pattern that matches the null string if exactly Count -- characters have already been matched, and otherwise fails. function Rest return Pattern; -- Constructs a pattern that always succeeds, matching the remaining -- unmatched characters in the pattern. function Rpos (Count : Natural) return Pattern; function Rpos (Count : not null access Natural) return Pattern; function Rpos (Count : Natural_Func) return Pattern; -- Constructs a pattern that matches the null string if exactly Count -- characters remain to be matched in the string, and otherwise fails. function Rtab (Count : Natural) return Pattern; function Rtab (Count : not null access Natural) return Pattern; function Rtab (Count : Natural_Func) return Pattern; -- Constructs a pattern that matches from the current location until -- exactly Count characters remain to be matched in the string. The -- pattern fails if fewer than Count characters remain to be matched. function Setcur (Var : not null access Natural) return Pattern; -- Constructs a pattern that matches the null string, and assigns the -- current cursor position in the string. This value is the number of -- characters matched so far. So it is zero at the start of the match. function Span (Str : String) return Pattern; function Span (Str : VString) return Pattern; function Span (Str : Character) return Pattern; function Span (Str : Character_Set) return Pattern; function Span (Str : not null access VString) return Pattern; function Span (Str : VString_Func) return Pattern; -- Constructs a pattern that matches the longest possible string -- consisting entirely of characters from the given argument. The -- string cannot be empty, so the pattern fails if the current -- character is not one of the characters in Str. function Succeed return Pattern; -- Constructs a pattern that succeeds matching null, both on the first -- attempt, and on any rematch attempt, i.e. it is equivalent to an -- infinite alternation of null strings. function Tab (Count : Natural) return Pattern; function Tab (Count : not null access Natural) return Pattern; function Tab (Count : Natural_Func) return Pattern; -- Constructs a pattern that from the current location until Count -- characters have been matched. The pattern fails if more than Count -- characters have already been matched. --------------------------------- -- Pattern Matching Operations -- --------------------------------- -- The Match function performs an actual pattern matching operation. -- The versions with three parameters perform a match without modifying -- the subject string and return a Boolean result indicating if the -- match is successful or not. The Anchor parameter is set to True to -- obtain an anchored match in which the pattern is required to match -- the first character of the string. In an unanchored match, which is -- the default, successive attempts are made to match the given pattern -- at each character of the subject string until a match succeeds, or -- until all possibilities have failed. -- Note that pattern assignment functions in the pattern may generate -- side effects, so these functions are not necessarily pure. Anchored_Mode : Boolean := False; -- This global variable can be set True to cause all subsequent pattern -- matches to operate in anchored mode. In anchored mode, no attempt is -- made to move the anchor point, so that if the match succeeds it must -- succeed starting at the first character. Note that the effect of -- anchored mode may be achieved in individual pattern matches by using -- Fence or Pos(0) at the start of the pattern. Pattern_Stack_Overflow : exception; -- Exception raised if internal pattern matching stack overflows. This -- is typically the result of runaway pattern recursion. If there is a -- genuine case of stack overflow, then either the match must be broken -- down into simpler steps, or the stack limit must be reset. Stack_Size : constant Positive := 2000; -- Size used for internal pattern matching stack. Increase this size if -- complex patterns cause Pattern_Stack_Overflow to be raised. -- Simple match functions. The subject is matched against the pattern. -- Any immediate or deferred assignments or writes are executed, and -- the returned value indicates whether or not the match succeeded. function Match (Subject : VString; Pat : Pattern) return Boolean; function Match (Subject : VString; Pat : PString) return Boolean; function Match (Subject : String; Pat : Pattern) return Boolean; function Match (Subject : String; Pat : PString) return Boolean; -- Replacement functions. The subject is matched against the pattern. -- Any immediate or deferred assignments or writes are executed, and -- the returned value indicates whether or not the match succeeded. -- If the match succeeds, then the matched part of the subject string -- is replaced by the given Replace string. function Match (Subject : VString_Var; Pat : Pattern; Replace : VString) return Boolean; function Match (Subject : VString_Var; Pat : PString; Replace : VString) return Boolean; function Match (Subject : VString_Var; Pat : Pattern; Replace : String) return Boolean; function Match (Subject : VString_Var; Pat : PString; Replace : String) return Boolean; -- Simple match procedures. The subject is matched against the pattern. -- Any immediate or deferred assignments or writes are executed. No -- indication of success or failure is returned. procedure Match (Subject : VString; Pat : Pattern); procedure Match (Subject : VString; Pat : PString); procedure Match (Subject : String; Pat : Pattern); procedure Match (Subject : String; Pat : PString); -- Replacement procedures. The subject is matched against the pattern. -- Any immediate or deferred assignments or writes are executed. No -- indication of success or failure is returned. If the match succeeds, -- then the matched part of the subject string is replaced by the given -- Replace string. procedure Match (Subject : in out VString; Pat : Pattern; Replace : VString); procedure Match (Subject : in out VString; Pat : PString; Replace : VString); procedure Match (Subject : in out VString; Pat : Pattern; Replace : String); procedure Match (Subject : in out VString; Pat : PString; Replace : String); -- Deferred Replacement type Match_Result is private; -- Type used to record result of pattern match subtype Match_Result_Var is Match_Result; -- This synonyms is used as a formal parameter type to a function where, -- if the language allowed, we would use an in out parameter, but we are -- not allowed to have in out parameters for functions. Instead we pass -- actuals which must be variables, and with a bit of trickery in the -- body, manage to interpret them properly as though they were indeed -- in out parameters. function Match (Subject : VString_Var; Pat : Pattern; Result : Match_Result_Var) return Boolean; procedure Match (Subject : in out VString; Pat : Pattern; Result : out Match_Result); procedure Replace (Result : in out Match_Result; Replace : VString); -- Given a previous call to Match which set Result, performs a pattern -- replacement if the match was successful. Has no effect if the match -- failed. This call should immediately follow the Match call. ------------------------ -- Debugging Routines -- ------------------------ -- Debugging pattern matching operations can often be quite complex, -- since there is no obvious way to trace the progress of the match. -- The declarations in this section provide some debugging assistance. Debug_Mode : Boolean := False; -- This global variable can be set True to generate debugging on all -- subsequent calls to Match. The debugging output is a full trace of -- the actions of the pattern matcher, written to Standard_Output. The -- level of this information is intended to be comprehensible at the -- abstract level of this package declaration. However, note that the -- use of this switch often generates large amounts of output. function "*" (P : Pattern; Fil : File_Access) return Pattern; function "*" (P : PString; Fil : File_Access) return Pattern; function "*" (P : PChar; Fil : File_Access) return Pattern; function "**" (P : Pattern; Fil : File_Access) return Pattern; function "**" (P : PString; Fil : File_Access) return Pattern; function "**" (P : PChar; Fil : File_Access) return Pattern; -- These are similar to the corresponding pattern assignment operations -- except that instead of setting the value of a variable, the matched -- substring is written to the appropriate file. This can be useful in -- following the progress of a match without generating the full amount -- of information obtained by setting Debug_Mode to True. Terminal : constant File_Access := Standard_Error; Output : constant File_Access := Standard_Output; -- Two handy synonyms for use with the above pattern write operations -- Finally we have some routines that are useful for determining what -- patterns are in use, particularly if they are constructed dynamically. function Image (P : Pattern) return String; function Image (P : Pattern) return VString; -- This procedures yield strings that corresponds to the syntax needed -- to create the given pattern using the functions in this package. The -- form of this string is such that it could actually be compiled and -- evaluated to yield the required pattern except for references to -- variables and functions, which are output using one of the following -- forms: -- -- access Natural NP(16#...#) -- access Pattern PP(16#...#) -- access VString VP(16#...#) -- -- Natural_Func NF(16#...#) -- VString_Func VF(16#...#) -- -- where 16#...# is the hex representation of the integer address that -- corresponds to the given access value procedure Dump (P : Pattern); -- This procedure writes information about the pattern to Standard_Out. -- The format of this information is keyed to the internal data structures -- used to implement patterns. The information provided by Dump is thus -- more precise than that yielded by Image, but is also a bit more obscure -- (i.e. it cannot be interpreted solely in terms of this spec, you have -- to know something about the data structures). ------------------ -- Private Part -- ------------------ private type PE; -- Pattern element, a pattern is a complex structure of PE's. This type -- is defined and described in the body of this package. type PE_Ptr is access all PE; -- Pattern reference. PE's use PE_Ptr values to reference other PE's type Pattern is new Controlled with record Stk : Natural := 0; -- Maximum number of stack entries required for matching this -- pattern. See description of pattern history stack in body. P : PE_Ptr := null; -- Pointer to initial pattern element for pattern end record; pragma Finalize_Storage_Only (Pattern); procedure Adjust (Object : in out Pattern); -- Adjust routine used to copy pattern objects procedure Finalize (Object : in out Pattern); -- Finalization routine used to release storage allocated for a pattern type VString_Ptr is access all VString; type Match_Result is record Var : VString_Ptr; -- Pointer to subject string. Set to null if match failed Start : Natural := 1; -- Starting index position (1's origin) of matched section of -- subject string. Only valid if Var is non-null. Stop : Natural := 0; -- Ending index position (1's origin) of matched section of -- subject string. Only valid if Var is non-null. end record; pragma Volatile (Match_Result); -- This ensures that the Result parameter is passed by reference, so -- that we can play our games with the bogus Match_Result_Var parameter -- in the function case to treat it as though it were an in out parameter. end GNAT.Spitbol.Patterns;