LLOOP Index | GSP Language | GSP Library | Framework Classes | Component Classes

block.gsp

This is the verbatim text of the file "block.gsp" part of the LLOOP package. The copyright remains with Michel MEHL. All rights reserved.



/**	
 * Reads any multi-line sequence of chars surrounded by start and end delimiters (i.e. any constant string). 
 */

token BlockToken extends class universal::String() 
  include <assert.h>, <ctype.h>
  alias "Block", "block"

  declare    /* Extra declarations in token class */
  {{
    bool readBlock(const char* pszStartDelimiter, 
		   const char* pszEndDelimiter,
		   std::istream& is,
		   bool bIgnoreCase = false		   
		   );
  }}   

  implement  /* Extra implementations in token class */
  {{
    bool BlockToken::readBlock(const char* pszStartDelimiter, 
			       const char* pszEndDelimiter,
			       std::istream& is,
			       bool bIgnoreCase
			       )
      {
	 cancelWhitespaceEating();
	
	 unsigned long uLineCount = 0;
	 universal::String sStartDelimiter = pszStartDelimiter;
	 universal::String sEndDelimiter = pszEndDelimiter;
	 char c = 0;
	 register char ccmp = 0, cprev = 0;        		
	 int iParity = 1;
	 unsigned long uWindowOffset = 0;
	 unsigned long uStartDelimLen = sStartDelimiter.length();
	 unsigned long uEndDelimLen = sEndDelimiter.length();
	 unsigned long uMaxDelimLen = uStartDelimLen >  uEndDelimLen ? uStartDelimLen : uEndDelimLen;
	 unsigned long uWindowOffsetIncr = 1;
	 bool bStartDelimFound = true;
	 bool bEndDelimFound = true;
	 bool bCommonPrefix = sStartDelimiter.empty() ? false : ( (uStartDelimLen != uEndDelimLen) && (sStartDelimiter.startsWith(sEndDelimiter) || sEndDelimiter.startsWith(sStartDelimiter)) );
	 bool bOtherDelimFound = false; // Tells whether to continue to look for the delimiter of the greatest size if there is a common prefix
	 std::streampos posWindow = is.tellg(); // Position in stream of the beginning of the scan window

	 register char cQuoteChar = 0;        		
	 bool bInQuote=false;
	 bool bCharIsQuote;
	 char currentQuoteChar=1;

	 /* Precondition check:
	  * The delimiters strings can't be empty.
	  */
	 
//     if (sStartDelimiter.empty())
//       {
// 	abort("Empty string provided as block start delimiter");
//       }
	 if (sEndDelimiter.empty() )
	   {
	     fatal("Empty string provided as block end delimiter");
	     return false;
	   }

	 /* Let's go 
	  */

	 uLineCount = 0;

	 is.get(c); ccmp = bIgnoreCase ? tolower(c) : c;

	 while (iParity && !is.fail())
	   {
	     /* Add the first char of the windows to block buffer 
	      */ 

	     uWindowOffset++;
	     if ((uWindowOffset-1) == 0)
	       {
		 append(c);	
		 // Begin line count management
		 if (((ccmp=='\n') && (cprev!='\r')) || ( ccmp=='\r')) 
		   uLineCount++;
		 cprev = ccmp;
		 // End line count management
	       }
      
	     /* Check whether there is an end delimiter match. 
	      */

	     if (uWindowOffset <= uEndDelimLen)
	       bEndDelimFound = (bEndDelimFound && (ccmp == pszEndDelimiter[uWindowOffset-1]));

	     /* Check whether there is a start delimiter match. 
	      * NOTE: 
	      * Do only if there is no end delimiter match or if there is a
	      * common prefix to the delimiters. This allows
	      * to have blocks having identical start and end delimiters.
	      */
      
	     if (sStartDelimiter.empty())
	       bStartDelimFound = false;
	     else 
	       if (uWindowOffset <= uStartDelimLen)
		 bStartDelimFound = (bStartDelimFound && (ccmp == pszStartDelimiter[uWindowOffset-1]) && !(bEndDelimFound && !bCommonPrefix));
      
	     /* If any delimiter was found, we reset the compare char offset.
	      * Do so also when the offset equals the maximum len that can have
	      * any of the delimiters. 
	      * If there was an end delimiter match, decrement parity number.
	      */
      
	     if (uWindowOffset == uEndDelimLen)
	       {
		 if (bEndDelimFound)
		   {
		     if (bCommonPrefix && (uMaxDelimLen != uEndDelimLen))
		       {
			 // Continue to scan input till we read the 
			 // maximum delimiter chars
			 bOtherDelimFound = true;
		       }
		     else
		       {
			 iParity--;
			 uWindowOffsetIncr = uEndDelimLen;
		       }
		   }
		 else
		   {
		     if (bOtherDelimFound)
		       {
			 iParity++;
			 uWindowOffsetIncr = uStartDelimLen;  
		       }
		   }
	       }

	     /* If there was a start delimiter match, increment
	      * parity number. 
	      */

	     if (uWindowOffset == uStartDelimLen)
	       {
		 if (bStartDelimFound)
		   {
		     if (bCommonPrefix && (uMaxDelimLen != uStartDelimLen))
		       {
			 // Continue to scan input till we read the 
			 // maximum delimiter chars
			 bOtherDelimFound = true;
		       }
		     else
		       {
			 iParity++;
			 uWindowOffsetIncr = uStartDelimLen;
		       }
		   }
		 else
		   {
		     if (bOtherDelimFound)
		       {
			 iParity--;
			 uWindowOffsetIncr = uEndDelimLen;  
		       }
		   }
	       }

	     /* If no parity get next char 
	      */

	     if (iParity)
	       {
		 /* Move and initialize the scan window if compare offset is 0
		  * or if anyway we know yet no delimiter was encountered in
		  * the window.
		  */

		 if (
		     ((uWindowOffset %  uMaxDelimLen) == 0) ||
		     (!bEndDelimFound && !bStartDelimFound)
		     )
		   {
		     is.seekg(posWindow + (std::streampos)1, ios::beg);
		     // If not parity match, the whole delimiter string 
		     // shall be added to the block buffer
		     while (--uWindowOffsetIncr > 0)
		       {
			 is.get(c); ccmp = bIgnoreCase ? tolower(c) : c;
			 append(c);
			 // Begin line count management
			 if (((ccmp=='\n') && (cprev!='\r')) || ( ccmp=='\r')) 
			   uLineCount++;
			 cprev = ccmp;
			 // End line count management
		       }
		     posWindow = is.tellg();
		     bOtherDelimFound = false;
		     bEndDelimFound = bStartDelimFound = true;
		     uWindowOffset = 0;
		     uWindowOffsetIncr = 1;

		     is.get(c); ccmp = bIgnoreCase ? tolower(c) : c;
		   }

		 /* Otherwise get next char 
		  * If the search was continued despite of a delimiter match
		  * and a failure occurs, the matched delimiter is taken into
		  * account.
		  */

		 else
		   {
		     is.get(c); ccmp = bIgnoreCase ? tolower(c) : c;

		     if (is.fail() && bOtherDelimFound)
		       {
			 assert(bOtherDelimFound && ((uWindowOffsetIncr == uEndDelimLen) || (uWindowOffsetIncr == uStartDelimLen)));

			 if (uWindowOffsetIncr == uEndDelimLen) 
			   iParity--;
			 else //if (uWindowOffsetIncr == uStartDelimLen) 
			   iParity++;

			 if (iParity)
			   {
			     is.clear();

			     is.seekg(posWindow + (std::streampos)1, ios::beg);
			     // If not parity match, the whole delimiter string 
			     // shall be added to the block buffer
			     while (--uWindowOffsetIncr > 0)
			       {
				 is.get(c); ccmp = bIgnoreCase ? tolower(c) : c;
				 append(c);
				 // Begin line count management
				 if (((ccmp=='\n') && (cprev!='\r')) || ( ccmp=='\r')) 
				   uLineCount++;
				 cprev = ccmp;
				 // End line count management
			       }
			     is.seekg(posWindow + (std::streampos)uWindowOffsetIncr, ios::beg);
			     posWindow = is.tellg();
			     bOtherDelimFound = false;
			     bEndDelimFound = bStartDelimFound = true;
			     uWindowOffset = 0;
			     uWindowOffsetIncr = 1;

			     is.get(c); ccmp = bIgnoreCase ? tolower(c) : c;
			   }
		       }
		   }
	       }
	   }

	 /* The block parsing ended either successfully in which
	  * case a pair number of start and end delimiters were found,
	  * or it failed.
	  */

	 if (iParity == 0)
	   {
	     /* Position the stream pointer to the char preceeding
	      * the last end delimiter. This corresponds to the
	      * beginning of the window.
	      */
	     is.seekg(posWindow, ios::beg);

	     /* Remove the first char of the end delimiter
	      * If this first char is a newline, it means
	      * that the line counting done before shall be 
	      * cancelled.
	      */

	     //if (sEndDelimiter[0] == '\n') uLineCount--;
	     erase(length() - 1);
	   }
	 else
	   {
	     return false;
	   }
  
#ifdef __DEBUG__
	 cout << "BlockToken='" << str() << "'" << endl;	 
#endif

	 incrementLineCount(uLineCount);
	 return true;
      }
   }}

  parse      /* Parse code */
  {{
#ifndef NO_STD_NAMESPACE
    using namespace gsp;
#endif

    /* Ensure the string is empty.
     */

    setEmpty();

    /* Check whether the block delimiters are valid constants.
     * The left delimiter is not mandatory.
     */
    
    const SymbolInfo& leftSym = getPreviousSymbolInfo(parser);
    const SymbolInfo& rightSym = getNextSymbolInfo(parser);
    if (leftSym.isValid() && !leftSym.isConstant())
      {
	cerr << "BlockToken: Left block delimiter '" << leftSym.representation() << "' is not a constant." << endl;
	abort("");
      }
    else if (!rightSym.isConstant())
      {
	cerr << "BlockToken: Right block delimiter '" << rightSym.representation() << "' is not a constant." << endl;
	abort("");
      }
    else
      {
	if (!readBlock(leftSym.representation(),
		       rightSym.representation(),
		       is,
		       parser.ignoreCase()))
	  abort("");
      }
  }}

  expand
  {{
    os << toString();
  }}




This file is part of the LLOOP Reversible Object-Oriented Parser Generator. Copyright (c) 2005-2006 Michel MEHL, France. All rights reserved. LLOOP is distributed by the company ERSA SaRL.


Copyright (c) 2005-2006 Michel MEHL, Haguenau, France
LLOOP version 1.1