diff options
Diffstat (limited to 'lib/cppparser/lexer.cpp')
-rw-r--r-- | lib/cppparser/lexer.cpp | 1032 |
1 files changed, 1032 insertions, 0 deletions
diff --git a/lib/cppparser/lexer.cpp b/lib/cppparser/lexer.cpp new file mode 100644 index 00000000..972b0bad --- /dev/null +++ b/lib/cppparser/lexer.cpp @@ -0,0 +1,1032 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <[email protected]> + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "lexer.h" +#include "lookup.h" + +#include <kdebug.h> +#include <klocale.h> + +#include <qregexp.h> +#include <qmap.h> +#include <qvaluelist.h> + +#if defined( KDEVELOP_BGPARSER ) +#include <qthread.h> + +class KDevTread: public QThread +{ +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 QMap<QString, QString> Scope; + typedef QValueList<Scope> StaticChain; + + StaticChain staticChain; + + void beginScope() + { + Scope scope; + staticChain.push_front( scope ); + } + + void endScope() + { + staticChain.pop_front(); + } + + void bind( const QString& name, const QString& value ) + { + Q_ASSERT( staticChain.size() > 0 ); + staticChain.front().insert( name, value ); + } + + bool hasBind( const QString& name ) const + { + StaticChain::ConstIterator it = staticChain.begin(); + while( it != staticChain.end() ){ + const Scope& scope = *it; + ++it; + + if( scope.contains(name) ) + return true; + } + + return false; + } + + QString apply( const QString& name ) const + { + StaticChain::ConstIterator it = staticChain.begin(); + while( it != staticChain.end() ){ + const Scope& scope = *it; + ++it; + + if( scope.contains(name) ) + return scope[ name ]; + } + + return QString::null; + } + +}; + +Lexer::Lexer( Driver* driver ) + : d( new LexerData), + m_driver( driver ), + m_recordComments( true ), + 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 QString& source ) +{ + reset(); + m_source = source; + m_ptr = offset( 0 ); + m_endPtr = offset( m_source.length() ); + m_inPreproc = false; + if( !source.isEmpty() ) { + m_currentChar = m_source[0]; + } else { + m_currentChar = QChar::null; + } + + tokenize(); +} + +int Lexer::skippedLines() const { + return m_skippedLines; +} + +void Lexer::reset() +{ + m_skippedLines = 0; + m_index = 0; + m_size = 0; + m_tokens.clear(); + m_source = QString::null; + 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 ) +{ + QString 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 ); + QString 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 + 1 ); + } + + readWhiteSpaces( !stopOnNewline ); + + int startLine = m_currentLine; + int startColumn = m_currentColumn; + + QChar ch = currentChar(); + QChar 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 + QString 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().isNull() && currentChar() != '\n' ){ + Token tok(m_source); + nextToken( tok, true ); + } + ++m_skippedLines; + 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(); + HashedString ide = m_source.mid( start, currentPosition() - start ); + int k = Lookup::find( ide ); + if( k == -1 && m_preprocessorEnabled ) m_driver->usingString( 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 ); + m_driver->usingMacro( m ); + + QString ellipsisArg; + + if( m.hasArguments() ){ + int endIde = currentPosition(); + + readWhiteSpaces(); + if( currentChar() == '(' ){ + nextChar(); + int argIdx = 0; + int argCount = m.argumentList().size(); + while( currentChar() && argIdx<argCount ){ + readWhiteSpaces(); + + QString argName = m.argumentList()[ argIdx ]; + + bool ellipsis = argName == "..."; + + QString 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 + insertCurrent( m.body() ); + + // tokenize the macro body + + QString textToInsert; + + setEndPtr( offset( currentPosition() + m.body().length() ) ); + + while( currentChar() ){ + + readWhiteSpaces(); + + Token tok(m_source); + nextToken( tok ); + + bool stringify = !m_inPreproc && tok == '#'; + bool merge = !m_inPreproc && tok == Token_concat; + + if( stringify || merge ) + nextToken( tok ); + + if( tok == Token_eof ) + break; + + QString tokText = tok.text(); + HashedString str = (tok == Token_identifier && d->hasBind(tokText)) ? d->apply( tokText ) : tokText; + if( str == ide ){ + //Problem p( i18n("unsafe use of macro '%1', macro is ignored").arg(ide.str()), m_currentLine, m_currentColumn, Problem::Level_Warning ); + //m_driver->addProblem( m_driver->currentFileName(), p ); + m_driver->removeMacro( ide ); + // str = QString::null; + } + + if( stringify ) { + textToInsert.append( QString::fromLatin1("\"") + str.str() + QString::fromLatin1("\" ") ); + } else if( merge ){ + textToInsert.truncate( textToInsert.length() - 1 ); + textToInsert.append( str.str() + QString::fromLatin1(" ") ); + } else if( tok == Token_ellipsis && d->hasBind("...") ){ + textToInsert.append( ellipsisArg ); + } else { + textToInsert.append( str.str() + QString::fromLatin1(" ") ); + } + } + +#if defined( KDEVELOP_BGPARSER ) + qthread_yield(); +#endif + insertCurrent( textToInsert ); //also corrects the end-pointer + + d->endScope(); + m_preprocessorEnabled = preproc; + //m_driver->addMacro( m ); + m_currentLine = argsEndAtLine; + m_currentColumn = argsEndAtColumn; + } 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 ){ + __gnu_cxx::hash_map< HashedString, QPair<SkipType, QString> >::iterator pos = m_words.find( ide ); + if( pos != m_words.end() ){ + if( (*pos).second.first == SkipWordAndArguments ){ + readWhiteSpaces(); + if( currentChar() == '(' ) + skip( '(', ')' ); + } + if( !(*pos).second.second.isEmpty() ){ +#if defined( KDEVELOP_BGPARSER ) + qthread_yield(); +#endif + insertCurrent( QString(" ") + (*pos).second.second + QString(" ") ); + } + } else if( /*qt_rx.exactMatch(ide) ||*/ + ide.str().endsWith("EXPORT") || + (ide.str().startsWith("Q_EXPORT") && ide.str() != "Q_EXPORT_INTERFACE") || + ide.str().startsWith("QM_EXPORT") || + ide.str().startsWith("QM_TEMPLATE")){ + + readWhiteSpaces(); + if( currentChar() == '(' ) + skip( '(', ')' ); + } else if( ide.str().startsWith("K_TYPELIST_") || ide.str().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, 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(m_source); + 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 QString& word, SkipType skipType, const QString& str ) +{ + m_words[ word ] = qMakePair( skipType, str ); +} + +void Lexer::skip( int l, int r ) +{ + int svCurrentLine = m_currentLine; + int svCurrentColumn = m_currentColumn; + + int count = 0; + + while( !eof() ){ + Token tk(m_source); + nextToken( tk ); + + if( (int)tk == l ) + ++count; + else if( (int)tk == r ) + --count; + + if( count == 0 ) + break; + } + + m_currentLine = svCurrentLine; + m_currentColumn = svCurrentColumn; +} + +QString Lexer::readArgument() +{ + int count = 0; + + QString arg; + + readWhiteSpaces(); + while( currentChar() ){ + + readWhiteSpaces(); + QChar ch = currentChar(); + + if( ch.isNull() || (!count && (ch == ',' || ch == ')')) ) + break; + + Token tk(m_source); + nextToken( tk ); + + if( tk == '(' ){ + ++count; + } else if( tk == ')' ){ + --count; + } + + if( tk != -1 ) + arg += tk.text() + " "; + } + + return arg.stripWhiteSpace(); +} + +void Lexer::handleDirective( const QString& 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(m_source); + 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(); + HashedString word = m_source.mid( startWord, currentPosition() - startWord ); + m_driver->usingString( word ); + bool r = m_driver->hasMacro( word ); + + if( r ) m_driver->usingMacro( m_driver->macro( word ) ); + + return r; +} + +void Lexer::processDefine( Macro& m ) +{ + m.setFileName( m_driver->currentFileName() ); + m.setLine( m_currentLine ); + m.setColumn( m_currentColumn ); + readWhiteSpaces( false ); + + int startMacroName = currentPosition(); + readIdentifier(); + QString macroName = m_source.mid( startMacroName, int(currentPosition()-startMacroName) ); + 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(); + + QString 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 ); + + QString body; + while( currentChar() && currentChar() != '\n' ){ + + if( currentChar().isSpace() ){ + readWhiteSpaces( false ); + body += " "; + } else { + + Token tk(m_source); + nextToken( tk, true ); + + //Do not ignore c-style comments, those may be useful in the body, and ignoring them using this check causes problems + if( tk.type() != -1 && (tk.type() != Token_comment || ( tk.text().length() >= 2 && tk.text()[1] == '*') ) ){ + QString 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() ){ + QChar ch = currentChar(); + if( ch == '"' || ch == '<' ){ + nextChar(); + QChar ch2 = ch == QChar('"') ? QChar('"') : QChar('>'); + + int startWord = currentPosition(); + while( currentChar() && currentChar() != ch2 ) + nextChar(); + if( currentChar() ){ + QString 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(); + QString word = m_source.mid( startWord, currentPosition() - startWord ); + + Macro m( word, "" ); + m.setFileName( m_driver->currentFileName() ); + m.setUndef(); + + ///Adds an undef-macro that shadows the previous macro + m_driver->addMacro( m ); +} + +int Lexer::macroPrimary() +{ + readWhiteSpaces( false ); + int result = 0; + switch( currentChar() ) { + case '(': + nextChar(); + result = macroExpression(); + if( currentChar() != ')' ){ + /// @todo report error + return 0; + } + nextChar(); + return result; + + case '+': + case '-': + case '!': + case '~': + { + QChar tk = currentChar(); + nextChar(); + int result = macroPrimary(); + if( tk == '-' ) return -result; + else if( tk == '!' ) return !result; + else if( tk == '~' ) return ~result; + } + break; + + default: + { + Token tk(m_source); + nextToken( tk, false ); + switch( tk.type() ){ + case Token_identifier: + if( tk.text() == "defined" ){ + return macroPrimary(); + } + /// @todo implement + { + HashedString h( tk.text() ); + m_driver->usingString( h ); + if( m_driver->hasMacro( h ) ) { + m_driver->usingMacro( m_driver->macro( h ) ); + return true; + } else { + return false; + } + } + 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; + QString 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 QObject class!! +// if you have problem while recompiling try to remove cppsupport/.deps, +// cppsupport/Makefile.in and rerun automake/autoconf + |