home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 277.lha / PatternLibrary / Pattern / pattern.txt < prev    next >
Encoding:
Text File  |  1989-08-08  |  14.7 KB  |  318 lines

  1.             Pattern Matching Library
  2.  
  3.  
  4. Introduction
  5.  
  6. This library (pattern.library) provides a shareable AmigaDOS library
  7. implementing the standard AmigaDOS pattern matcher. It contains two
  8. entries - 'Compile', which compiles a pattern into a form which allows the
  9. matcher to more efficiently work with the pattern, and 'Match', which uses
  10. both the original pattern and the compiled form to match subject strings.
  11. As is standard with AmigaDOS libraries, all parameters are passed in
  12. registers. Thus, although this version of the library is written in Draco,
  13. it can be called from C, Modula-2, assembler, etc. just as easily as the
  14. CBM-supplied libraries. The supplied "fd" file contains the information
  15. needed to build compiler-specific interface stubs. "pattern.lib" contains
  16. the stubs for Draco. To use the library, copy "pattern.library" to your
  17. LIBS: directory. If you use Draco, also copy "pattern.lib" to your DRLIB:
  18. directory and "pattern.g" to your DRINC: directory.
  19.  
  20.  
  21. Using the Library
  22.  
  23. Communication with the library is done via a 'PatternState' structure,
  24. which contains all of the state which the library needs to perform its
  25. operations. This structure plays a role similar to that played by a Window
  26. or Screen structure with regards to 'intuition.library'. Since the library
  27. must be fully shareable, all working storage must be provided by the
  28. caller. The shared structure, a Draco declaration of which is provided in
  29. file 'pattern.g', is as follows:
  30.  
  31.     ps_pattern - pointer to the pattern to be matched. This must be present
  32.     on calls to both Compile and Match.
  33.  
  34.     ps_compiled - points to a vector of longwords which will contain the
  35.     compiled form of the pattern. The vector must contain at least one
  36.     more longword than there are characters in the pattern.
  37.  
  38.     ps_activeStates - points to a vector of longwords which Match needs to
  39.     maintain the set of active states. It should be the same length as
  40.     the ps_compiled vector. (For most patterns, this amount is not
  41.     needed, but for some strange patterns, it is.)
  42.  
  43.     ps_length - 32 bit length of the pattern string. This library allows
  44.     the pattern and subject strings to contain any characters - no
  45.     special end-of-string character is reserved; thus the lengths of
  46.     both the pattern and the subject string are required.
  47.  
  48.     ps_ignoreCase - set to indicate that capitalization of letters should
  49.     be ignored when comparing them. This is an 8 bit field. A value of
  50.     0 means do not ignore case, any other value means do ignore case.
  51.  
  52.     ps_error - filled in by Compile to indicate the success/failure of the
  53.     pattern compilation. The values are 8 bits in size, and are:
  54.  
  55.         0 - compilation successful
  56.         1 - the pattern ended prematurely, e.g. missing ')'
  57.         2 - unexpected ')', e.g. missing '('
  58.         3 - unexpected '|', e.g. after '('
  59.         4 - missing ')'
  60.  
  61.     ps_pad - 2 bytes of unused padding
  62.  
  63.     ps_position, ps_stateCount - 4 byte fields used internally
  64.  
  65.     ps_char, ps_end, ps_matched - 1 byte fields used internally
  66.  
  67. Entry "Compile" has one argument - the address of the above structure. It
  68. returns no result - ps_error should be checked for the compilation status.
  69. The address of the structure should be passed to Compile in register A0.
  70. Entry "Match" has three arguments. The first is the address of the above
  71. structure, the second is the address of the subject string, and the third
  72. is the length of the subject string (32 bit value). It returns 'true' (1)
  73. if the match succeeded, else 'false' (0). Only the low 8 bits of the result
  74. are guaranteed to be set. The arguments should be passed to Match in
  75. registers A0, A1 and D0 respectively.
  76.  
  77. Thus, to use the library, something like the following is done (putting the
  78. parameters in the proper registers is done by the interface stubs):
  79.  
  80.     [100] char pattern, subject;
  81.     [100] ulong compiled, states;
  82.     PatternState_t ps;
  83.  
  84.     if OpenPatternLibrary(0) ~= nil then
  85.     ps.ps_compiled := &compiled[0];
  86.     ps.ps_activeStates := &states[0];
  87.     ps.ps_ignoreCase := false;
  88.     write("Enter pattern: ");
  89.     readln(&pattern[0]);
  90.     ps.ps_pattern := &pattern[0];
  91.     ps.ps_length := CharsLen(&pattern[0]);
  92.     Compile(&ps);
  93.     if ps.ps_error = pse_ok then
  94.         writeln("Enter subjects, one per line:");
  95.         while readln(&subject[0]) do
  96.         if Match(&ps, &subject[0], CharsLen(&subject[0])) then
  97.             writeln("Subject matched the pattern.");
  98.         else
  99.             writeln("Subject did not match the pattern.");
  100.         fi;
  101.         od;
  102.     else
  103.         writeln("The pattern did not compile - it is badly formed.");
  104.     fi;
  105.     ClosePatternLibrary();
  106.     else
  107.     writeln("Could not open 'pattern.library'.");
  108.     writeln("Does it exist in your LIBS: directory?");
  109.     fi;
  110.  
  111. Code fragments for C, Modula-2 and assembler would be similar. Note that
  112. the compiled form of a pattern can be used for many matches without having
  113. to change any of the PatternState structure.
  114.  
  115.  
  116. General Operation of the Library
  117.  
  118. The original form of this matcher is the BCPL version (which presumeably
  119. exists somewhere in AmigaDOS as part of the BCPL run-time system). It can
  120. be found in:
  121.  
  122.     "A Compact Function for Regular Expression Pattern Matching"
  123.     by Martin Richards    (then at the University of Cambridge)
  124.     Software - Practice and Experience, Vol 9, 527-534 (1979)
  125.  
  126. The article contains a description of how the compiler and matcher operate,
  127. along with an example compilation and match. The BCPL code has very few
  128. comments and uses one or two character identifiers that have little or no
  129. meaning to the reader. The author states that the code is straightforward
  130. and needs no further description. I found that to be far from the truth,
  131. and view my main contribution here to be that of simply explaining what the
  132. code does. I have borrowed the example below, along with its explanation,
  133. from the article.
  134.  
  135. A C translation of the BCPL code was done by:
  136.  
  137.     Jeff Lydiatt
  138.     Richmond, B.C. Canada
  139.  
  140. I have also seen another C version which first converts the C string of the
  141. pattern into a BCPL-style string with a length at the beginning, but my
  142. listing of that version doesn't have a name on it. I haven't tried either
  143. version, but they appear to be correct. Neither is suitable for use as a
  144. shared library, however, and both have limitations on the length of the
  145. pattern which they can handle without error. The BCPL version appears to
  146. have a similar limitation - I suspect it would break if given a string of
  147. length 255 containing a long alternation of single characters (which would
  148. not be used in real life, since it would be necessary to repeat some char-
  149. aracters). I tried this with the CLI, and it's not clear what is happen-
  150. ing - somewhere over about a line of input like "dir a|a|a|..." I get
  151. "Bad Arguments" - it could be the matcher overwriting something, or it
  152. could be DIR checking the string length.
  153.  
  154. The first thing to note in the implementation is the cheating I did. The
  155. user-level include file describes the first three elements of the structure
  156. as simple pointers. In the implementation include file, they are pointers
  157. to arrays. This is done to allow array indexing from the pointers, rather
  158. than requiring explicit pointer manipulation.
  159.  
  160.  
  161. Operation of the Pattern Compiler
  162.  
  163. The compiler uses a very simple recursive-descent parser to parse the
  164. pattern. During parsing, it builds the compiled form of the pattern in the
  165. ps_compiled vector. This vector contains indices into the pattern (and also
  166. into the vector, since the two correspond). The first longword in the
  167. vector does not correspond to any pattern character. It is the head of any
  168. top-level alternation chain (described later).
  169.  
  170. The compiled vector is essentially a series of pointers showing all of the
  171. routes of matches through the pattern. For simple characters with no
  172. special meaning, the entries simply point to the next character. For
  173. alternations using '|', the entries make up a linked list of the alterna-
  174. tives. For '#', an entry points back to the '#', indicating the looping
  175. nature of the construct. The entries for specific pattern characters are:
  176.  
  177.     | - index of next '|' in alternation, or 0 if none. These pointers are
  178.     followed on match failure. The pointers from the alternatives will
  179.     all point to the subpattern to be tried next - they are followed on
  180.     match success.
  181.     ( - index of first enclosed '|', or 0 if none
  182.     ) - compiled vector value not used
  183.     # - index of the subpattern to be tried when the subpattern being
  184.     repeated does not match anymore. The pointers from the subpattern
  185.     being repeated all point to the '#', allowing further repetitions.
  186.     ' - index of next subpattern to try on success. The index associated
  187.     with the escaped character itself is not used.
  188.     % - index of next subpattern to try on success
  189.       - other, non-special characters all have pointers to the next
  190.     subpattern to try on success
  191.  
  192. The special 0th element of the compiled form is the head of any chain of
  193. alternatives at the top level. It corresponds to an implicit '(' around the
  194. entire pattern. As an example, taken from the above article, the pattern
  195.  
  196.     (A|B|#?:|'#)#(0|1)|$
  197.  
  198. compiles into the following:
  199.  
  200.     position  pattern  compiled  notes
  201.     ----------------------------------
  202.     0          19     outermost alternation list
  203.     1     (       3     enclosed alternation list
  204.     2     A      13     successor to A (followed if A is matched)
  205.     3     |       5     pointer to next alternative
  206.     4     B      13     successor to B
  207.     5     |       9     pointer to next alternative
  208.     6     #       8     successor to #?
  209.     7     ?       6     successor to ? (repetition loop)
  210.     8     :      13     successor to : (and #?:)
  211.     9     |       0     no more alternatives
  212.        10     '      13     successor to '#
  213.        11     #       -     not used
  214.        12     )       -     not used
  215.        13     #       0     successor to #(0|1) (successful match)
  216.        14     (      16     enclosed alternation list
  217.        15     0      13     successor to 0 (repetition loop)
  218.        16     |       0     no more alternatives
  219.        17     1      13     successor to 1 (repetition loop)
  220.        18     )       -     not used
  221.        19     |       0     end of outermost alternation
  222.        20     $       0     successor to $ (successful match)
  223.  
  224.  
  225. Operation of the Pattern Matcher
  226.  
  227. The matcher uses the additional storage of the ps_activeStates vector. It
  228. stores in here the indexes into the pattern of those parts of the pattern
  229. that can be reached by matching the subject string upto the current point.
  230. Note that 1-origin indexing into the vector is used. This is so that the
  231. indexes correspond to the 1-origin indexes into the compiled pattern form.
  232. These indexes are states in the automata represented by the pattern. At the
  233. beginning of the match, the only active states are those represented by the
  234. top level alternatives of the match. E.g., for the above pattern, the two
  235. states active at the very beginning are states 1 and 19. The way the
  236. matcher operates, however, requires that it first add on all of the states
  237. that immediately follow from these initial ones. State 19 is a '|', so the
  238. part of the pattern to actually examine is state 20, the '$'. Thus state 20
  239. is added to the set of active states. State 1 is itself an alternation
  240. list, so all of the alternatives must be added. This adds states 3, 5, and
  241. 9. Similary, these alteratives are just markers for the actual subpatterns,
  242. so states 4, 6, and 10 are added. State 6 is a repetition, which can occur
  243. zero or more times, so its successor, state 8, must also be added. Its
  244. subpattern, state 7, must also be added. Thus, before we even look at the
  245. first character of the subject string, the following states are active:
  246.  
  247.     1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 19, 20
  248.  
  249. The matcher does not care what order it examines the states in, so they are
  250. not sorted, but simply added as a breadth-first scan through the existing
  251. list. Thus, for the above pattern, the set as seen by the matcher is:
  252.  
  253.     1, 19, 2*, 3, 20*, 4*, 5, 6, 9, 7*, 8*, 10*
  254.  
  255. States marked with '*' are those which actually contain characters to match
  256. against, and are not just control states. They are the only ones that can
  257. contribute to the set of states for the next time around.
  258.  
  259. The matcher will now examine the subject string, one character at a time.
  260. For each character, it will check all of the active states, to see if they
  261. succeed. It will build a new state set containing all of the states which
  262. are successors to those that succeed. Expansion of the set, as above, will
  263. happen before examining successive characters of the subject. If state 0 is
  264. reached when the subject string is exhausted, then the match has been
  265. successful (ALL of the pattern must be matched).
  266.  
  267. Consider the subject string "AB:1". The first character, 'A', matches only
  268. states 2* and 7*. They, after expansion, yield the set
  269.  
  270.     13, 6, 14, 7*, 8*, 15*, 16, 17*
  271.  
  272. The end-of-pattern state, 0, is also active since it is a successor to
  273. state 13, but its presence is maintained only be setting 'ps_matched'. The
  274. next subject character, 'B', matches state 7* only, thus we get the set
  275.  
  276.     6, 7*, 8*
  277.  
  278. The next character, ':', matches both states 7* and 8*, giving
  279.  
  280.     6, 13, 7*, 8*, 14, 15*, 16, 17*
  281.  
  282. The final character, '1' satisfies states 7* and 17*, which will yield the
  283. same set again. But, we have used up the subject string, and the special
  284. state 0 (ps_matched) is set, so the total match succeeds.
  285.  
  286.  
  287. Final Notes
  288.  
  289. This Draco implementation of the compiler and matcher are longer than the
  290. BCPL and C variants. There are a number of reasons for this. One is that
  291. the conversion to a shareable library precludes the use of global
  292. variables, so many references to the PatternState structure are used.
  293. A second is that this version of the compiler returns more information
  294. about a failed compilation. A third is that no special end-of-string
  295. character has been taken away from the set that can be matched. This
  296. required a number of tests for end-of-string that were avoided in the other
  297. versions. The final reason of course is that this code is one of those
  298. convoluted ones in which a return statement and case-statement fallthrough
  299. are useful, and Draco does not provide those.
  300.  
  301. I've been using the pattern library as a test library for my disassembler,
  302. so I've noticed the Draco code produced. In response to that, I've added a
  303. few more peepholes to the compiler, producing version Beta 1.3. If anyone
  304. wants this new version, let me know - if there is enough demand I can send
  305. it out (e.g. upload to CompuServe, send to Fred Fish, whatever). I've also
  306. hand-tuned the source to the pattern library. Those interested may want to
  307. study the techniques used - they can be applied to your own programs. My
  308. disassembler, which should be going out in a couple of days (after I
  309. document it and the shared library it uses), will be of help to those
  310. wishing to wring the last ounce out of their code.
  311.  
  312. Note: BLink version 5.7, which is what I have been distributing with Draco,
  313. insists on putting out an extra hunk at the beginning of the output file.
  314. This is a problem with libraries, so I've been using a newer version (from
  315. Lattice C V4.0) to build the libraries. It, (version 7.2) insists on not
  316. passing through HUNK_SYMBOLs for HUNK_BSSs, so I use the old version for
  317. linking everything else. Sigh.
  318.