You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdesdk/umbrello/umbrello/codeimport/kdevcppparser/lexer.cpp

1003 lines
25 KiB

/* This file is part of KDevelop
Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "lexer.h"
#include "lookup.h"
#include "keywords.lut.h"
#include <kdebug.h>
#include <tdelocale.h>
#include <tqregexp.h>
#include <tqmap.h>
#include <tqvaluelist.h>
#if defined( KDEVELOP_BGPARSER )
#include <tqthread.h>
class KDevTread: public TQThread
{
public:
static void yield()
{
msleep( 0 );
}
};
inline void qthread_yield()
{
KDevTread::yield();
}
#endif
#define CREATE_TOKEN(type, start, len) Token( (type), (start), (len), m_source )
#define ADD_TOKEN(tk) m_tokens.insert( m_size++, new Token(tk) );
using namespace std;
struct LexerData
{
typedef TQMap<TQString, TQString> Scope;
typedef TQValueList<Scope> StaticChain;
StaticChain staticChain;
void beginScope()
{
Scope scope;
staticChain.push_front( scope );
}
void endScope()
{
staticChain.pop_front();
}
void bind( const TQString& name, const TQString& value )
{
Q_ASSERT( staticChain.size() > 0 );
staticChain.front().insert( name, value );
}
bool hasBind( const TQString& name ) const
{
StaticChain::ConstIterator it = staticChain.begin();
while( it != staticChain.end() ){
const Scope& scope = *it;
++it;
if( scope.contains(name) )
return true;
}
return false;
}
TQString apply( const TQString& name ) const
{
StaticChain::ConstIterator it = staticChain.begin();
while( it != staticChain.end() ){
const Scope& scope = *it;
++it;
if( scope.contains(name) )
return scope[ name ];
}
return TQString();
}
};
Lexer::Lexer( Driver* driver )
: d( new LexerData),
m_driver( driver ),
m_recordComments( false ),
m_recordWhiteSpaces( false ),
m_skipWordsEnabled( true ),
m_preprocessorEnabled( true ),
m_reportWarnings( false ),
m_reportMessages( false )
{
m_tokens.setAutoDelete( true );
reset();
d->beginScope();
}
Lexer::~Lexer()
{
d->endScope();
delete( d );
}
void Lexer::setSource( const TQString& source )
{
reset();
m_source = source;
m_ptr = 0;
m_endPtr = m_source.length();
m_inPreproc = false;
tokenize();
}
void Lexer::reset()
{
m_index = 0;
m_size = 0;
m_tokens.clear();
m_source = TQString();
m_ptr = 0;
m_endPtr = 0;
m_startLine = false;
m_ifLevel = 0;
m_skipping.resize( 200 );
m_skipping.fill( 0 );
m_trueTest.resize( 200 );
m_trueTest.fill( 0 );
m_currentLine = 0;
m_currentColumn = 0;
}
// ### should all be done with a "long" type IMO
int Lexer::toInt( const Token& token )
{
TQString s = token.text();
if( token.type() == Token_number_literal ){
// hex literal ?
if( s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
return s.mid( 2 ).toInt( 0, 16 );
TQString n;
int i = 0;
while( i < int(s.length()) && s[i].isDigit() )
n += s[i++];
// ### respect more prefixes and suffixes ?
return n.toInt();
} else if( token.type() == Token_char_literal ){
int i = s[0] == 'L' ? 2 : 1; // wide char ?
if( s[i] == '\\' ){
// escaped char
int c = s[i+1].unicode();
switch( c ) {
case '0':
return 0;
case 'n':
return '\n';
// ### more
default:
return c;
}
} else {
return s[i].unicode();
}
} else {
return 0;
}
}
void Lexer::getTokenPosition( const Token& token, int* line, int* col )
{
token.getStartPosition( line, col );
}
void Lexer::nextToken( Token& tk, bool stopOnNewline )
{
int op = 0;
if( m_size == (int)m_tokens.size() ){
m_tokens.resize( m_tokens.size() + 5000 );
}
readWhiteSpaces( !stopOnNewline );
int startLine = m_currentLine;
int startColumn = m_currentColumn;
TQChar ch = currentChar();
TQChar ch1 = peekChar();
if( ch.isNull() || ch.isSpace() ){
/* skip */
} else if( m_startLine && ch == '#' ){
nextChar(); // skip #
readWhiteSpaces( false ); // skip white spaces
m_startLine = false;
int start = currentPosition();
readIdentifier(); // read the directive
TQString directive = m_source.mid( start, currentPosition() - start );
handleDirective( directive );
} else if( m_startLine && m_skipping[ m_ifLevel ] ){
// skip line and continue
m_startLine = false;
int ppe = preprocessorEnabled();
setPreprocessorEnabled( false );
while( currentChar() && currentChar() != '\n' ){
Token tok;
nextToken( tok, true );
}
m_startLine = true;
setPreprocessorEnabled( ppe );
return;
} else if( ch == '/' && ch1 == '/' ){
int start = currentPosition();
readLineComment();
if( recordComments() ){
tk = CREATE_TOKEN( Token_comment, start, currentPosition() - start );
tk.setStartPosition( startLine, startColumn );
tk.setEndPosition( m_currentLine, m_currentColumn );
}
} else if( ch == '/' && ch1 == '*' ){
int start = currentPosition();
nextChar( 2 );
readMultiLineComment();
if( recordComments() ){
tk = CREATE_TOKEN( Token_comment, start, currentPosition() - start );
tk.setStartPosition( startLine, startColumn );
tk.setEndPosition( m_currentLine, m_currentColumn );
}
} else if( ch == '\'' || (ch == 'L' && ch1 == '\'') ){
int start = currentPosition();
readCharLiteral();
tk = CREATE_TOKEN( Token_char_literal, start, currentPosition() - start );
tk.setStartPosition( startLine, startColumn );
tk.setEndPosition( m_currentLine, m_currentColumn );
} else if( ch == '"' ){
int start = currentPosition();
readStringLiteral();
tk = CREATE_TOKEN( Token_string_literal, start, currentPosition() - start );
tk.setStartPosition( startLine, startColumn );
tk.setEndPosition( m_currentLine, m_currentColumn );
} else if( ch.isLetter() || ch == '_' ){
int start = currentPosition();
readIdentifier();
TQString ide = m_source.mid( start, currentPosition() - start );
int k = Lookup::find( &keyword, ide );
if( m_preprocessorEnabled && m_driver->hasMacro(ide) &&
(k == -1 || !m_driver->macro(ide).body().isEmpty()) ){
bool preproc = m_preprocessorEnabled;
m_preprocessorEnabled = false;
d->beginScope();
int svLine = currentLine();
int svColumn = currentColumn();
// Macro& m = m_driver->macro( ide );
Macro m = m_driver->macro( ide );
//m_driver->removeMacro( m.name() );
TQString ellipsisArg;
if( m.hasArguments() ){
int endIde = currentPosition();
readWhiteSpaces();
if( currentChar() == '(' ){
nextChar();
int argIdx = 0;
int argCount = m.argumentList().size();
while( currentChar() && argIdx<argCount ){
readWhiteSpaces();
TQString argName = m.argumentList()[ argIdx ];
bool ellipsis = argName == "...";
TQString arg = readArgument();
if( !ellipsis )
d->bind( argName, arg );
else
ellipsisArg += arg;
if( currentChar() == ',' ){
nextChar();
if( !ellipsis ){
++argIdx;
} else {
ellipsisArg += ", ";
}
} else if( currentChar() == ')' ){
break;
}
}
if( currentChar() == ')' ){
// valid macro
nextChar();
}
} else {
tk = CREATE_TOKEN( Token_identifier, start, endIde - start );
tk.setStartPosition( svLine, svColumn );
tk.setEndPosition( svLine, svColumn + (endIde - start) );
m_startLine = false;
d->endScope(); // OPS!!
m_preprocessorEnabled = preproc;
return;
}
}
int argsEndAtLine = currentLine();
int argsEndAtColumn = currentColumn();
#if defined( KDEVELOP_BGPARSER )
qthread_yield();
#endif
m_source.insert( currentPosition(), m.body() );
// tokenize the macro body
TQString textToInsert;
m_endPtr = currentPosition() + m.body().length();
while( currentChar() ){
readWhiteSpaces();
Token tok;
nextToken( tok );
bool stringify = !m_inPreproc && tok == '#';
bool merge = !m_inPreproc && tok == Token_concat;
if( stringify || merge )
nextToken( tok );
if( tok == Token_eof )
break;
TQString tokText = tok.text();
TQString str = (tok == Token_identifier && d->hasBind(tokText)) ? d->apply( tokText ) : tokText;
if( str == ide ){
//Problem p( i18n("unsafe use of macro '%1'").arg(ide), m_currentLine, m_currentColumn );
//m_driver->addProblem( m_driver->currentFileName(), p );
m_driver->removeMacro( ide );
// str = TQString();
}
if( stringify ) {
textToInsert.append( TQString::fromLatin1("\"") + str + TQString::fromLatin1("\" ") );
} else if( merge ){
textToInsert.truncate( textToInsert.length() - 1 );
textToInsert.append( str );
} else if( tok == Token_ellipsis && d->hasBind("...") ){
textToInsert.append( ellipsisArg );
} else {
textToInsert.append( str + TQString::fromLatin1(" ") );
}
}
#if defined( KDEVELOP_BGPARSER )
qthread_yield();
#endif
m_source.insert( currentPosition(), textToInsert );
d->endScope();
m_preprocessorEnabled = preproc;
//m_driver->addMacro( m );
m_currentLine = argsEndAtLine;
m_currentColumn = argsEndAtColumn;
m_endPtr = m_source.length();
} else if( k != -1 ){
tk = CREATE_TOKEN( k, start, currentPosition() - start );
tk.setStartPosition( startLine, startColumn );
tk.setEndPosition( m_currentLine, m_currentColumn );
} else if( m_skipWordsEnabled ){
TQMap< TQString, TQPair<SkipType, TQString> >::Iterator pos = m_words.find( ide );
if( pos != m_words.end() ){
if( (*pos).first == SkipWordAndArguments ){
readWhiteSpaces();
if( currentChar() == '(' )
skip( '(', ')' );
}
if( !(*pos).second.isEmpty() ){
#if defined( KDEVELOP_BGPARSER )
qthread_yield();
#endif
m_source.insert( currentPosition(), TQString(" ") + (*pos).second + TQString(" ") );
m_endPtr = m_source.length();
}
} else if( /*qt_rx.exactMatch(ide) ||*/
ide.endsWith("EXPORT") ||
(ide.startsWith("TQ_EXPORT") && ide != "TQ_EXPORT_INTERFACE") ||
ide.startsWith("TQM_EXPORT") ||
ide.startsWith("TQM_TEMPLATE")){
readWhiteSpaces();
if( currentChar() == '(' )
skip( '(', ')' );
} else if( ide.startsWith("K_TYPELIST_") || ide.startsWith("TYPELIST_") ){
tk = CREATE_TOKEN( Token_identifier, start, currentPosition() - start );
tk.setStartPosition( startLine, startColumn );
tk.setEndPosition( m_currentLine, m_currentColumn );
readWhiteSpaces();
if( currentChar() == '(' )
skip( '(', ')' );
} else{
tk = CREATE_TOKEN( Token_identifier, start, currentPosition() - start );
tk.setStartPosition( startLine, startColumn );
tk.setEndPosition( m_currentLine, m_currentColumn );
}
} else {
tk = CREATE_TOKEN( Token_identifier, start, currentPosition() - start );
tk.setStartPosition( startLine, startColumn );
tk.setEndPosition( m_currentLine, m_currentColumn );
}
} else if( ch.isNumber() ){
int start = currentPosition();
readNumberLiteral();
tk = CREATE_TOKEN( Token_number_literal, start, currentPosition() - start );
tk.setStartPosition( startLine, startColumn );
tk.setEndPosition( m_currentLine, m_currentColumn );
} else if( -1 != (op = findOperator3()) ){
tk = CREATE_TOKEN( op, currentPosition(), 3 );
nextChar( 3 );
tk.setStartPosition( startLine, startColumn );
tk.setEndPosition( m_currentLine, m_currentColumn );
} else if( -1 != (op = findOperator2()) ){
tk = CREATE_TOKEN( op, currentPosition(), 2 );
nextChar( 2 );
tk.setStartPosition( startLine, startColumn );
tk.setEndPosition( m_currentLine, m_currentColumn );
} else {
tk = CREATE_TOKEN( ch.unicode(), currentPosition(), 1 );
nextChar();
tk.setStartPosition( startLine, startColumn );
tk.setEndPosition( m_currentLine, m_currentColumn );
}
m_startLine = false;
}
void Lexer::tokenize()
{
m_startLine = true;
m_size = 0;
for( ;; ) {
Token tk;
nextToken( tk );
if( tk.type() != -1 )
ADD_TOKEN( tk );
if( currentChar().isNull() )
break;
}
Token tk = CREATE_TOKEN( Token_eof, currentPosition(), 0 );
tk.setStartPosition( m_currentLine, m_currentColumn );
tk.setEndPosition( m_currentLine, m_currentColumn );
ADD_TOKEN( tk );
}
void Lexer::resetSkipWords()
{
m_words.clear();
}
void Lexer::addSkipWord( const TQString& word, SkipType skipType, const TQString& str )
{
m_words[ word ] = tqMakePair( skipType, str );
}
void Lexer::skip( int l, int r )
{
int svCurrentLine = m_currentLine;
int svCurrentColumn = m_currentColumn;
int count = 0;
while( !eof() ){
Token tk;
nextToken( tk );
if( (int)tk == l )
++count;
else if( (int)tk == r )
--count;
if( count == 0 )
break;
}
m_currentLine = svCurrentLine;
m_currentColumn = svCurrentColumn;
}
TQString Lexer::readArgument()
{
int count = 0;
TQString arg;
readWhiteSpaces();
while( currentChar() ){
readWhiteSpaces();
TQChar ch = currentChar();
if( ch.isNull() || (!count && (ch == ',' || ch == ')')) )
break;
Token tk;
nextToken( tk );
if( tk == '(' ){
++count;
} else if( tk == ')' ){
--count;
}
if( tk != -1 )
arg += tk.text() + ' ';
}
return arg.stripWhiteSpace();
}
void Lexer::handleDirective( const TQString& directive )
{
m_inPreproc = true;
bool skip = skipWordsEnabled();
bool preproc = preprocessorEnabled();
setSkipWordsEnabled( false );
setPreprocessorEnabled( false );
if( directive == "define" ){
if( !m_skipping[ m_ifLevel ] ){
Macro m;
processDefine( m );
}
} else if( directive == "else" ){
processElse();
} else if( directive == "elif" ){
processElif();
} else if( directive == "endif" ){
processEndif();
} else if( directive == "if" ){
processIf();
} else if( directive == "ifdef" ){
processIfdef();
} else if( directive == "ifndef" ){
processIfndef();
} else if( directive == "include" ){
if( !m_skipping[ m_ifLevel ] ){
processInclude();
}
} else if( directive == "undef" ){
if( !m_skipping[ m_ifLevel ] ){
processUndef();
}
}
// skip line
while( currentChar() && currentChar() != '\n' ){
Token tk;
nextToken( tk, true );
}
setSkipWordsEnabled( skip );
setPreprocessorEnabled( preproc );
m_inPreproc = false;
}
int Lexer::testIfLevel()
{
int rtn = !m_skipping[ m_ifLevel++ ];
m_skipping[ m_ifLevel ] = m_skipping[ m_ifLevel - 1 ];
return rtn;
}
int Lexer::macroDefined()
{
readWhiteSpaces( false );
int startWord = currentPosition();
readIdentifier();
TQString word = m_source.mid( startWord, currentPosition() - startWord );
bool r = m_driver->hasMacro( word );
return r;
}
void Lexer::processDefine( Macro& m )
{
m.setFileName( m_driver->currentFileName() );
readWhiteSpaces( false );
int startMacroName = currentPosition();
readIdentifier();
TQString macroName = m_source.mid( startMacroName, int(currentPosition()-startMacroName) );
m_driver->removeMacro( macroName );
m.setName( macroName );
if( currentChar() == '(' ){
m.setHasArguments( true );
nextChar();
readWhiteSpaces( false );
while( currentChar() && currentChar() != ')' ){
readWhiteSpaces( false );
int startArg = currentPosition();
if( currentChar() == '.' && peekChar() == '.' && peekChar(2) == '.' )
nextChar( 3 );
else
readIdentifier();
TQString arg = m_source.mid( startArg, int(currentPosition()-startArg) );
m.addArgument( Macro::Argument(arg) );
readWhiteSpaces( false );
if( currentChar() != ',' )
break;
nextChar(); // skip ','
}
if( currentChar() == ')' )
nextChar(); // skip ')'
}
setPreprocessorEnabled( true );
TQString body;
while( currentChar() && currentChar() != '\n' ){
if( currentChar().isSpace() ){
readWhiteSpaces( false );
body += ' ';
} else {
Token tk;
nextToken( tk, true );
if( tk.type() != -1 ){
TQString s = tk.text();
body += s;
}
}
}
m.setBody( body );
m_driver->addMacro( m );
}
void Lexer::processElse()
{
if( m_ifLevel == 0 )
/// @todo report error
return;
if( m_ifLevel > 0 && m_skipping[m_ifLevel-1] )
m_skipping[ m_ifLevel ] = m_skipping[ m_ifLevel - 1 ];
else
m_skipping[ m_ifLevel ] = m_trueTest[ m_ifLevel ];
}
void Lexer::processElif()
{
if( m_ifLevel == 0 )
/// @todo report error
return;
if( !m_trueTest[m_ifLevel] ){
/// @todo implement the correct semantic for elif!!
bool inSkip = m_ifLevel > 0 && m_skipping[ m_ifLevel-1 ];
m_trueTest[ m_ifLevel ] = macroExpression() != 0;
m_skipping[ m_ifLevel ] = inSkip ? inSkip : !m_trueTest[ m_ifLevel ];
}
else
m_skipping[ m_ifLevel ] = true;
}
void Lexer::processEndif()
{
if( m_ifLevel == 0 )
/// @todo report error
return;
m_skipping[ m_ifLevel ] = 0;
m_trueTest[ m_ifLevel-- ] = 0;
}
void Lexer::processIf()
{
bool inSkip = m_skipping[ m_ifLevel ];
if( testIfLevel() ) {
#if 0
int n;
if( (n = testDefined()) != 0 ) {
int isdef = macroDefined();
m_trueTest[ m_ifLevel ] = (n == 1 && isdef) || (n == -1 && !isdef);
} else
#endif
m_trueTest[ m_ifLevel ] = macroExpression() != 0;
m_skipping[ m_ifLevel ] = inSkip ? inSkip : !m_trueTest[ m_ifLevel ];
}
}
void Lexer::processIfdef()
{
bool inSkip = m_skipping[ m_ifLevel ];
if( testIfLevel() ){
m_trueTest[ m_ifLevel ] = macroDefined();
m_skipping[ m_ifLevel ] = inSkip ? inSkip : !m_trueTest[ m_ifLevel ];
}
}
void Lexer::processIfndef()
{
bool inSkip = m_skipping[ m_ifLevel ];
if( testIfLevel() ){
m_trueTest[ m_ifLevel ] = !macroDefined();
m_skipping[ m_ifLevel ] = inSkip ? inSkip : !m_trueTest[ m_ifLevel ];
}
}
void Lexer::processInclude()
{
if( m_skipping[m_ifLevel] )
return;
readWhiteSpaces( false );
if( currentChar() ){
TQChar ch = currentChar();
if( ch == '"' || ch == '<' ){
nextChar();
TQChar ch2 = ch == TQChar('"') ? TQChar('"') : TQChar('>');
int startWord = currentPosition();
while( currentChar() && currentChar() != ch2 )
nextChar();
if( currentChar() ){
TQString word = m_source.mid( startWord, int(currentPosition()-startWord) );
m_driver->addDependence( m_driver->currentFileName(),
Dependence(word, ch == '"' ? Dep_Local : Dep_Global) );
nextChar();
}
}
}
}
void Lexer::processUndef()
{
readWhiteSpaces();
int startWord = currentPosition();
readIdentifier();
TQString word = m_source.mid( startWord, currentPosition() - startWord );
m_driver->removeMacro( word );
}
int Lexer::macroPrimary()
{
readWhiteSpaces( false );
int result = 0;
switch( currentChar().unicode() ) {
case '(':
nextChar();
result = macroExpression();
if( currentChar() != ')' ){
/// @todo report error
return 0;
}
nextChar();
return result;
case '+':
case '-':
case '!':
case '~':
{
TQChar tk = currentChar();
nextChar();
int result = macroPrimary();
if( tk == '-' ) return -result;
else if( tk == '!' ) return !result;
else if( tk == '~' ) return ~result;
}
break;
default:
{
Token tk;
nextToken( tk, false );
switch( tk.type() ){
case Token_identifier:
if( tk.text() == "defined" ){
return macroPrimary();
}
/// @todo implement
return m_driver->hasMacro( tk.text() );
case Token_number_literal:
case Token_char_literal:
return toInt( tk );
default:
break;
} // end switch
} // end default
} // end switch
return 0;
}
int Lexer::macroMultiplyDivide()
{
int result = macroPrimary();
int iresult, op;
for (;;) {
readWhiteSpaces( false );
if( currentChar() == '*' )
op = 0;
else if( currentChar() == '/' && !(peekChar() == '*' || peekChar() == '/') )
op = 1;
else if( currentChar() == '%' )
op = 2;
else
break;
nextChar();
iresult = macroPrimary();
result = op == 0 ? (result * iresult) :
op == 1 ? (iresult == 0 ? 0 : (result / iresult)) :
(iresult == 0 ? 0 : (result % iresult)) ;
}
return result;
}
int Lexer::macroAddSubtract()
{
int result = macroMultiplyDivide();
int iresult, ad;
readWhiteSpaces( false );
while( currentChar() == '+' || currentChar() == '-') {
ad = currentChar() == '+';
nextChar();
iresult = macroMultiplyDivide();
result = ad ? (result+iresult) : (result-iresult);
}
return result;
}
int Lexer::macroRelational()
{
int result = macroAddSubtract();
int iresult;
readWhiteSpaces( false );
while( currentChar() == '<' || currentChar() == '>') {
int lt = currentChar() == '<';
nextChar();
if( currentChar() == '=') {
nextChar();
iresult = macroAddSubtract();
result = lt ? (result <= iresult) : (result >= iresult);
}
else {
iresult = macroAddSubtract();
result = lt ? (result < iresult) : (result > iresult);
}
}
return result;
}
int Lexer::macroEquality()
{
int result = macroRelational();
int iresult, eq;
readWhiteSpaces( false );
while ((currentChar() == '=' || currentChar() == '!') && peekChar() == '=') {
eq = currentChar() == '=';
nextChar( 2 );
iresult = macroRelational();
result = eq ? (result==iresult) : (result!=iresult);
}
return result;
}
int Lexer::macroBoolAnd()
{
int result = macroEquality();
readWhiteSpaces( false );
while( currentChar() == '&' && peekChar() != '&') {
nextChar();
result &= macroEquality();
}
return result;
}
int Lexer::macroBoolXor()
{
int result = macroBoolAnd();
readWhiteSpaces( false );
while( currentChar() == '^') {
nextChar();
result ^= macroBoolAnd();
}
return result;
}
int Lexer::macroBoolOr()
{
int result = macroBoolXor();
readWhiteSpaces( false );
while( currentChar() == '|' && peekChar() != '|') {
nextChar();
result |= macroBoolXor();
}
return result;
}
int Lexer::macroLogicalAnd()
{
int result = macroBoolOr();
readWhiteSpaces( false );
while( currentChar() == '&' && peekChar() == '&') {
nextChar( 2 );
int start = currentPosition();
result = macroBoolOr() && result;
TQString s = m_source.mid( start, currentPosition() - start );
}
return result;
}
int Lexer::macroLogicalOr()
{
int result = macroLogicalAnd();
readWhiteSpaces( false );
while( currentChar() == '|' && peekChar() == '|') {
nextChar( 2 );
result = macroLogicalAnd() || result;
}
return result;
}
int Lexer::macroExpression()
{
readWhiteSpaces( false );
return macroLogicalOr();
}
// *IMPORTANT*
// please, don't include lexer.moc here, because Lexer isn't a TQObject class!!
// if you have problem while recompiling try to remove cppsupport/.deps,
// cppsupport/Makefile.in and rerun automake/autoconf