| LLOOP Index | GSP Language | GSP Library | Framework Classes | Component Classes |
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 |