home *** CD-ROM | disk | FTP | other *** search
/ The Best of Select: Windows 95 Special 1 / WINDOWS95_1.ISO / utils / w32-rex / regina / srccode / src / parsing.c < prev    next >
Text File  |  1995-06-30  |  23KB  |  630 lines

  1. #ifndef lint
  2. static char *RCSid = "$Id: parsing.c,v 1.13 1993/05/10 06:11:00 anders Exp anders $";
  3. #endif
  4.  
  5. /*
  6.  *  The Regina Rexx Interpreter
  7.  *  Copyright (C) 1992-1994  Anders Christensen <anders@pvv.unit.no>
  8.  *
  9.  *  This library is free software; you can redistribute it and/or
  10.  *  modify it under the terms of the GNU Library General Public
  11.  *  License as published by the Free Software Foundation; either
  12.  *  version 2 of the License, or (at your option) any later version.
  13.  *
  14.  *  This library is distributed in the hope that it will be useful,
  15.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17.  *  Library General Public License for more details.
  18.  *
  19.  *  You should have received a copy of the GNU Library General Public
  20.  *  License along with this library; if not, write to the Free
  21.  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  */
  23.  
  24. /*
  25.     This code modified for Win32 port by Ataman Software, Inc. June 29, 1995.
  26. */
  27.  
  28. /*
  29.  * This implementation of the REXX parse template is quite near to
  30.  * what Cowlishaw specified in his book, but there is one very
  31.  * important difference. Cowlishaw sees a template as patterns and
  32.  * variables, with patterns in each end (and some of the patterns
  33.  * and/or the variables might be empty).
  34.  * 
  35.  * The concept here is any sequence of variables and patterns, and 
  36.  * the system is parsed into two levels and interpreted as such. 
  37.  * First there is the level of patterns, which is anchored to 
  38.  * particular locations in the string to be parsed. 
  39.  *
  40.  * When the anchor-points are determined, the variables are filled 
  41.  * in with the parts of the string that comes between each anchor. 
  42.  * Here is an example:
  43.  *
  44.  *    parse value 'That is a parse string!' with a b 'is' c d 'i' e
  45.  *
  46.  * In this example, there are four anchors in the string, the two 
  47.  * patterns 'is' and 'i', and the two implicit anchors start-of-string 
  48.  * and end-of-string. They anchor particular locations in the string 
  49.  * to the lists of variables: (a b), (c d) and (e). At the first level
  50.  * the anchors are found and the text between them are deternmined 
  51.  * (SOS=Start-Of-String, EOS=End-Of-String):
  52.  *
  53.  *                            That is a parse string!
  54.  *    anchors            (SOS)-----is------------i---(EOS)
  55.  *    strings in-between      <a b>  <<<<c d>>>>> <e>
  56.  *
  57.  * Now, we have a set of substrings, and to each substring, there is 
  58.  * possibly empty set of variable which are to receive its value from
  59.  * the contents of that substring. Doing that, we get:
  60.  *
  61.  *    (a b) = 'That '        ==> a='That'   b=' '
  62.  *    (c d) = ' a parse str' ==> c='a'      c=' parse str'
  63.  *    (e)   = 'ng!'          ==> e='ng!'
  64.  *
  65.  * To some extent, one might say that there are one anchor between 
  66.  * each variable, since these match a sequence of blank characters. 
  67.  * However, these have less priority than the explicit patterns. 
  68.  *
  69.  * This file makes available three functions:
  70.  *
  71.  *   doparse()      parses a single string into a single template
  72.  *   parseargtree() parses a set of strings into a set of templates
  73.  *   bmstrstr()     implementation of Boyer-Moore string search
  74.  *
  75.  * Only one external variable are referenced in the code, 'currlevel'
  76.  * in order to determine the currently tracing mode, and to cache the 
  77.  * value for the duration of the tracing of one template.
  78.  */
  79.  
  80. #include "rexx.h"
  81. #include <ctype.h>
  82. #include <string.h>
  83. #include <stdio.h>  /* Stupid SunOS acc uses stderr in assert(), yuk! */
  84. #include <assert.h>
  85. #include "strings.h"
  86.  
  87. /* 
  88.  * Using the 'tracestat' field of 'currlevel' in order to determine 
  89.  * the current tracing mode. 
  90.  */
  91. extern proclevel currlevel ;
  92.  
  93. /* 
  94.  * This file-scope variable is use to cache the value of the setting 
  95.  * of the current trace mode. It is initialized at the entrypoint of 
  96.  * each parse statement. However, if functionality is expanded, so that 
  97.  * expression can occur inside a template, then this variable must be
  98.  * checked in order to ensure that it is up-to-date after the expression
  99.  * has been evaluated. 
  100.  */
  101. static int traceparse = (-1) ;
  102.  
  103. /*
  104.  * We have to seek for the nullstring rather often, so we make it a 
  105.  * file-scope variable, in order to optimize a bit. This way, we have 
  106.  * to neither allocate space for it, or deallocate it. 
  107.  */
  108. static streng nullstring={ 0, 0, "" } ;
  109.  
  110.  
  111. /*
  112.  * This is an implementation of the strstr() function, for the 'streng' 
  113.  * datatype. The algorithm is a straight forward implementation of the 
  114.  * Boyer-Moore string search algorithm. 
  115.  *
  116.  * The input parameters are first the string to search in, then the start
  117.  * position of the search in that string, and then the string to search 
  118.  * for. If the whole of the input string is to be searched, then the 
  119.  * start position is set to zero. If start position is set to one, then 
  120.  * a match will not be found if it includes the first character of the
  121.  * the string 'heystack' as the first character of the match.  
  122.  *
  123.  * The returned value is an integer, which will be the index of the 
  124.  * first character of 'heystack->value' that match. If no match is 
  125.  * found, -1 is returned, if a match is found in the start of 'heystack' 
  126.  * then 0 is returned. This means that both the 'start' parameter and 
  127.  * the return value are zero-based. 
  128.  */
  129. int bmstrstr( streng *heystack, int Offset, streng *needle ) 
  130. {
  131.    unsigned char *TmpPtr ;
  132.    int NeedLen, HeyLen ;
  133.    unsigned char *NeedPtr, *HeyPtr, *HeyBase ;
  134.    unsigned int NextChr[256] ;
  135.    int Tmp ;
  136.  
  137.    unsigned char *eptr ;
  138.  
  139.    NeedPtr = (unsigned char*)needle->value ;
  140.    NeedLen = needle->len ;
  141.  
  142.    HeyBase = heystack->value ;
  143.    HeyLen = heystack->len - Offset ;
  144.    HeyPtr = HeyBase + Offset ;
  145.  
  146.    /* 
  147.     * First, sometimes we want to search for one character only. Although
  148.     * Boyer-Moore works for that case, it is hardly efficient. So, if the 
  149.     * search pattern has length one, we use the ANSI C memchr() to find
  150.     * the string. That function is likely to to be more efficient, maybe
  151.     * even written in hand-optimized assembly. 
  152.     */
  153.    if (NeedLen==1)
  154.    {
  155.       TmpPtr = memchr( HeyPtr, *NeedPtr, HeyLen ) ;
  156.       if (TmpPtr)
  157.          return (TmpPtr-HeyBase) ;
  158.       else
  159.          return -1 ;
  160.    }
  161.  
  162.    /* 
  163.     * First we test that the pattern isn't longer than the string to 
  164.     * search in. If the pattern is long, builing the 'next' table will
  165.     * take a lot of time, so we really want to avoid any action in that 
  166.     * case.
  167.     */
  168.    else if (HeyLen < NeedLen)
  169.       return -1 ;
  170.    /*
  171.     * OK, here is the 'real' search. Basically, it consists of two 
  172.     * phases: first a table (next) is built up, and then the string 
  173.     * is searched using the table to decide how far to hop if a match 
  174.     * was not found. 
  175.     *
  176.     * Let's recount some of the theory behind Boyer-Moore search. If you 
  177.     * got a pattern, P, and string to search in, S, then the first match
  178.     * may be S[1..len(P)]. To verify whether that is actually a match, 
  179.     * we have to iterate through all the characters, i.e. over len(P). 
  180.     *
  181.     * If that is a match, then return the correct position, else we must
  182.     * continue. Here comes the 'tricky' part. Suppose P is 'abcccd' and 
  183.     * that S is 'gddeebfgghhhiiijjj'. Now we check character S[len(P)] 
  184.     * and depending on its value we can determine something about the 
  185.     * possibiltiy of having a match starting somewhere in the area from 
  186.     * S[1] to S[len(P)]. Clearly, if S[len(P)] is a character not in P, 
  187.     * then no match can occur in this area. Else, determine the first, 
  188.     * of the next possible match, and move forward to check that. 
  189.     */
  190.    else
  191.    {
  192.       /* 
  193.        * First, we have to set up the table to use during the search. 
  194.        * Initially, we fill the whole table with the 'default' value, and 
  195.        * then we patch the values which should have other values in the
  196.        * loop following the call to memset().
  197.        */
  198.       for (Tmp=0; Tmp<256; NextChr[Tmp++]=NeedLen) ;
  199.       TmpPtr = NeedPtr ;
  200.       for (Tmp=NeedLen; Tmp; )
  201.          NextChr[*(TmpPtr++)] = --Tmp ; 
  202.  
  203.       eptr = HeyPtr + HeyLen - NeedLen-- ;
  204.       
  205.       while (1)
  206.       {
  207.          if (HeyPtr>eptr)
  208.             return -1 ;
  209.  
  210.          HeyPtr += Tmp = NextChr[*(HeyPtr+NeedLen)] ;
  211.          if (!Tmp)
  212.          {
  213.             Tmp=NeedLen ;
  214.             TmpPtr = HeyPtr++ ;
  215.             while (1)
  216.             {
  217.                if (--Tmp<0)
  218.                   return (TmpPtr - HeyBase ) ;
  219.  
  220.                if (TmpPtr[Tmp] != NeedPtr[Tmp])
  221.                   break ; 
  222.             }
  223.          }
  224.       }   
  225.    }
  226. }
  227.  
  228.  
  229. static streng *handle_var( nodeptr this ) 
  230. {
  231.    if (this->type == X_HEAD_SYMBOL)
  232.       return fix_compound( this, NULL ) ;
  233.    else
  234.       return shortcut( this ) ;
  235. }
  236.  
  237. /*
  238.  * This parses a part of the source string, determined by (start)
  239.  * and (stop) into the variables of the (this) tree. Only variables
  240.  * are handled, not patterns. It will be called by doparse() to fit a
  241.  * string into a set of variables and placeholder. 
  242.  *
  243.  * Start points to the first character to be parsed, while stop points
  244.  * to the last character to be parsed. If start==stop, only one char 
  245.  * is to be parsed into variables and placeholders. 
  246.  * 
  247.  * There is no returnvalue from this function, it is only called to 
  248.  * achieve the special effects of setting variables and tracing 
  249.  * variables and placeholders. Actually, this routine used to be 
  250.  * tailrecursive, but I changed it into a goto. 
  251.  *
  252.  * The variables and placeholders to parse ares stored in a chained 
  253.  * of pointers, chased through p[0]. 'start' is a ptr to the first 
  254.  * characters to be parsed by this function. 'stop' is a ptr to the 
  255.  * last character to be parsed by this function. Note that is points
  256.  * to the last char to be parsed, not to the character after the last
  257.  * character to be parsed. 
  258.  */
  259. static void doparse3( streng *string, nodeptr this, char *start, char *stop ) 
  260. {
  261.    char *rstart ;
  262.    streng *tptr ;
  263.  
  264.    recurse:
  265.    /*
  266.     * Since we are going to put the next word of the input string 
  267.     * into a variable, we must first find that value. The if tests 
  268.     * whether we are the last word before an 'anchor' to be parsed. 
  269.     * if so, use the rest of the string. If not, scan forwards to 
  270.     * identify a word.
  271.     */
  272.    if (this->p[0]) 
  273.    {
  274.       /*
  275.        * We shall only fetch out one word. First skip leading spaces, 
  276.        * then find the end of the next word.
  277.        */
  278.       for (;(start<=stop)&&(isspace(*start));start++) ;
  279.       for (rstart=start;(rstart<=stop)&&(!isspace(*rstart));rstart++) ;
  280.    }
  281.    else 
  282.       /* 
  283.        * We are last word, use rest of string as value. 
  284.        */
  285.       rstart = stop + 1 ;
  286.  
  287.    /* 
  288.     * We have found the word to be parsed into something. Now we have
  289.     * to decide what to parse it into. There are two possibilities.
  290.     * It might be a variable, or just a placeholder (dot). The two are
  291.     * handled in each part of the if-statement below. The setting of 
  292.     * 'tptr' could be lifted out of the if-statement to save space, 
  293.     * but at the cost of more CPU.
  294.     */
  295.    tptr = Str_ncre( start, rstart-start<0 ? 0 : rstart-start ) ;
  296.    if (this->type==X_TPL_SYMBOL) 
  297.    {
  298.       if (traceparse)
  299.          tracevalue(tptr,'>') ; 
  300.  
  301.       if ( this->p[1]->type == X_HEAD_SYMBOL)
  302.          fix_compound( this->p[1], tptr ) ;
  303.       else
  304.          setshortcut( this->p[1], tptr ) ;
  305.    }
  306.    else if (traceparse && (rstart-start)>=0)
  307.    {
  308.       /*
  309.        * It's a placeholder, actually, we skip this if we arn't 
  310.        * tracing. No harm is done if tracevalue() is called unnecessary,
  311.        * but this way we save three function calls whenever we're not 
  312.        * doing TRACE INT.
  313.        *
  314.        * The three operations below do: 1) get the value to be traced,
  315.        * 2) then output the trace information, 3) and then free the
  316.        * temporary storage. 
  317.        */
  318.  
  319.       tracevalue(tptr,'.') ;
  320.       Free_string( tptr ) ;
  321.    }
  322.  
  323.    /*
  324.     * Now, this should actually be a tail recursion, but since be don't
  325.     * trust compilers, we are optimizeing it ourselves.
  326.     */
  327.     if ((this = this->p[0]))
  328.     {
  329.        start = rstart + 1 ;
  330.        goto recurse ;
  331.     }
  332. }
  333.  
  334.  
  335.  
  336. /*
  337.  * This routine parses a string (source) into the template that is 
  338.  * specified by the structure in the (this) tree. (start) is the 
  339.  * start postion into the (source) string. It handles find the next
  340.  * template, and handles the aread between two templates. 
  341.  *
  342.  * It calls it self recursively to handle a sequence of templates.
  343.  * Well, actually, it used to call it self recuseively, but the 
  344.  * tail recursion has been removed to improve efficiency. 
  345.  *
  346.  * A sequence of patterns must be chained together using p[1], while
  347.  * each pattern can contain the vars/placeholders as a chain linked
  348.  * in at p[0].
  349.  *
  350.  * 'source' is the string to be parsed, 'this' it a ptr to the top 
  351.  * of the parsetree that describes how 'source' after start'th 
  352.  * position is to be parsed. 'start' is a ptr to the first char in
  353.  * 'source' to be parsed by this part of the template. 'point' is a 
  354.  * ptr to the current positional point in 'source', i.e. if we find
  355.  * a positional pattern, we use point, else we use start. 
  356.  */
  357.  
  358. void doparse( streng *source, nodeptr this, int start, int point ) 
  359. {
  360.    int length, end, nextstart, solid ;
  361.    streng *pattern, *xtmp ; 
  362.    char tch ;
  363.  
  364.    nextstart = 0 ;  /* too keep gcc from complaining about uninitialized */
  365.    tch = currlevel->tracestat ;
  366.    traceparse = ((tch=='I') || (tch=='R')) ;
  367.  
  368. recurse:
  369.    /*
  370.     * Cache the length of source, to avoid chasing ponters later.
  371.     * Then make pattern default to the nullstring. The nullstring is
  372.     * so muched used, that we don't want to allocate and deallocate
  373.     * that all the time.
  374.     */
  375.    length = source->len ;
  376.    pattern = &nullstring ;
  377.  
  378.    /*
  379.     * There are two main cases, either this is the last pattern, in 
  380.     * which case we use the rest of the string. Or either there is 
  381.     * another pattern further out, in which case we have to find it. 
  382.     *
  383.     */
  384.    if (this->p[1]) 
  385.    {
  386.       /*
  387.        * We are not the last pattern, so first find the next pattern.
  388.        * First cache the type, so we don't chase pointers. There are
  389.        * two main choises: either seek for a string of some sort, or 
  390.        * use an offset of some sort. 
  391.        */
  392.       solid = this->p[1]->type ;
  393.       if ((solid==X_TPL_MVE)||(solid==X_TPL_VAR)) 
  394.       {
  395.          /* 
  396.           * The pattern to search for is either a literal string, or it
  397.           * is the value hold in a variable, set pattern to whatever
  398.           * it might be. Pattern previous points to a statically 
  399.           * allocated variable, so don't bother to deallocate.
  400.           */
  401.          if (solid==X_TPL_MVE) 
  402.             pattern = this->p[1]->name ;
  403.          else
  404.             pattern = handle_var( this->p[1]->p[0] ) ;
  405.          /*
  406.           * Then we must find where in the source string pattern occurs.
  407.           * If it don't occur there, we use the rest of the string, else
  408.           * we use the string up to, but not including, the first char
  409.           * that matched pattern. The 'bmstrstr' returns -1 for not 
  410.           * found, so correct that to rest-of-string. Also note that if
  411.           * the pattern is the nullstring, it should match the end of 
  412.           * the string. 
  413.           */
  414.          if (Str_len(pattern))
  415.          {
  416.             end = bmstrstr( source, start, pattern ) ;
  417.             if (end<0)
  418.             {
  419.                point = end = length ;
  420.                nextstart = end ;
  421.             }
  422.             else
  423.             {        
  424.                nextstart = end + Str_len(pattern) ;
  425.                point = end ;
  426.             }
  427.          }
  428.          else
  429.          {
  430.             nextstart = point = end = length ;
  431.          }
  432.  
  433.          /* 
  434.           * While 'end' marks how much to stuff into variables, nextstart
  435.           * marks where to start the search for the next pattern (if
  436.           * any). Remember that patterns "eat" up the part of the 
  437.           * parse string that they match. 
  438.           */
  439. /*       nextstart = end + Str_len(pattern) ; */
  440.       }
  441.       else
  442.       {
  443.          /*
  444.           * The next pattern to match is not a string to match, but a 
  445.           * positional movement, which will always be numeric, and if 
  446.           * it contains a sign, that should have been stripped off during
  447.           * parsing, so check that it is non-negative.
  448.           */
  449.          if (this->p[1]->name)
  450.             xtmp = this->p[1]->name ;
  451.          else
  452.             xtmp = handle_var( this->p[1]->p[0] ) ;
  453.  
  454.          end = atozpos( xtmp ) ;
  455.          assert( end >= 0 ) ;
  456.      
  457.          /* 
  458.           * Depending on what sort of positional movement, do the right 
  459.           * thing.
  460.           */
  461.          if (solid==X_NEG_OFFS) 
  462.          {
  463.             /*
  464.              * If it is a movement backwards, the concept goes something
  465.              * like move-it-foreward-to-a-backwards-position. That is,
  466.              * the string to be parsed continues forwards to the end of 
  467.              * the sting and stops there, while the nextposition wraps
  468.              * round to the start again. 
  469.              *
  470.              * Anyway, parse all the rest of the sting in this parse, and
  471.              * start on the specified position for the next parse.
  472.              */
  473.             start = point ;
  474.             nextstart = point - end ;
  475.             end = length ; 
  476.             if (nextstart < 0)
  477.                nextstart = 0 ;
  478.  
  479.             point = nextstart ;
  480.          }
  481.  
  482.          else if (solid==X_POS_OFFS)
  483.          {
  484.             /*
  485.              * If the movement is forward, it is simpler, just move the 
  486.              * position of both the end of this, and the start of next
  487.              * to the right point. 
  488.              */
  489.             start = point ;
  490.             nextstart = point + end ;
  491.             if (nextstart > length)
  492.                nextstart = length ;
  493.             end = nextstart ;
  494.             if (end<=start) 
  495.                end = length ;
  496.  
  497.             point = nextstart ;
  498.          }
  499.  
  500.          else if (solid==X_ABS_OFFS)
  501.          {
  502.             /*
  503.              * Same applies if the position is absolute, just move it. 
  504.              */
  505.             if ((end--)==0)
  506.                exiterror( ERR_INVALID_INTEGER ) ;
  507.  
  508.             if (end>length)
  509.                end = length ;
  510.  
  511.             point = nextstart = end ; 
  512.             if (end <= start)
  513.                end = length ;
  514.          }
  515.       }
  516.    }
  517.    else 
  518.       /* 
  519.        * We are last pattern to match, set the end of the string to 
  520.        * be parsed to the rest of the string available. 
  521.        */
  522.       end = nextstart = length ;
  523.  
  524.    /*
  525.     * Make sure that we didn't do anything illegal when we pushed 
  526.     * around on the value of end and nextstart. These should have been
  527.     * set correctly in the statements above. 
  528.     */
  529.    assert((0<=nextstart) && (nextstart<=length)) ;
  530.  
  531.    /* 
  532.     * Then handle end. It must be _after_ the last character in 
  533.     * the pattern, while it must not be larger than length. 
  534.     */
  535.    assert((start <= end) && (end <= length)) ;
  536.  
  537.    /* 
  538.     * Now we have marked off an area to be parsed, so call 'doparse3' to 
  539.     * put values into the variables. Note that end is decremented, 
  540.     * since doparse3 expects ptr to last char to use, not ptr to char
  541.     * after last char to use. 
  542.     */
  543.    if (this->p[0])
  544.       doparse3( source, this->p[0],source->value+start,source->value+(--end));
  545.  
  546.    /*
  547.     * Then make a tailrecursive call, or rather, simulate one. This 
  548.     * operation will take care of the next set of variables to be 
  549.     * parsed values into. 
  550.     */
  551.    if ((this=this->p[2]))
  552.    {
  553.       start = nextstart ;
  554.       goto recurse ;
  555.    }
  556. }
  557.  
  558.  
  559.  
  560. /*
  561.  * A single parse clause can parse multiple strings into multiple 
  562.  * patterns. Normally, this is only done using 'parse arg'. The 
  563.  * following piece of code parses multiples strings in an structure 
  564.  * of arguments into the matching pattern. 
  565.  *
  566.  * There are no limits on the number of arguments to be parsed, 
  567.  * other than memory. 
  568.  */
  569. void parseargtree( paramboxptr argbox, nodeptr this, int upper ) 
  570. {
  571.    streng *source ;
  572.  
  573.    /* 
  574.     * All templates in a list of template are connected though the 
  575.     * next field of the template.
  576.     */
  577.    for (; this; this=this->next)
  578.    {
  579.       assert(this->type==X_TPL_SOLID) ;
  580.  
  581.       /*
  582.        * Else, it is a tempate into which a string is to be parsed.
  583.        * That string is an argument, to first get that argument, 
  584.        * if it exist, else use the nullstring. Never bother about
  585.        * deallocating this string; either it is part of an arguemnt
  586.        * which is deallocated somewhere else, or it is the statically
  587.        * allocated nullstring.
  588.        */
  589.       if ((argbox)&&(argbox->value))
  590.          source = argbox->value ;
  591.       else
  592.          source = &nullstring ;
  593.  
  594.       /* 
  595.        * If we are using ARG, or PARSE UPPER ARG, we have to make 
  596.        * sure that the arguments are in upper case. Only one way to
  597.        * do that, and unfortunately, we have to copy the string 
  598.        * for the time being. Upper mode will therefore be slower 
  599.        * than normal mode. 
  600.        */
  601.       if (upper) 
  602.          source = upcase( Str_dup( source )) ;
  603.    
  604.       /*
  605.        * Now we have isolated a string in the right case, and a 
  606.        * ptr to the parse tree of the pattern, so we just have to 
  607.        * call the code to actually parse the string into the pattern.
  608.        */         
  609.       doparse( source, this, 0, 0 ) ;
  610.  
  611.       /* 
  612.        * If the mode was upper, then we must remember to deallocate
  613.        * the temporary variable in which we stored the upper case
  614.        * version of the string to parse. 
  615.        */   
  616.       if (upper)
  617.          Free_string( source ) ;
  618.  
  619.       /*
  620.        * And then, at last, we have to increment the pointer in the 
  621.        * chain of agruments, but only if there are more arguments to
  622.        * be parsed. 
  623.        */
  624.       if (argbox)
  625.          argbox = argbox->next ; 
  626.    }
  627. }
  628.  
  629.  
  630.