diff options
Diffstat (limited to 'lib/cppparser')
-rw-r--r-- | lib/cppparser/Makefile.am | 11 | ||||
-rw-r--r-- | lib/cppparser/ast.cpp | 1262 | ||||
-rw-r--r-- | lib/cppparser/ast.h | 1578 | ||||
-rw-r--r-- | lib/cppparser/cachemanager.cpp | 89 | ||||
-rw-r--r-- | lib/cppparser/cachemanager.h | 102 | ||||
-rw-r--r-- | lib/cppparser/driver.cpp | 965 | ||||
-rw-r--r-- | lib/cppparser/driver.h | 460 | ||||
-rw-r--r-- | lib/cppparser/errors.cpp | 25 | ||||
-rw-r--r-- | lib/cppparser/errors.h | 45 | ||||
-rw-r--r-- | lib/cppparser/keywords.h | 95 | ||||
-rw-r--r-- | lib/cppparser/lexer.cpp | 1032 | ||||
-rw-r--r-- | lib/cppparser/lexer.h | 867 | ||||
-rw-r--r-- | lib/cppparser/lexercache.cpp | 249 | ||||
-rw-r--r-- | lib/cppparser/lexercache.h | 162 | ||||
-rw-r--r-- | lib/cppparser/lookup.cpp | 42 | ||||
-rw-r--r-- | lib/cppparser/lookup.h | 45 | ||||
-rw-r--r-- | lib/cppparser/macro.h | 452 | ||||
-rw-r--r-- | lib/cppparser/parser.cpp | 4330 | ||||
-rw-r--r-- | lib/cppparser/parser.h | 459 | ||||
-rw-r--r-- | lib/cppparser/tree_parser.cpp | 212 | ||||
-rw-r--r-- | lib/cppparser/tree_parser.h | 60 |
21 files changed, 12542 insertions, 0 deletions
diff --git a/lib/cppparser/Makefile.am b/lib/cppparser/Makefile.am new file mode 100644 index 00000000..1ee03ce7 --- /dev/null +++ b/lib/cppparser/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = -I$(top_srcdir)/lib/interfaces $(all_includes) +AM_CXXFLAGS = -DKDEVELOP_BGPARSER +lib_LTLIBRARIES = libkdevcppparser.la +libkdevcppparser_la_LDFLAGS = $(all_libraries) +libkdevcppparser_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) $(top_builddir)/lib/interfaces/libkdevinterfaces.la -lDCOP $(LIB_KDEUI) $(LIB_KPARTS) -lktexteditor $(LIB_KIO) -lkscript +libkdevcppparser_la_SOURCES = ast.cpp cachemanager.cpp driver.cpp errors.cpp \ + lexer.cpp lexercache.cpp lookup.cpp parser.cpp tree_parser.cpp + +kdevcppparserincludedir = $(includedir)/kdevelop/cppparser +kdevcppparserinclude_HEADERS = ast.h driver.h lexer.h parser.h tree_parser.h errors.h lookup.h cachemanager.h lexercache.h macro.h + diff --git a/lib/cppparser/ast.cpp b/lib/cppparser/ast.cpp new file mode 100644 index 00000000..b6f452c0 --- /dev/null +++ b/lib/cppparser/ast.cpp @@ -0,0 +1,1262 @@ +/* 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "ast.h" +#include <qstringlist.h> +#include <kdebug.h> + +QString nodeTypeToString( int type ) +{ + switch( type ) + { + case NodeType_Generic: + return "Generic"; + case NodeType_TemplateArgumentList: + return "TemplateArgumentList"; + case NodeType_ClassOrNamespaceName: + return "ClassOrNamespaceName"; + case NodeType_Name: + return "Name"; + case NodeType_Declaration: + return "Declaration"; + case NodeType_TypeSpecifier: + return "TypeSpecifier"; + case NodeType_BaseSpecifier: + return "BaseSpecifier"; + case NodeType_BaseClause: + return "BaseClause"; + case NodeType_ClassSpecifier: + return "ClassSpecifier"; + case NodeType_Enumerator: + return "Enumerator"; + case NodeType_EnumSpecifier: + return "EnumSpecifier"; + case NodeType_ElaboratedTypeSpecifier: + return "ElaboratedTypeSpecifier"; + case NodeType_LinkageBody: + return "LinkageBody"; + case NodeType_LinkageSpecification: + return "LinkageSpecification"; + case NodeType_Namespace: + return "Namespace"; + case NodeType_NamespaceAlias: + return "NamespaceAlias"; + case NodeType_Using: + return "Using"; + case NodeType_UsingDirective: + return "UsingDirective"; + case NodeType_InitDeclaratorList: + return "InitDeclaratorList"; + case NodeType_Typedef: + return "Typedef"; + case NodeType_Declarator: + return "Declarator"; + case NodeType_InitDeclarator: + return "InitDeclarator"; + case NodeType_TemplateDeclaration: + return "TemplateDeclaration"; + case NodeType_SimpleDeclaration: + return "SimpleDeclaration"; + case NodeType_Statement: + return "Statement"; + case NodeType_IfStatement: + return "IfStatement"; + case NodeType_WhileStatement: + return "WhileStatement"; + case NodeType_DoStatement: + return "DoStatement"; + case NodeType_ForStatement: + return "ForStatement"; + case NodeType_ForEachStatement: // qt4 [erbsland] + return "ForEachStatement"; + case NodeType_SwitchStatement: + return "SwitchStatement"; + case NodeType_CatchStatement: + return "CatchStatement"; + case NodeType_CatchStatementList: + return "CatchStatementList"; + case NodeType_TryBlockStatement: + return "TryBlockStatement"; + case NodeType_DeclarationStatement: + return "DeclarationStatement"; + case NodeType_StatementList: + return "StatementList"; + case NodeType_TranslationUnit: + return "TranslationUnit"; + case NodeType_FunctionDefinition: + return "FunctionDefinition"; + case NodeType_ExpressionStatement: + return "ExpressionStatement"; + case NodeType_ParameterDeclaration: + return "ParameterDeclaration"; + case NodeType_ParameterDeclarationList: + return "ParameterDeclarationList"; + case NodeType_ParameterDeclarationClause: + return "ParameterDeclarationClause"; + case NodeType_Group: + return "Group"; + case NodeType_AccessDeclaration: + return "AccessDeclaration"; + case NodeType_TypeParameter: + return "TypeParameter"; + case NodeType_TemplateParameter: + return "TemplateParameter"; + case NodeType_TemplateParameterList: + return "TemplateParameterList"; + case NodeType_Condition: + return "Condition"; + case NodeType_Custom: + return "Custom"; + } + + return QString::null; +} + + +// ------------------------------------------------------------------------ +AST::AST() + : m_nodeType( NodeType_Generic ), m_parent( 0 ), + m_startLine( 0 ), m_startColumn( 0 ), + m_endLine( 0 ), m_endColumn( 0 ) +{ +#ifndef CPPPARSER_NO_CHILDREN + m_children.setAutoDelete( false ); +#endif +} + +AST::~AST() +{ +#ifndef CPPPARSER_NO_CHILDREN + if( m_parent ) + m_parent->removeChild( this ); +#endif +} + +void AST::setStartPosition( int line, int col ) +{ + m_startLine = line; + m_startColumn = col; +} + +void AST::getStartPosition( int* line, int* col ) const +{ + if( line ) + *line = m_startLine; + + if( col ) + * col = m_startColumn; +} + +void AST::setEndPosition( int line, int col ) +{ + m_endLine = line; + m_endColumn = col; +} + +void AST::getEndPosition( int* line, int* col ) const +{ + if( line ) + *line = m_endLine; + + if( col ) + * col = m_endColumn; +} + +void AST::setParent( AST* parent ) +{ +#ifndef CPPPARSER_NO_CHILDREN + if( m_parent ) + m_parent->removeChild( this ); +#endif + + m_parent = parent; + +#ifndef CPPPARSER_NO_CHILDREN + if( m_parent ) + m_parent->appendChild( this ); +#endif +} + +#ifndef CPPPARSER_NO_CHILDREN +void AST::appendChild( AST* child ) +{ + m_children.append( child ); +} + +void AST::removeChild( AST* child ) +{ + m_children.remove( child ); +} +#endif + +// ------------------------------------------------------------------------ +NameAST::NameAST() + : m_global( false ) +{ + m_classOrNamespaceNameList.setAutoDelete( true ); +} + +void NameAST::setGlobal( bool b ) +{ + m_global = b; +} + +void NameAST::setUnqualifiedName( ClassOrNamespaceNameAST::Node& unqualifiedName ) +{ + m_unqualifiedName = unqualifiedName; + if( m_unqualifiedName.get() ) m_unqualifiedName->setParent( this ); +} + +void NameAST::addClassOrNamespaceName( ClassOrNamespaceNameAST::Node& classOrNamespaceName ) +{ + if( !classOrNamespaceName.get() ) + return; + + classOrNamespaceName->setParent( this ); + m_classOrNamespaceNameList.append( classOrNamespaceName.release() ); +} + +QString NameAST::text() const +{ + if( !m_unqualifiedName.get() ) + return QString::null; + + QString str; + + if( m_global ) + str += "::"; + + QStringList l; + QPtrListIterator<ClassOrNamespaceNameAST> it( m_classOrNamespaceNameList ); + while( it.current() ){ + str += it.current()->text() + "::"; + ++it; + } + + if( m_unqualifiedName.get() ) + str += m_unqualifiedName->text(); + + return str; +} + +// ------------------------------------------------------------------------ +DeclarationAST::DeclarationAST() +{ +} + +// ------------------------------------------------------------------------ +LinkageBodyAST::LinkageBodyAST() +{ + m_declarationList.setAutoDelete( true ); +} + +void LinkageBodyAST::addDeclaration( DeclarationAST::Node& ast ) +{ + if( !ast.get() ) + return; + + ast->setParent( this ); + m_declarationList.append( ast.release() ); +} + +// ------------------------------------------------------------------------ +LinkageSpecificationAST::LinkageSpecificationAST() +{ +} + +void LinkageSpecificationAST::setExternType( AST::Node& externType ) +{ + m_externType = externType; + if( m_externType.get() ) m_externType->setParent( this ); +} + +void LinkageSpecificationAST::setLinkageBody( LinkageBodyAST::Node& linkageBody ) +{ + m_linkageBody = linkageBody; + if( m_linkageBody.get() ) m_linkageBody->setParent( this ); +} + +void LinkageSpecificationAST::setDeclaration( DeclarationAST::Node& decl ) +{ + m_declaration = decl; + if( m_declaration.get() ) m_declaration->setParent( this ); +} + +// ------------------------------------------------------------------------ +TranslationUnitAST::TranslationUnitAST() +{ + ////kdDebug(9007) << "++ TranslationUnitAST::TranslationUnitAST()" << endl; + m_declarationList.setAutoDelete( true ); +} + +void TranslationUnitAST::addDeclaration( DeclarationAST::Node& ast ) +{ + if( !ast.get() ) + return; + + ast->setParent( this ); + m_declarationList.append( ast.release() ); +} + +// ------------------------------------------------------------------------ +NamespaceAST::NamespaceAST() +{ +} + +void NamespaceAST::setNamespaceName( AST::Node& namespaceName ) +{ + m_namespaceName = namespaceName; + if( m_namespaceName.get() ) m_namespaceName->setParent( this ); +} + +void NamespaceAST::setLinkageBody( LinkageBodyAST::Node& linkageBody ) +{ + m_linkageBody = linkageBody; + if( m_linkageBody.get() ) m_linkageBody->setParent( this ); +} + + +// ------------------------------------------------------------------------ +NamespaceAliasAST::NamespaceAliasAST() +{ +} + +void NamespaceAliasAST::setNamespaceName( AST::Node& namespaceName ) +{ + m_namespaceName = namespaceName; + if( m_namespaceName.get() ) m_namespaceName->setParent( this ); +} + +void NamespaceAliasAST::setAliasName( NameAST::Node& name ) +{ + m_aliasName = name; + if( m_aliasName.get() ) m_aliasName->setParent( this ); +} + +// ------------------------------------------------------------------------ +UsingAST::UsingAST() +{ +} + +void UsingAST::setTypeName( AST::Node& typeName ) +{ + m_typeName = typeName; + if( m_typeName.get() ) m_typeName->setParent( this ); +} + +void UsingAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +// ------------------------------------------------------------------------ +UsingDirectiveAST::UsingDirectiveAST() +{ +} + +void UsingDirectiveAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +TypedefAST::TypedefAST() +{ +} + +void TypeSpecifierAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +void TypedefAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void TypedefAST::setInitDeclaratorList( InitDeclaratorListAST::Node& initDeclaratorList ) +{ + m_initDeclaratorList = initDeclaratorList; + if( m_initDeclaratorList.get() ) m_initDeclaratorList->setParent( this ); +} + +// ------------------------------------------------------------------------ +TemplateArgumentListAST::TemplateArgumentListAST() +{ + m_argumentList.setAutoDelete( true ); +} + +void TemplateArgumentListAST::addArgument( AST::Node& arg ) +{ + if( !arg.get() ) + return; + + arg->setParent( this ); + m_argumentList.append( arg.release() ); +} + +QString TemplateArgumentListAST::text() const +{ + QStringList l; + + QPtrListIterator<AST> it( m_argumentList ); + while( it.current() ){ + l.append( it.current()->text() ); + ++it; + } + + return l.join( ", " ); +} + +// ------------------------------------------------------------------------ +TemplateDeclarationAST::TemplateDeclarationAST() +{ +} + +void TemplateDeclarationAST::setExported( AST::Node& exported ) +{ + m_exported = exported; + if( m_exported.get() ) m_exported->setParent( this ); +} + +void TemplateDeclarationAST::setTemplateParameterList( TemplateParameterListAST::Node& templateParameterList ) +{ + m_templateParameterList = templateParameterList; + if( m_templateParameterList.get() ) m_templateParameterList->setParent( this ); +} + +void TemplateDeclarationAST::setDeclaration( DeclarationAST::Node& declaration ) +{ + m_declaration = declaration; + if( m_declaration.get() ) m_declaration->setParent( this ); +} + +// ------------------------------------------------------------------------ +ClassOrNamespaceNameAST::ClassOrNamespaceNameAST() +{ +} + +void ClassOrNamespaceNameAST::setName( AST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +void ClassOrNamespaceNameAST::setTemplateArgumentList( TemplateArgumentListAST::Node& templateArgumentList ) +{ + m_templateArgumentList = templateArgumentList; + if( m_templateArgumentList.get() ) m_templateArgumentList->setParent( this ); +} + +QString ClassOrNamespaceNameAST::text() const +{ + if( !m_name.get() ) + return QString::null; + + QString str = m_name->text(); + if( m_templateArgumentList.get() ) + str += QString::fromLatin1("< ") + m_templateArgumentList->text() + QString::fromLatin1(" >"); + + return str; +} + +// ------------------------------------------------------------------------ +TypeSpecifierAST::TypeSpecifierAST() +{ +} + +void TypeSpecifierAST::setCvQualify( GroupAST::Node& cvQualify ) +{ + m_cvQualify = cvQualify; + if( m_cvQualify.get() ) m_cvQualify->setParent( this ); +} + +void TypeSpecifierAST::setCv2Qualify( GroupAST::Node& cv2Qualify ) +{ + m_cv2Qualify = cv2Qualify; + if( m_cv2Qualify.get() ) m_cv2Qualify->setParent( this ); +} + +QString TypeSpecifierAST::text() const +{ + QString str; + + if( m_cvQualify.get() ) + str += m_cvQualify->text() + " "; + + if( m_name.get() ) + str += m_name->text(); + + if( m_cv2Qualify.get() ) + str += QString(" ") + m_cv2Qualify->text(); + + return str; +} + +// ------------------------------------------------------------------------ +ClassSpecifierAST::ClassSpecifierAST() +{ + m_declarationList.setAutoDelete( true ); +} + +void ClassSpecifierAST::setClassKey( AST::Node& classKey ) +{ + m_classKey = classKey; + if( m_classKey.get() ) m_classKey->setParent( this ); +} + +void ClassSpecifierAST::addDeclaration( DeclarationAST::Node& declaration ) +{ + if( !declaration.get() ) + return; + + declaration->setParent( this ); + m_declarationList.append( declaration.release() ); +} + +void ClassSpecifierAST::setBaseClause( BaseClauseAST::Node& baseClause ) +{ + m_baseClause = baseClause; + if( m_baseClause.get() ) m_baseClause->setParent( this ); +} + +// ------------------------------------------------------------------------ +EnumSpecifierAST::EnumSpecifierAST() +{ + m_enumeratorList.setAutoDelete( true ); +} + +void EnumSpecifierAST::addEnumerator( EnumeratorAST::Node& enumerator ) +{ + if( !enumerator.get() ) + return; + + enumerator->setParent( this ); + m_enumeratorList.append( enumerator.release() ); +} + + +// ------------------------------------------------------------------------ +ElaboratedTypeSpecifierAST::ElaboratedTypeSpecifierAST() +{ +} + +void ElaboratedTypeSpecifierAST::setKind( AST::Node& kind ) +{ + m_kind = kind; + if( m_kind.get() ) m_kind->setParent( this ); +} + +QString ElaboratedTypeSpecifierAST::text() const +{ + if( m_kind.get() ) + return m_kind->text() + " " + TypeSpecifierAST::text(); + + return TypeSpecifierAST::text(); +} + +// ------------------------------------------------------------------------ +StatementAST::StatementAST() +{ +} + +// ------------------------------------------------------------------------ +EnumeratorAST::EnumeratorAST() +{ +} + +void EnumeratorAST::setId( AST::Node& id ) +{ + m_id = id; + if( m_id.get() ) m_id->setParent( this ); +} + +void EnumeratorAST::setExpr( AST::Node& expr ) +{ + m_expr = expr; + if( m_expr.get() ) m_expr->setParent( this ); +} + +// ------------------------------------------------------------------------ +BaseClauseAST::BaseClauseAST() +{ + m_baseSpecifierList.setAutoDelete( true ); +} + +void BaseClauseAST::addBaseSpecifier( BaseSpecifierAST::Node& baseSpecifier ) +{ + if( !baseSpecifier.get() ) + return; + + baseSpecifier->setParent( this ); + m_baseSpecifierList.append( baseSpecifier.release() ); +} + +// ------------------------------------------------------------------------ +BaseSpecifierAST::BaseSpecifierAST() +{ +} + +void BaseSpecifierAST::setIsVirtual( AST::Node& isVirtual ) +{ + m_isVirtual = isVirtual; + if( m_isVirtual.get() ) m_isVirtual->setParent( this ); +} + +void BaseSpecifierAST::setAccess( AST::Node& access ) +{ + m_access = access; + if( m_access.get() ) m_access->setParent( this ); +} + +void BaseSpecifierAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +// ------------------------------------------------------------------------ +SimpleDeclarationAST::SimpleDeclarationAST() +{ +} + +void SimpleDeclarationAST::setFunctionSpecifier( GroupAST::Node& functionSpecifier ) +{ + m_functionSpecifier = functionSpecifier; + if( m_functionSpecifier.get() ) m_functionSpecifier->setParent( this ); +} + +void SimpleDeclarationAST::setStorageSpecifier( GroupAST::Node& storageSpecifier ) +{ + m_storageSpecifier = storageSpecifier; + if( m_storageSpecifier.get() ) m_storageSpecifier->setParent( this ); +} + +void SimpleDeclarationAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void SimpleDeclarationAST::setInitDeclaratorList( InitDeclaratorListAST::Node& initDeclaratorList ) +{ + m_initDeclaratorList = initDeclaratorList; + if( m_initDeclaratorList.get() ) m_initDeclaratorList->setParent( this ); +} + +void SimpleDeclarationAST::setWinDeclSpec( GroupAST::Node& winDeclSpec ) +{ + m_winDeclSpec = winDeclSpec; + if( m_winDeclSpec.get() ) m_winDeclSpec->setParent( this ); +} + + +// ------------------------------------------------------------------------ +InitDeclaratorListAST::InitDeclaratorListAST() +{ + m_initDeclaratorList.setAutoDelete( true ); +} + +void InitDeclaratorListAST::addInitDeclarator( InitDeclaratorAST::Node& decl ) +{ + if( !decl.get() ) + return; + + decl->setParent( this ); + m_initDeclaratorList.append( decl.release() ); +} + +// ------------------------------------------------------------------------ +DeclaratorAST::DeclaratorAST() +{ + m_ptrOpList.setAutoDelete( true ); + m_arrayDimensionList.setAutoDelete( true ); +} + +void DeclaratorAST::setSubDeclarator( DeclaratorAST::Node& subDeclarator ) +{ + m_subDeclarator = subDeclarator; + if( m_subDeclarator.get() ) m_subDeclarator->setParent( this ); +} + +void DeclaratorAST::setDeclaratorId( NameAST::Node& declaratorId ) +{ + m_declaratorId = declaratorId; + if( m_declaratorId.get() ) m_declaratorId->setParent( this ); +} + +void DeclaratorAST::setBitfieldInitialization( AST::Node& bitfieldInitialization ) +{ + m_bitfieldInitialization = bitfieldInitialization; + if( m_bitfieldInitialization.get() ) m_bitfieldInitialization->setParent( this ); +} + +void DeclaratorAST::addArrayDimension( AST::Node& arrayDimension ) +{ + if( !arrayDimension.get() ) + return; + + arrayDimension->setParent( this ); + m_arrayDimensionList.append( arrayDimension.release() ); +} + +void DeclaratorAST::setParameterDeclarationClause( ParameterDeclarationClauseAST::Node& parameterDeclarationClause ) +{ + m_parameterDeclarationClause = parameterDeclarationClause; + if( m_parameterDeclarationClause.get() ) m_parameterDeclarationClause->setParent( this ); +} + +void DeclaratorAST::setConstant( AST::Node& constant ) +{ + m_constant = constant; + if( m_constant.get() ) m_constant->setParent( this ); +} + +void DeclaratorAST::setExceptionSpecification( GroupAST::Node& exceptionSpecification ) +{ + m_exceptionSpecification = exceptionSpecification; + if( m_exceptionSpecification.get() ) m_exceptionSpecification->setParent( this ); +} + +void DeclaratorAST::addPtrOp( AST::Node& ptrOp ) +{ + if( !ptrOp.get() ) + return; + + ptrOp->setParent( this ); + m_ptrOpList.append( ptrOp.release() ); +} + +// -------------------------------------------------------------------------- +InitDeclaratorAST::InitDeclaratorAST() +{ +} + +void InitDeclaratorAST::setDeclarator( DeclaratorAST::Node& declarator ) +{ + m_declarator = declarator; + if( m_declarator.get() ) m_declarator->setParent( this ); +} + +void InitDeclaratorAST::setInitializer( AST::Node& initializer ) +{ + m_initializer = initializer; + if( m_initializer.get() ) m_initializer->setParent( this ); +} + +// -------------------------------------------------------------------------- +FunctionDefinitionAST::FunctionDefinitionAST() +{ +} + +void FunctionDefinitionAST::setFunctionSpecifier( GroupAST::Node& functionSpecifier ) +{ + m_functionSpecifier = functionSpecifier; + if( m_functionSpecifier.get() ) m_functionSpecifier->setParent( this ); +} + +void FunctionDefinitionAST::setStorageSpecifier( GroupAST::Node& storageSpecifier ) +{ + m_storageSpecifier = storageSpecifier; + if( m_storageSpecifier.get() ) m_storageSpecifier->setParent( this ); +} + +void FunctionDefinitionAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void FunctionDefinitionAST::setInitDeclarator( InitDeclaratorAST::Node& initDeclarator ) +{ + m_initDeclarator = initDeclarator; + if( m_initDeclarator.get() ) m_initDeclarator->setParent( this ); +} + +void FunctionDefinitionAST::setFunctionBody( StatementListAST::Node& functionBody ) +{ + m_functionBody = functionBody; + if( m_functionBody.get() ) m_functionBody->setParent( this ); +} + +void FunctionDefinitionAST::setWinDeclSpec( GroupAST::Node& winDeclSpec ) +{ + m_winDeclSpec = winDeclSpec; + if( m_winDeclSpec.get() ) m_winDeclSpec->setParent( this ); +} + +// -------------------------------------------------------------------------- +StatementListAST::StatementListAST() +{ + m_statementList.setAutoDelete( true ); +} + +void StatementListAST::addStatement( StatementAST::Node& statement ) +{ + if( !statement.get() ) + return; + + statement->setParent( this ); + m_statementList.append( statement.release() ); +} + +// -------------------------------------------------------------------------- +IfStatementAST::IfStatementAST() +{ +} + +void IfStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void IfStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +void IfStatementAST::setElseStatement( StatementAST::Node& elseStatement ) +{ + m_elseStatement = elseStatement; + if( m_elseStatement.get() ) m_elseStatement->setParent( this ); +} + +// -------------------------------------------------------------------------- +WhileStatementAST::WhileStatementAST() +{ +} + +void WhileStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void WhileStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +// -------------------------------------------------------------------------- +DoStatementAST::DoStatementAST() +{ +} + +void DoStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void DoStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +// -------------------------------------------------------------------------- +ForStatementAST::ForStatementAST() +{ +} + +void ForStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void ForStatementAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + +void ForStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +void ForStatementAST::setInitStatement( StatementAST::Node& initStatement ) +{ + m_initStatement = initStatement; + if( m_initStatement.get() ) m_initStatement->setParent( this ); +} + +// -------------------------------------------------------------------------- +ForEachStatementAST::ForEachStatementAST() +{ +} + +void ForEachStatementAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + +void ForEachStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +void ForEachStatementAST::setInitStatement( StatementAST::Node& initStatement ) +{ + m_initStatement = initStatement; + if( m_initStatement.get() ) m_initStatement->setParent( this ); +} + +// -------------------------------------------------------------------------- +SwitchStatementAST::SwitchStatementAST() +{ +} + +void SwitchStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void SwitchStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +// -------------------------------------------------------------------------- +CatchStatementListAST::CatchStatementListAST() +{ + m_statementList.setAutoDelete( true ); +} + +void CatchStatementListAST::addStatement( CatchStatementAST::Node& statement ) +{ + if( !statement.get() ) + return; + + statement->setParent( this ); + m_statementList.append( statement.release() ); +} + +// -------------------------------------------------------------------------- +CatchStatementAST::CatchStatementAST() +{ +} + +void CatchStatementAST::setCondition( ConditionAST::Node& condition ) +{ + m_condition = condition; + if( m_condition.get() ) m_condition->setParent( this ); +} + +void CatchStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +// -------------------------------------------------------------------------- +TryBlockStatementAST::TryBlockStatementAST() +{ +} + +void TryBlockStatementAST::setStatement( StatementAST::Node& statement ) +{ + m_statement = statement; + if( m_statement.get() ) m_statement->setParent( this ); +} + +void TryBlockStatementAST::setCatchStatementList( CatchStatementListAST::Node& statementList ) +{ + m_catchStatementList = statementList; + if( m_catchStatementList.get() ) m_catchStatementList->setParent( this ); +} + +// -------------------------------------------------------------------------- +DeclarationStatementAST::DeclarationStatementAST() +{ +} + +void DeclarationStatementAST::setDeclaration( DeclarationAST::Node& declaration ) +{ + m_declaration = declaration; + if( m_declaration.get() ) m_declaration->setParent( this ); +} + +// -------------------------------------------------------------------------- +ExpressionStatementAST::ExpressionStatementAST() +{ +} + +void ExpressionStatementAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + + +// -------------------------------------------------------------------------- +ParameterDeclarationAST::ParameterDeclarationAST() +{ +} + +void ParameterDeclarationAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void ParameterDeclarationAST::setDeclarator( DeclaratorAST::Node& declarator ) +{ + m_declarator = declarator; + if( m_declarator.get() ) m_declarator->setParent( this ); +} + +void ParameterDeclarationAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + +QString ParameterDeclarationAST::text() const +{ + QString str; + if( m_typeSpec.get() ) + str += m_typeSpec->text() + " "; + + if( m_declarator.get() ) + str += m_declarator->text(); + + if( m_expression.get() ) + str += QString( " = " ) + m_expression->text(); + + return str; +} + +// -------------------------------------------------------------------------- +ParameterDeclarationListAST::ParameterDeclarationListAST() +{ + m_parameterList.setAutoDelete( true ); +} + +void ParameterDeclarationListAST::addParameter( ParameterDeclarationAST::Node& parameter ) +{ + if( !parameter.get() ) + return; + + parameter->setParent( this ); + m_parameterList.append( parameter.release() ); +} + +QString ParameterDeclarationListAST::text() const +{ + QStringList l; + + QPtrListIterator<ParameterDeclarationAST> it( m_parameterList ); + while( it.current() ){ + l.append( it.current()->text() ); + ++it; + } + + return l.join( ", " ); +} + + +// -------------------------------------------------------------------------- +ParameterDeclarationClauseAST::ParameterDeclarationClauseAST() +{ +} + +void ParameterDeclarationClauseAST::setParameterDeclarationList( ParameterDeclarationListAST::Node& parameterDeclarationList ) +{ + m_parameterDeclarationList = parameterDeclarationList; + if( m_parameterDeclarationList.get() ) m_parameterDeclarationList->setParent( this ); +} + +void ParameterDeclarationClauseAST::setEllipsis( AST::Node& ellipsis ) +{ + m_ellipsis = ellipsis; + if( m_ellipsis.get() ) m_ellipsis->setParent( this ); +} + +QString ParameterDeclarationClauseAST::text() const +{ + QString str; + + if( m_parameterDeclarationList.get() ) + str += m_parameterDeclarationList->text(); + + if( m_ellipsis.get() ) + str += " ..."; + + return str; +} + + +// -------------------------------------------------------------------------- +GroupAST::GroupAST() +{ + m_nodeList.setAutoDelete( true ); +} + +void GroupAST::addNode( AST::Node& node ) +{ + if( !node.get() ) + return; + + node->setParent( this ); + m_nodeList.append( node.release() ); +} + +QString GroupAST::text() const +{ + QStringList l; + + QPtrListIterator<AST> it( m_nodeList ); + while( it.current() ){ + l.append( it.current()->text() ); + ++it; + } + + return l.join( " " ); +} + +// -------------------------------------------------------------------------- +AccessDeclarationAST::AccessDeclarationAST() +{ + m_accessList.setAutoDelete( true ); +} + +void AccessDeclarationAST::addAccess( AST::Node& access ) +{ + if( !access.get() ) + return; + + access->setParent( this ); + m_accessList.append( access.release() ); +} + +QString AccessDeclarationAST::text() const +{ + QStringList l; + + QPtrListIterator<AST> it( m_accessList ); + while( it.current() ){ + l.append( it.current()->text() ); + ++it; + } + + return l.join( " " ); +} + +// -------------------------------------------------------------------------- +TypeParameterAST::TypeParameterAST() +{ +} + +void TypeParameterAST::setKind( AST::Node& kind ) +{ + m_kind = kind; + if( m_kind.get() ) m_kind->setParent( this ); +} + +void TypeParameterAST::setTemplateParameterList( TemplateParameterListAST::Node& templateParameterList ) +{ + m_templateParameterList = templateParameterList; + if( m_templateParameterList.get() ) m_templateParameterList->setParent( this ); +} + +void TypeParameterAST::setName( NameAST::Node& name ) +{ + m_name = name; + if( m_name.get() ) m_name->setParent( this ); +} + +void TypeParameterAST::setTypeId( AST::Node& typeId ) +{ + m_typeId = typeId; + if( m_typeId.get() ) m_typeId->setParent( this ); +} + +// -------------------------------------------------------------------------- +TemplateParameterAST::TemplateParameterAST() +{ +} + +void TemplateParameterAST::setTypeParameter( TypeParameterAST::Node& typeParameter ) +{ + m_typeParameter = typeParameter; + if( m_typeParameter.get() ) m_typeParameter->setParent( this ); +} + +void TemplateParameterAST::setTypeValueParameter( ParameterDeclarationAST::Node& typeValueParameter ) +{ + m_typeValueParameter = typeValueParameter; + if( m_typeValueParameter.get() ) m_typeValueParameter->setParent( this ); +} + +// -------------------------------------------------------------------------- +TemplateParameterListAST::TemplateParameterListAST() +{ + m_templateParameterList.setAutoDelete( true ); +} + +void TemplateParameterListAST::addTemplateParameter( TemplateParameterAST::Node& templateParameter ) +{ + if( !templateParameter.get() ) + return; + + templateParameter->setParent( this ); + m_templateParameterList.append( templateParameter.release() ); +} + +// -------------------------------------------------------------------------- +ConditionAST::ConditionAST() +{ +} + +void ConditionAST::setTypeSpec( TypeSpecifierAST::Node& typeSpec ) +{ + m_typeSpec = typeSpec; + if( m_typeSpec.get() ) m_typeSpec->setParent( this ); +} + +void ConditionAST::setDeclarator( DeclaratorAST::Node& declarator ) +{ + m_declarator = declarator; + if( m_declarator.get() ) m_declarator->setParent( this ); +} + +void ConditionAST::setExpression( AST::Node& expression ) +{ + m_expression = expression; + if( m_expression.get() ) m_expression->setParent( this ); +} + +void ClassSpecifierAST::setWinDeclSpec( GroupAST::Node & winDeclSpec ) +{ + m_winDeclSpec = winDeclSpec; + if( m_winDeclSpec.get() ) m_winDeclSpec->setParent( this ); +} diff --git a/lib/cppparser/ast.h b/lib/cppparser/ast.h new file mode 100644 index 00000000..4e877e0e --- /dev/null +++ b/lib/cppparser/ast.h @@ -0,0 +1,1578 @@ +/* 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef __ast_h +#define __ast_h + +#include <memory> +#include <qstring.h> +#include <qptrlist.h> +#include <qstringlist.h> +#include <ksharedptr.h> + +#if defined( Q_OS_WIN32 ) || defined( Q_CC_SUN ) + +#ifndef _THROW0 +# define _THROW0() +#endif + +template <class _Tp> class AUTO_PTR { +private: + _Tp* _M_ptr; + +public: + typedef _Tp element_type; + + explicit AUTO_PTR(_Tp* __p = 0) _THROW0() : _M_ptr(__p) {} + + template <class _Tp1> AUTO_PTR(AUTO_PTR<_Tp1>& __a) _THROW0() + : _M_ptr(__a.release()) {} + + AUTO_PTR(AUTO_PTR& __a) _THROW0() : _M_ptr(__a.release()) {} + + + + template <class _Tp1> + AUTO_PTR& operator=(AUTO_PTR<_Tp1>& __a) _THROW0() { + if (__a.get() != this->get()) { + delete _M_ptr; + _M_ptr = __a.release(); + } + return *this; + } + + AUTO_PTR& operator=(AUTO_PTR& __a) _THROW0() { + if (&__a != this) { + delete _M_ptr; + _M_ptr = __a.release(); + } + return *this; + } + + ~AUTO_PTR() _THROW0() { delete _M_ptr; } + + _Tp& operator*() const _THROW0() { + return *_M_ptr; + } + _Tp* operator->() const _THROW0() { + return _M_ptr; + } + _Tp* get() const _THROW0() { + return _M_ptr; + } + _Tp* release() _THROW0() { + _Tp* __tmp = _M_ptr; + _M_ptr = 0; + return __tmp; + } + void reset(_Tp* __p = 0) _THROW0() { + delete _M_ptr; + _M_ptr = __p; + } + + // According to the C++ standard, these conversions are required. Most + // present-day compilers, however, do not enforce that requirement---and, + // in fact, most present-day compilers do not support the language + // features that these conversions rely on. + + +private: + template<class _Tp1> struct AUTO_PTR_ref { + _Tp1* _M_ptr; + AUTO_PTR_ref(_Tp1* __p) : _M_ptr(__p) {} + }; + +public: + AUTO_PTR(AUTO_PTR_ref<_Tp> __ref) _THROW0() + : _M_ptr(__ref._M_ptr) {} + template <class _Tp1> operator AUTO_PTR_ref<_Tp1>() _THROW0() + { return AUTO_PTR_ref<_Tp>(this->release()); } + template <class _Tp1> operator AUTO_PTR<_Tp1>() _THROW0() + { return AUTO_PTR<_Tp1>(this->release()) } + +}; + +#else +#define AUTO_PTR std::auto_ptr +#endif + +template <class T> typename T::Node CreateNode() +{ + typename T::Node node( new T ); + node->setNodeType( T::Type ); + return node; +} + +template <class T> typename T::Node NullNode() +{ + typename T::Node node; + return node; +} + +enum NodeType +{ + NodeType_Generic = 0, + + NodeType_TemplateArgumentList = 1000, + NodeType_ClassOrNamespaceName, + NodeType_Name, + NodeType_Declaration, + NodeType_TypeSpecifier, + NodeType_BaseSpecifier, + NodeType_BaseClause, + NodeType_ClassSpecifier, + NodeType_Enumerator, + NodeType_EnumSpecifier, + NodeType_ElaboratedTypeSpecifier, + NodeType_LinkageBody, + NodeType_LinkageSpecification, + NodeType_Namespace, + NodeType_NamespaceAlias, + NodeType_Using, + NodeType_UsingDirective, + NodeType_InitDeclaratorList, + NodeType_Typedef, + NodeType_Declarator, + NodeType_InitDeclarator, + NodeType_TemplateDeclaration, + NodeType_SimpleDeclaration, + NodeType_Statement, + NodeType_StatementList, + NodeType_IfStatement, + NodeType_WhileStatement, + NodeType_DoStatement, + NodeType_ForStatement, + NodeType_ForEachStatement, // qt4 [erbsland] + NodeType_SwitchStatement, + NodeType_CatchStatement, + NodeType_CatchStatementList, + NodeType_TryBlockStatement, + NodeType_DeclarationStatement, + NodeType_TranslationUnit, + NodeType_FunctionDefinition, + NodeType_ExpressionStatement, + NodeType_ParameterDeclaration, + NodeType_ParameterDeclarationList, + NodeType_ParameterDeclarationClause, + NodeType_Group, + NodeType_AccessDeclaration, + NodeType_TypeParameter, + NodeType_TemplateParameter, + NodeType_TemplateParameterList, + NodeType_Condition, + + NodeType_Custom = 2000 +}; + +QString nodeTypeToString( int type ); + + +#if defined(CPPPARSER_QUICK_ALLOCATOR) + +#include <quick_allocator.h> + +#define DECLARE_ALLOC(tp) \ + void * operator new(std::size_t) \ + { \ + return quick_allocator< tp >::alloc(); \ + } \ + \ + void operator delete(void * p) \ + { \ + quick_allocator< tp >::dealloc(p); \ + } +#else + +#define DECLARE_ALLOC(tp) + +#endif + +struct Slice +{ + QString source; + int position; + int length; + + inline Slice() + : position(0), length(0) {} +}; + + +class CommentAST { + QString m_comment; + public: + void setComment( const QString& comment ) { + m_comment = comment; + } + + void addComment( const QString& comment ) { + if( !m_comment.isEmpty() ) { + m_comment += "\n(" + comment + ")"; + } else { + m_comment = comment; + } + } + + QString comment() const { + return m_comment; + } + + bool haveComment() const { + return !m_comment.isEmpty(); + } +}; + +class AST : public CommentAST +{ +public: + typedef AUTO_PTR<AST> Node; + enum { Type=NodeType_Generic }; + + DECLARE_ALLOC( AST ) + +public: + AST(); + virtual ~AST(); + + int nodeType() const { return m_nodeType; } + void setNodeType( int nodeType ) { m_nodeType = nodeType; } + + AST* parent() { return m_parent; } + void setParent( AST* parent ); + + void setStartPosition( int line, int col ); + void getStartPosition( int* line, int* col ) const; + + void setEndPosition( int line, int col ); + void getEndPosition( int* line, int* col ) const; + +#ifndef CPPPARSER_NO_CHILDREN + QPtrList<AST> children() { return m_children; } + void appendChild( AST* child ); + void removeChild( AST* child ); +#endif + + virtual inline QString text() const + { return m_slice.source.mid(m_slice.position, m_slice.length); } + + inline void setSlice( const Slice& slice ) + { m_slice = slice; } + + inline void setSlice( const QString &text, int position, int length ) + { + CommentAST a; + m_slice.source = text; + m_slice.position = position; + m_slice.length = length; + } + + inline void setText(const QString &text) + { setSlice(text, 0, text.length()); } + +private: + int m_nodeType; + AST* m_parent; + int m_startLine, m_startColumn; + int m_endLine, m_endColumn; + Slice m_slice; +#ifndef CPPPARSER_NO_CHILDREN + QPtrList<AST> m_children; +#endif + +private: + AST( const AST& source ); + void operator = ( const AST& source ); +}; + +class GroupAST: public AST +{ +public: + typedef AUTO_PTR<GroupAST> Node; + enum { Type = NodeType_Group }; + + DECLARE_ALLOC( GroupAST ) + +public: + GroupAST(); + + QPtrList<AST> nodeList() { return m_nodeList; } + void addNode( AST::Node& node ); + + virtual QString text() const; + +private: + QPtrList<AST> m_nodeList; + +private: + GroupAST( const GroupAST& source ); + void operator = ( const GroupAST& source ); +}; + + +class TemplateArgumentListAST: public AST +{ +public: + typedef AUTO_PTR<TemplateArgumentListAST> Node; + enum { Type = NodeType_TemplateArgumentList }; + + DECLARE_ALLOC( TemplateArgumentListAST ) + +public: + TemplateArgumentListAST(); + + void addArgument( AST::Node& arg ); + QPtrList<AST> argumentList() { return m_argumentList; } + + virtual QString text() const; + +private: + QPtrList<AST> m_argumentList; + +private: + TemplateArgumentListAST( const TemplateArgumentListAST& source ); + void operator = ( const TemplateArgumentListAST& source ); +}; + +class ClassOrNamespaceNameAST: public AST +{ +public: + typedef AUTO_PTR<ClassOrNamespaceNameAST> Node; + enum { Type = NodeType_ClassOrNamespaceName }; + + DECLARE_ALLOC( ClassOrNamespaceNameAST ) + +public: + ClassOrNamespaceNameAST(); + + AST* name() { return m_name.get(); } + void setName( AST::Node& name ); + + TemplateArgumentListAST* templateArgumentList() { return m_templateArgumentList.get(); } + void setTemplateArgumentList( TemplateArgumentListAST::Node& templateArgumentList ); + + virtual QString text() const; + +private: + AST::Node m_name; + TemplateArgumentListAST::Node m_templateArgumentList; + +private: + ClassOrNamespaceNameAST( const ClassOrNamespaceNameAST& source ); + void operator = ( const ClassOrNamespaceNameAST& source ); +}; + +class NameAST: public AST +{ +public: + typedef AUTO_PTR<NameAST> Node; + enum { Type = NodeType_Name }; + + DECLARE_ALLOC( NameAST ) + +public: + NameAST(); + + bool isGlobal() const { return m_global; } + void setGlobal( bool b ); + + void addClassOrNamespaceName( ClassOrNamespaceNameAST::Node& classOrNamespaceName ); + QPtrList<ClassOrNamespaceNameAST> classOrNamespaceNameList() { return m_classOrNamespaceNameList; } + + ClassOrNamespaceNameAST* unqualifiedName() { return m_unqualifiedName.get(); } + void setUnqualifiedName( ClassOrNamespaceNameAST::Node& unqualifiedName ); + + virtual QString text() const; + +private: + bool m_global; + ClassOrNamespaceNameAST::Node m_unqualifiedName; + QPtrList<ClassOrNamespaceNameAST> m_classOrNamespaceNameList; + +private: + NameAST( const NameAST& source ); + void operator = ( const NameAST& source ); +}; + +class TypeParameterAST: public AST +{ +public: + typedef AUTO_PTR<TypeParameterAST> Node; + enum { Type = NodeType_TypeParameter }; + + DECLARE_ALLOC( TypeParameterAST ) + +public: + TypeParameterAST(); + + AST* kind() { return m_kind.get(); } + void setKind( AST::Node& kind ); + + class TemplateParameterListAST* templateParameterList() { return m_templateParameterList.get(); } + void setTemplateParameterList( AUTO_PTR<class TemplateParameterListAST>& templateParameterList ); + + NameAST* name() { return m_name.get(); } + void setName( NameAST::Node& name ); + + AST* typeId() { return m_typeId.get(); } + void setTypeId( AST::Node& typeId ); + +private: + AST::Node m_kind; + AUTO_PTR<class TemplateParameterListAST> m_templateParameterList; + NameAST::Node m_name; + AST::Node m_typeId; + +private: + TypeParameterAST( const TypeParameterAST& source ); + void operator = ( const TypeParameterAST& source ); +}; + +class DeclarationAST: public AST +{ +public: + typedef AUTO_PTR<DeclarationAST> Node; + enum { Type = NodeType_Declaration }; + + DECLARE_ALLOC( DeclarationAST ) + +public: + DeclarationAST(); + +private: + DeclarationAST( const DeclarationAST& source ); + void operator = ( const DeclarationAST& source ); +}; + +class AccessDeclarationAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<AccessDeclarationAST> Node; + enum { Type = NodeType_AccessDeclaration }; + + DECLARE_ALLOC( AccessDeclarationAST ) + +public: + AccessDeclarationAST(); + + QPtrList<AST> accessList() { return m_accessList; } + void addAccess( AST::Node& access ); + + virtual QString text() const; + +private: + QPtrList<AST> m_accessList; + +private: + AccessDeclarationAST( const AccessDeclarationAST& source ); + void operator = ( const AccessDeclarationAST& source ); +}; + +class TypeSpecifierAST: public AST +{ +public: + typedef AUTO_PTR<TypeSpecifierAST> Node; + enum { Type = NodeType_TypeSpecifier }; + + DECLARE_ALLOC( TypeSpecifierAST ) + +public: + TypeSpecifierAST(); + + virtual NameAST* name() { return m_name.get(); } + virtual void setName( NameAST::Node& name ); + + GroupAST* cvQualify() { return m_cvQualify.get(); } + void setCvQualify( GroupAST::Node& cvQualify ); + + GroupAST* cv2Qualify() { return m_cv2Qualify.get(); } + void setCv2Qualify( GroupAST::Node& cv2Qualify ); + + virtual QString text() const; + +private: + NameAST::Node m_name; + GroupAST::Node m_cvQualify; + GroupAST::Node m_cv2Qualify; + +private: + TypeSpecifierAST( const TypeSpecifierAST& source ); + void operator = ( const TypeSpecifierAST& source ); +}; + +class BaseSpecifierAST: public AST +{ +public: + typedef AUTO_PTR<BaseSpecifierAST> Node; + enum { Type = NodeType_BaseSpecifier }; + + DECLARE_ALLOC( BaseSpecifierAST ) + +public: + BaseSpecifierAST(); + + AST* isVirtual() { return m_isVirtual.get(); } + void setIsVirtual( AST::Node& isVirtual ); + + AST* access() { return m_access.get(); } + void setAccess( AST::Node& access ); + + NameAST* name() { return m_name.get(); } + void setName( NameAST::Node& name ); + +private: + AST::Node m_isVirtual; + AST::Node m_access; + NameAST::Node m_name; + +private: + BaseSpecifierAST( const BaseSpecifierAST& source ); + void operator = ( const BaseSpecifierAST& source ); +}; + +class BaseClauseAST: public AST +{ +public: + typedef AUTO_PTR<BaseClauseAST> Node; + enum { Type = NodeType_BaseClause }; + + DECLARE_ALLOC( BaseClauseAST ) + +public: + BaseClauseAST(); + + void addBaseSpecifier( BaseSpecifierAST::Node& baseSpecifier ); + QPtrList<BaseSpecifierAST> baseSpecifierList() { return m_baseSpecifierList; } + +private: + QPtrList<BaseSpecifierAST> m_baseSpecifierList; + +private: + BaseClauseAST( const BaseClauseAST& source ); + void operator = ( const BaseClauseAST& source ); +}; + +class ClassSpecifierAST: public TypeSpecifierAST +{ +public: + typedef AUTO_PTR<ClassSpecifierAST> Node; + enum { Type = NodeType_ClassSpecifier }; + + DECLARE_ALLOC( ClassSpecifierAST ) + +public: + ClassSpecifierAST(); + + GroupAST* winDeclSpec() { return m_winDeclSpec.get(); } + void setWinDeclSpec( GroupAST::Node& winDeclSpec ); + + AST* classKey() { return m_classKey.get(); } + void setClassKey( AST::Node& classKey ); + + BaseClauseAST* baseClause() { return m_baseClause.get(); } + void setBaseClause( BaseClauseAST::Node& baseClause ); + + QPtrList<DeclarationAST> declarationList() { return m_declarationList; } + void addDeclaration( DeclarationAST::Node& declaration ); + +private: + GroupAST::Node m_winDeclSpec; + AST::Node m_classKey; + BaseClauseAST::Node m_baseClause; + QPtrList<DeclarationAST> m_declarationList; +private: + ClassSpecifierAST( const ClassSpecifierAST& source ); + void operator = ( const ClassSpecifierAST& source ); +}; + +class EnumeratorAST: public AST +{ +public: + typedef AUTO_PTR<EnumeratorAST> Node; + enum { Type = NodeType_Enumerator }; + + DECLARE_ALLOC( EnumeratorAST ) + +public: + EnumeratorAST(); + + AST* id() { return m_id.get(); } + void setId( AST::Node& id ); + + AST* expr() { return m_expr.get(); } + void setExpr( AST::Node& expr ); + +private: + AST::Node m_id; + AST::Node m_expr; + +private: + EnumeratorAST( const EnumeratorAST& source ); + void operator = ( const EnumeratorAST& source ); +}; + +class EnumSpecifierAST: public TypeSpecifierAST +{ +public: + typedef AUTO_PTR<EnumSpecifierAST> Node; + enum { Type = NodeType_EnumSpecifier }; + + DECLARE_ALLOC( EnumSpecifierAST ) + +public: + EnumSpecifierAST(); + + void addEnumerator( EnumeratorAST::Node& enumerator ); + QPtrList<EnumeratorAST> enumeratorList() { return m_enumeratorList; } + +private: + QPtrList<EnumeratorAST> m_enumeratorList; + +private: + EnumSpecifierAST( const EnumSpecifierAST& source ); + void operator = ( const EnumSpecifierAST& source ); +}; + +class ElaboratedTypeSpecifierAST: public TypeSpecifierAST +{ +public: + typedef AUTO_PTR<ElaboratedTypeSpecifierAST> Node; + enum { Type = NodeType_ElaboratedTypeSpecifier }; + + DECLARE_ALLOC( ElaboratedTypeSpecifierAST ) + +public: + ElaboratedTypeSpecifierAST(); + + AST* kind() { return m_kind.get(); } + void setKind( AST::Node& kind ); + + virtual QString text() const; + +private: + AST::Node m_kind; + +private: + ElaboratedTypeSpecifierAST( const ElaboratedTypeSpecifierAST& source ); + void operator = ( const ElaboratedTypeSpecifierAST& source ); +}; + + +class LinkageBodyAST: public AST +{ +public: + typedef AUTO_PTR<LinkageBodyAST> Node; + enum { Type = NodeType_LinkageBody }; + + DECLARE_ALLOC( LinkageBodyAST ) + +public: + LinkageBodyAST(); + + void addDeclaration( DeclarationAST::Node& ast ); + QPtrList<DeclarationAST> declarationList() { return m_declarationList; } + +private: + QPtrList<DeclarationAST> m_declarationList; + +private: + LinkageBodyAST( const LinkageBodyAST& source ); + void operator = ( const LinkageBodyAST& source ); +}; + +class LinkageSpecificationAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<LinkageSpecificationAST> Node; + enum { Type = NodeType_LinkageSpecification }; + + DECLARE_ALLOC( LinkageSpecificationAST ) + +public: + LinkageSpecificationAST(); + + AST* externType() { return m_externType.get(); } + void setExternType( AST::Node& externType ); + + LinkageBodyAST* linkageBody() { return m_linkageBody.get(); } + void setLinkageBody( LinkageBodyAST::Node& linkageBody ); + + DeclarationAST* declaration() { return m_declaration.get(); } + void setDeclaration( DeclarationAST::Node& decl ); + +private: + AST::Node m_externType; + LinkageBodyAST::Node m_linkageBody; + DeclarationAST::Node m_declaration; + +private: + LinkageSpecificationAST( const LinkageSpecificationAST& source ); + void operator = ( const LinkageSpecificationAST& source ); +}; + +class NamespaceAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<NamespaceAST> Node; + enum { Type = NodeType_Namespace }; + + DECLARE_ALLOC( NamespaceAST ) + +public: + NamespaceAST(); + + AST* namespaceName() { return m_namespaceName.get(); } + void setNamespaceName( AST::Node& namespaceName ); + + LinkageBodyAST* linkageBody() { return m_linkageBody.get(); } + void setLinkageBody( LinkageBodyAST::Node& linkageBody ); + +private: + AST::Node m_namespaceName; + LinkageBodyAST::Node m_linkageBody; + +private: + NamespaceAST( const NamespaceAST& source ); + void operator = ( const NamespaceAST& source ); +}; + +class NamespaceAliasAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<NamespaceAliasAST> Node; + enum { Type = NodeType_NamespaceAlias }; + + DECLARE_ALLOC( NamespaceAliasAST ) + +public: + NamespaceAliasAST(); + + AST* namespaceName() { return m_namespaceName.get(); } + void setNamespaceName( AST::Node& name ); + + NameAST* aliasName() { return m_aliasName.get(); } + void setAliasName( NameAST::Node& name ); + +private: + AST::Node m_namespaceName; + NameAST::Node m_aliasName; + +private: + NamespaceAliasAST( const NamespaceAliasAST& source ); + void operator = ( const NamespaceAliasAST& source ); +}; + +class UsingAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<UsingAST> Node; + enum { Type = NodeType_Using }; + + DECLARE_ALLOC( UsingAST ) + +public: + UsingAST(); + + AST* typeName() { return m_typeName.get(); } + void setTypeName( AST::Node& typeName ); + + NameAST* name() { return m_name.get(); } + void setName( NameAST::Node& name ); + +private: + AST::Node m_typeName; + NameAST::Node m_name; + +private: + UsingAST( const UsingAST& source ); + void operator = ( const UsingAST& source ); +}; + +class UsingDirectiveAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<UsingDirectiveAST> Node; + enum { Type = NodeType_UsingDirective }; + + DECLARE_ALLOC( UsingDirectiveAST ) + +public: + UsingDirectiveAST(); + + NameAST* name() { return m_name.get(); } + void setName( NameAST::Node& name ); + +private: + NameAST::Node m_name; + +private: + UsingDirectiveAST( const UsingDirectiveAST& source ); + void operator = ( const UsingDirectiveAST& source ); +}; + +class DeclaratorAST: public AST +{ +public: + typedef AUTO_PTR<DeclaratorAST> Node; + enum { Type = NodeType_Declarator }; + + DECLARE_ALLOC( DeclaratorAST ) + +public: + DeclaratorAST(); + + QPtrList<AST> ptrOpList() { return m_ptrOpList; } + void addPtrOp( AST::Node& ptrOp ); + + DeclaratorAST* subDeclarator() { return m_subDeclarator.get(); } + void setSubDeclarator( AUTO_PTR<DeclaratorAST>& subDeclarator ); + + NameAST* declaratorId() { return m_declaratorId.get(); } + void setDeclaratorId( NameAST::Node& declaratorId ); + + AST* bitfieldInitialization() { return m_bitfieldInitialization.get(); } + void setBitfieldInitialization( AST::Node& bitfieldInitialization ); + + QPtrList<AST> arrayDimensionList() { return m_arrayDimensionList; } + void addArrayDimension( AST::Node& arrayDimension ); + + class ParameterDeclarationClauseAST* parameterDeclarationClause() { return m_parameterDeclarationClause.get(); } + void setParameterDeclarationClause( AUTO_PTR<class ParameterDeclarationClauseAST>& parameterDeclarationClause ); + + // ### replace 'constant' with cvQualify + AST* constant() { return m_constant.get(); } + void setConstant( AST::Node& constant ); + + GroupAST* exceptionSpecification() { return m_exceptionSpecification.get(); } + void setExceptionSpecification( GroupAST::Node& exceptionSpecification ); + +private: + QPtrList<AST> m_ptrOpList; + AUTO_PTR<DeclaratorAST> m_subDeclarator; + NameAST::Node m_declaratorId; + AST::Node m_bitfieldInitialization; + QPtrList<AST> m_arrayDimensionList; + AUTO_PTR<class ParameterDeclarationClauseAST> m_parameterDeclarationClause; + AST::Node m_constant; + GroupAST::Node m_exceptionSpecification; + +private: + DeclaratorAST( const DeclaratorAST& source ); + void operator = ( const DeclaratorAST& source ); +}; + +class ParameterDeclarationAST: public AST +{ +public: + typedef AUTO_PTR<ParameterDeclarationAST> Node; + enum { Type = NodeType_ParameterDeclaration }; + + DECLARE_ALLOC( ParameterDeclarationAST ) + +public: + ParameterDeclarationAST(); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + DeclaratorAST* declarator() { return m_declarator.get(); } + void setDeclarator( DeclaratorAST::Node& declarator ); + + AST* expression() { return m_expression.get(); } + void setExpression( AST::Node& expression ); + + virtual QString text() const; + +private: + TypeSpecifierAST::Node m_typeSpec; + DeclaratorAST::Node m_declarator; + AST::Node m_expression; + +private: + ParameterDeclarationAST( const ParameterDeclarationAST& source ); + void operator = ( const ParameterDeclarationAST& source ); +}; + +class ParameterDeclarationListAST: public AST +{ +public: + typedef AUTO_PTR<ParameterDeclarationListAST> Node; + enum { Type = NodeType_ParameterDeclarationList }; + + DECLARE_ALLOC( ParameterDeclarationListAST ) + +public: + ParameterDeclarationListAST(); + + QPtrList<ParameterDeclarationAST> parameterList() { return m_parameterList; } + void addParameter( ParameterDeclarationAST::Node& parameter ); + + virtual QString text() const; + +private: + QPtrList<ParameterDeclarationAST> m_parameterList; + +private: + ParameterDeclarationListAST( const ParameterDeclarationListAST& source ); + void operator = ( const ParameterDeclarationListAST& source ); +}; + +class ParameterDeclarationClauseAST: public AST +{ +public: + typedef AUTO_PTR<ParameterDeclarationClauseAST> Node; + enum { Type = NodeType_ParameterDeclarationClause }; + + DECLARE_ALLOC( ParameterDeclarationClauseAST ) + +public: + ParameterDeclarationClauseAST(); + + ParameterDeclarationListAST* parameterDeclarationList() { return m_parameterDeclarationList.get(); } + void setParameterDeclarationList( ParameterDeclarationListAST::Node& parameterDeclarationList ); + + AST* ellipsis() { return m_ellipsis.get(); } + void setEllipsis( AST::Node& ellipsis ); + + virtual QString text() const; + +private: + ParameterDeclarationListAST::Node m_parameterDeclarationList; + AST::Node m_ellipsis; + +private: + ParameterDeclarationClauseAST( const ParameterDeclarationClauseAST& source ); + void operator = ( const ParameterDeclarationClauseAST& source ); +}; + + +class InitDeclaratorAST: public AST +{ +public: + typedef AUTO_PTR<InitDeclaratorAST> Node; + enum { Type = NodeType_InitDeclarator }; + + DECLARE_ALLOC( InitDeclaratorAST ) + +public: + InitDeclaratorAST(); + + DeclaratorAST* declarator() { return m_declarator.get(); } + void setDeclarator( DeclaratorAST::Node& declarator ); + + AST* initializer() { return m_initializer.get(); } + void setInitializer( AST::Node& initializer ); + +private: + DeclaratorAST::Node m_declarator; + AST::Node m_initializer; + +private: + InitDeclaratorAST( const InitDeclaratorAST& source ); + void operator = ( const InitDeclaratorAST& source ); +}; + +class InitDeclaratorListAST: public AST +{ +public: + typedef AUTO_PTR<InitDeclaratorListAST> Node; + enum { Type = NodeType_InitDeclaratorList }; + + DECLARE_ALLOC( InitDeclaratorListAST ) + +public: + InitDeclaratorListAST(); + + QPtrList<InitDeclaratorAST> initDeclaratorList() { return m_initDeclaratorList; } + void addInitDeclarator( InitDeclaratorAST::Node& decl ); + +private: + QPtrList<InitDeclaratorAST> m_initDeclaratorList; + +private: + InitDeclaratorListAST( const InitDeclaratorListAST& source ); + void operator = ( const InitDeclaratorListAST& source ); +}; + +class TypedefAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<TypedefAST> Node; + enum { Type = NodeType_Typedef }; + + DECLARE_ALLOC( TypedefAST ) + +public: + TypedefAST(); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + InitDeclaratorListAST* initDeclaratorList() { return m_initDeclaratorList.get(); } + void setInitDeclaratorList( InitDeclaratorListAST::Node& initDeclaratorList ); + +private: + TypeSpecifierAST::Node m_typeSpec; + InitDeclaratorListAST::Node m_initDeclaratorList; + +private: + TypedefAST( const TypedefAST& source ); + void operator = ( const TypedefAST& source ); +}; + +class TemplateParameterAST: public AST +{ +public: + typedef AUTO_PTR<TemplateParameterAST> Node; + enum { Type = NodeType_TemplateParameter }; + + DECLARE_ALLOC( TemplateParameterAST ) + +public: + TemplateParameterAST(); + + TypeParameterAST* typeParameter() { return m_typeParameter.get(); } + void setTypeParameter( TypeParameterAST::Node& typeParameter ); + + ParameterDeclarationAST* typeValueParameter() { return m_typeValueParameter.get(); } + void setTypeValueParameter( ParameterDeclarationAST::Node& typeValueParameter ); + +private: + TypeParameterAST::Node m_typeParameter; + ParameterDeclarationAST::Node m_typeValueParameter; + +private: + TemplateParameterAST( const TemplateParameterAST& source ); + void operator = ( const TemplateParameterAST& source ); +}; + +class TemplateParameterListAST: public AST +{ +public: + typedef AUTO_PTR<TemplateParameterListAST> Node; + enum { Type = NodeType_TemplateParameterList }; + + DECLARE_ALLOC( TemplateParameterListAST ) + +public: + TemplateParameterListAST(); + + QPtrList<TemplateParameterAST> templateParameterList() { return m_templateParameterList; } + void addTemplateParameter( TemplateParameterAST::Node& templateParameter ); + +private: + QPtrList<TemplateParameterAST> m_templateParameterList; + +private: + TemplateParameterListAST( const TemplateParameterListAST& source ); + void operator = ( const TemplateParameterListAST& source ); +}; + +class TemplateDeclarationAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<TemplateDeclarationAST> Node; + enum { Type = NodeType_TemplateDeclaration }; + + DECLARE_ALLOC( TemplateDeclarationAST ) + +public: + TemplateDeclarationAST(); + + AST* exported() { return m_exported.get(); } + void setExported( AST::Node& exported ); + + TemplateParameterListAST* templateParameterList() { return m_templateParameterList.get(); } + void setTemplateParameterList( TemplateParameterListAST::Node& templateParameterList ); + + DeclarationAST* declaration() { return m_declaration.get(); } + void setDeclaration( DeclarationAST::Node& declaration ); + +private: + AST::Node m_exported; + TemplateParameterListAST::Node m_templateParameterList; + DeclarationAST::Node m_declaration; + +private: + TemplateDeclarationAST( const TemplateDeclarationAST& source ); + void operator = ( const TemplateDeclarationAST& source ); +}; + +class SimpleDeclarationAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<SimpleDeclarationAST> Node; + enum { Type = NodeType_SimpleDeclaration }; + + DECLARE_ALLOC( SimpleDeclarationAST ) + +public: + SimpleDeclarationAST(); + + GroupAST* functionSpecifier() { return m_functionSpecifier.get(); } + void setFunctionSpecifier( GroupAST::Node& functionSpecifier ); + + GroupAST* storageSpecifier() { return m_storageSpecifier.get(); } + void setStorageSpecifier( GroupAST::Node& storageSpecifier ); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + InitDeclaratorListAST* initDeclaratorList() { return m_initDeclaratorList.get(); } + void setInitDeclaratorList( InitDeclaratorListAST::Node& initDeclaratorList ); + + GroupAST* winDeclSpec() { return m_winDeclSpec.get(); } + void setWinDeclSpec( GroupAST::Node& winDeclSpec ); + +private: + GroupAST::Node m_functionSpecifier; + GroupAST::Node m_storageSpecifier; + TypeSpecifierAST::Node m_typeSpec; + InitDeclaratorListAST::Node m_initDeclaratorList; + GroupAST::Node m_winDeclSpec; + +private: + SimpleDeclarationAST( const SimpleDeclarationAST& source ); + void operator = ( const SimpleDeclarationAST& source ); +}; + +class StatementAST: public AST +{ +public: + typedef AUTO_PTR<StatementAST> Node; + enum { Type = NodeType_Statement }; + + DECLARE_ALLOC( StatementAST ) + +public: + StatementAST(); + +private: + StatementAST( const StatementAST& source ); + void operator = ( const StatementAST& source ); +}; + +class ExpressionStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<ExpressionStatementAST> Node; + enum { Type = NodeType_ExpressionStatement }; + + DECLARE_ALLOC( ExpressionStatementAST ) + +public: + ExpressionStatementAST(); + + AST* expression() { return m_expression.get(); } + void setExpression( AST::Node& expression ); + +private: + AST::Node m_expression; + +private: + ExpressionStatementAST( const ExpressionStatementAST& source ); + void operator = ( const ExpressionStatementAST& source ); +}; + +class ConditionAST: public AST +{ +public: + typedef AUTO_PTR<ConditionAST> Node; + enum { Type = NodeType_Condition }; + + DECLARE_ALLOC( ConditionAST ) + +public: + ConditionAST(); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + DeclaratorAST* declarator() { return m_declarator.get(); } + void setDeclarator( DeclaratorAST::Node& declarator ); + + AST* expression() { return m_expression.get(); } + void setExpression( AST::Node& expression ); + +private: + TypeSpecifierAST::Node m_typeSpec; + DeclaratorAST::Node m_declarator; + AST::Node m_expression; + +private: + ConditionAST( const ConditionAST& source ); + void operator = ( const ConditionAST& source ); +}; + +class IfStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<IfStatementAST> Node; + enum { Type = NodeType_IfStatement }; + + DECLARE_ALLOC( IfStatementAST ) + +public: + IfStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + + StatementAST* elseStatement() { return m_elseStatement.get(); } + void setElseStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + StatementAST::Node m_elseStatement; + +private: + IfStatementAST( const IfStatementAST& source ); + void operator = ( const IfStatementAST& source ); +}; + +class WhileStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<WhileStatementAST> Node; + enum { Type = NodeType_WhileStatement }; + + DECLARE_ALLOC( WhileStatementAST ) + +public: + WhileStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + +private: + WhileStatementAST( const WhileStatementAST& source ); + void operator = ( const WhileStatementAST& source ); +}; + +class DoStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<DoStatementAST> Node; + enum { Type = NodeType_DoStatement }; + + DECLARE_ALLOC( DoStatementAST ) + +public: + DoStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + +private: + DoStatementAST( const DoStatementAST& source ); + void operator = ( const DoStatementAST& source ); +}; + +class ForStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<ForStatementAST> Node; + enum { Type = NodeType_ForStatement }; + + DECLARE_ALLOC( ForStatementAST ) + +public: + ForStatementAST(); + + StatementAST* initStatement() { return m_initStatement.get(); } + void setInitStatement( StatementAST::Node& statement ); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + AST* expression() const { return m_expression.get(); } + void setExpression( AST::Node& expression ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_initStatement; + StatementAST::Node m_statement; + AST::Node m_expression; + +private: + ForStatementAST( const ForStatementAST& source ); + void operator = ( const ForStatementAST& source ); +}; + +// qt4 [erbsland] +class ForEachStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<ForEachStatementAST> Node; + enum { Type = NodeType_ForEachStatement }; + + DECLARE_ALLOC( ForEachStatementAST ) + +public: + ForEachStatementAST(); + + StatementAST* initStatement() { return m_initStatement.get(); } + void setInitStatement( StatementAST::Node& statement ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + + AST* expression() const { return m_expression.get(); } + void setExpression( AST::Node& expression ); + +private: + StatementAST::Node m_initStatement; + StatementAST::Node m_statement; + AST::Node m_expression; + +private: + ForEachStatementAST( const ForEachStatementAST& source ); + void operator = ( const ForEachStatementAST& source ); +}; + +class SwitchStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<SwitchStatementAST> Node; + enum { Type = NodeType_SwitchStatement }; + + DECLARE_ALLOC( SwitchStatementAST ) + +public: + SwitchStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + +private: + SwitchStatementAST( const SwitchStatementAST& source ); + void operator = ( const SwitchStatementAST& source ); +}; + +class StatementListAST: public StatementAST +{ +public: + typedef AUTO_PTR<StatementListAST> Node; + enum { Type = NodeType_StatementList }; + + DECLARE_ALLOC( StatementListAST ) + +public: + StatementListAST(); + + QPtrList<StatementAST> statementList() { return m_statementList; } + void addStatement( StatementAST::Node& statement ); + +private: + QPtrList<StatementAST> m_statementList; + +private: + StatementListAST( const StatementListAST& source ); + void operator = ( const StatementListAST& source ); +}; + +class CatchStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<CatchStatementAST> Node; + enum { Type = NodeType_CatchStatement }; + + DECLARE_ALLOC( CatchStatementAST ) + +public: + CatchStatementAST(); + + ConditionAST* condition() const { return m_condition.get(); } + void setCondition( ConditionAST::Node& condition ); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + +private: + ConditionAST::Node m_condition; + StatementAST::Node m_statement; + +private: + CatchStatementAST( const CatchStatementAST& source ); + void operator = ( const CatchStatementAST& source ); +}; + +class CatchStatementListAST: public StatementAST +{ +public: + typedef AUTO_PTR<CatchStatementListAST> Node; + enum { Type = NodeType_CatchStatementList }; + + DECLARE_ALLOC( CatchStatementListAST ) + +public: + CatchStatementListAST(); + + QPtrList<CatchStatementAST> statementList() { return m_statementList; } + void addStatement( CatchStatementAST::Node& statement ); + +private: + QPtrList<CatchStatementAST> m_statementList; + +private: + CatchStatementListAST( const CatchStatementListAST& source ); + void operator = ( const CatchStatementListAST& source ); +}; + +class TryBlockStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<TryBlockStatementAST> Node; + enum { Type = NodeType_TryBlockStatement }; + + DECLARE_ALLOC( TryBlockStatementAST ) + +public: + TryBlockStatementAST(); + + StatementAST* statement() { return m_statement.get(); } + void setStatement( StatementAST::Node& statement ); + + CatchStatementListAST* catchStatementList() { return m_catchStatementList.get(); } + void setCatchStatementList( CatchStatementListAST::Node& statementList ); + +private: + StatementAST::Node m_statement; + CatchStatementListAST::Node m_catchStatementList; + +private: + TryBlockStatementAST( const TryBlockStatementAST& source ); + void operator = ( const TryBlockStatementAST& source ); +}; + +class DeclarationStatementAST: public StatementAST +{ +public: + typedef AUTO_PTR<DeclarationStatementAST> Node; + enum { Type = NodeType_DeclarationStatement }; + + DECLARE_ALLOC( DeclarationStatementAST ) + +public: + DeclarationStatementAST(); + + DeclarationAST* declaration() { return m_declaration.get(); } + void setDeclaration( DeclarationAST::Node& declaration ); + +private: + DeclarationAST::Node m_declaration; + +private: + DeclarationStatementAST( const DeclarationStatementAST& source ); + void operator = ( const DeclarationStatementAST& source ); +}; + +class FunctionDefinitionAST: public DeclarationAST +{ +public: + typedef AUTO_PTR<FunctionDefinitionAST> Node; + enum { Type = NodeType_FunctionDefinition }; + + DECLARE_ALLOC( FunctionDefinitionAST ) + +public: + FunctionDefinitionAST(); + + GroupAST* functionSpecifier() { return m_functionSpecifier.get(); } + void setFunctionSpecifier( GroupAST::Node& functionSpecifier ); + + GroupAST* storageSpecifier() { return m_storageSpecifier.get(); } + void setStorageSpecifier( GroupAST::Node& storageSpecifier ); + + TypeSpecifierAST* typeSpec() { return m_typeSpec.get(); } + void setTypeSpec( TypeSpecifierAST::Node& typeSpec ); + + InitDeclaratorAST* initDeclarator() { return m_initDeclarator.get(); } + void setInitDeclarator( InitDeclaratorAST::Node& initDeclarator ); + + StatementListAST* functionBody() { return m_functionBody.get(); } + void setFunctionBody( StatementListAST::Node& functionBody ); + + GroupAST* winDeclSpec() { return m_winDeclSpec.get(); } + void setWinDeclSpec( GroupAST::Node& winDeclSpec ); + +private: + GroupAST::Node m_functionSpecifier; + GroupAST::Node m_storageSpecifier; + TypeSpecifierAST::Node m_typeSpec; + InitDeclaratorAST::Node m_initDeclarator; + StatementListAST::Node m_functionBody; + GroupAST::Node m_winDeclSpec; + +private: + FunctionDefinitionAST( const FunctionDefinitionAST& source ); + void operator = ( const FunctionDefinitionAST& source ); +}; + + +class TranslationUnitAST: public AST, public KShared +{ +public: + typedef KSharedPtr<TranslationUnitAST> Node; + enum { Type = NodeType_TranslationUnit }; + + DECLARE_ALLOC( TranslationUnitAST ) + +public: + TranslationUnitAST(); + + void addDeclaration( DeclarationAST::Node& ast ); + QPtrList<DeclarationAST> declarationList() { return m_declarationList; } + +private: + QPtrList<DeclarationAST> m_declarationList; + +private: + TranslationUnitAST( const TranslationUnitAST& source ); + void operator = ( const TranslationUnitAST& source ); +}; + +#endif diff --git a/lib/cppparser/cachemanager.cpp b/lib/cppparser/cachemanager.cpp new file mode 100644 index 00000000..966f4440 --- /dev/null +++ b/lib/cppparser/cachemanager.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + copyright : (C) 2006 by David Nolden + email : david.nolden.kdevelop@art-master.de +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "cachemanager.h" +#include <kdebug.h> + +void CacheNode::access() const { + m_manager->access( this ); +} + +void CacheManager::remove( const CacheNode* node ) { + m_set.erase( node ); +} + +void CacheManager::add( const CacheNode* node ) { + m_set.insert( node ); +} + +CacheNode::CacheNode( Manager* manager ) : m_manager( manager ), m_value(manager->currentMax()) { //initialize m_value with the current maximum, so the new node has a chance even in a cache full of high-rated nodes + m_manager->add( this ); +} + +CacheNode::~CacheNode() { + m_manager->remove( this ); +}; + +void CacheManager::restart( uint normalizeby ) { + m_currentFrame = 1; + m_currentMax = 1; + SetType oldSet = m_set; + m_set = SetType(); + for( SetType::iterator it = oldSet.begin(); it != oldSet.end(); ++it ) { + int newValue = (*it)->value() / ( normalizeby / 1000 ); + if( newValue > m_currentMax ) m_currentMax = newValue; + (*it)->setValue( newValue ); ///This way not all information is discarded + m_set.insert( *it ); + } +} + +void CacheManager::access( const CacheNode* node ) { + static const int limit = (std::numeric_limits<uint>::max() / 3)*2; + m_set.erase( node ); + node->setValue( m_currentMax+1 ); + m_set.insert( node ); + if( node->value() > m_currentMax ) + m_currentMax = node->value(); + if( node->value() > limit ) + restart( node->value() ); +} + +void CacheManager::setMaxNodes ( int maxNodes ) { + m_maxNodes = maxNodes; + increaseFrame(); +} + +void CacheManager::increaseFrame() { + m_currentFrame ++; + if( m_set.size() > m_maxNodes ) { + //kdDebug( 9007 ) << "CacheManager: Have " << m_set.size() << " nodes, maximum is " << m_maxNodes << ", erasing." << endl; + int mustErase = m_set.size() - m_maxNodes; + while( !m_set.empty() && mustErase != 0 ) { + --mustErase; + SetType::iterator it = m_set.begin(); + erase( *it ); + } + //kdDebug( 9007 ) << "CacheManager: Have " << m_set.size() << " nodes after erasing." << endl; + } +} + +void CacheManager::removeLowerHalf() { + int maxNodes = m_maxNodes; + setMaxNodes( m_set.size() / 2 ); + setMaxNodes( maxNodes ); +} + +void CacheManager::saveMemory() { + removeLowerHalf(); +} diff --git a/lib/cppparser/cachemanager.h b/lib/cppparser/cachemanager.h new file mode 100644 index 00000000..3804356f --- /dev/null +++ b/lib/cppparser/cachemanager.h @@ -0,0 +1,102 @@ + +/*************************************************************************** + copyright : (C) 2006 by David Nolden + email : david.nolden.kdevelop@art-master.de +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef CACHEMANAGER_H +#define CACHEMANAGER_H +#include <cstdlib> +#include <set> +#include <limits> + +class CacheManager; + + +class CacheNode { + typedef CacheManager Manager; + public: + CacheNode( Manager* manager ); + + virtual ~CacheNode(); + + void access() const; + + inline uint value() const { + return m_value; + } + + inline void setValue( const uint v ) const { + m_value = v; + } + + inline void addValue( const uint diff ) const { + m_value += diff; + } + + private: + Manager* m_manager; + mutable uint m_value; //This value stands for the priority of the node(higher is better) +}; + +class CacheNodeCompare { + public: + bool operator() ( const CacheNode* lhs, const CacheNode* rhs ) const { + if( lhs->value() != rhs->value() ) + return lhs->value() < rhs->value(); + else + return lhs < rhs; //To be able to identify nodes precisely + } +}; + + +class CacheManager { + typedef std::set< const CacheNode*, CacheNodeCompare > SetType; + public: + CacheManager( int maxNodes = 1000 ) : m_currentFrame(1), m_maxNodes( maxNodes ), m_currentMax(1) { + }; + + inline int currentFrame() const { + return m_currentFrame; + } + + void access( const CacheNode* node ); + + ///Can be used from outside to set the maximum count of nodes to keep. + void setMaxNodes ( int maxNodes ); + + void increaseFrame(); + + ///Can be used on a regular basis(time-triggered) to save memory: Removes half of all triggered + void removeLowerHalf(); + + virtual void saveMemory(); + + int currentMax() const { + return m_currentMax; + } + + ///This triggered function should erase the given node. + virtual void erase( const CacheNode* node ) = 0; + private: + void restart( uint normalizeby ); + friend class CacheNode; + void remove( const CacheNode* node ); + void add( const CacheNode* node ); + int m_currentFrame; + int m_maxNodes; + int m_currentMax; + SetType m_set; +}; + + +#endif diff --git a/lib/cppparser/driver.cpp b/lib/cppparser/driver.cpp new file mode 100644 index 00000000..c5614d3e --- /dev/null +++ b/lib/cppparser/driver.cpp @@ -0,0 +1,965 @@ +/* This file is part of KDevelop + Copyright (C) 2002,2003 Roberto Raggi <roberto@kdevelop.org> + Copyright (C) 2006 David Nolden <david.nolden.kdevelop@art-master.de> + + 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. +*/ + +#define CACHELEXER + +#include "driver.h" +#include "lexer.h" +#include "parser.h" +#include <kdebug.h> +#include <klocale.h> +#include <stdlib.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qdir.h> +#include <qdatastream.h> +#include <qbuffer.h> +#include <assert.h> + +#include <iostream> + + +// void Macro::read( QDataStream& stream ) { +// stream >> m_idHashValid; +// stream >> m_valueHashValid; +// stream >> m_idHash; +// stream >> m_valueHash; +// stream >> m_name; +// stream >> m_body; +// stream >> m_fileName; +// stream >> m_hasArguments; +// stream >> m_argumentList; +// } +// +// void Macro::write( QDataStream& stream ) const { +// stream << m_idHashValid; +// stream << m_valueHashValid; +// stream << m_idHash; +// stream << m_valueHash; +// stream << m_name; +// stream << m_body; +// stream << m_fileName; +// stream << m_hasArguments; +// stream << m_argumentList; +// } + + +class IntIncreaser { + public: + IntIncreaser( int& i ) : m_i( i ) { + ++m_i; + } + ~IntIncreaser() { + --m_i; + } + private: + int& m_i; +}; + +class DefaultSourceProvider: public SourceProvider { + public: + DefaultSourceProvider() {} + + virtual QString contents( const QString& fileName ) { + QString source; + + QFile f( fileName ); + if ( f.open( IO_ReadOnly ) ) { + QTextStream s( &f ); + source = s.read(); + f.close(); + } + return source; + } + + virtual bool isModified( const QString& fileName ) { + Q_UNUSED( fileName ); + return true; + } + + private: + DefaultSourceProvider( const DefaultSourceProvider& source ); + void operator = ( const DefaultSourceProvider& source ); +}; + + +Driver::Driver() + : depresolv( FALSE ), lexer( 0 ), m_lexerCache( this ), m_dependenceDepth( 0 ), m_maxDependenceDepth( 20 ) { + m_sourceProvider = new DefaultSourceProvider(); +} + +Driver::~Driver() { + reset(); + delete m_sourceProvider; +} + +void Driver::setMaxDependenceDepth( int depth ) { + m_maxDependenceDepth = depth; +} + +SourceProvider* Driver::sourceProvider() { + return m_sourceProvider; +} + +void Driver::setSourceProvider( SourceProvider* sourceProvider ) { + delete m_sourceProvider; + m_sourceProvider = sourceProvider; +} + +void Driver::reset( ) { + m_lexerCache.clear(); + m_dependences.clear(); + m_macros.clear(); + m_problems.clear(); + m_includePaths.clear(); + + while ( m_parsedUnits.size() ) { + //TranslationUnitAST* unit = **m_parsedUnits.begin(); + m_parsedUnits.remove( m_parsedUnits.begin() ); + //delete( unit ); + } +} + +QStringList Driver::getCustomIncludePath( const QString& ) { + return includePaths(); +} + +void Driver::remove + ( const QString & fileName ) { + m_dependences.remove( fileName ); + m_problems.remove( fileName ); + if( !isResolveDependencesEnabled() ) + removeAllMacrosInFile( fileName ); + + QMap<QString, ParsedFilePointer>::Iterator it = m_parsedUnits.find( fileName ); + if ( it != m_parsedUnits.end() ) { + //TranslationUnitAST * unit = **it; + m_parsedUnits.remove( it ); + //delete( unit ); + } +} + +void Driver::removeAllMacrosInFile( const QString& fileName ) { + MacroMap::iterator it = m_macros.begin(); + while ( it != m_macros.end() ) { + Macro m = ( *it ).second; + if ( m.fileName() == fileName ) { + m_macros.erase( it++ ); + } else { + ++it; + } + } +} + +void Driver::usingString( const HashedString& str ) { + #ifdef CACHELEXER + if( m_currentLexerCache ) { + m_currentLexerCache->addString( m_lexerCache.unifyString( str ) ); + } + #endif +} + +bool Driver::hasMacro( const HashedString& name ) { + std::pair< MacroMap::const_iterator, MacroMap::const_iterator > range = m_macros.equal_range( name ); + if ( range.first == range.second ) { + return false; + } else { + const Macro& m( ( *( --range.second ) ).second ); + if ( m.isUndef() ) + return false; + else + return true; + } + return false; +} + +QString deepCopy( const QString& str ) { + return str; + //return str.ascii(); +} + +const Macro& Driver::macro( const HashedString& name ) const { + std::pair< MacroMap::const_iterator, MacroMap::const_iterator > range = m_macros.equal_range( name ); + if ( range.first == range.second ) { + return ( *const_cast<MacroMap&>( m_macros ).insert( std::make_pair( deepCopy( name.str() ), Macro() ) ) ).second; ///Since we need to return a reference, there's no other way. + } else { + return ( *( --range.second ) ).second; + } +} +Macro& Driver::macro( const HashedString& name ) { + std::pair< MacroMap::iterator, MacroMap::iterator > range = m_macros.equal_range( name ); + if ( range.first == range.second ) { + return ( *m_macros.insert( std::make_pair( deepCopy( name.str() ), Macro() ) ) ).second; + } else { + return ( *( --range.second ) ).second; + } +} + +void Driver::addMacro( const Macro & macro ) { + std::pair< MacroMap::iterator, MacroMap::iterator > range = m_macros.equal_range( macro.name() ); + + if ( range.first == range.second ) { + m_macros.insert( std::make_pair( deepCopy( macro.name() ), macro ) ); + } else { + ///Insert behind the other macros + m_macros.insert( range.second, std::make_pair( deepCopy( macro.name() ), macro ) ); + Macro cp = this->macro( macro.name() ); + assert( macro == cp ); + } + +#ifdef CACHELEXER + if( m_currentLexerCache ) + m_currentLexerCache->addDefinedMacro( macro ); +#endif +} + +void Driver::removeMacro( const HashedString& macroName ) { + std::pair< MacroMap::iterator, MacroMap::iterator > range = m_macros.equal_range( macroName ); + if ( range.first != range.second ) { + m_macros.erase( --range.second ); + } +} + +ParsedFilePointer Driver::takeTranslationUnit( const QString& fileName ) { + QMap<QString, ParsedFilePointer>::Iterator it = m_parsedUnits.find( fileName ); + ParsedFilePointer unit( *it ); + //m_parsedUnits.remove( it ); + m_parsedUnits[ fileName ] = 0; + return unit; +} + +void Driver::takeTranslationUnit( const ParsedFile& file ) { + QMap<QString, ParsedFilePointer>::Iterator it = m_parsedUnits.find( file.fileName() ); + m_parsedUnits[ file.fileName() ] = 0; +} + +ParsedFilePointer Driver::translationUnit( const QString& fileName ) const { + QMap<QString, ParsedFilePointer>::ConstIterator it = m_parsedUnits.find( fileName ); + return it != m_parsedUnits.end() ? *it : 0; +} + +class Driver::ParseHelper { + public: + ParseHelper( const QString& fileName, bool force, Driver* driver, bool reportMessages = true, QString includedFrom = QString() ) : m_wasReset( false ), m_fileName( fileName ), m_previousFileName( driver->m_currentFileName ), m_previousLexer( driver->lexer ), m_previousParsedFile( driver->m_currentParsedFile ), m_previousCachedLexedFile( driver->m_currentLexerCache ), m_force( force ), m_driver( driver ), m_lex( m_driver ) { + QFileInfo fileInfo( fileName ); + m_driver->m_currentParsedFile = new ParsedFile( fileName, fileInfo.lastModified() ); + if( !includedFrom.isEmpty() ) + m_driver->m_currentParsedFile->setIncludedFrom( includedFrom ); +#ifdef CACHELEXER + m_driver->m_currentLexerCache = new CachedLexedFile( fileName, &m_driver->m_lexerCache ); +#endif + m_absFilePath = fileInfo.absFilePath(); + + QMap<QString, ParsedFilePointer>::Iterator it = m_driver->m_parsedUnits.find( m_absFilePath ); + + if ( force && it != m_driver->m_parsedUnits.end() ) { + m_driver->takeTranslationUnit( m_absFilePath ); + } else if ( it != m_driver->m_parsedUnits.end() && *it != 0 ) { + // file already processed + return ; + } + + CachedLexedFilePointer lexedFileP = m_driver->m_lexerCache.lexedFile( HashedString( fileName ) ); + + m_driver->m_dependences.remove( fileName ); + m_driver->m_problems.remove( fileName ); + + driver->m_currentFileName = fileName; + + m_driver->lexer = &m_lex; + m_driver->setupLexer( &m_lex ); + + m_lex.setReportMessages( reportMessages ); + + //kdDebug( 9007 ) << "lexing file " << fileName << endl; + m_lex.setSource( m_driver->sourceProvider() ->contents( fileName ) ); + if(m_previousCachedLexedFile) + m_previousCachedLexedFile->merge( *m_driver->m_currentLexerCache ); + else + m_driver->findOrInsertProblemList( m_driver->m_currentMasterFileName ) += m_driver->m_currentLexerCache->problems(); + + if( !lexedFileP && m_previousParsedFile ) //only add the new cache-instance if a fitting isn't already stored, and if this file was included by another one. + m_driver->m_lexerCache.addLexedFile( m_driver->m_currentLexerCache ); + + //Copy the recursive include-files into the ParsedFile + m_driver->m_currentParsedFile->addIncludeFiles( m_driver->m_currentLexerCache->includeFiles() ); + m_driver->m_currentParsedFile->setSkippedLines( m_lex.skippedLines() ); + } + + void parse() { + QString oldMasterFileName = m_driver->m_currentMasterFileName; //Change the master-file so problems will be reported correctly + m_driver->m_currentMasterFileName = m_absFilePath; + + CachedLexedFilePointer lf = m_driver->m_currentLexerCache; //Set the lexer-cache to zero, so the problems registered through addProblem go directly into the file + m_driver->m_currentLexerCache = 0; + + Parser parser( m_driver, m_driver->lexer ); + m_driver->setupParser( &parser ); + + TranslationUnitAST::Node unit; + parser.parseTranslationUnit( unit ); + m_driver->m_currentParsedFile->setTranslationUnit( unit ); + m_driver->m_parsedUnits.insert( m_fileName, m_driver->m_currentParsedFile ); + m_driver->fileParsed( *m_driver->m_currentParsedFile ); + + m_driver->m_currentLexerCache = lf; + + m_driver->m_currentMasterFileName = oldMasterFileName; + } + + ParsedFilePointer parsedFile() const { + return m_driver->m_currentParsedFile; + } + + void reset() { + if ( !m_wasReset ) { + m_driver->m_currentFileName = m_previousFileName; + m_driver->lexer = m_previousLexer; + m_driver->m_currentParsedFile = m_previousParsedFile; + m_driver->m_currentLexerCache = m_previousCachedLexedFile; + if( m_driver->m_currentLexerCache == 0 ) { + + } + + m_wasReset = true; + } + } + + ~ParseHelper() { + reset(); + } + + + private: + bool m_wasReset; + QString m_fileName; + QString m_absFilePath; + QString m_previousFileName; + Lexer* m_previousLexer; + ParsedFilePointer m_previousParsedFile; + CachedLexedFilePointer m_previousCachedLexedFile; + bool m_force; + Driver* m_driver; + Lexer m_lex; +}; + + +void Driver::addDependence( const QString & fileName, const Dependence & dep ) { + + // this can happen if the parser was invoked on a snippet of text and not a file + if ( fileName.isEmpty() || !m_currentParsedFile ) + return; + + //@todo prevent cyclic dependency-loops + QFileInfo fileInfo( dep.first ); + QString fn = fileInfo.absFilePath(); + + if ( !depresolv ) { + findOrInsertDependenceList( fileName ).insert( fn, dep ); + m_currentParsedFile->addIncludeFile( dep.first, 0, dep.second == Dep_Local ); + return ; + } + + QString file = findIncludeFile( dep ); + + findOrInsertDependenceList( fileName ).insert( file, dep ); + m_currentParsedFile->addIncludeFile( file, 0, dep.second == Dep_Local ); + + if ( !QFile::exists( file ) ) { + Problem p( i18n( "Could not find include file %1" ).arg( dep.first ), + lexer ? lexer->currentLine() : -1, + lexer ? lexer->currentColumn() : -1, Problem::Level_Warning ); + addProblem( fileName, p ); + return ; + } + + if( m_currentLexerCache ) + m_currentLexerCache->addIncludeFile( file, QDateTime() ); ///The time will be overwritten in CachedLexedFile::merge(...) + + /**What should be done: + * 1. Lex the file to get all the macros etc. + * 2. TODO: Check what previously set macros the file was affected by, and compare those macros to any previously parsed instances of this file. + * 2.1 If there is a parse-instance of the file where all macros that played a role had the same values, we do not need to reparse this file. + * 2.2 If there is no such fitting instance, the file needs to be parsed and put to the code-model. + * + * It'll be necessary to have multiple versions of one file in the code-model. + */ + + IntIncreaser i( m_dependenceDepth ); + if( m_dependenceDepth > m_maxDependenceDepth ) { + //kdDebug( 9007 ) << "maximum dependence-depth of " << m_maxDependenceDepth << " was reached, " << fileName << " will not be processed" << endl; + return; + } + + CachedLexedFilePointer lexedFileP = m_lexerCache.lexedFile( HashedString( file ) ); + if( lexedFileP ) { + CachedLexedFile& lexedFile( *lexedFileP ); + m_currentLexerCache->merge( lexedFile ); //The ParseHelper will will copy the include-files into the result later + for( MacroSet::Macros::const_iterator it = lexedFile.definedMacros().macros().begin(); it != lexedFile.definedMacros().macros().end(); ++it ) { + addMacro( (*it) ); + } + ///@todo fill usingMacro(...) + return; + } + + ParseHelper h( file, true, this, false, m_currentMasterFileName ); + + /*if ( m_parsedUnits.find(file) != m_parsedUnits.end() ) + return;*/ + + if( shouldParseIncludedFile( m_currentParsedFile ) ) ///Until the ParseHelper is destroyed, m_currentParsedFile will stay the included file + h.parse(); +} + +void Driver::addProblem( const QString & fileName, const Problem & problem ) { + Problem p( problem ); + p.setFileName( fileName ); + + if( m_currentLexerCache ) + m_currentLexerCache->addProblem( p ); + else + findOrInsertProblemList( m_currentMasterFileName ).append( problem ); +} + +QMap< QString, Dependence >& Driver::findOrInsertDependenceList( const QString & fileName ) { + QMap<QString, QMap<QString, Dependence> >::Iterator it = m_dependences.find( fileName ); + if ( it != m_dependences.end() ) + return it.data(); + + QMap<QString, Dependence> l; + m_dependences.insert( deepCopy( fileName ), l ); + return m_dependences[ fileName ]; +} + +QValueList < Problem >& Driver::findOrInsertProblemList( const QString & fileName ) { + QMap<QString, QValueList<Problem> >::Iterator it = m_problems.find( fileName ); + if ( it != m_problems.end() ) + return it.data(); + + QValueList<Problem> l; + m_problems.insert( fileName, l ); + return m_problems[ fileName ]; +} + +QMap< QString, Dependence > Driver::dependences( const QString & fileName ) const { + QMap<QString, QMap<QString, Dependence> >::ConstIterator it = m_dependences.find( fileName ); + if ( it != m_dependences.end() ) + return it.data(); + return QMap<QString, Dependence>(); +} + +const Driver::MacroMap& Driver::macros() const { + return m_macros; +} + +void Driver::insertMacros( const MacroSet& macros ) { + for( MacroSet::Macros::const_iterator it = macros.m_usedMacros.begin(); it != macros.m_usedMacros.end(); ++it ) { + addMacro( *it ); + } +} + +QValueList < Problem > Driver::problems( const QString & fileName ) const { + QMap<QString, QValueList<Problem> >::ConstIterator it = m_problems.find( fileName ); + if ( it != m_problems.end() ) + return it.data(); + return QValueList<Problem>(); +} + +void Driver::clearMacros() { + m_macros.clear(); +} + +void Driver::clearParsedMacros() { + //Keep global macros + for( MacroMap::iterator it = m_macros.begin(); it != m_macros.end(); ) { + if( !(*it).second.fileName().isEmpty() ) { + m_macros.erase( it++ ); + } else { + ++it; + } + } +} + +void Driver::parseFile( const QString& fileName, bool onlyPreProcess, bool force , bool macrosGlobal ) +{ + QString oldMasterFileName = m_currentMasterFileName; + m_currentMasterFileName = fileName; + + //if( isResolveDependencesEnabled() ) + clearParsedMacros(); ///Since everything will be re-lexed, we do not need any old macros + + m_lexerCache.increaseFrame(); + + //Remove the problems now instead of in ParseHelper, because this way the problems reported by getCustomIncludePath(...) will not be discarded + m_problems.remove( fileName ); + + QStringList oldIncludePaths = m_includePaths; + m_includePaths = getCustomIncludePath( fileName ); + + ParseHelper p( fileName, force, this ); + if( !onlyPreProcess ){ + p.parse(); + } + if( macrosGlobal ) { + for( MacroMap::iterator it = m_macros.begin(); it != m_macros.end(); ++it) { + if( (*it).second.fileName() == fileName ) { + (*it).second.setFileName( QString::null ); + } + } + } + + m_includePaths = oldIncludePaths; + m_currentMasterFileName = oldMasterFileName; +} + +void Driver::setupLexer( Lexer * lexer ) { + // stl + lexer->addSkipWord( "__STL_BEGIN_NAMESPACE" ); + lexer->addSkipWord( "__STL_END_NAMESPACE" ); + lexer->addSkipWord( "__STL_BEGIN_RELOPS_NAMESPACE" ); + lexer->addSkipWord( "__STL_END_RELOPS_NAMESPACE" ); + lexer->addSkipWord( "__STL_TEMPLATE_NULL" ); + lexer->addSkipWord( "__STL_TRY" ); + lexer->addSkipWord( "__STL_UNWIND" ); + lexer->addSkipWord( "__STL_NOTHROW" ); + lexer->addSkipWord( "__STL_NULL_TMPL_ARGS" ); + lexer->addSkipWord( "__STL_UNWIND", SkipWordAndArguments ); + lexer->addSkipWord( "__GC_CONST" ); + lexer->addSkipWord( "__HASH_ALLOC_INIT", SkipWordAndArguments ); + lexer->addSkipWord( "__STL_DEFAULT_ALLOCATOR", SkipWordAndArguments, "T" ); + lexer->addSkipWord( "__STL_MUTEX_INITIALIZER" ); + lexer->addSkipWord( "__STL_NULL_TMPL_ARGS" ); + + // antlr + lexer->addSkipWord( "ANTLR_BEGIN_NAMESPACE", SkipWordAndArguments ); + lexer->addSkipWord( "ANTLR_USE_NAMESPACE", SkipWordAndArguments ); + lexer->addSkipWord( "ANTLR_USING_NAMESPACE", SkipWordAndArguments ); + lexer->addSkipWord( "ANTLR_END_NAMESPACE" ); + lexer->addSkipWord( "ANTLR_C_USING", SkipWordAndArguments ); + lexer->addSkipWord( "ANTLR_API" ); + + // gnu + lexer->addSkipWord( "__extension__", SkipWordAndArguments ); + lexer->addSkipWord( "__attribute__", SkipWordAndArguments ); + lexer->addSkipWord( "__BEGIN_DECLS" ); + lexer->addSkipWord( "__END_DECLS" ); + lexer->addSkipWord( "__THROW" ); + lexer->addSkipWord( "__restrict" ); + lexer->addSkipWord( "__restrict__" ); + lexer->addSkipWord( "__attribute_pure__" ); + lexer->addSkipWord( "__attribute_malloc__" ); + lexer->addSkipWord( "__attribute_format_strfmon__" ); + lexer->addSkipWord( "__asm__", SkipWordAndArguments ); + lexer->addSkipWord( "__devinit" ); + lexer->addSkipWord( "__devinit__" ); + lexer->addSkipWord( "__init" ); + lexer->addSkipWord( "__init__" ); + lexer->addSkipWord( "__signed" ); + lexer->addSkipWord( "__signed__" ); + lexer->addSkipWord( "__unsigned" ); + lexer->addSkipWord( "__unsigned__" ); + lexer->addSkipWord( "asmlinkage" ); + lexer->addSkipWord( "____cacheline_aligned" ); + lexer->addSkipWord( "__glibcpp_class_requires", SkipWordAndArguments ); + lexer->addSkipWord( "__glibcpp_class2_requires", SkipWordAndArguments ); + lexer->addSkipWord( "__glibcpp_class4_requires", SkipWordAndArguments ); + lexer->addSkipWord( "__glibcpp_function_requires", SkipWordAndArguments ); + lexer->addSkipWord( "restrict" ); + + lexer->addSkipWord( "__BEGIN_NAMESPACE_STD" ); + lexer->addSkipWord( "__END_NAMESPACE_STD" ); + lexer->addSkipWord( "__BEGIN_NAMESPACE_C99" ); + lexer->addSkipWord( "__END_NAMESPACE_C99" ); + lexer->addSkipWord( "__USING_NAMESPACE_STD", SkipWordAndArguments ); + + // kde + lexer->addSkipWord( "K_SYCOCATYPE", SkipWordAndArguments ); + lexer->addSkipWord( "EXPORT_DOCKCLASS" ); + lexer->addSkipWord( "K_EXPORT_COMPONENT_FACTORY", SkipWordAndArguments ); + lexer->addSkipWord( "K_SYCOCAFACTORY", SkipWordAndArguments ); + lexer->addSkipWord( "KDE_DEPRECATED" ); + + // qt + lexer->addSkipWord( "Q_OBJECT" ); + lexer->addSkipWord( "Q_OVERRIDE", SkipWordAndArguments ); + lexer->addSkipWord( "Q_ENUMS", SkipWordAndArguments ); + lexer->addSkipWord( "Q_PROPERTY", SkipWordAndArguments ); + lexer->addSkipWord( "Q_CLASSINFO", SkipWordAndArguments ); + lexer->addSkipWord( "Q_SETS", SkipWordAndArguments ); + lexer->addSkipWord( "Q_UNUSED", SkipWordAndArguments ); + lexer->addSkipWord( "Q_CREATE_INSTANCE", SkipWordAndArguments ); + lexer->addSkipWord( "Q_DUMMY_COMPARISON_OPERATOR", SkipWordAndArguments ); + lexer->addSkipWord( "ACTIVATE_SIGNAL_WITH_PARAM", SkipWordAndArguments ); + lexer->addSkipWord( "Q_INLINE_TEMPLATES" ); + lexer->addSkipWord( "Q_TEMPLATE_EXTERN" ); + lexer->addSkipWord( "Q_TYPENAME" ); + lexer->addSkipWord( "Q_REFCOUNT" ); + lexer->addSkipWord( "Q_EXPLICIT" ); + lexer->addSkipWord( "QMAC_PASCAL" ); + lexer->addSkipWord( "QT_STATIC_CONST" ); + lexer->addSkipWord( "QT_STATIC_CONST_IMPL" ); + lexer->addSkipWord( "QT_WIN_PAINTER_MEMBERS" ); + lexer->addSkipWord( "QT_NC_MSGBOX" ); + lexer->addSkipWord( "Q_VARIANT_AS", SkipWordAndArguments ); + lexer->addSkipWord( "CALLBACK_CALL_TYPE" ); + + // qt4 [erbsland] + lexer->addSkipWord( "Q_DECLARE_FLAGS", SkipWordAndArguments ); + lexer->addSkipWord( "Q_DECLARE_OPERATORS_FOR_FLAGS", SkipWordAndArguments ); + + // flex + lexer->addSkipWord( "yyconst" ); + lexer->addSkipWord( "YY_RULE_SETUP" ); + lexer->addSkipWord( "YY_BREAK" ); + lexer->addSkipWord( "YY_RESTORE_YY_MORE_OFFSET" ); + + // gtk + lexer->addSkipWord( "G_BEGIN_DECLS" ); + lexer->addSkipWord( "G_END_DECLS" ); + lexer->addSkipWord( "G_GNUC_CONST" ); + lexer->addSkipWord( "G_CONST_RETURN" ); + lexer->addSkipWord( "GTKMAIN_C_VAR" ); + lexer->addSkipWord( "GTKVAR" ); + lexer->addSkipWord( "GDKVAR" ); + lexer->addSkipWord( "G_GNUC_PRINTF", SkipWordAndArguments ); + + // windows + lexer->addSkipWord( "WINAPI" ); + lexer->addSkipWord( "__stdcall" ); + lexer->addSkipWord( "__cdecl" ); + lexer->addSkipWord( "_cdecl" ); + lexer->addSkipWord( "CALLBACK" ); + + // gcc extensions + if( !hasMacro( "__asm__" ) ) addMacro( Macro( "__asm__", "asm" ) ); + if( !hasMacro( "__inline" ) ) addMacro( Macro( "__inline", "inline" ) ); + if( !hasMacro( "__inline__" ) ) addMacro( Macro( "__inline__", "inline" ) ); + if( !hasMacro( "__const" ) ) addMacro( Macro( "__const", "const" ) ); + if( !hasMacro( "__const__" ) ) addMacro( Macro( "__const__", "const" ) ); + if( !hasMacro( "__volatile__" ) ) addMacro( Macro( "__volatile__", "volatile" ) ); + if( !hasMacro( "__complex__" ) ) addMacro( Macro( "__complex__", "" ) ); +} + +void Driver::setupParser( Parser * parser ) { + Q_UNUSED( parser ); +} + +void Driver::clearIncludePaths() { + m_includePaths.clear(); +} + +void Driver::addIncludePath( const QString &path ) { + if ( !path.stripWhiteSpace().isEmpty() ) + m_includePaths << path; +} + +QString Driver::findIncludeFile( const Dependence& dep, const QString& fromFile ) { + QString fileName = dep.first; + + if ( dep.second == Dep_Local ) { + QString path = QFileInfo( fromFile ).dirPath( true ); + QFileInfo fileInfo( QFileInfo( path, fileName ) ); + if ( fileInfo.exists() && fileInfo.isFile() ) + return fileInfo.absFilePath(); + } + + QStringList includePaths = getCustomIncludePath( fromFile ); + + for ( QStringList::ConstIterator it = includePaths.begin(); it != includePaths.end(); ++it ) { + QFileInfo fileInfo( *it, fileName ); + if ( fileInfo.exists() && fileInfo.isFile() ) + return fileInfo.absFilePath(); + } + + return QString::null; +} + +QString Driver::findIncludeFile( const Dependence& dep ) const { + QString fileName = dep.first; + + if ( dep.second == Dep_Local ) { + QString path = QFileInfo( currentFileName() ).dirPath( true ); + QFileInfo fileInfo( QFileInfo( path, fileName ) ); + if ( fileInfo.exists() && fileInfo.isFile() ) + return fileInfo.absFilePath(); + } + + for ( QStringList::ConstIterator it = m_includePaths.begin(); it != m_includePaths.end(); ++it ) { + QFileInfo fileInfo( *it, fileName ); + if ( fileInfo.exists() && fileInfo.isFile() ) + return fileInfo.absFilePath(); + } + + return QString::null; +} + +void Driver::setResolveDependencesEnabled( bool enabled ) { + depresolv = enabled; + if ( depresolv ) + setupPreProcessor(); +} + +bool Driver::shouldParseIncludedFile( const ParsedFilePointer& /*file*/) { + return false; +} + +void Driver::setupPreProcessor() {} + +void Driver::fileParsed( ParsedFile & fileName ) { + Q_UNUSED( fileName ); +} + +void Driver::usingMacro( const Macro& macro ) { + if( m_currentParsedFile ) + m_currentParsedFile->usedMacros().addMacro( macro ); +#ifdef CACHELEXER + if( m_currentLexerCache ) + m_currentLexerCache->addUsedMacro( macro ); +#endif +} + +// void Macro::computeHash() const { +// m_idHash = 7 * ( HashedString::hashString( m_name ) + m_argumentList.count() * 13 ); +// int a = 1; +// m_idHash += 31 * m_argumentList.count(); +// +// m_valueHash = 27 * ( HashedString::hashString( m_body ) + (m_isUndefMacro ? 1 : 0 ) ); +// +// for( QValueList<Argument>::const_iterator it = m_argumentList.begin(); it != m_argumentList.end(); ++it ) { +// a *= 19; +// m_valueHash += a * HashedString::hashString( *it ); +// } +// m_valueHashValid = true; +// m_idHashValid = true; +// } + +// MacroSet::MacroSet() : m_idHashValid( false ), m_valueHashValid( false ) { +// } + +void MacroSet::addMacro( const Macro& macro ) { + std::pair<Macros::const_iterator, bool> r = m_usedMacros.insert( macro ); + if( !r.second ) { + //Make sure the macro added later will be used + m_usedMacros.erase( r.first ); + m_usedMacros.insert( macro ); + } + + m_idHashValid = m_valueHashValid = false; +} + +void MacroSet::merge( const MacroSet& macros ) { + Macros m = macros.m_usedMacros; //Swap is needed so the merged macros take precedence + m.insert( m_usedMacros.begin(), m_usedMacros.end() ); + m_usedMacros = m; + m_idHashValid = m_valueHashValid = false; +} + + +size_t MacroSet::idHash() const { + if( !m_idHashValid ) computeHash(); + return m_idHash; +} + +size_t MacroSet::valueHash() const { + if( !m_valueHashValid ) computeHash(); + return m_valueHash; +} + +void MacroSet::computeHash() const { + m_idHash = 0; + m_valueHash = 0; + int mult = 1; + for( Macros::const_iterator it = m_usedMacros.begin(); it != m_usedMacros.end(); ++it ) { + mult *= 31; + m_idHash += (*it).idHash(); + m_valueHash += (*it).valueHash(); + } +} + +// void MacroSet::read( QDataStream& stream ) { +// stream >> m_idHashValid >> m_idHash >> m_valueHashValid >> m_valueHash; +// int cnt; +// stream >> cnt; +// m_usedMacros.clear(); +// Macro m; +// for( int a = 0; a < cnt; a++ ) { +// m.read( stream ); +// m_usedMacros.insert( m ); +// } +// } +// +// void MacroSet::write( QDataStream& stream ) const { +// stream << m_idHashValid << m_idHash << m_valueHashValid << m_valueHash; +// stream << m_usedMacros.size(); +// for( Macros::const_iterator it = m_usedMacros.begin(); it != m_usedMacros.end(); ++it ) { +// (*it).write( stream ); +// } +// } + +/** + * @return All Macros that were used while processing this translation-unit + * */ +MacroSet& ParsedFile::usedMacros() { + return m_usedMacros; +} + +const MacroSet& ParsedFile::usedMacros() const { + return m_usedMacros; +} + +ParsedFile::ParsedFile( const QString& fileName, const QDateTime& timeStamp ) : m_translationUnit( 0 ), m_fileName( fileName ), m_timeStamp( timeStamp ) { + m_includeFiles.insert( fileName ); +} + +ParsedFile::ParsedFile( const QByteArray& array ) { + QBuffer b( array ); + QDataStream d( &b ); + read( d ); +} + +QString ParsedFile::includedFrom() const { + return m_includedFrom; +} + +void ParsedFile::setIncludedFrom( const QString& str ) { + m_includedFrom = str; +} + +QByteArray ParsedFile::serialize() const { + QByteArray array; + QBuffer b( array ); + QDataStream d( &b ); + write( d ); + return array; +} + +// void ParsedFile::read( QDataStream& stream ) { +// int directIncludeFilesCount; +// stream >> directIncludeFilesCount; +// m_directIncludeFiles.clear(); +// for( int a = 0; a < directIncludeFilesCount; a++ ) { +// IncludeDesc i; +// stream >> i.local; +// stream >> i.includePath; +// //"parsed" will not be reconstructed +// m_directIncludeFiles.push_back( i ); +// } +// stream >> m_fileName; +// stream >> m_timeStamp; +// m_usedMacros.read( stream ); +// m_translationUnit = 0; +// m_includeFiles.read( stream ); +// } +// +// void ParsedFile::write( QDataStream& stream ) const { +// for( QValueList<IncludeDesc>::const_iterator it = m_directIncludeFiles.begin(); it != m_directIncludeFiles.end(); ++it ) { +// stream << (*it).local; +// stream << (*it).includePath; +// } +// stream << m_fileName; +// stream << m_timeStamp; +// m_usedMacros.write( stream ); +// m_includeFiles.write( stream ); +// } + +ParsedFile::operator TranslationUnitAST* () const { + if( !this ) return 0; + return m_translationUnit; +} + +void ParsedFile::setTranslationUnit( const TranslationUnitAST::Node& trans ) { + m_translationUnit = trans; +} + +// HashedStringSet& ParsedFile::includeFiles() { +// return m_includeFiles; +// } + +int ParsedFile::skippedLines() const { + return m_skippedLines; +} + +void ParsedFile::setSkippedLines( int lines ) { + m_skippedLines = lines; +} + +const HashedStringSet& ParsedFile::includeFiles() const { + return m_includeFiles; +} + +QString ParsedFile::fileName() const { + return m_fileName; +} + +QDateTime ParsedFile::timeStamp() const { + return m_timeStamp; +} + +void ParsedFile::addIncludeFiles( const HashedStringSet& includeFiles ) { + m_includeFiles += includeFiles; +} + +void ParsedFile::addIncludeFile( const QString& includePath, const ParsedFilePointer& parsed, bool localInclude ) { + m_includeFiles.insert( includePath ); + if( parsed ) + m_includeFiles += parsed->includeFiles(); + IncludeDesc d; + d.local = localInclude; + d.includePath = includePath; + d.parsed = parsed; + m_directIncludeFiles << d; +} + +const QValueList<ParsedFile::IncludeDesc>& ParsedFile::directIncludeFiles() const { + return m_directIncludeFiles; +} + +bool MacroSet::hasMacro( const QString& name ) const { + Macros::const_iterator it = m_usedMacros.find( Macro( name, "" ) ); + if( it != m_usedMacros.end() ) { + return true; + } else { + return false; + } +} + +bool MacroSet::hasMacro( const HashedString& name ) const { + Macros::const_iterator it = m_usedMacros.find( Macro( name.str(), "" ) ); + if( it != m_usedMacros.end() ) { + return true; + } else { + return false; + } +} + +Macro MacroSet::macro( const QString& name ) const { + Macros::const_iterator it = m_usedMacros.find( Macro( name, "" ) ); + + if( it != m_usedMacros.end() ) { + return *it; + } else { + return Macro(); + } +} + +LexerCache* Driver::lexerCache() { + return &m_lexerCache; +} + diff --git a/lib/cppparser/driver.h b/lib/cppparser/driver.h new file mode 100644 index 00000000..31f62443 --- /dev/null +++ b/lib/cppparser/driver.h @@ -0,0 +1,460 @@ +/* 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef DRIVER_H +#define DRIVER_H + +#include "ast.h" + +#include "macro.h" +#include <qpair.h> +#include <qvaluestack.h> +#include <qstringlist.h> +#include <qcstring.h> +#include <qdatastream.h> +#include <qmap.h> +#include <qdatetime.h> +#include <qvaluelist.h> +#include <map> +#include <set> +#include <hashedstring.h> +#include <ksharedptr.h> +#include <codemodel.h> +#include <ext/hash_map> +#include "lexercache.h" + + +class Lexer; +class Parser; + +enum +{ + Dep_Global, + Dep_Local +}; + +typedef QPair<QString, int> Dependence; + +class ParsedFile; +typedef KSharedPtr< ParsedFile > ParsedFilePointer; + +class ParsedFile : public AbstractParseResult { + public: + struct IncludeDesc { + bool local; //Whether it is a local include(#include "local.h", not #include <global.h>) + QString includePath; + ParsedFilePointer parsed; //May be zero! + }; +// ParsedFile() { +// } + ParsedFile( QDataStream& s ) { + read( s ); + } + + ParsedFile( const QString& fileName, const QDateTime& timeStamp ); + + ///Deserializes the ParsedFile from a previous call to serialize(). AST will always be zero after a call to this. + ParsedFile( const QByteArray& array ); + + /** + * @return All Macros that were used while processing this translation-unit. May be modified. + */ + MacroSet& usedMacros(); + const MacroSet& usedMacros() const; + + /** + * @return the count of lines that were skipped while preprocessing the file + * */ + int skippedLines() const; + + void setSkippedLines( int lines ); + /** + * @return Absolutely all files included by this one(no matter through how many other files they were included) + */ +// HashedStringSet& includeFiles(); + + const HashedStringSet& includeFiles() const; + + void addIncludeFiles( const HashedStringSet& includeFiles ); + + void addIncludeFile( const QString& includePath, const ParsedFilePointer& parsed, bool localInclude ); + + ///If this file was parsed while resolving the dependencies of another file, this returns the file this one was included from. Else returns an empty string. + QString includedFrom() const; + + void setIncludedFrom( const QString& str ); + /** + * @return Reference to the internal list of all directly included files(without those included indirectly) + */ + const QValueList<IncludeDesc>& directIncludeFiles() const; + + operator TranslationUnitAST* () const; //May be zero! + + TranslationUnitAST* operator -> () const { + if( !this ) return 0; + return m_translationUnit; + } + + void setTranslationUnit( const TranslationUnitAST::Node& trans ); + + QString fileName() const; + + QDateTime timeStamp() const; + + ///Serializes the content of this class into a byte-array. Note that this does not serialize the AST. + QByteArray serialize() const; + + /*void read( QDataStream& stream ); + void write( QDataStream& stream ) const;*/ + + virtual void read( QDataStream& stream ) { + int directIncludeFilesCount; + stream >> directIncludeFilesCount; + m_directIncludeFiles.clear(); + for( int a = 0; a < directIncludeFilesCount; a++ ) { + IncludeDesc i; + Q_INT8 in; + stream >> in; + i.local = in; + stream >> i.includePath; + //"parsed" will not be reconstructed + m_directIncludeFiles.push_back( i ); + } + stream >> m_skippedLines; + stream >> m_fileName; + stream >> m_timeStamp; + stream >> m_includedFrom; + m_usedMacros.read( stream ); + m_translationUnit = 0; + m_includeFiles.read( stream ); + } + + virtual void write( QDataStream& stream ) const { + int i = m_directIncludeFiles.size(); + stream << i; + for( QValueList<IncludeDesc>::const_iterator it = m_directIncludeFiles.begin(); it != m_directIncludeFiles.end(); ++it ) { + Q_INT8 i = (*it).local; + stream << i; + stream << (*it).includePath; + } + stream << m_skippedLines; + stream << m_fileName; + stream << m_timeStamp; + stream << m_includedFrom; + m_usedMacros.write( stream ); + m_includeFiles.write( stream ); + } + + virtual ParsedFileType type() const { + return CppParsedFile; + } + + private: + QValueList<IncludeDesc> m_directIncludeFiles; + MacroSet m_usedMacros; + TranslationUnitAST::Node m_translationUnit; + HashedStringSet m_includeFiles; + int m_skippedLines; + QString m_fileName; + QDateTime m_timeStamp; + QString m_includedFrom; +}; + +/** + * An interface that provides source code to the Driver + */ +class SourceProvider { + public: + SourceProvider() {} + virtual ~SourceProvider() {} + + /** + * Get the contents of a file + * \param fileName The name of the file to get the contents for. An absolute + * path should be used. + * \return A QString that contains the contents of the file + */ + virtual QString contents( const QString& fileName ) = 0; + + /** + * Check to see if a file has been modified + * \param fileName The name of hte file to get the modification state of. An + * absolute path should be used. + * \return true if the file has been modified + * \return false if the file has not been modified + */ + virtual bool isModified( const QString& fileName ) = 0; + + private: + SourceProvider( const SourceProvider& source ); + void operator = ( const SourceProvider& source ); +}; + +class Driver { + public: + Driver(); + virtual ~Driver(); + + typedef std::multimap< HashedString, Macro > MacroMap; + + /** + * Get the source provider for this driver. This would be useful for + * getting the text the driver is working with. + */ + SourceProvider* sourceProvider(); + /** + * Sets the source provider the driver will use + * @param sourceProvider the SourceProvider the driver will use + */ + void setSourceProvider( SourceProvider* sourceProvider ); + + /** + * @brief Resets the driver + * + * Clears the driver of all problems, dependencies, macros, and include paths and + * removes any translation units that have been parsed + */ + virtual void reset(); + + /** + * Tells the driver to start parsing a file + * @param fileName The name of the file to parse + * @param onlyPreProcesss Tells the driver to only run the file through the preprocessor. Defaults to false + * @param force Force the parsing of the file. Defaults to false + * @param macrosGlobal Should the macros be global? (Global macros are not deleted once a new translation-unit is parsed) + */ + virtual void parseFile( const QString& fileName, bool onlyPreProcesss = false, bool force = false, bool macrosGlobal = false ); + + /** + * Indicates that the file has been parsed + * @param fileName The name of the file parsed. It is legal to create a ParsedFilePointer on the given item. + */ + virtual void fileParsed( ParsedFile& fileName ); + + /** + * Removes the file specified by @p fileName from the driver + * @param fileName The name of the file to remove + */ + virtual void remove + ( const QString& fileName ); + + /** + * Add a dependency on another header file for @p fileName + * @param fileName The file name to add the dependency for + * @param dep The dependency to add + */ + virtual void addDependence( const QString& fileName, const Dependence& dep ); + + /** + * Add a macro to the driver + * @param macro The macro to add to the driver + */ + virtual void addMacro( const Macro& macro ); + + /** + * Add a problem to the driver + * @param fileName The file name to add the problem for + * @param problem The problem to add + */ + virtual void addProblem( const QString& fileName, const Problem& problem ); + + /** + * The current file name the driver is working with + */ + QString currentFileName() const { + return m_currentFileName; + } + ParsedFilePointer takeTranslationUnit( const QString& fileName ); + + void takeTranslationUnit( const ParsedFile& file ); + /** + * Get the translation unit contained in the driver for @p fileName. + * @param fileName The name of the file to get the translation unit for + * @return The TranslationUnitAST pointer that represents the translation unit + * @return 0 if no translation unit exists for the file + */ + ParsedFilePointer translationUnit( const QString& fileName ) const; + /** + * Get the dependencies for a file + * @param fileName The file name to get dependencies for + * @return The dependencies for the file + */ + QMap<QString, Dependence> dependences( const QString& fileName ) const; + /** + * Get all the macros the driver contains + * @return The macros + */ + const MacroMap& macros() const; + + /** + * Take all macros from the given map(forgetting own macros) */ + void insertMacros( const MacroSet& macros ); + /** + * Get the list of problem areas the driver contains + * @param fileName The filename to get problems for + * @return The list of problems for @p fileName + */ + QValueList<Problem> problems( const QString& fileName ) const; + + void usingString( const HashedString& str ); + /** + * Check if we have a macro in the driver + * If the last stacked macro of that name is an undef-macro, false is returned. + * @param name The name of the macro to check for + * @return true if we have the macro in the driver + * @return false if we don't have the macro in the driver + */ + bool hasMacro( const HashedString& name ) ; + /** + * Get the macro identified by @p name + * @param name The name of the macro to get + * @return A const reference of the macro object represented by @p name + */ + const Macro& macro( const HashedString& name ) const; + /** + * Get the last inserted macro identified by @p name + * @override + * @param name The name of the macro to get + * @return A non-const reference of the macro object represented by @p name + * + */ + Macro& macro( const HashedString& name ); + + /** + * Remove the last inserted Macro of that name + * @param macroName The name of the macro to remove + */ + virtual void removeMacro( const HashedString& macroName ); + + /** + * Remove all macros from the driver for a certain file + * @param fileName The file name + */ + virtual void removeAllMacrosInFile( const QString& fileName ); ///Check when this is called. It may be wrong. + + QStringList includePaths() const { + return m_includePaths; + } + + virtual QStringList getCustomIncludePath( const QString& ); + + + virtual void addIncludePath( const QString &path ); + + virtual void clearIncludePaths(); + + /// @todo remove + const QMap<QString, ParsedFilePointer> &parsedUnits() const { + return m_parsedUnits; + } + + /** + * Set whether or not to enable dependency resolving for files added to the driver + */ + virtual void setResolveDependencesEnabled( bool enabled ); + /** + * Check if dependency resolving is enabled + * \return true if dependency resolving is enabled + * \return false if dependency resolving is disabled + */ + bool isResolveDependencesEnabled() const { + return depresolv; + } + + void setMaxDependenceDepth( int depth ); + + /** + * Used by the Lexer to indicate that a Macro was used + * @param macro The used macro + * */ + void usingMacro( const Macro& macro ); + + /** + * Returns the local instance of the lexer-cache, can be used from outside to control the cache-behavior. + * */ + LexerCache* lexerCache(); + + ///This uses getCustomIncludePath(..) to resolve the include-path internally + QString findIncludeFile( const Dependence& dep, const QString& fromFile ); + + protected: + ///This uses the state of the parser to find the include-file + QString findIncludeFile( const Dependence& dep ) const; + + /** + * Set up the lexer. + */ + virtual void setupLexer( Lexer* lexer ); + /** + * Setup the parser + */ + virtual void setupParser( Parser* parser ); + /** + * Set up the preprocessor + */ + virtual void setupPreProcessor(); + + /** + * Is code-information for this file already available? If false is returned, the file will be parsed. + * Code-model and static repository should be checked to find out whether the file is already available. + * This function is only used when dependency-resolving is activated. + * @arg file absolute path to the file + */ + virtual bool shouldParseIncludedFile( const ParsedFilePointer& /*file*/ ); + + void clearMacros(); + + void clearParsedMacros(); + + private: + QMap<QString, Dependence>& findOrInsertDependenceList( const QString& fileName ); + QValueList<Problem>& findOrInsertProblemList( const QString& fileName ); + + + private: + QString m_currentFileName; + QString m_currentMasterFileName; + typedef QMap<QString, Dependence> DependenceMap; + typedef QMap< QString, DependenceMap> DependencesMap; + DependencesMap m_dependences; + MacroMap m_macros; + QMap< QString, QValueList<Problem> > m_problems; + QMap<QString, ParsedFilePointer> m_parsedUnits; + QStringList m_includePaths; + uint depresolv : + 1; + Lexer *lexer; + SourceProvider* m_sourceProvider; + + ParsedFilePointer m_currentParsedFile; + CachedLexedFilePointer m_currentLexerCache; + LexerCache m_lexerCache; + + int m_dependenceDepth; + int m_maxDependenceDepth; + + class ParseHelper; + friend class ParseHelper; + + private: + Driver( const Driver& source ); + void operator = ( const Driver& source ); +}; + +#endif diff --git a/lib/cppparser/errors.cpp b/lib/cppparser/errors.cpp new file mode 100644 index 00000000..154301c6 --- /dev/null +++ b/lib/cppparser/errors.cpp @@ -0,0 +1,25 @@ +/* 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "errors.h" +#include <klocale.h> + +QT_STATIC_CONST_IMPL Error& Errors::InternalError = Error( 1, -1, i18n("Internal Error") ); +QT_STATIC_CONST_IMPL Error& Errors::SyntaxError = Error( 2, -1, i18n("Syntax Error before '%1'") ); +QT_STATIC_CONST_IMPL Error& Errors::ParseError = Error( 3, -1, i18n("Parse Error before '%1'") ); diff --git a/lib/cppparser/errors.h b/lib/cppparser/errors.h new file mode 100644 index 00000000..f846533d --- /dev/null +++ b/lib/cppparser/errors.h @@ -0,0 +1,45 @@ +/* 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef ERRORS_H +#define ERRORS_H + +#include <qstring.h> + + +struct Error{ + int code; + int level; + QString text; + + Error( int c, int l, const QString& s ) + : code( c ), level( l ), text( s ) + {} +}; + +class Errors{ +public: + QT_STATIC_CONST Error& InternalError; + QT_STATIC_CONST Error& SyntaxError; + QT_STATIC_CONST Error& ParseError; +}; + + + +#endif diff --git a/lib/cppparser/keywords.h b/lib/cppparser/keywords.h new file mode 100644 index 00000000..e649a5a9 --- /dev/null +++ b/lib/cppparser/keywords.h @@ -0,0 +1,95 @@ +// +// Keywords file is included in lookup.cpp +// [erbsland] replacement for old hash table +// +#define INSERT( x, y ) keywords.insert( std::pair<HashedString, Type>( x, y ) ) +// KDE Keywords +INSERT( "K_DCOP", Token_K_DCOP ); +INSERT( "k_dcop", Token_k_dcop ); +INSERT( "k_dcop_signals", Token_k_dcop_signals ); + +// Qt Keywords +INSERT( "Q_OBJECT", Token_Q_OBJECT ); +INSERT( "signals", Token_signals ); +INSERT( "slots", Token_slots ); +INSERT( "emit", Token_emit ); +INSERT( "foreach", Token_foreach ); + +// C++ Keywords +INSERT( "__int64", Token_int ); +INSERT( "__asm__", Token_asm ); +INSERT( "and", Token_and ); +INSERT( "and_eq", Token_and_eq ); +INSERT( "asm", Token_asm ); +INSERT( "auto", Token_auto ); +INSERT( "bitand", Token_bitand ); +INSERT( "bitor", Token_bitor ); +INSERT( "bool", Token_bool ); +INSERT( "break", Token_break ); +INSERT( "case", Token_case ); +INSERT( "catch", Token_catch ); +INSERT( "char", Token_char ); +INSERT( "class", Token_class ); +INSERT( "compl", Token_compl ); +INSERT( "const", Token_const ); +INSERT( "const_cast", Token_const_cast ); +INSERT( "continue", Token_continue ); +INSERT( "default", Token_default ); +INSERT( "delete", Token_delete ); +INSERT( "do", Token_do ); +INSERT( "double", Token_double ); +INSERT( "dynamic_cast", Token_dynamic_cast ); +INSERT( "else", Token_else ); +INSERT( "enum", Token_enum ); +INSERT( "explicit", Token_explicit ); +INSERT( "export", Token_export ); +INSERT( "extern", Token_extern ); +INSERT( "float", Token_float ); +INSERT( "for", Token_for ); +INSERT( "friend", Token_friend ); +INSERT( "goto", Token_goto ); +INSERT( "if", Token_if ); +INSERT( "inline", Token_inline ); +INSERT( "int", Token_int ); +INSERT( "long", Token_long ); +INSERT( "mutable", Token_mutable ); +INSERT( "namespace", Token_namespace ); +INSERT( "new", Token_new ); +INSERT( "not", Token_not ); +INSERT( "not_eq", Token_not_eq ); +INSERT( "operator", Token_operator ); +INSERT( "or", Token_or ); +INSERT( "or_eq", Token_or_eq ); +INSERT( "private", Token_private ); +INSERT( "protected", Token_protected ); +INSERT( "public", Token_public ); +INSERT( "register", Token_register ); +INSERT( "reinterpret_cast", Token_reinterpret_cast ); +INSERT( "return", Token_return ); +INSERT( "short", Token_short ); +INSERT( "signed", Token_signed ); +INSERT( "sizeof", Token_sizeof ); +INSERT( "static", Token_static ); +INSERT( "static_cast", Token_static_cast ); +INSERT( "struct", Token_struct ); +INSERT( "switch", Token_switch ); +INSERT( "template", Token_template ); +INSERT( "this", Token_this ); +INSERT( "throw", Token_throw ); +INSERT( "try", Token_try ); +INSERT( "typedef", Token_typedef ); +INSERT( "typeid", Token_typeid ); +INSERT( "typename", Token_typename ); +INSERT( "union", Token_union ); +INSERT( "unsigned", Token_unsigned ); +INSERT( "using", Token_using ); +INSERT( "virtual", Token_virtual ); +INSERT( "void", Token_void ); +INSERT( "volatile", Token_volatile ); +INSERT( "while", Token_while ); +INSERT( "xor", Token_xor ); +INSERT( "xor_eq", Token_xor_eq ); + +// +// End of file +// 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 <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., 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 + diff --git a/lib/cppparser/lexer.h b/lib/cppparser/lexer.h new file mode 100644 index 00000000..8b3f57ed --- /dev/null +++ b/lib/cppparser/lexer.h @@ -0,0 +1,867 @@ +/* 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef LEXER_H +#define LEXER_H + +#include "driver.h" + +#include <qstring.h> +#include <qmap.h> +#include <qvaluestack.h> +#include <qpair.h> +#include <qptrvector.h> +#include <hashedstring.h> +#include <ext/hash_map> + +#define CHARTYPE QChar + +enum Type { + Token_eof = 0, + Token_identifier = 1000, + Token_number_literal, + Token_char_literal, + Token_string_literal, + Token_whitespaces, + Token_comment, + Token_preproc, + + Token_assign = 2000, + Token_ptrmem, + Token_ellipsis, + Token_scope, + Token_shift, + Token_eq, + Token_leq, + Token_geq, + Token_incr, + Token_decr, + Token_arrow, + + Token_concat, + + Token_K_DCOP, + Token_k_dcop, + Token_k_dcop_signals, + + Token_Q_OBJECT, + Token_signals, + Token_slots, + Token_emit, + Token_foreach, // qt4 [erbsland] + + Token_and, + Token_and_eq, + Token_asm, + Token_auto, + Token_bitand, + Token_bitor, + Token_bool, + Token_break, + Token_case, + Token_catch, + Token_char, + Token_class, + Token_compl, + Token_const, + Token_const_cast, + Token_continue, + Token_default, + Token_delete, + Token_do, + Token_double, + Token_dynamic_cast, + Token_else, + Token_enum, + Token_explicit, + Token_export, + Token_extern, + Token_false, + Token_float, + Token_for, + Token_friend, + Token_goto, + Token_if, + Token_inline, + Token_int, + Token_long, + Token_mutable, + Token_namespace, + Token_new, + Token_not, + Token_not_eq, + Token_operator, + Token_or, + Token_or_eq, + Token_private, + Token_protected, + Token_public, + Token_register, + Token_reinterpret_cast, + Token_return, + Token_short, + Token_signed, + Token_sizeof, + Token_static, + Token_static_cast, + Token_struct, + Token_switch, + Token_template, + Token_this, + Token_throw, + Token_true, + Token_try, + Token_typedef, + Token_typeid, + Token_typename, + Token_union, + Token_unsigned, + Token_using, + Token_virtual, + Token_void, + Token_volatile, + Token_wchar_t, + Token_while, + Token_xor, + Token_xor_eq +}; + +enum SkipType { + SkipWord, + SkipWordAndArguments +}; + +struct LexerData; + +class Token +{ + Token(const QString &); + Token( int type, int position, int length, const QString& text ); + Token( const Token& source ); + + Token& operator = ( const Token& source ); + bool operator == ( const Token& token ) const; + operator int () const; + +public: + bool isNull() const; + + int type() const; + void setType( int type ); + + void getStartPosition( int* line, int* column ) const; + void setStartPosition( int line, int column ); + void getEndPosition( int* line, int* column ) const; + void setEndPosition( int line, int column ); + + unsigned int length() const; + void setLength( unsigned int length ); + + int position() const; + void setPosition( int position ); + + QString text() const; + +private: + int m_type; + int m_position; + int m_length; + int m_startLine; + int m_startColumn; + int m_endLine; + int m_endColumn; + const QString & m_text; + + friend class Lexer; + friend class Parser; +}; // class Token + +class Lexer +{ +public: + Lexer( Driver* driver ); + ~Lexer(); + + bool recordComments() const; + void setRecordComments( bool record ); + + bool recordWhiteSpaces() const; + void setRecordWhiteSpaces( bool record ); + + bool reportWarnings() const; + void setReportWarnings( bool enable ); + + bool reportMessages() const; + void setReportMessages( bool enable ); + + bool skipWordsEnabled() const; + void setSkipWordsEnabled( bool enabled ); + + bool preprocessorEnabled() const; + void setPreprocessorEnabled( bool enabled ); + + void resetSkipWords(); + void addSkipWord( const QString& word, SkipType skipType=SkipWord, const QString& str = QString::null ); + + QString source() const; + void setSource( const QString& source ); + + int index() const; + void setIndex( int index ); + + //returns the count of lines that wer skipped due to #ifdef's + int skippedLines() const; + + void reset(); + + const Token& tokenAt( int position ) const; + const Token& nextToken(); + const Token& lookAhead( int n ) const; + + static int toInt( const Token& token ); + + int tokenPosition( const Token& token ) const; + void getTokenPosition( const Token& token, int* line, int* col ); + + int currentLine() const { return m_currentLine; } + int currentColumn() const { return m_currentColumn; } + + inline const CHARTYPE* offset( int offset ) const { + return m_source.unicode() + offset; + } + + inline int getOffset( const QChar* p ) const { + return int(p - (m_source.unicode())); + } + +private: + void setEndPtr( const QChar* c ) { + m_endPtr = c; + if( m_ptr < m_endPtr ) + m_currentChar = *m_ptr; + else + m_currentChar = QChar::null; + } + const QChar currentChar() const; + QChar peekChar( int n=1 ) const; + int currentPosition() const; + + void insertCurrent( const QString& str ); + + void tokenize(); + void nextToken( Token& token, bool stopOnNewline=false ); + void nextChar(); + void nextChar( int n ); + void skip( int l, int r ); + void readIdentifier(); + void readWhiteSpaces( bool skipNewLine=true, bool skipOnlyOnce=false ); + void readLineComment(); + void readMultiLineComment(); + void readCharLiteral(); + void readStringLiteral(); + void readNumberLiteral(); + + int findOperator3() const; + int findOperator2() const; + bool eof() const; + + // preprocessor (based on an article of Al Stevens on Dr.Dobb's journal) + int testIfLevel(); + int macroDefined(); + QString readArgument(); + + int macroPrimary(); + int macroMultiplyDivide(); + int macroAddSubtract(); + int macroRelational(); + int macroEquality(); + int macroBoolAnd(); + int macroBoolXor(); + int macroBoolOr(); + int macroLogicalAnd(); + int macroLogicalOr(); + int macroExpression(); + + void handleDirective( const QString& directive ); + void processDefine( Macro& macro ); + void processElse(); + void processElif(); + void processEndif(); + void processIf(); + void processIfdef(); + void processIfndef(); + void processInclude(); + void processUndef(); + +private: + LexerData* d; + Driver* m_driver; + QPtrVector< Token > m_tokens; + int m_size; + int m_index; + QString m_source; + const QChar* m_ptr; + const QChar* m_endPtr; + QChar m_currentChar; + bool m_recordComments; + bool m_recordWhiteSpaces; + bool m_startLine; + __gnu_cxx::hash_map< HashedString, QPair<SkipType, QString> > m_words; + + + int m_skippedLines; + int m_currentLine; + int m_currentColumn; + bool m_skipWordsEnabled; + + // preprocessor + QMemArray<bool> m_skipping; + QMemArray<bool> m_trueTest; + int m_ifLevel; + bool m_preprocessorEnabled; + bool m_inPreproc; + + bool m_reportWarnings; + bool m_reportMessages; + +private: + Lexer( const Lexer& source ); + void operator = ( const Lexer& source ); +}; + + +inline Token::Token(const QString & text = "") + : m_type( -1 ), + m_position( 0 ), + m_length( 0 ), + m_text( text ) +{ +} + +inline Token::Token( int type, int position, int length, const QString& text ) + : m_type( type ), + m_position( position ), + m_length( length ), + m_text( text ) +{ +} + +inline Token::Token( const Token& source ) + : m_type( source.m_type ), + m_position( source.m_position ), + m_length( source.m_length ), + m_startLine( source.m_startLine ), + m_startColumn( source.m_startColumn ), + m_endLine( source.m_endLine ), + m_endColumn( source.m_endColumn ), + m_text( source.m_text ) +{ +} + +inline Token& Token::operator = ( const Token& source ) +{ + m_type = source.m_type; + m_position = source.m_position; + m_length = source.m_length; + m_startLine = source.m_startLine; + m_startColumn = source.m_startColumn; + m_endLine = source.m_endLine; + m_endColumn = source.m_endColumn; +// m_text = source.m_text; + return( *this ); +} + +inline Token::operator int () const +{ + return m_type; +} + +inline bool Token::operator == ( const Token& token ) const +{ + return m_type == token.m_type && + m_position == token.m_position && + m_length == token.m_length && + m_startLine == token.m_startLine && + m_startColumn == token.m_startColumn && + m_endLine == token.m_endLine && + m_endColumn == token.m_endColumn && + m_text == token.m_text; +} + +inline bool Token::isNull() const +{ + return m_type == Token_eof || m_length == 0; +} + +inline int Token::type() const +{ + return m_type; +} + +inline void Token::setType( int type ) +{ + m_type = type; +} + +inline int Token::position() const +{ + return m_position; +} + +inline QString Token::text() const +{ + return m_text.mid(m_position, m_length); +} + +inline void Token::setStartPosition( int line, int column ) +{ + m_startLine = line; + m_startColumn = column; +} + +inline void Token::setEndPosition( int line, int column ) +{ + m_endLine = line; + m_endColumn = column; +} + +inline void Token::getStartPosition( int* line, int* column ) const +{ + if( line ) *line = m_startLine; + if( column ) *column = m_startColumn; +} + +inline void Token::getEndPosition( int* line, int* column ) const +{ + if( line ) *line = m_endLine; + if( column ) *column = m_endColumn; +} + +inline void Token::setPosition( int position ) +{ + m_position = position; +} + +inline unsigned int Token::length() const +{ + return m_length; +} + +inline void Token::setLength( unsigned int length ) +{ + m_length = length; +} + +inline bool Lexer::recordComments() const +{ + return m_recordComments; +} + +inline void Lexer::setRecordComments( bool record ) +{ + m_recordComments = record; +} + +inline bool Lexer::recordWhiteSpaces() const +{ + return m_recordWhiteSpaces; +} + +inline void Lexer::setRecordWhiteSpaces( bool record ) +{ + m_recordWhiteSpaces = record; +} + +inline QString Lexer::source() const +{ + return m_source; +} + +inline int Lexer::index() const +{ + return m_index; +} + +inline void Lexer::setIndex( int index ) +{ + m_index = index; +} + +inline const Token& Lexer::nextToken() +{ + if( m_index < m_size ) + return *m_tokens[ m_index++ ]; + + return *m_tokens[ m_index ]; +} + +inline const Token& Lexer::tokenAt( int n ) const +{ + return *m_tokens[ QMIN(n, m_size-1) ]; +} + +inline const Token& Lexer::lookAhead( int n ) const +{ + return *m_tokens[ QMIN(m_index + n, m_size-1) ]; +} + +inline int Lexer::tokenPosition( const Token& token ) const +{ + return token.position(); +} + +inline void Lexer::nextChar() +{ + if(*m_ptr == '\n') { + ++m_currentLine; + m_currentColumn = 0; + m_startLine = true; + } else { + ++m_currentColumn; + } + ++m_ptr; + + if( m_ptr < m_endPtr ) + m_currentChar = *m_ptr; + else + m_currentChar = QChar::null; +} + +inline void Lexer::nextChar( int n ) +{ + m_currentColumn += n; + m_ptr += n; + + if( m_ptr < m_endPtr ) + m_currentChar = *m_ptr; + else + m_currentChar = QChar::null; +} + +inline void Lexer::readIdentifier() +{ + while( currentChar().isLetterOrNumber() || currentChar() == '_' ) + nextChar(); +} + +inline void Lexer::readWhiteSpaces( bool skipNewLine, bool skipOnlyOnce ) +{ + while( !currentChar().isNull() ){ + QChar ch = currentChar(); + + if( ch == '\n' && !skipNewLine ){ + break; + } else if( ch.isSpace() ){ + nextChar(); + } else if( m_inPreproc && currentChar() == '\\' ){ + nextChar(); + readWhiteSpaces( true, true ); + } else { + break; + } + if( skipOnlyOnce && ch == '\n' ) { + skipNewLine = false; + } + } +} + +//little hack for better performance +inline bool isTodo( const QString& txt, int position ) { + if( txt.length() < position + 4 ) return false; + return (txt[ position ] == 't' || txt[ position ] == 'T') + && (txt[ position+1 ] == 'o' || txt[ position+1 ] == 'O') + && (txt[ position+2 ] == 'd' || txt[ position+2 ] == 'D') + && (txt[ position+3 ] == 'o' || txt[ position+3 ] == 'O'); +} + +inline bool isFixme( const QString& txt, int position ) { + if( txt.length() < position + 5 ) return false; + return (txt[ position ] == 'f' || txt[ position ] == 'F') + && (txt[ position+1 ] == 'i' || txt[ position+1 ] == 'I') + && (txt[ position+2 ] == 'x' || txt[ position+2 ] == 'X') + && (txt[ position+3 ] == 'm' || txt[ position+3 ] == 'M') + && (txt[ position+4 ] == 'e' || txt[ position+4 ] == 'E'); +} + +inline void Lexer::readLineComment() +{ + while( !currentChar().isNull() && currentChar() != '\n' ){ + if( m_reportMessages && isTodo( m_source, currentPosition() ) ){ + nextChar( 4 ); + QString msg; + int line = m_currentLine; + int col = m_currentColumn; + + while( currentChar() ){ + if( currentChar() == '*' && peekChar() == '/' ) + break; + else if( currentChar() == '\n' ) + break; + + msg += currentChar(); + nextChar(); + } + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col, Problem::Level_Todo) ); + } else + if( m_reportMessages && isFixme( m_source, currentPosition() ) ){ + nextChar( 5 ); + QString msg; + int line = m_currentLine; + int col = m_currentColumn; + + while( currentChar() ){ + if( currentChar() == '*' && peekChar() == '/' ) + break; + else if( currentChar() == '\n' ) + break; + + msg += currentChar(); + nextChar(); + } + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col, Problem::Level_Fixme) ); + } else + nextChar(); + } +} + +inline void Lexer::readMultiLineComment() +{ + while( !currentChar().isNull() ){ + if( currentChar() == '*' && peekChar() == '/' ){ + nextChar( 2 ); + return; + } else if( m_reportMessages && isTodo( m_source, currentPosition() ) ){ + nextChar( 4 ); + QString msg; + int line = m_currentLine; + int col = m_currentColumn; + + while( currentChar() ){ + if( currentChar() == '*' && peekChar() == '/' ) + break; + else if( currentChar() == '\n' ) + break; + msg += currentChar(); + nextChar(); + } + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col, Problem::Level_Todo) ); + } else + if( m_reportMessages && isFixme( m_source, currentPosition() ) ) { + nextChar( 5 ); + QString msg; + int line = m_currentLine; + int col = m_currentColumn; + + while( currentChar() ){ + if( currentChar() == '*' && peekChar() == '/' ) + break; + else if( currentChar() == '\n' ) + break; + + msg += currentChar(); + nextChar(); + } + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col, Problem::Level_Fixme) ); + } else + nextChar(); + } +} + +inline void Lexer::readCharLiteral() +{ + if( currentChar() == '\'' ) + nextChar(); // skip ' + else if( currentChar() == 'L' && peekChar() == '\'' ) + nextChar( 2 ); // slip L' + else + return; + + while( !currentChar().isNull() ){ + int len = getOffset( m_endPtr ) - currentPosition(); + + if( len>=2 && (currentChar() == '\\' && peekChar() == '\'') ){ + nextChar( 2 ); + } else if( len>=2 && (currentChar() == '\\' && peekChar() == '\\') ){ + nextChar( 2 ); + } else if( currentChar() == '\'' ){ + nextChar(); + break; + } else { + nextChar(); + } + } +} + +inline void Lexer::readStringLiteral() +{ + if( currentChar() != '"' ) + return; + + nextChar(); // skip " + + while( !currentChar().isNull() ){ + int len = getOffset( m_endPtr ) - currentPosition(); + + if( len>=2 && currentChar() == '\\' && peekChar() == '"' ){ + nextChar( 2 ); + } else if( len>=2 && currentChar() == '\\' && peekChar() == '\\' ){ + nextChar( 2 ); + } else if( currentChar() == '"' ){ + nextChar(); + break; + } else { + nextChar(); + } + } +} + +inline void Lexer::readNumberLiteral() +{ + while( currentChar().isLetterOrNumber() || currentChar() == '.' ) + nextChar(); +} + +inline int Lexer::findOperator3() const +{ + int n = getOffset( m_endPtr ) - currentPosition(); + + if( n >= 3){ + QChar ch = currentChar(), ch1=peekChar(), ch2=peekChar(2); + + if( ch == '<' && ch1 == '<' && ch2 == '=' ) return Token_assign; + else if( ch == '>' && ch1 == '>' && ch2 == '=' ) return Token_assign; + else if( ch == '-' && ch1 == '>' && ch2 == '*' ) return Token_ptrmem; + else if( ch == '.' && ch1 == '.' && ch2 == '.' ) return Token_ellipsis; + } + + return -1; +} + +inline int Lexer::findOperator2() const +{ + int n = getOffset( m_endPtr ) - currentPosition(); + + if( n>=2 ){ + QChar ch = currentChar(), ch1=peekChar(); + + if( ch == ':' && ch1 == ':' ) return Token_scope; + else if( ch == '.' && ch1 == '*' ) return Token_ptrmem; + else if( ch == '+' && ch1 == '=' ) return Token_assign; + else if( ch == '-' && ch1 == '=' ) return Token_assign; + else if( ch == '*' && ch1 == '=' ) return Token_assign; + else if( ch == '/' && ch1 == '=' ) return Token_assign; + else if( ch == '%' && ch1 == '=' ) return Token_assign; + else if( ch == '^' && ch1 == '=' ) return Token_assign; + else if( ch == '&' && ch1 == '=' ) return Token_assign; + else if( ch == '|' && ch1 == '=' ) return Token_assign; + else if( ch == '<' && ch1 == '<' ) return Token_shift; + else if( ch == '>' && ch1 == '>' ) return Token_shift; + else if( ch == '=' && ch1 == '=' ) return Token_eq; + else if( ch == '!' && ch1 == '=' ) return Token_eq; + else if( ch == '<' && ch1 == '=' ) return Token_leq; + else if( ch == '>' && ch1 == '=' ) return Token_geq; + else if( ch == '&' && ch1 == '&' ) return Token_and; + else if( ch == '|' && ch1 == '|' ) return Token_or; + else if( ch == '+' && ch1 == '+' ) return Token_incr; + else if( ch == '-' && ch1 == '-' ) return Token_decr; + else if( ch == '-' && ch1 == '>' ) return Token_arrow; + else if( ch == '#' && ch1 == '#' ) return Token_concat; + } + + return -1; +} + +inline bool Lexer::skipWordsEnabled() const +{ + return m_skipWordsEnabled; +} + +inline void Lexer::setSkipWordsEnabled( bool enabled ) +{ + m_skipWordsEnabled = enabled; +} + +inline bool Lexer::preprocessorEnabled() const +{ + return m_preprocessorEnabled; +} + +inline void Lexer::setPreprocessorEnabled( bool enabled ) +{ + m_preprocessorEnabled = enabled; +} + +inline int Lexer::currentPosition() const +{ + return getOffset( m_ptr ); +} + +inline const QChar Lexer::currentChar() const +{ + return m_currentChar; +} + +inline QChar Lexer::peekChar( int n ) const +{ + const QChar* p = m_ptr + n; + + if( p < m_endPtr ) + return *p; + else + return QChar::null; +} + +inline bool Lexer::eof() const +{ + return m_ptr >= m_endPtr; +} + +inline bool Lexer::reportWarnings() const +{ + return m_reportWarnings; +} + +inline void Lexer::setReportWarnings( bool enable ) +{ + m_reportWarnings = enable; +} + +inline bool Lexer::reportMessages() const +{ + return m_reportMessages; +} + +inline void Lexer::setReportMessages( bool enable ) +{ + m_reportMessages = enable; +} + +inline void Lexer::insertCurrent( const QString& str ) { + int posi = currentPosition(); + m_source.insert( posi, str ); + + m_ptr = offset( posi ); + m_endPtr = offset( m_source.length() ); + if( m_ptr < m_endPtr ) + m_currentChar = *m_ptr; + else + m_currentChar = QChar::null; +} + +#endif diff --git a/lib/cppparser/lexercache.cpp b/lib/cppparser/lexercache.cpp new file mode 100644 index 00000000..a7e40dd6 --- /dev/null +++ b/lib/cppparser/lexercache.cpp @@ -0,0 +1,249 @@ +/*************************************************************************** + copyright : (C) 2006 by David Nolden + email : david.nolden.kdevelop@art-master.de +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "lexercache.h" +#include "driver.h" +#include <kdebug.h> + +LexerCache::LexerCache( Driver* d ) : m_driver( d ) {} + +void LexerCache::addLexedFile( const CachedLexedFilePointer& file ) { + //kdDebug( 9007 ) << "LexerCache: adding an instance of " << file->fileName().str() << endl; + + std::pair< CachedLexedFileMap::iterator, CachedLexedFileMap::iterator> files = m_files.equal_range( file->fileName() ); + + if ( files.first == files.second ) { + m_files.insert( std::make_pair( file->fileName(), file ) ); + } else { + //Make sure newer files appear first + m_files.insert( files.first, std::make_pair( file->fileName(), file ) ); + } + + int cnt = 0; + while ( files.first != files.second ) { + if ( sourceChanged( *( *( files.first ) ).second ) ) { + m_files.erase( files.first++ ); + } else { + cnt++; + files.first++; + } + } + //kdDebug( 9007 ) << "LexerCache: new count of cached instances for the file: " << cnt << endl; +} + +CachedLexedFilePointer LexerCache::lexedFile( const HashedString& fileName ) { + initFileModificationCache(); + std::pair< CachedLexedFileMap::iterator, CachedLexedFileMap::iterator> files = m_files.equal_range( fileName ); + + ///@todo optimize with standard-algorithms(by first computing the intersection) + + /* if( files.first != files.second ) + //kdDebug( 9007 ) << "LexerCache: cache for file " << fileName.str() << " is not empty" << endl; + else + //kdDebug( 9007 ) << "LexerCache: cache for file " << fileName.str() << " is empty" << endl;*/ + + while ( files.first != files.second ) { + const CachedLexedFile& file( *( *( files.first ) ).second ); + if ( sourceChanged( file ) ) { + //kdDebug( 9007 ) << "LexerCache: cache for file " << fileName.str() << " is being discarded because the file was modified" << endl; + m_files.erase( files.first++ ); + continue; + } + bool success = true; + //Make sure that none of the macros stored in the driver affect the file in a different way than the one before + Driver::MacroMap::const_iterator end = m_driver->macros().end(); + for ( Driver::MacroMap::const_iterator rit = m_driver->macros().begin(); rit != end; ) { + Driver::MacroMap::const_iterator it = rit; + ++rit; + if ( rit != end && ( *it ).first == ( *rit ).first ) continue; //Always only use the last macro of the same name for comparison, it is on top of the macro-stack + if (( *it ).second.isUndef() ) continue; //Undef-macros theoretically don't exist + + if ( file.hasString(( *it ).first ) ) { + if ( file.m_usedMacros.hasMacro(( *it ).first ) ) { + Macro m( file.m_usedMacros.macro(( *it ).first.str() ) ); + if ( !( m == ( *it ).second ) ) { + //kdDebug( 9007 ) << "LexerCache: The cached file " << fileName.str() << " depends on the string \"" << ( *it ).first.str() << "\" and used a macro for it with the body \"" << m.body() << "\"(from " << m.fileName() << "), but the driver contains the same macro with body \"" << ( *it ).second.body() << "\"(from " << ( *it ).second.fileName() << "), cache is not used" << endl; + + //Macro with the same name was used, but it is different + success = false; + break; + } + + } else { + //There is a macro that affects the file, but was not used while the previous parse + //kdDebug( 9007 ) << "LexerCache: The cached file " << fileName.str() << " depends on the string \"" << ( *it ).first.str() << "\" and the driver contains a macro of that name with body \"" << ( *it ).second.body() << "\"(from " << ( *it ).second.fileName() << "), the cached file is not used" << endl; + success = false; + break; + } + } + } + //Make sure that all external macros used by the file now exist too + MacroSet::Macros::const_iterator end2 = file.usedMacros().macros().end(); + for ( MacroSet::Macros::const_iterator it = file.usedMacros().macros().begin(); it != end2; ++it ) { + if ( !m_driver->hasMacro( HashedString(( *it ).name() ) ) ) { + //kdDebug( 9007 ) << "LexerCache: The cached file " << fileName.str() << " used a macro called \"" << it->name() << "\"(from " << it->fileName() << "), but the driver does not contain that macro, the cached file is not used" << endl; + success = false; + break; + } + } + + if ( success ) { + //kdDebug( 9007 ) << "LexerCache: Using cached file " << fileName.str() << endl; + (*files.first).second->access(); + return ( *files.first ).second; + } + ++files.first; + } + return CachedLexedFilePointer(); +} + +QDateTime LexerCache::fileModificationTimeCached( const HashedString& fileName ) { + FileModificationMap::const_iterator it = m_fileModificationCache.find( fileName ); + if( it != m_fileModificationCache.end() ) { + ///Use the cache for 10 seconds + if( (*it).second.m_readTime.secsTo( m_currentDateTime ) < 10 ) { + return (*it).second.m_modificationTime; + } + } + + QFileInfo fileInfo( fileName.str() ); + m_fileModificationCache[fileName].m_readTime = QDateTime::currentDateTime(); + m_fileModificationCache[fileName].m_modificationTime = fileInfo.lastModified(); + return fileInfo.lastModified(); + +} + +//Should be cached too! +bool LexerCache::sourceChanged( const CachedLexedFile& file ) { + //@todo Check if any of the dependencies changed + + QDateTime modTime = fileModificationTimeCached( file.fileName() ); + + if ( modTime != file.modificationTime() ) + return true; + + for( QMap<HashedString, QDateTime>::const_iterator it = file.allModificationTimes().begin(); it != file.allModificationTimes().end(); ++it ) { + QDateTime modTime = fileModificationTimeCached( it.key() ); + if( modTime != *it ) + return true; + } + + return false; +} + + +void LexerCache::clear() { + m_files.clear(); + m_totalStringSet.clear(); + m_fileModificationCache.clear(); +} + +void LexerCache::erase( const CacheNode* node ) { + std::pair< CachedLexedFileMap::iterator, CachedLexedFileMap::iterator> files = m_files.equal_range( ((const CachedLexedFile*)(node))->fileName() ); + while ( files.first != files.second ) { + if( (*files.first).second.data() == ((const CachedLexedFile*)(node)) ) { + m_files.erase( files.first ); + return; + } + files.first++; + } + //kdDebug( 9007 ) << "Error: could not find a node in the list for file " << ((const CachedLexedFile*)(node))->fileName().str() << endl; +} + +CachedLexedFile::CachedLexedFile( const HashedString& fileName, LexerCache* manager ) : CacheNode( manager ), m_fileName( fileName ) { + QFileInfo fileInfo( fileName.str() ); + m_modificationTime = fileInfo.lastModified(); + m_allModificationTimes[ fileName ] = m_modificationTime; +} + +void CachedLexedFile::addDefinedMacro( const Macro& macro ) { +#ifdef LEXERCACHE_DEBUG + //kdDebug( 9007 ) << "defined macro " << macro.name() << endl; +#endif + m_definedMacros.addMacro( macro ); + m_definedMacroNames.insert( HashedString( macro.name() ) ); +} + +void CachedLexedFile::addUsedMacro( const Macro& macro ) { + if ( !m_definedMacros.hasMacro( macro.name() ) ) { +#ifdef LEXERCACHE_DEBUG + //kdDebug( 9007 ) << "used macro " << macro.name() << endl; +#endif + m_usedMacros.addMacro( macro ); + } +} + +void CachedLexedFile::addIncludeFile( const HashedString& file, const QDateTime& modificationTime ) { + m_includeFiles.insert( file ); + m_allModificationTimes[file] = modificationTime; +} + + +QDateTime CachedLexedFile::modificationTime() const { + return m_modificationTime; +} + +void CachedLexedFile::addProblem( const Problem& p ) { + m_problems << p; +} + +QValueList<Problem> CachedLexedFile::problems() const { + return m_problems; +} + +//The parameter should be a CachedLexedFile that was lexed AFTER the content of this file +void CachedLexedFile::merge( const CachedLexedFile& file ) { +#ifdef LEXERCACHE_DEBUG + //kdDebug( 9007 ) << fileName().str() << ": merging " << file.fileName().str() << endl << "defined in this: " << m_definedMacroNames.print().c_str() << endl << "defined macros in other: " << file.m_definedMacroNames.print().c_str() << endl;; +#endif + HashedStringSet tempStrings = file.m_strings; + tempStrings -= m_definedMacroNames; + m_strings += tempStrings; + m_includeFiles += file.m_includeFiles; + //Only add macros to the usedMacros-list that were not defined locally + for ( MacroSet::Macros::const_iterator it = file.m_usedMacros.macros().begin(); it != file.m_usedMacros.macros().end(); ++it ) { + if ( !m_definedMacros.hasMacro(( *it ).name() ) ) {///If the macro was not defined locally, add it to the macros-list. +#ifdef LEXERCACHE_DEBUG + //kdDebug( 9007 ) << "inserting used macro " << ( *it ).name() << endl; +#endif + m_usedMacros.addMacro( *it ); + } + } + + m_definedMacros.merge( file.m_definedMacros ); + m_definedMacroNames += file.m_definedMacroNames; + + for( QMap<HashedString, QDateTime>::const_iterator it = file.m_allModificationTimes.begin(); it != file.m_allModificationTimes.end(); ++it ) + m_allModificationTimes[it.key()] = *it; + + +#ifdef LEXERCACHE_DEBUG + //kdDebug( 9007 ) << fileName().str() << ": defined in this after merge: " << m_definedMacroNames.print().c_str() << endl; +#endif + m_problems += file.m_problems; +} + +size_t CachedLexedFile::hash() const { + return m_usedMacros.valueHash() + m_usedMacros.idHash() + m_definedMacros.idHash() + m_definedMacros.valueHash() + m_strings.hash(); +} + +void LexerCache::initFileModificationCache() { + m_currentDateTime = QDateTime::currentDateTime(); +} + +void LexerCache::saveMemory() { + m_fileModificationCache.clear(); + + m_totalStringSet.clear(); ///it's unclear how often this should be emptied. It may happen that completely unused strings end up in this set, then deleting it will save us memory. +} diff --git a/lib/cppparser/lexercache.h b/lib/cppparser/lexercache.h new file mode 100644 index 00000000..c32d5406 --- /dev/null +++ b/lib/cppparser/lexercache.h @@ -0,0 +1,162 @@ +/*************************************************************************** + copyright : (C) 2006 by David Nolden + email : david.nolden.kdevelop@art-master.de +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef LEXERCACHE_H +#define LEXERCACHE_H +#include <hashedstring.h> +#include <ext/hash_map> +#include <ksharedptr.h> +#include "macro.h" +#include <kdebug.h> +#include <qdatetime.h> +#include <qfileinfo.h> +#include <ext/hash_set> +#include "cachemanager.h" + +//#define LEXERCACHE_DEBUG + +class LexerCache; + +class CachedLexedFile : public KShared, public CacheNode { + public: + ///@todo add and manage the set of included files + CachedLexedFile( const HashedString& fileName, LexerCache* manager ); + + inline void addString( const HashedString& string ) { + if( !m_definedMacroNames[ string ] ) { + m_strings.insert( string ); + } + } + + void addDefinedMacro( const Macro& macro ); + + void addUsedMacro( const Macro& macro ); + + void addIncludeFile( const HashedString& file, const QDateTime& modificationTime ); + + inline bool hasString( const HashedString& string ) const { + return m_strings[string]; + } + + QDateTime modificationTime() const; + + void addProblem( const Problem& p ); + + QValueList<Problem> problems() const; + + //The parameter should be a CachedLexedFile that was lexed AFTER the content of this file + void merge( const CachedLexedFile& file ); + + bool operator < ( const CachedLexedFile& rhs ) const { + return m_fileName < rhs.m_fileName; + } + + size_t hash() const; + + HashedString fileName() const { + return m_fileName; + } + + const HashedStringSet& includeFiles() const { + return m_includeFiles; + } + + const MacroSet& definedMacros() const { + return m_definedMacros; + } + + const MacroSet& usedMacros() const { + return m_usedMacros; + } + + ///Should contain a modification-time for each include-file + const QMap<HashedString, QDateTime>& allModificationTimes() const { + return m_allModificationTimes; + } + + private: + friend class LexerCache; + HashedString m_fileName; + QDateTime m_modificationTime; + HashedStringSet m_strings; //Set of all strings that can be affected by macros from outside + HashedStringSet m_includeFiles; //Set of all files + MacroSet m_usedMacros; //Set of all macros that were used, and were defined outside of this file + MacroSet m_definedMacros; //Set of all macros that were defined while lexing this file + HashedStringSet m_definedMacroNames; + QValueList<Problem> m_problems; + QMap<HashedString, QDateTime> m_allModificationTimes; + /* + Needed data: + 1. Set of all strings that appear in this file(For memory-reasons they should be taken from a global string-repository, because many will be the same) + 2. Set of all macros that were defined outside of, but affected the file + + Algorithm: + Iterate over all available macros, and check whether they affect the file. If it does, make sure that the macro is in the macro-set and has the same body. + If the check fails: We need to reparse. + */ +}; + +typedef KSharedPtr<CachedLexedFile> CachedLexedFilePointer; + +struct CachedLexedFilePointerCompare { + bool operator() ( const CachedLexedFilePointer& lhs, const CachedLexedFilePointer& rhs ) const { + return (*lhs) < (*rhs ); + } +}; + +class Driver; + +class LexerCache : public CacheManager { + public: + LexerCache( Driver* d ); + + void addLexedFile( const CachedLexedFilePointer& file ); + + ///Returns zero if no fitting file is available for the current context + CachedLexedFilePointer lexedFile( const HashedString& fileName ); + + void clear(); + + const HashedString& unifyString( const HashedString& str ) { + __gnu_cxx::hash_set<HashedString>::const_iterator it = m_totalStringSet.find( str ); + if( it != m_totalStringSet.end() ) { + return *it; + } else { + m_totalStringSet.insert( str ); + return str; + } + } + virtual void saveMemory(); + private: + ///before this can be called, initFileModificationCache should be called once + QDateTime fileModificationTimeCached( const HashedString& fileName ); + void initFileModificationCache(); + virtual void erase( const CacheNode* node ); + bool sourceChanged( const CachedLexedFile& file );///Returns true if the file itself, or any of its dependencies was modified. + //typedef __gnu_cxx::hash_multimap<HashedString, CachedLexedFilePointer> CachedLexedFileMap; + typedef std::multimap<HashedString, CachedLexedFilePointer> CachedLexedFileMap; + CachedLexedFileMap m_files; + __gnu_cxx::hash_set<HashedString> m_totalStringSet; ///This is used to reduce memory-usage: Most strings appear again and again. Because QString is reference-counted, this set contains a unique copy of each string to used for each appearance of the string + struct FileModificationCache { + QDateTime m_readTime; + QDateTime m_modificationTime; + }; + typedef __gnu_cxx::hash_map<HashedString, FileModificationCache> FileModificationMap; + FileModificationMap m_fileModificationCache; + Driver* m_driver; + QDateTime m_currentDateTime; +}; + + +#endif diff --git a/lib/cppparser/lookup.cpp b/lib/cppparser/lookup.cpp new file mode 100644 index 00000000..192b4d4c --- /dev/null +++ b/lib/cppparser/lookup.cpp @@ -0,0 +1,42 @@ +/* This file is part of KDevelop + Copyright (C) 2005 Tobias Erbsland <te@profzone.ch> + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "lookup.h" + +int Lookup::find( const HashedString& s ) +{ + KeywordMap::const_iterator it = keywords().find( s ); + if( it == keywords().end() ) + return -1; + return static_cast<int>((*it).second); +} + +const Lookup::KeywordMap& Lookup::keywords() +{ + static KeywordMap keywords; + + if( keywords.empty() ) + { +#include "keywords.h" + } + + return keywords; +} + diff --git a/lib/cppparser/lookup.h b/lib/cppparser/lookup.h new file mode 100644 index 00000000..6e41481e --- /dev/null +++ b/lib/cppparser/lookup.h @@ -0,0 +1,45 @@ +/* This file is part of KDevelop + Copyright (C) 2005 Tobias Erbsland <te@profzone.ch> + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _LOOKUP_H_ +#define _LOOKUP_H_ + +#include <qstring.h> +#include <qmap.h> + +#include "lexer.h" +#include <ext/hash_map> +#include <hashedstring.h> + +/** +* @short Fast keyword lookup. +*/ +class Lookup { +public: + typedef __gnu_cxx::hash_map<HashedString,Type> KeywordMap; + /** + * Find an entry in the table, and return its value + */ + static int find( const HashedString& s ); + + static const KeywordMap& keywords(); +}; + +#endif diff --git a/lib/cppparser/macro.h b/lib/cppparser/macro.h new file mode 100644 index 00000000..03e76984 --- /dev/null +++ b/lib/cppparser/macro.h @@ -0,0 +1,452 @@ +/*************************************************************************** + copyright : (C) 2006 by David Nolden + email : david.nolden.kdevelop@art-master.de +***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef MACRO_H +#define MACRO_H + +#include <qpair.h> +#include <qvaluestack.h> +#include <qstringlist.h> +#include <qcstring.h> +#include <qdatastream.h> +#include <qmap.h> +#include <qdatetime.h> +#include <qvaluelist.h> +#include <map> +#include <set> +#include <hashedstring.h> +#include <ksharedptr.h> +#include <codemodel.h> +#include <ext/hash_map> + +//This files should be renamed to something like "helpers.h" + +/** + * Encapsulates a problem in a piece of source code. + */ +class Problem { + public: + enum + { + Level_Error = 0, ///< Indicates an error that will prevent the code from compiling + Level_Warning, ///< Indicates a warning + Level_Todo, ///< Indicates there is still something left to do + Level_Fixme ///< Indicates that something needs to be fixed + }; + + public: + Problem() {} + Problem( const Problem& source ) + : m_text( source.m_text ), m_line( source.m_line ), + m_column( source.m_column ), m_level( source.m_level ), m_file( source.m_file ) {} + Problem( const QString& text, int line, int column, int level = Level_Error ) + : m_text( text ), m_line( line ), m_column( column ), m_level( level ) {} + + Problem( const Problem& source, bool /*threadSafeClone*/ ) + : m_text( QString::fromUtf8( source.m_text.utf8().data() ) ), m_line( source.m_line ), + m_column( source.m_column ), m_level( source.m_level ), m_file( QString::fromUtf8( source.m_file.utf8().data() ) ) {} + + Problem& operator = ( const Problem& source ) { + m_text = source.m_text; + m_line = source.m_line; + m_column = source.m_column; + m_level = source.m_level; + m_file = source.m_file; + return ( *this ); + } + + void setFileName( const QString& fileName ) { + m_file = fileName; + } + + bool operator == ( const Problem& p ) const { + return m_text == p.m_text && m_line == p.m_line && m_column == p.m_column && m_level == p.m_level && m_file == p.m_file; + } + + /** Get the filename in which the problem was encountered */ + QString fileName() const { + return m_file; + } + + /** Get the text for the problem */ + QString text() const { + return m_text; + } + /** Get the line number of the problem */ + int line() const { + return m_line; + } + /** Get the column of the problem */ + int column() const { + return m_column; + } + /** + * Get the seriousness of the problem. There are four possibilities: + * \li Error + * \li Warning + * \li Todo + * \li Fixme + */ + int level() const { + return m_level; + } + + private: + QString m_text; + int m_line; + int m_column; + int m_level; + QString m_file; +}; + + +/** + * A datatype that represents a preprocessor macro. + * Most of the functions in this class need to be inline, so we do not have to import cppparser to many modules. The other solution would be moving macro into interfaces. + */ +class Macro { + public: + typedef QString Argument; + + public: + Macro( bool hasArguments = false ) : m_idHashValid( false ), m_valueHashValid( false ), m_line( 0 ), m_column( 0 ), m_hasArguments( hasArguments ), m_isUndefMacro( false ) {} + Macro( const QString &n, const QString &b ) : m_idHashValid( false ), m_valueHashValid( false ), m_name( n ), m_line( 0 ), m_column( 0 ), m_body( b ), m_hasArguments( false ), m_isUndefMacro( false ) {} + + //Sorts the macros by their hash-value, then by their name. + struct NameArgCompare { + bool operator () ( const Macro& lhs, const Macro& rhs ) const { + size_t lhash = lhs.idHash(); + size_t rhash = rhs.idHash(); + if( lhash < rhash ) return true; + else if( lhash > rhash ) return false; + + int df = lhs.m_name.compare( rhs.m_name ); + if ( df < 0 ) + return true; + if ( df == 0 ) { + if ( !lhs.m_hasArguments && rhs.m_hasArguments ) { + return true; + } else if ( lhs.m_hasArguments == rhs.m_hasArguments ) { + return lhs.m_argumentList.count() < rhs.m_argumentList.count(); + + } else { + return false; + } + } + return false; + } + }; + struct NameCompare { + bool operator () ( const Macro& lhs, const Macro& rhs ) const { + size_t lhash = lhs.idHash(); + size_t rhash = rhs.idHash(); + if( lhash < rhash ) return true; + else if( lhash > rhash ) return false; + + int df = lhs.m_name.compare( rhs.m_name ); + return df < 0; + } + }; + + struct NameArgHash { + size_t operator () ( const Macro& macro ) const { + return macro.idHash(); + } + }; + + struct NameArgEqual { + bool operator () ( const Macro& lhs, const Macro& rhs ) const { + int df = lhs.m_name.compare( rhs.m_name ); + if ( df == 0 ) { + if ( lhs.m_hasArguments != rhs.m_hasArguments ) { + return false; + } else { + if( lhs.m_argumentList.count() != rhs.m_argumentList.count() ) return false; + /*QStringList::const_iterator it2 = rhs.m_argumentList.begin(); + for( QStringList::const_iterator it = lhs.m_argumentList.begin(); it != lhs.m_argumentList.end(); ) { + if( *it != *it2 ) return false; + + ++it; + ++it2; + }*/ + return true; + + } + } + return false; + } + }; + + Macro( const Macro& source ) + : m_idHashValid( source.m_idHashValid ), m_valueHashValid( source.m_valueHashValid ), m_idHash( source.m_idHash ), m_valueHash( source.m_valueHash ), m_name( source.m_name ), + m_fileName( source.m_fileName ), + m_line( source.m_line ), + m_column( source.m_column ), + m_body( source.m_body ), + m_hasArguments( source.m_hasArguments ), + m_argumentList( source.m_argumentList ), m_isUndefMacro( source.m_isUndefMacro ) {} + + Macro& operator = ( const Macro& source ) { + m_idHashValid = source.m_idHashValid; + m_valueHashValid = source.m_valueHashValid; + m_idHash = source.m_idHash; + m_valueHash = source.m_valueHash; + m_name = source.m_name; + m_fileName = source.m_fileName; + m_line = source.m_line; + m_column = source.m_column; + m_body = source.m_body; + m_hasArguments = source.m_hasArguments; + m_argumentList = source.m_argumentList; + m_isUndefMacro = source.m_isUndefMacro; + return *this; + } + + bool operator == ( const Macro& source ) const { + if( !m_idHashValid || !m_valueHashValid ) computeHash(); + if( !source.m_idHashValid || !source.m_valueHashValid ) source.computeHash(); + + if( m_idHash != source.m_idHash ) return false; + if( m_valueHash != source.m_valueHash ) return false; + + return m_name == source.m_name && + m_fileName == source.m_fileName && + m_body == source.m_body && + m_hasArguments == source.m_hasArguments && + m_argumentList == source.m_argumentList && m_isUndefMacro == source.m_isUndefMacro; + } + + void read( QDataStream& stream ) { + Q_INT8 i; + stream >> i; m_idHashValid = i; + stream >> i; m_valueHashValid = i; + stream >> i; m_hasArguments = i; + + stream >> m_idHash; + stream >> m_valueHash; + stream >> m_name; + stream >> m_line; + stream >> m_column; + stream >> m_body; + stream >> m_fileName; + stream >> m_argumentList; + } + + void write( QDataStream& stream ) const { + Q_INT8 i; + i = m_idHashValid; stream << i; + i = m_valueHashValid; stream << i; + i = m_hasArguments; stream << i; + + stream << m_idHash; + stream << m_valueHash; + stream << m_name; + stream << m_line; + stream << m_column; + stream << m_body; + stream << m_fileName; + stream << m_argumentList; + } + + /** Get the name for this macro */ + QString name() const { + return m_name; + } + /** Set the name for this macro */ + void setName( const QString& name ) { + m_name = name; + invalidateHash(); + } + + /** Get the file name that contains this macro */ + QString fileName() const { + return m_fileName; + } + /** Set the file name that contains this macro */ + void setFileName( const QString& fileName ) { + m_fileName = fileName; + invalidateHash(); + } + + /** Get the line the macro is defined on */ + int line() const { + return m_line; + } + /** Set the line the macro is defined on */ + void setLine( int line ) { + m_line = line; + } + + /** Get the column the macro starts at */ + int column() const { + return m_column; + } + /** Set the column the macro starts at */ + void setColumn( int column ) { + m_column = column; + } + + /** Get the body of the macro */ + QString body() const { + return m_body; + } + /** Set the body of the macro */ + void setBody( const QString& body ) { + m_body = body; + invalidateHash(); + } + + /** This is used so the lexer does not have to remove macros that should really stay(they are just temporarily shadowed by an isUndef-macro */ + bool isUndef() const { + return m_isUndefMacro; + }; + + void setUndef() { + m_isUndefMacro = true; + invalidateHash(); + }; + + /** Check whether the macro has arguments that are passed to it */ + bool hasArguments() const { + return m_hasArguments; + } + void setHasArguments( bool hasArguments ) { + m_hasArguments = hasArguments; + invalidateHash(); + } + /** Get a list of arguments passed to this macro */ + QValueList<Argument> argumentList() const { + return m_argumentList; + } + + /** Clear the list of arguments this macro has */ + void clearArgumentList() { + m_argumentList.clear(); + m_hasArguments = false; + invalidateHash(); + } + /** Add an argument to this macro */ + void addArgument( const Argument& argument ) { + m_argumentList << argument; + } + /** Add a list of arguments to this macro */ + void addArgumentList( const QValueList<Argument>& arguments ) { + m_argumentList += arguments; + invalidateHash(); + } + + ///This hash respects macro-name and argument-count + size_t idHash() const { + if( !m_idHashValid ) computeHash(); + return m_idHash; + } + + ///This hash respects body and if it is an undef-macro + size_t valueHash() const { + if( !m_valueHashValid ) computeHash(); + return m_valueHash; + } + + private: + inline void invalidateHash() const { + m_idHashValid = m_valueHashValid = false; + } + + void computeHash() const { + m_idHash = 7 * ( HashedString::hashString( m_name ) ); + int a = 1; + //m_idHash += 31 * m_argumentList.count(); + + m_valueHash = 27 * ( HashedString::hashString( m_body ) + (m_isUndefMacro ? 1 : 0 ) ); + + for( QValueList<Argument>::const_iterator it = m_argumentList.begin(); it != m_argumentList.end(); ++it ) { + a *= 19; + m_valueHash += a * HashedString::hashString( *it ); + } + m_valueHashValid = true; + m_idHashValid = true; + } + + mutable bool m_idHashValid; + mutable bool m_valueHashValid; + mutable size_t m_idHash; //Hash that represents the ids of all macros + mutable size_t m_valueHash; //Hash that represents the values of all macros + + QString m_name; + QString m_fileName; + int m_line; + int m_column; + QString m_body; + bool m_hasArguments; + QStringList m_argumentList; //While identification, only the count plays a role, not the values. + bool m_isUndefMacro; + friend class NameCompare; + friend class NameArgEqual; +}; + +class MacroSet { + public: + //typedef __gnu_cxx::hash_set< Macro, Macro::NameArgHash, Macro::NameArgEqual > Macros; + typedef std::set< Macro, Macro::NameCompare > Macros; + MacroSet() : m_idHashValid( false ), m_valueHashValid( false ) { + } + + void addMacro( const Macro& macro ); + + void read( QDataStream& stream ) { + //stream >> m_idHashValid >> m_idHash >> m_valueHashValid >> m_valueHash; + m_idHashValid = false; + m_valueHashValid = false; + int cnt; + stream >> cnt; + m_usedMacros.clear(); + Macro m; + for( int a = 0; a < cnt; a++ ) { + m.read( stream ); + m_usedMacros.insert( m ); + } + } + + void write( QDataStream& stream ) const { + //stream << m_idHashValid << m_idHash << m_valueHashValid << m_valueHash; + stream << int( m_usedMacros.size() ); + for( Macros::const_iterator it = m_usedMacros.begin(); it != m_usedMacros.end(); ++it ) { + (*it).write( stream ); + } + } + + bool hasMacro( const QString& name ) const; + bool hasMacro( const HashedString& name ) const; + Macro macro( const QString& name ) const; + + size_t idHash() const; + size_t valueHash() const; + + const Macros& macros() const { + return m_usedMacros; + } + + void merge( const MacroSet& macros ); + private: + void computeHash() const; + Macros m_usedMacros; + mutable bool m_idHashValid; + mutable bool m_valueHashValid; + mutable size_t m_idHash; //Hash that represents the ids of all macros + mutable size_t m_valueHash; //Hash that represents the values of all macros + + friend class Driver; +}; + +#endif diff --git a/lib/cppparser/parser.cpp b/lib/cppparser/parser.cpp new file mode 100644 index 00000000..b790f5a1 --- /dev/null +++ b/lib/cppparser/parser.cpp @@ -0,0 +1,4330 @@ +/* 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +// c++ support +#include "driver.h" +#include "lexer.h" +#include "parser.h" +#include "errors.h" + +// qt +#include <qstring.h> +#include <qstringlist.h> +#include <qasciidict.h> + +#include <kdebug.h> +#include <klocale.h> + +using namespace std; + +#define ADVANCE(tk, descr) \ +{ \ + const Token& token = lex->lookAhead( 0 ); \ + if( token != tk ){ \ + reportError( i18n("'%1' expected found '%2'").arg(descr).arg(token.text()) ); \ + return false; \ + } \ + nextToken(); \ +} + +#define ADVANCE_NR(tk, descr) \ +{ \ + const Token& token = lex->lookAhead( 0 ); \ + if( token != tk ){ \ + reportError( i18n("'%1' expected found '%2'").arg(descr).arg(token.text()) ); \ + } \ + else \ + nextToken(); \ +} + +#define CHECK(tk, descr) \ +{ \ + const Token& token = lex->lookAhead( 0 ); \ + if( token != tk ){ \ + return false; \ + } \ + nextToken(); \ +} + +#define MATCH(tk, descr) \ +{ \ + const Token& token = lex->lookAhead( 0 ); \ + if( token != tk ){ \ + reportError( Errors::SyntaxError ); \ + return false; \ + } \ +} + + + +#define UPDATE_POS(node, start, end) \ +{ \ + int line, col; \ + const Token &a = lex->tokenAt(start); \ + const Token &b = lex->tokenAt( end!=start ? end-1 : end ); \ + a.getStartPosition( &line, &col ); \ + (node)->setStartPosition( line, col ); \ + b.getEndPosition( &line, &col ); \ + (node)->setEndPosition( line, col ); \ + if( (node)->nodeType() == NodeType_Generic ) { \ + if ((start) == (end) || (end) == (start)+1) \ + (node)->setSlice(lex->source(), a.position(), a.length()); \ + else \ + (node)->setText( toString((start),(end)) ); \ + } \ +} + +#define AST_FROM_TOKEN(node, tk) \ + AST::Node node = CreateNode<AST>(); \ + UPDATE_POS( node, (tk), (tk)+1 ); + + +//@todo remove me +enum +{ + OBJC_CLASS, + OBJC_PROTOCOL, + OBJC_ALIAS +}; + +struct ParserPrivateData +{ + ParserPrivateData() + {} +}; + +Parser::Parser( Driver* driver, Lexer* lexer ) + : m_driver( driver ), + lex( lexer ), m_problems(0) +{ + d = new ParserPrivateData(); + + m_maxProblems = 5; + objcp = false; +} + +Parser::~Parser() +{ + delete d; + d = 0; +} + +bool Parser::reportError( const Error& err ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::reportError()" << endl; + if( m_problems < m_maxProblems ){ + ++m_problems; + int line=0, col=0; + const Token& token = lex->lookAhead( 0 ); + lex->getTokenPosition( token, &line, &col ); + + QString s = lex->lookAhead(0).text(); + s = s.left( 30 ).stripWhiteSpace(); + if( s.isEmpty() ) + s = i18n( "<eof>" ); + + m_driver->addProblem( m_driver->currentFileName(), Problem(err.text.arg(s), line, col) ); + } + + return true; +} + +bool Parser::reportError( const QString& msg ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::reportError()" << endl; + if( m_problems < m_maxProblems ){ + ++m_problems; + int line=0, col=0; + const Token& token = lex->lookAhead( 0 ); + lex->getTokenPosition( token, &line, &col ); + + m_driver->addProblem( m_driver->currentFileName(), Problem(msg, line, col) ); + } + + return true; +} + +void Parser::syntaxError() +{ + (void) reportError( Errors::SyntaxError ); +} + +bool Parser::skipUntil( int token ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipUntil()" << endl; + while( !lex->lookAhead(0).isNull() ){ + if( lex->lookAhead(0) == token ) + return true; + + nextToken(); + } + + return false; +} + +bool Parser::skipUntilDeclaration() +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipUntilDeclaration()" << endl; + clearComment(); + + while( !lex->lookAhead(0).isNull() ){ + + switch( lex->lookAhead(0) ){ + case ';': + case '~': + case Token_scope: + case Token_identifier: + case Token_operator: + case Token_char: + case Token_wchar_t: + case Token_bool: + case Token_short: + case Token_int: + case Token_long: + case Token_signed: + case Token_unsigned: + case Token_float: + case Token_double: + case Token_void: + case Token_extern: + case Token_namespace: + case Token_using: + case Token_typedef: + case Token_asm: + case Token_template: + case Token_export: + + case Token_const: // cv + case Token_volatile: // cv + + case Token_public: + case Token_protected: + case Token_private: + case Token_signals: // Qt + case Token_slots: // Qt + return true; + + default: + nextToken(); + } + } + + return false; +} + +bool Parser::skipUntilStatement() +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipUntilStatement() -- token = " << lex->lookAhead(0).text() << endl; + + while( !lex->lookAhead(0).isNull() ){ + switch( lex->lookAhead(0) ){ + case ';': + case '{': + case '}': + case Token_const: + case Token_volatile: + case Token_identifier: + case Token_case: + case Token_default: + case Token_if: + case Token_switch: + case Token_while: + case Token_do: + case Token_for: + case Token_break: + case Token_continue: + case Token_return: + case Token_goto: + case Token_try: + case Token_catch: + case Token_throw: + case Token_char: + case Token_wchar_t: + case Token_bool: + case Token_short: + case Token_int: + case Token_long: + case Token_signed: + case Token_unsigned: + case Token_float: + case Token_double: + case Token_void: + case Token_class: + case Token_struct: + case Token_union: + case Token_enum: + case Token_scope: + case Token_template: + case Token_using: + return true; + + default: + nextToken(); + } + } + + return false; +} + +bool Parser::skip( int l, int r ) +{ + int count = 0; + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + + if( tk == l ) + ++count; + else if( tk == r ) + --count; + else if( l != '{' && (tk == '{' || tk == '}' || tk == ';') ) + return false; + + if( count == 0 ) + return true; + + nextToken(); + } + + return false; +} + +bool Parser::skipCommaExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipCommaExpression()" << endl; + + int start = lex->index(); + + AST::Node expr; + if( !skipExpression(expr) ) + return false; + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !skipExpression(expr) ){ + reportError( i18n("expression expected") ); + return false; + } + } + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::skipExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipExpression()" << endl; + + int start = lex->index(); + + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + + switch( tk ){ + case '(': + skip( '(', ')' ); + nextToken(); + break; + + case '[': + skip( '[', ']' ); + nextToken(); + break; + +#if 0 + case Token_identifier: + nextToken(); + if( lex->lookAhead( 0 ) == Token_identifier ) + return true; + break; +#endif + + case ';': + case ',': + case ']': + case ')': + case '{': + case '}': + case Token_case: + case Token_default: + case Token_if: + case Token_while: + case Token_do: + case Token_for: + case Token_break: + case Token_continue: + case Token_return: + case Token_goto: + { + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + } + return true; + + default: + nextToken(); + } + } + + return false; +} + +bool Parser::parseName( NameAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseName()" << endl; + + GroupAST::Node winDeclSpec; + parseWinDeclSpec( winDeclSpec ); + + int start = lex->index(); + + NameAST::Node ast = CreateNode<NameAST>(); + + if( lex->lookAhead(0) == Token_scope ){ + ast->setGlobal( true ); + nextToken(); + } + + int idx = lex->index(); + + while( true ){ + ClassOrNamespaceNameAST::Node n; + if( !parseUnqualifiedName(n) ) { + return false; + } + + if( lex->lookAhead(0) == Token_scope ){ + nextToken(); + ast->addClassOrNamespaceName( n ); + if( lex->lookAhead(0) == Token_template ) + nextToken(); /// skip optional template #### @todo CHECK + } else { + ast->setUnqualifiedName( n ); + break; + } + } + + if( idx == lex->index() ) + return false; + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTranslationUnit( TranslationUnitAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTranslationUnit()" << endl; + + int start = lex->index(); + + m_problems = 0; + TranslationUnitAST::Node tun = CreateNode<TranslationUnitAST>(); + node = tun; + if( lex->lookAhead(0) == Token_comment ) { + node->setComment( lex->lookAhead(0).text() ); + nextToken(); + } + + while( !lex->lookAhead(0).isNull() ){ + DeclarationAST::Node def; + int startDecl = lex->index(); + if( !parseDeclaration(def) ){ + // error recovery + if( startDecl == lex->index() ) + nextToken(); // skip at least one token + skipUntilDeclaration(); + } + node->addDeclaration( def ); + } + + UPDATE_POS( node, start, lex->index() ); + + // force (0,0) as start position + node->setStartPosition( 0, 0 ); + + return m_problems == 0; +} + +bool Parser::parseDeclaration( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclaration()" << endl; + + int start = lex->index(); + + switch( lex->lookAhead(0) ){ + + case ';': + nextToken(); + return true; + + case Token_extern: + return parseLinkageSpecification( node ); + + case Token_namespace: + return parseNamespace( node ); + + case Token_using: + return parseUsing( node ); + + case Token_typedef: + return parseTypedef( node ); + + case Token_asm: + return parseAsmDefinition( node ); + + case Token_template: + case Token_export: + return parseTemplateDeclaration( node ); + + default: + { + // lex->setIndex( start ); + + if( objcp && parseObjcDef(node) ) + return true; + + lex->setIndex( start ); + + GroupAST::Node storageSpec; + parseStorageClassSpecifier( storageSpec ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + TypeSpecifierAST::Node spec; + AST::Node declarator; + if( parseEnumSpecifier(spec) || parseClassSpecifier(spec) ) { + int line, c; + spec->getEndPosition( &line, &c ); + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify( cv2 ); + spec->setCv2Qualify( cv2 ); + + InitDeclaratorListAST::Node declarators; + parseInitDeclaratorList(declarators); + + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + + ADVANCE( ';', ";" ); + + preparseLineComments( line ); + + ast->setComment( m_commentStore.getCommentInRange( line ) ); + + ast->setStorageSpecifier( storageSpec ); + ast->setTypeSpec( spec ); + ast->setInitDeclaratorList( declarators ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + + lex->setIndex( start ); + return parseDeclarationInternal( node ); + } + + } // end switch +} + +bool Parser::parseLinkageSpecification( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLinkageSpecification()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_extern ){ + return false; + } + nextToken(); + + LinkageSpecificationAST::Node ast = CreateNode<LinkageSpecificationAST>(); + + int startExternType = lex->index(); + if( lex->lookAhead(0) == Token_string_literal ){ + nextToken(); + AST::Node externType = CreateNode<AST>(); + UPDATE_POS( externType, startExternType, lex->index() ); + + ast->setExternType( externType ); + } + + if( lex->lookAhead(0) == '{' ){ + LinkageBodyAST::Node linkageBody; + parseLinkageBody( linkageBody ); + ast->setLinkageBody( linkageBody ); + } else { + DeclarationAST::Node decl; + if( !parseDeclaration(decl) ){ + reportError( i18n("Declaration syntax error") ); + } + ast->setDeclaration( decl ); + } + + UPDATE_POS( ast, start, lex->index() ); + + node = ast; + + return true; +} + +bool Parser::parseLinkageBody( LinkageBodyAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLinkageBody()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != '{' ){ + return false; + } + nextToken(); + + LinkageBodyAST::Node lba = CreateNode<LinkageBodyAST>(); + node = lba; + + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + + if( tk == '}' ) + break; + + DeclarationAST::Node def; + int startDecl = lex->index(); + if( parseDeclaration(def) ){ + node->addDeclaration( def ); + } else { + // error recovery + if( startDecl == lex->index() ) + nextToken(); // skip at least one token + skipUntilDeclaration(); + } + } + + clearComment(); + + if( lex->lookAhead(0) != '}' ){ + reportError( i18n("} expected") ); + } else + nextToken(); + + UPDATE_POS( node, start, lex->index() ); + return true; +} + +bool Parser::parseNamespace( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNamespace()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_namespace ){ + return false; + } + nextToken(); + + int startNamespaceName = lex->index(); + if( lex->lookAhead(0) == Token_identifier ){ + nextToken(); + } + AST::Node namespaceName = CreateNode<AST>(); + UPDATE_POS( namespaceName, startNamespaceName, lex->index() ); + + if ( lex->lookAhead(0) == '=' ) { + // namespace alias + nextToken(); + + NameAST::Node name; + if( parseName(name) ){ + ADVANCE( ';', ";" ); + + NamespaceAliasAST::Node ast = CreateNode<NamespaceAliasAST>(); + ast->setNamespaceName( namespaceName ); + ast->setAliasName( name ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; + } else { + reportError( i18n("namespace expected") ); + return false; + } + } else if( lex->lookAhead(0) != '{' ){ + reportError( i18n("{ expected") ); + return false; + } + + NamespaceAST::Node ast = CreateNode<NamespaceAST>(); + ast->setNamespaceName( namespaceName ); + + LinkageBodyAST::Node linkageBody; + parseLinkageBody( linkageBody ); + + ast->setLinkageBody( linkageBody ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseUsing( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseUsing()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_using ){ + return false; + } + nextToken(); + + if( lex->lookAhead(0) == Token_namespace ){ + if( !parseUsingDirective(node) ){ + return false; + } + UPDATE_POS( node, start, lex->index() ); + return true; + } + + UsingAST::Node ast = CreateNode<UsingAST>(); + + int startTypeName = lex->index(); + if( lex->lookAhead(0) == Token_typename ){ + nextToken(); + AST::Node tn = CreateNode<AST>(); + UPDATE_POS( tn, startTypeName, lex->index() ); + ast->setTypeName( tn ); + } + + NameAST::Node name; + if( !parseName(name) ) + return false; + + ast->setName( name ); + + ADVANCE( ';', ";" ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseUsingDirective( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseUsingDirective()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_namespace ){ + return false; + } + nextToken(); + + NameAST::Node name; + if( !parseName(name) ){ + reportError( i18n("Namespace name expected") ); + return false; + } + + ADVANCE( ';', ";" ); + + UsingDirectiveAST::Node ast = CreateNode<UsingDirectiveAST>(); + ast->setName( name ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseOperatorFunctionId( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseOperatorFunctionId()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_operator ){ + return false; + } + nextToken(); + + AST::Node op; + if( parseOperator(op) ){ + AST::Node asn = CreateNode<AST>(); + node = asn; + UPDATE_POS( node, start, lex->index() ); + return true; + } else { + // parse cast operator + GroupAST::Node cv; + parseCvQualify(cv); + + TypeSpecifierAST::Node spec; + if( !parseSimpleTypeSpecifier(spec) ){ + syntaxError(); + return false; + } + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify(cv2); + spec->setCv2Qualify( cv2 ); + + AST::Node ptrOp; + while( parsePtrOperator(ptrOp) ) + ; + + AST::Node asn = CreateNode<AST>(); + node = asn; + UPDATE_POS( node, start, lex->index() ); + return true; + } +} + +bool Parser::parseTemplateArgumentList( TemplateArgumentListAST::Node& node, bool reportError ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateArgumentList()" << endl; + + int start = lex->index(); + + TemplateArgumentListAST::Node ast = CreateNode<TemplateArgumentListAST>(); + + AST::Node templArg; + if( !parseTemplateArgument(templArg) ) + return false; + ast->addArgument( templArg ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !parseTemplateArgument(templArg) ){ + if( reportError ){ + syntaxError(); + break; + } else + return false; + } + ast->addArgument( templArg ); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTypedef( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypedef()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_typedef ){ + return false; + } + nextToken(); + + TypeSpecifierAST::Node spec; + if( !parseTypeSpecifierOrClassSpec(spec) ){ + reportError( i18n("Need a type specifier to declare") ); + return false; + } + + InitDeclaratorListAST::Node declarators; + if( !parseInitDeclaratorList(declarators) ){ + //reportError( i18n("Need an identifier to declare") ); + //return false; + } + + TypedefAST::Node ast = CreateNode<TypedefAST>(); + + + if( comment() ) { + ast->setComment( comment() ); + clearComment(); + + preparseLineComments( currentLine() ); + + if( comment() ) { + ast->addComment( comment() ); + clearComment(); + } + } + + ADVANCE( ';', ";" ); + + ast->setTypeSpec( spec ); + ast->setInitDeclaratorList( declarators ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseAsmDefinition( DeclarationAST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAsmDefinition()" << endl; + + ADVANCE( Token_asm, "asm" ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + skip( '(', ')' ); + ADVANCE( ')', ")" ); + ADVANCE( ';', ';' ); + + return true; +} + +bool Parser::parseTemplateDeclaration( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateDeclaration()" << endl; + + int start = lex->index(); + + AST::Node exp; + + int startExport = lex->index(); + if( lex->lookAhead(0) == Token_export ){ + nextToken(); + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, startExport, lex->index() ); + exp = n; + } + + if( lex->lookAhead(0) != Token_template ){ + return false; + } + nextToken(); + + TemplateParameterListAST::Node params; + if( lex->lookAhead(0) == '<' ){ + nextToken(); + parseTemplateParameterList( params ); + + ADVANCE( '>', ">" ); + } + + DeclarationAST::Node def; + if( !parseDeclaration(def) ){ + reportError( i18n("expected a declaration") ); + } + + TemplateDeclarationAST::Node ast = CreateNode<TemplateDeclarationAST>(); + ast->setExported( exp ); + ast->setTemplateParameterList( params ); + ast->setDeclaration( def ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseOperator( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseOperator()" << endl; + QString text = lex->lookAhead(0).text(); + + switch( lex->lookAhead(0) ){ + case Token_new: + case Token_delete: + nextToken(); + if( lex->lookAhead(0) == '[' && lex->lookAhead(1) == ']' ){ + nextToken(); + nextToken(); + text += "[]"; + } + return true; + + case '+': + case '-': + case '*': + case '/': + case '%': + case '^': + case '&': + case '|': + case '~': + case '!': + case '=': + case '<': + case '>': + case ',': + case Token_assign: + case Token_shift: + case Token_eq: + case Token_not_eq: + case Token_leq: + case Token_geq: + case Token_and: + case Token_or: + case Token_incr: + case Token_decr: + case Token_ptrmem: + case Token_arrow: + nextToken(); + return true; + + default: + if( lex->lookAhead(0) == '(' && lex->lookAhead(1) == ')' ){ + nextToken(); + nextToken(); + return true; + } else if( lex->lookAhead(0) == '[' && lex->lookAhead(1) == ']' ){ + nextToken(); + nextToken(); + return true; + } + } + + return false; +} + +bool Parser::parseCvQualify( GroupAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCvQualify()" << endl; + + int start = lex->index(); + + GroupAST::Node ast = CreateNode<GroupAST>(); + + int n = 0; + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + if( tk == Token_const || tk == Token_volatile ){ + ++n; + int startWord = lex->index(); + nextToken(); + AST::Node word = CreateNode<AST>(); + UPDATE_POS( word, startWord, lex->index() ); + ast->addNode( word ); + } else + break; + } + + if( n == 0 ) + return false; + + + ////kdDebug(9007)<< "-----------------> token = " << lex->lookAhead(0).text() << endl; + UPDATE_POS( ast, start, lex->index() ); + + node = ast; + return true; +} + +bool Parser::parseSimpleTypeSpecifier( TypeSpecifierAST::Node& node ) +{ + int start = lex->index(); + bool isIntegral = false; + bool done = false; + + while( !done ){ + + switch( lex->lookAhead(0) ){ + case Token_char: + case Token_wchar_t: + case Token_bool: + case Token_short: + case Token_int: + case Token_long: + case Token_signed: + case Token_unsigned: + case Token_float: + case Token_double: + case Token_void: + isIntegral = true; + nextToken(); + break; + + default: + done = true; + } + } + + TypeSpecifierAST::Node ast = CreateNode<TypeSpecifierAST>(); + if( isIntegral ){ + ClassOrNamespaceNameAST::Node cl = CreateNode<ClassOrNamespaceNameAST>(); + + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, start, lex->index() ); + cl->setName( n ); + UPDATE_POS( cl, start, lex->index() ); + + NameAST::Node name = CreateNode<NameAST>(); + name->setUnqualifiedName( cl ); + UPDATE_POS( name, start, lex->index() ); + ast->setName( name ); + + } else { + NameAST::Node name; + if( !parseName(name) ){ + lex->setIndex( start ); + return false; + } + ast->setName( name ); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parsePtrOperator( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parsePtrOperator()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) == '&' ){ + nextToken(); + } else if( lex->lookAhead(0) == '*' ){ + nextToken(); + } else { + int index = lex->index(); + AST::Node memPtr; + if( !parsePtrToMember(memPtr) ){ + lex->setIndex( index ); + return false; + } + } + + GroupAST::Node cv; + parseCvQualify( cv ); + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseTemplateArgument( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateArgument()" << endl; + + int start = lex->index(); + if( parseTypeId(node) ){ + if( lex->lookAhead(0) == ',' || lex->lookAhead(0) == '>' ) + return true; + } + + lex->setIndex( start ); + if( !parseLogicalOrExpression(node, true) ){ + return false; + } + + return true; +} + +bool Parser::parseTypeSpecifier( TypeSpecifierAST::Node& spec ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypeSpecifier()" << endl; + + GroupAST::Node cv; + parseCvQualify( cv ); + + if( parseElaboratedTypeSpecifier(spec) || parseSimpleTypeSpecifier(spec) ){ + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify( cv2 ); + spec->setCv2Qualify( cv2 ); + + return true; + } + + return false; +} + +bool Parser::parseDeclarator( DeclaratorAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclarator()" << endl; + + int start = lex->index(); + + DeclaratorAST::Node ast = CreateNode<DeclaratorAST>(); + + DeclaratorAST::Node decl; + NameAST::Node declId; + + AST::Node ptrOp; + while( parsePtrOperator(ptrOp) ){ + ast->addPtrOp( ptrOp ); + } + + if( lex->lookAhead(0) == '(' ){ + nextToken(); + + if( !parseDeclarator(decl) ){ + return false; + } + ast->setSubDeclarator( decl ); + + if( lex->lookAhead(0) != ')'){ + return false; + } + nextToken(); + } else { + + if( lex->lookAhead(0) == ':' ){ + // unnamed bitfield + } else if( parseDeclaratorId(declId) ){ + ast->setDeclaratorId( declId ); + } else { + lex->setIndex( start ); + return false; + } + + if( lex->lookAhead(0) == ':' ){ + nextToken(); + AST::Node expr; + if( !parseConstantExpression(expr) ){ + reportError( i18n("Constant expression expected") ); + } + goto update_pos; + } + } + + { + bool isVector = true; + + while( lex->lookAhead(0) == '[' ){ + int startArray = lex->index(); + nextToken(); + AST::Node expr; + parseCommaExpression( expr ); + + ADVANCE( ']', "]" ); + AST::Node array = CreateNode<AST>(); + UPDATE_POS( array, startArray, lex->index() ); + ast->addArrayDimension( array ); + isVector = true; + } + + bool skipParen = false; + if( lex->lookAhead(0) == Token_identifier && lex->lookAhead(1) == '(' && lex->lookAhead(2) == '(' ){ + nextToken(); + nextToken(); + skipParen = true; + } + + if( ast->subDeclarator() && (!isVector || lex->lookAhead(0) != '(') ){ + lex->setIndex( start ); + return false; + } + + int index = lex->index(); + if( lex->lookAhead(0) == '(' ){ + nextToken(); + + ParameterDeclarationClauseAST::Node params; + if( !parseParameterDeclarationClause(params) ){ + ////kdDebug(9007)<< "----------------------> not a parameter declaration, maybe an initializer!?" << endl; + lex->setIndex( index ); + goto update_pos; + } + ast->setParameterDeclarationClause( params ); + + if( lex->lookAhead(0) != ')' ){ + lex->setIndex( index ); + goto update_pos; + } + + nextToken(); // skip ')' + + int startConstant = lex->index(); + if( lex->lookAhead(0) == Token_const ){ + nextToken(); + AST::Node constant = CreateNode<AST>(); + UPDATE_POS( constant, startConstant, lex->index() ); + ast->setConstant( constant ); + } + + GroupAST::Node except; + if( parseExceptionSpecification(except) ){ + ast->setExceptionSpecification( except ); + } + } + + if( skipParen ){ + if( lex->lookAhead(0) != ')' ){ + reportError( i18n("')' expected") ); + } else + nextToken(); + } + + } + +update_pos: + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseAbstractDeclarator( DeclaratorAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclarator()" << endl; + int start = lex->index(); + + DeclaratorAST::Node ast = CreateNode<DeclaratorAST>(); + + DeclaratorAST::Node decl; + NameAST::Node declId; + + AST::Node ptrOp; + while( parsePtrOperator(ptrOp) ){ + ast->addPtrOp( ptrOp ); + } + + if( lex->lookAhead(0) == '(' ){ + nextToken(); + + if( !parseAbstractDeclarator(decl) ){ + return false; + } + ast->setSubDeclarator( decl ); + + if( lex->lookAhead(0) != ')'){ + return false; + } + nextToken(); + } + + { + + while( lex->lookAhead(0) == '[' ){ + int startArray = lex->index(); + nextToken(); + AST::Node expr; + skipCommaExpression( expr ); + + ADVANCE( ']', "]" ); + AST::Node array = CreateNode<AST>(); + UPDATE_POS( array, startArray, lex->index() ); + ast->addArrayDimension( array ); + } + + bool skipParen = false; + if( lex->lookAhead(0) == Token_identifier && lex->lookAhead(1) == '(' && lex->lookAhead(2) == '(' ){ + nextToken(); + nextToken(); + skipParen = true; + } + + int index = lex->index(); + if( lex->lookAhead(0) == '(' ){ + nextToken(); + + ParameterDeclarationClauseAST::Node params; + if( !parseParameterDeclarationClause(params) ){ + lex->setIndex( index ); + goto UPDATE_POS; + } + ast->setParameterDeclarationClause( params ); + + if( lex->lookAhead(0) != ')' ){ + lex->setIndex( index ); + goto UPDATE_POS; + } else + nextToken(); + + int startConstant = lex->index(); + if( lex->lookAhead(0) == Token_const ){ + nextToken(); + AST::Node constant = CreateNode<AST>(); + UPDATE_POS( constant, startConstant, lex->index() ); + ast->setConstant( constant ); + } + + GroupAST::Node except; + if( parseExceptionSpecification(except) ){ + ast->setExceptionSpecification( except ); + } + } + + if( skipParen ){ + if( lex->lookAhead(0) != ')' ){ + reportError( i18n("')' expected") ); + } else + nextToken(); + } + + } + +UPDATE_POS: + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseEnumSpecifier( TypeSpecifierAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseEnumSpecifier()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_enum ){ + return false; + } + + nextToken(); + + Comment c = comment(); + clearComment(); + NameAST::Node name; + parseName( name ); + + if( lex->lookAhead(0) != '{' ){ + lex->setIndex( start ); + return false; + } + nextToken(); + + EnumSpecifierAST::Node ast = CreateNode<EnumSpecifierAST>(); + ast->setName( name ); + + ast->setComment( c ); + + EnumeratorAST::Node enumerator; + if( parseEnumerator(enumerator) ){ + ast->addEnumerator( enumerator ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !parseEnumerator(enumerator) ){ + //reportError( i18n("Enumerator expected") ); + break; + } + + ast->addEnumerator( enumerator ); + } + } + + clearComment( ); + + if( lex->lookAhead(0) != '}' ) + reportError( i18n("} missing") ); + else + nextToken(); + + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTemplateParameterList( TemplateParameterListAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateParameterList()" << endl; + + int start = lex->index(); + + TemplateParameterListAST::Node ast = CreateNode<TemplateParameterListAST>(); + + TemplateParameterAST::Node param; + if( !parseTemplateParameter(param) ){ + return false; + } + ast->addTemplateParameter( param ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !parseTemplateParameter(param) ){ + syntaxError(); + break; + } else { + ast->addTemplateParameter( param ); + } + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTemplateParameter( TemplateParameterAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTemplateParameter()" << endl; + + int start = lex->index(); + TemplateParameterAST::Node ast = CreateNode<TemplateParameterAST>(); + + TypeParameterAST::Node typeParameter; + ParameterDeclarationAST::Node param; + + int tk = lex->lookAhead( 0 ); + + if( (tk == Token_class || tk == Token_typename || tk == Token_template) && parseTypeParameter(typeParameter) ){ + ast->setTypeParameter( typeParameter ); + goto ok; + } + + if( !parseParameterDeclaration(param) ) + return false; + ast->setTypeValueParameter( param ); + +ok: + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseTypeParameter( TypeParameterAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypeParameter()" << endl; + + int start = lex->index(); + TypeParameterAST::Node ast = CreateNode<TypeParameterAST>(); + + AST_FROM_TOKEN( kind, lex->index() ); + ast->setKind( kind ); + + switch( lex->lookAhead(0) ){ + + case Token_class: + case Token_typename: + { + nextToken(); // skip class + + // parse optional name + NameAST::Node name; + if( parseName(name) ){ + ast->setName( name ); + if( lex->lookAhead(0) == '=' ){ + nextToken(); + + AST::Node typeId; + if( !parseTypeId(typeId) ){ + syntaxError(); + return false; + } + ast->setTypeId( typeId ); + } + } + } + break; + + case Token_template: + { + nextToken(); // skip template + ADVANCE( '<', '<' ); + + TemplateParameterListAST::Node params; + if( !parseTemplateParameterList(params) ){ + return false; + } + ast->setTemplateParameterList( params ); + + ADVANCE( '>', ">" ); + + if( lex->lookAhead(0) == Token_class ) + nextToken(); + + // parse optional name + NameAST::Node name; + if( parseName(name) ){ + ast->setName( name ); + if( lex->lookAhead(0) == '=' ){ + nextToken(); + + AST::Node typeId; + if( !parseTypeId(typeId) ){ + syntaxError(); + return false; + } + ast->setTypeId( typeId ); + } + } + + if( lex->lookAhead(0) == '=' ){ + nextToken(); + + NameAST::Node templ_name; + parseName( templ_name ); + } + } + break; + + default: + return false; + + } // end switch + + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseStorageClassSpecifier( GroupAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseStorageClassSpecifier()" << endl; + + int start = lex->index(); + GroupAST::Node ast = CreateNode<GroupAST>(); + + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + if( tk == Token_friend || tk == Token_auto || tk == Token_register || tk == Token_static || + tk == Token_extern || tk == Token_mutable ){ + int startNode = lex->index(); + nextToken(); + + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, startNode, lex->index() ); + ast->addNode( n ); + } else + break; + } + + if( ast->nodeList().count() == 0 ) + return false; + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseFunctionSpecifier( GroupAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseFunctionSpecifier()" << endl; + + int start = lex->index(); + GroupAST::Node ast = CreateNode<GroupAST>(); + + while( !lex->lookAhead(0).isNull() ){ + int tk = lex->lookAhead( 0 ); + if( tk == Token_inline || tk == Token_virtual || tk == Token_explicit ){ + int startNode = lex->index(); + nextToken(); + + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, startNode, lex->index() ); + ast->addNode( n ); + } else { + break; + } + } + + if( ast->nodeList().count() == 0 ) + return false; + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseTypeId( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypeId()" << endl; + + /// @todo implement the AST for typeId + int start = lex->index(); + AST::Node ast = CreateNode<AST>(); + + TypeSpecifierAST::Node spec; + if( !parseTypeSpecifier(spec) ){ + return false; + } + + DeclaratorAST::Node decl; + parseAbstractDeclarator( decl ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseInitDeclaratorList( InitDeclaratorListAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitDeclaratorList()" << endl; + + int start = lex->index(); + + InitDeclaratorListAST::Node ast = CreateNode<InitDeclaratorListAST>(); + InitDeclaratorAST::Node decl; + + if( !parseInitDeclarator(decl) ){ + return false; + } + ast->addInitDeclarator( decl ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !parseInitDeclarator(decl) ){ + syntaxError(); + break; + } + ast->addInitDeclarator( decl ); + } + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitDeclaratorList() -- end" << endl; + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseParameterDeclarationClause( ParameterDeclarationClauseAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseParameterDeclarationClause()" << endl; + + int start = lex->index(); + + ParameterDeclarationClauseAST::Node ast = CreateNode<ParameterDeclarationClauseAST>(); + + ParameterDeclarationListAST::Node params; + if( !parseParameterDeclarationList(params) ){ + + if ( lex->lookAhead(0) == ')' ) + goto good; + + if( lex->lookAhead(0) == Token_ellipsis && lex->lookAhead(1) == ')' ){ + AST_FROM_TOKEN( ellipsis, lex->index() ); + ast->setEllipsis( ellipsis ); + nextToken(); + goto good; + } + return false; + } + + if( lex->lookAhead(0) == Token_ellipsis ){ + AST_FROM_TOKEN( ellipsis, lex->index() ); + ast->setEllipsis( ellipsis ); + nextToken(); + } + +good: + ast->setParameterDeclarationList( params ); + + /// @todo add ellipsis + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +void Parser::nextToken( bool skipComm ) { + lex->nextToken(); + if( skipComm ) { + if( lex->lookAhead(0) == Token_comment ) { + processComment(); + nextToken(); + } + } +} + + +Comment Parser::comment() { + return m_commentStore.latestComment(); +} + + +void Parser::preparseLineComments( int l ) { + for( int a = 0; a < 40; a++ ) { + if( lex->lookAhead(a).isNull() ) break; + int line, col; + lex->lookAhead( a ).getStartPosition( &line, &col ); + if( line < l ) { + continue; + } else if( line == l ) { + if( lex->lookAhead( a ) == Token_comment ) { + processComment( a ); + } + } else { + break; + } + } +} + + +void Parser::processComment( int offset ) { + int line, col; + lex->lookAhead( offset ).getStartPosition( &line, &col ); + m_commentStore.addComment( Comment( lex->lookAhead(offset).text(), line ) ); +} + +template<class Type> +void Parser::eventuallyTakeComment( int startLn, int endLn, Type& ast ) { + if( comment().line() >= startLn && comment().line() <= endLn ) { + if( &(*ast) ) { + if( comment() ) { + ast->setComment( comment() ); + } + } + + clearComment(); + } +} + +template<class Type> +void Parser::eventuallyTakeComment( Type& ast ) { + if( &(*ast) && comment() ) { + ast->setComment( comment() ); + } + + clearComment(); +} + +void Parser::clearComment( ) { + + m_commentStore.clear(); + +} + +int Parser::currentLine() { + int ln, col; + lex->lookAhead( 0 ).getStartPosition( &ln, &col ); + return ln; +} + +bool Parser::parseParameterDeclarationList( ParameterDeclarationListAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseParameterDeclarationList()" << endl; + + int start = lex->index(); + + ParameterDeclarationListAST::Node ast = CreateNode<ParameterDeclarationListAST>(); + + ParameterDeclarationAST::Node param; + if( !parseParameterDeclaration(param) ){ + lex->setIndex( start ); + return false; + } + ast->addParameter( param ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( lex->lookAhead(0) == Token_ellipsis ) + break; + + if( !parseParameterDeclaration(param) ){ + lex->setIndex( start ); + return false; + } + ast->addParameter( param ); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseParameterDeclaration( ParameterDeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseParameterDeclaration()" << endl; + + int start = lex->index(); + + // parse decl spec + TypeSpecifierAST::Node spec; + if( !parseTypeSpecifier(spec) ){ + lex->setIndex( start ); + return false; + } + + int index = lex->index(); + + DeclaratorAST::Node decl; + if( !parseDeclarator(decl) ){ + lex->setIndex( index ); + + // try with abstract declarator + if( !parseAbstractDeclarator(decl) ) + return false; + } + + AST::Node expr; + if( lex->lookAhead(0) == '=' ){ + nextToken(); + if( !parseLogicalOrExpression(expr,true) ){ + //reportError( i18n("Expression expected") ); + } + } + + ParameterDeclarationAST::Node ast = CreateNode<ParameterDeclarationAST>(); + ast->setTypeSpec( spec ); + ast->setDeclarator( decl ); + ast->setExpression( expr ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseClassSpecifier( TypeSpecifierAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseClassSpecifier()" << endl; + + int start = lex->index(); + + AST::Node classKey; + int classKeyStart = lex->index(); + + int kind = lex->lookAhead( 0 ); + if( kind == Token_class || kind == Token_struct || kind == Token_union ){ + AST::Node asn = CreateNode<AST>(); + classKey = asn; + nextToken(); + UPDATE_POS( classKey, classKeyStart, lex->index() ); + } else { + return false; + } + + GroupAST::Node winDeclSpec; + parseWinDeclSpec( winDeclSpec ); + + while( lex->lookAhead(0) == Token_identifier && lex->lookAhead(1) == Token_identifier ) + nextToken(); + + NameAST::Node name; + parseName( name ); + + BaseClauseAST::Node bases; + if( lex->lookAhead(0) == ':' ){ + if( !parseBaseClause(bases) ){ + skipUntil( '{' ); + } + } + + if( lex->lookAhead(0) != '{' ){ + lex->setIndex( start ); + return false; + } + + ClassSpecifierAST::Node ast = CreateNode<ClassSpecifierAST>(); + + eventuallyTakeComment( ast ); + + ADVANCE( '{', '{' ); + + + ast->setWinDeclSpec( winDeclSpec ); + ast->setClassKey( classKey ); + ast->setName( name ); + ast->setBaseClause( bases ); + + + while( !lex->lookAhead(0).isNull() ){ + if( lex->lookAhead(0) == '}' ) + break; + + DeclarationAST::Node memSpec; + int startDecl = lex->index(); + if( !parseMemberSpecification(memSpec) ){ + if( startDecl == lex->index() ) + nextToken(); // skip at least one token + skipUntilDeclaration(); + } else + ast->addDeclaration( memSpec ); + } + + clearComment(); + + if( lex->lookAhead(0) != '}' ){ + reportError( i18n("} missing") ); + } else + nextToken(); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseAccessSpecifier( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAccessSpecifier()" << endl; + + int start = lex->index(); + + switch( lex->lookAhead(0) ){ + case Token_public: + case Token_protected: + case Token_private: { + AST::Node asn = CreateNode<AST>(); + node = asn; + nextToken(); + UPDATE_POS( node, start, lex->index() ); + return true; + } + } + + return false; +} + +bool Parser::parseMemberSpecification( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMemberSpecification()" << endl; + + int start = lex->index(); + + AST::Node access; + + if( lex->lookAhead(0) == ';' ){ + nextToken(); + return true; + } else if( lex->lookAhead(0) == Token_Q_OBJECT || lex->lookAhead(0) == Token_K_DCOP ){ + nextToken(); + return true; + } else if( lex->lookAhead(0) == Token_signals || lex->lookAhead(0) == Token_k_dcop || lex->lookAhead(0) == Token_k_dcop_signals ){ + AccessDeclarationAST::Node ast = CreateNode<AccessDeclarationAST>(); + nextToken(); + AST::Node n = CreateNode<AST>(); + UPDATE_POS( n, start, lex->index() ); + ast->addAccess( n ); + ADVANCE( ':', ":" ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; + } else if( parseTypedef(node) ){ + return true; + } else if( parseUsing(node) ){ + return true; + } else if( parseTemplateDeclaration(node) ){ + return true; + } else if( parseAccessSpecifier(access) ){ + AccessDeclarationAST::Node ast = CreateNode<AccessDeclarationAST>(); + ast->addAccess( access ); + + int startSlot = lex->index(); + if( lex->lookAhead(0) == Token_slots ){ + nextToken(); + AST::Node sl = CreateNode<AST>(); + UPDATE_POS( sl, startSlot, lex->index() ); + ast->addAccess( sl ); + } + ADVANCE( ':', ":" ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; + } + + lex->setIndex( start ); + + GroupAST::Node storageSpec; + parseStorageClassSpecifier( storageSpec ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + TypeSpecifierAST::Node spec; + if( parseEnumSpecifier(spec) || parseClassSpecifier(spec) ){ + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify( cv2 ); + spec->setCv2Qualify( cv2 ); + + InitDeclaratorListAST::Node declarators; + parseInitDeclaratorList( declarators ); + ADVANCE( ';', ";" ); + + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + ast->setTypeSpec( spec ); + ast->setInitDeclaratorList( declarators ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + + lex->setIndex( start ); + return parseDeclarationInternal( node ); +} + +bool Parser::parseCtorInitializer( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCtorInitializer()" << endl; + + if( lex->lookAhead(0) != ':' ){ + return false; + } + nextToken(); + + AST::Node inits; + if( !parseMemInitializerList(inits) ){ + reportError( i18n("Member initializers expected") ); + } + + return true; +} + +bool Parser::parseElaboratedTypeSpecifier( TypeSpecifierAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseElaboratedTypeSpecifier()" << endl; + + int start = lex->index(); + + int tk = lex->lookAhead( 0 ); + if( tk == Token_class || + tk == Token_struct || + tk == Token_union || + tk == Token_enum || + tk == Token_typename ) + { + AST::Node kind = CreateNode<AST>(); + nextToken(); + UPDATE_POS( kind, start, lex->index() ); + + NameAST::Node name; + + if( parseName(name) ){ + ElaboratedTypeSpecifierAST::Node ast = CreateNode<ElaboratedTypeSpecifierAST>(); + ast->setKind( kind ); + ast->setName( name ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + } + + lex->setIndex( start ); + return false; +} + +bool Parser::parseDeclaratorId( NameAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclaratorId()" << endl; + return parseName( node ); +} + +bool Parser::parseExceptionSpecification( GroupAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseExceptionSpecification()" << endl; + + if( lex->lookAhead(0) != Token_throw ){ + return false; + } + nextToken(); + + ADVANCE( '(', "(" ); + if( lex->lookAhead(0) == Token_ellipsis ){ + // extension found in MSVC++ 7.x headers + int start = lex->index(); + GroupAST::Node ast = CreateNode<GroupAST>(); + AST_FROM_TOKEN( ellipsis, lex->index() ); + ast->addNode( ellipsis ); + nextToken(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + } else if( lex->lookAhead(0) == ')' ) { + node = CreateNode<GroupAST>(); + } else { + parseTypeIdList( node ); + } + ADVANCE( ')', ")" ); + + return true; +} + +bool Parser::parseEnumerator( EnumeratorAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseEnumerator()" << endl; + + int start = lex->index(); + + if( lex->lookAhead(0) != Token_identifier ){ + return false; + } + + nextToken(); + + + EnumeratorAST::Node ena = CreateNode<EnumeratorAST>(); + node = ena; + + AST::Node id = CreateNode<AST>(); + UPDATE_POS( id, start, lex->index() ); + node->setId( id ); + int line = currentLine(); + + if( lex->lookAhead(0) == '=' ){ + nextToken(); + + AST::Node expr; + line = currentLine(); + if( !parseConstantExpression(expr) ){ + reportError( i18n("Constant expression expected") ); + } + node->setExpr( expr ); + } + + UPDATE_POS( node, start, lex->index() ); + + preparseLineComments( line ); + + node->setComment( m_commentStore.getCommentInRange( line ) ); + + return true; +} + +bool Parser::parseInitDeclarator( InitDeclaratorAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitDeclarator()" << endl; + + int start = lex->index(); + + DeclaratorAST::Node decl; + AST::Node init; + if( !parseDeclarator(decl) ){ + return false; + } + + parseInitializer( init ); + + InitDeclaratorAST::Node ast = CreateNode<InitDeclaratorAST>(); + ast->setDeclarator( decl ); + ast->setInitializer( init ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + + +bool Parser::parseBaseClause( BaseClauseAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseBaseClause()" << endl; + + int start = lex->index(); + if( lex->lookAhead(0) != ':' ){ + return false; + } + nextToken(); + + BaseClauseAST::Node bca = CreateNode<BaseClauseAST>(); + + BaseSpecifierAST::Node baseSpec; + if( parseBaseSpecifier(baseSpec) ){ + bca->addBaseSpecifier( baseSpec ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !parseBaseSpecifier(baseSpec) ){ + reportError( i18n("Base class specifier expected") ); + return false; + } + bca->addBaseSpecifier( baseSpec ); + } + } else + return false; + + UPDATE_POS( bca, start, lex->index() ); + node = bca; + + return true; +} + +bool Parser::parseInitializer( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitializer()" << endl; + + if( lex->lookAhead(0) == '=' ){ + nextToken(); + + AST::Node init; + if( !parseInitializerClause(node) ){ + reportError( i18n("Initializer clause expected") ); + return false; + } + } else if( lex->lookAhead(0) == '(' ){ + nextToken(); + AST::Node expr; + skipCommaExpression( expr ); + + ADVANCE( ')', ")" ); + } + + return false; +} + +bool Parser::parseMemInitializerList( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMemInitializerList()" << endl; + + AST::Node init; + if( !parseMemInitializer(init) ){ + return false; + } + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( parseMemInitializer(init) ){ + } else { + break; + } + } + + return true; +} + +bool Parser::parseMemInitializer( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMemInitializer()" << endl; + + NameAST::Node initId; + if( !parseMemInitializerId(initId) ){ + reportError( i18n("Identifier expected") ); + return false; + } + ADVANCE( '(', '(' ); + AST::Node expr; + skipCommaExpression( expr ); + ADVANCE( ')', ')' ); + + return true; +} + +bool Parser::parseTypeIdList( GroupAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTypeIdList()" << endl; + + int start = lex->index(); + + AST::Node typeId; + if( !parseTypeId(typeId) ){ + return false; + } + + GroupAST::Node ast = CreateNode<GroupAST>(); + ast->addNode( typeId ); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + if( parseTypeId(typeId) ){ + ast->addNode( typeId ); + } else { + reportError( i18n("Type id expected") ); + break; + } + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseBaseSpecifier( BaseSpecifierAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseBaseSpecifier()" << endl; + + int start = lex->index(); + BaseSpecifierAST::Node ast = CreateNode<BaseSpecifierAST>(); + + AST::Node access; + if( lex->lookAhead(0) == Token_virtual ){ + AST_FROM_TOKEN( virt, lex->index() ); + ast->setIsVirtual( virt ); + + nextToken(); + + parseAccessSpecifier( access ); + } else { + parseAccessSpecifier( access ); + + if( lex->lookAhead(0) == Token_virtual ){ + AST_FROM_TOKEN( virt, lex->index() ); + ast->setIsVirtual( virt ); + nextToken(); + } + } + + NameAST::Node name; + if( !parseName(name) ){ + reportError( i18n("Class name expected") ); + } + + ast->setAccess( access ); + ast->setName( name ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + + +bool Parser::parseInitializerClause( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInitializerClause()" << endl; + + if( lex->lookAhead(0) == '{' ){ + if( !skip('{','}') ){ + reportError( i18n("} missing") ); + } else { + clearComment(); + nextToken(); + } + } else { + if( !parseAssignmentExpression(node) ){ + //reportError( i18n("Expression expected") ); + } + } + + return true; +} + +bool Parser::parseMemInitializerId( NameAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMemInitializerId()" << endl; + + return parseName( node ); +} + +bool Parser::parsePtrToMember( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parsePtrToMember()" << endl; + + if( lex->lookAhead(0) == Token_scope ){ + nextToken(); + } + + while( lex->lookAhead(0) == Token_identifier ){ + nextToken(); + + if( lex->lookAhead(0) == Token_scope && lex->lookAhead(1) == '*' ){ + nextToken(); // skip :: + nextToken(); // skip * + return true; + } else + break; + } + + return false; +} + +bool Parser::parseUnqualifiedName( ClassOrNamespaceNameAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseUnqualifiedName()" << endl; + + int start = lex->index(); + bool isDestructor = false; + + ClassOrNamespaceNameAST::Node ast = CreateNode<ClassOrNamespaceNameAST>(); + + if( lex->lookAhead(0) == Token_identifier ){ + int startName = lex->index(); + AST::Node n = CreateNode<AST>(); + nextToken(); + UPDATE_POS( n, startName, lex->index() ); + ast->setName( n ); + } else if( lex->lookAhead(0) == '~' && lex->lookAhead(1) == Token_identifier ){ + int startName = lex->index(); + AST::Node n = CreateNode<AST>(); + nextToken(); // skip ~ + nextToken(); // skip classname + UPDATE_POS( n, startName, lex->index() ); + ast->setName( n ); + isDestructor = true; + } else if( lex->lookAhead(0) == Token_operator ){ + AST::Node n; + if( !parseOperatorFunctionId(n) ) + return false; + ast->setName( n ); + } else { + return false; + } + + if( !isDestructor ){ + + int index = lex->index(); + + if( lex->lookAhead(0) == '<' ){ + nextToken(); + + // optional template arguments + TemplateArgumentListAST::Node args; + parseTemplateArgumentList( args ); + + if( lex->lookAhead(0) != '>' ){ + lex->setIndex( index ); + } else { + nextToken(); + ast->setTemplateArgumentList( args ); + } + } + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseStringLiteral( AST::Node& /*node*/ ) +{ + while( !lex->lookAhead(0).isNull() ) { + if( lex->lookAhead(0) == Token_identifier && + lex->lookAhead(0).text() == "L" && lex->lookAhead(1) == Token_string_literal ) { + + nextToken(); + nextToken(); + } else if( lex->lookAhead(0) == Token_string_literal ) { + nextToken(); + } else + return false; + } + return true; +} + +bool Parser::skipExpressionStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::skipExpressionStatement()" << endl; + + int start = lex->index(); + + AST::Node expr; + skipCommaExpression( expr ); + + ADVANCE( ';', ";" ); + + ExpressionStatementAST::Node ast = CreateNode<ExpressionStatementAST>(); + ast->setExpression( expr ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseStatement( StatementAST::Node& node ) // thanks to fiore@8080.it ;) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseStatement()" << endl; + switch( lex->lookAhead(0) ){ + + case Token_while: + return parseWhileStatement( node ); + + case Token_do: + return parseDoStatement( node ); + + case Token_for: + return parseForStatement( node ); + + case Token_foreach: + return parseForEachStatement( node ); + + case Token_if: + return parseIfStatement( node ); + + case Token_switch: + return parseSwitchStatement( node ); + + case Token_try: + return parseTryBlockStatement( node ); + + case Token_case: + case Token_default: + return parseLabeledStatement( node ); + + case Token_break: + case Token_continue: + nextToken(); + ADVANCE( ';', ";" ); + return true; + + case Token_goto: + nextToken(); + ADVANCE( Token_identifier, "identifier" ); + ADVANCE( ';', ";" ); + return true; + + case Token_return: + { + nextToken(); + AST::Node expr; + skipCommaExpression( expr ); + ADVANCE( ';', ";" ); + } + return true; + + case '{': + return parseCompoundStatement( node ); + + case Token_identifier: + if( parseLabeledStatement(node) ) + return true; + break; + } + + ////kdDebug(9007)<< "------------> try with declaration statement" << endl; + if ( parseDeclarationStatement(node) ) + return true; + + return skipExpressionStatement( node ); +} + +bool Parser::parseCondition( ConditionAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCondition()" << endl; + + int start = lex->index(); + + ConditionAST::Node ast = CreateNode<ConditionAST>(); + + TypeSpecifierAST::Node spec; + if( parseTypeSpecifier(spec) ){ + DeclaratorAST::Node decl; + if( parseDeclarator(decl) ) { + if( lex->lookAhead(0) == '=' ) { + nextToken(); + + AST::Node expr; + if( skipExpression(expr) ){ + ast->setTypeSpec( spec ); + ast->setDeclarator( decl ); + ast->setExpression( expr ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + } else { + ast->setTypeSpec( spec ); + ast->setDeclarator( decl ); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; + } + } + } + + lex->setIndex( start ); + + AST::Node expr; + if( !skipCommaExpression(expr) ) + return false; + + ast->setExpression( expr ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + + +bool Parser::parseWhileStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseWhileStatement()" << endl; + int start = lex->index(); + + ADVANCE( Token_while, "while" ); + ADVANCE( '(' , "(" ); + + ConditionAST::Node cond; + if( !parseCondition(cond) ){ + reportError( i18n("condition expected") ); + return false; + } + ADVANCE( ')', ")" ); + + StatementAST::Node body; + if( !parseStatement(body) ){ + reportError( i18n("statement expected") ); + } + + WhileStatementAST::Node ast = CreateNode<WhileStatementAST>(); + ast->setCondition( cond ); + ast->setStatement( body ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseDoStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDoStatement()" << endl; + int start = lex->index(); + + ADVANCE( Token_do, "do" ); + + StatementAST::Node body; + if( !parseStatement(body) ){ + reportError( i18n("statement expected") ); + //return false; + } + + ADVANCE_NR( Token_while, "while" ); + ADVANCE_NR( '(' , "(" ); + + AST::Node expr; + if( !skipCommaExpression(expr) ){ + reportError( i18n("expression expected") ); + //return false; + } + + ADVANCE_NR( ')', ")" ); + ADVANCE_NR( ';', ";" ); + + DoStatementAST::Node ast = CreateNode<DoStatementAST>(); + ast->setStatement( body ); + //ast->setCondition( condition ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseForStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseForStatement()" << endl; + int start = lex->index(); + + ADVANCE( Token_for, "for" ); + ADVANCE( '(', "(" ); + + StatementAST::Node init; + if( !parseForInitStatement(init) ){ + reportError( i18n("for initialization expected") ); + return false; + } + + ConditionAST::Node cond; + parseCondition( cond ); + ADVANCE( ';', ";" ); + + AST::Node expr; + skipCommaExpression( expr ); + ADVANCE( ')', ")" ); + + StatementAST::Node body; + parseStatement(body); + + ForStatementAST::Node ast = CreateNode<ForStatementAST>(); + ast->setInitStatement( init ); + ast->setCondition( cond ); + // ast->setExpression( expression ); + ast->setStatement( body ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +// qt4 [erbsland] +///@todo add the right parsing for the foreach statement +bool Parser::parseForEachStatement( StatementAST::Node& node ) +{ + int start = lex->index(); + + ADVANCE( Token_foreach, "foreach" ); + ADVANCE( '(', "(" ); + + AST::Node expr; + // replace with the right parsing + skipCommaExpression( expr ); + ADVANCE( ')', ")" ); + + StatementAST::Node body; + parseStatement(body); + + ForEachStatementAST::Node ast = CreateNode<ForEachStatementAST>(); + // add here the parser results + ast->setStatement( body ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseForInitStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseForInitStatement()" << endl; + + if ( parseDeclarationStatement(node) ) + return true; + + return skipExpressionStatement( node ); +} + +bool Parser::parseCompoundStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCompoundStatement()" << endl; + int start = lex->index(); + + if( lex->lookAhead(0) != '{' ){ + return false; + } + nextToken(); + + StatementListAST::Node ast = CreateNode<StatementListAST>(); + + while( !lex->lookAhead(0).isNull() ){ + if( lex->lookAhead(0) == '}' ) + break; + + StatementAST::Node stmt; + int startStmt = lex->index(); + if( !parseStatement(stmt) ){ + if( startStmt == lex->index() ) + nextToken(); + skipUntilStatement(); + } else { + ast->addStatement( stmt ); + } + } + + clearComment(); + + if( lex->lookAhead(0) != '}' ){ + reportError( i18n("} expected") ); + } else { + nextToken(); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseIfStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseIfStatement()" << endl; + + int start = lex->index(); + + ADVANCE( Token_if, "if" ); + + ADVANCE( '(' , "(" ); + + IfStatementAST::Node ast = CreateNode<IfStatementAST>(); + + ConditionAST::Node cond; + if( !parseCondition(cond) ){ + reportError( i18n("condition expected") ); + return false; + } + ADVANCE( ')', ")" ); + + StatementAST::Node stmt; + if( !parseStatement(stmt) ){ + reportError( i18n("statement expected") ); + } + + ast->setCondition( cond ); + ast->setStatement( stmt ); + + if( lex->lookAhead(0) == Token_else ){ + nextToken(); + StatementAST::Node elseStmt; + if( !parseStatement(elseStmt) ) { + reportError( i18n("statement expected") ); + } + ast->setElseStatement( elseStmt ); + } + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseSwitchStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseSwitchStatement()" << endl; + int start = lex->index(); + ADVANCE( Token_switch, "switch" ); + + ADVANCE( '(' , "(" ); + + ConditionAST::Node cond; + if( !parseCondition(cond) ){ + reportError( i18n("condition expected") ); + return false; + } + ADVANCE( ')', ")" ); + + StatementAST::Node stmt; + if( !parseCompoundStatement(stmt) ){ + syntaxError(); + return false; + } + + SwitchStatementAST::Node ast = CreateNode<SwitchStatementAST>(); + ast->setCondition( cond ); + ast->setStatement( stmt ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseLabeledStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLabeledStatement()" << endl; + switch( lex->lookAhead(0) ){ + case Token_identifier: + case Token_default: + if( lex->lookAhead(1) == ':' ){ + nextToken(); + nextToken(); + + StatementAST::Node stmt; + if( parseStatement(stmt) ){ + node = stmt; + return true; + } + } + break; + + case Token_case: + { + nextToken(); + AST::Node expr; + if( !parseConstantExpression(expr) ){ + reportError( i18n("expression expected") ); + } else if( lex->lookAhead(0) == Token_ellipsis ){ + nextToken(); + + AST::Node expr2; + if( !parseConstantExpression(expr2) ){ + reportError( i18n("expression expected") ); + } + } + ADVANCE( ':', ":" ); + + StatementAST::Node stmt; + if( parseStatement(stmt) ){ + node = stmt; + return true; + } + } + break; + + } + + return false; +} + +bool Parser::parseBlockDeclaration( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseBlockDeclaration()" << endl; + switch( lex->lookAhead(0) ) { + case Token_typedef: + return parseTypedef( node ); + case Token_using: + return parseUsing( node ); + case Token_asm: + return parseAsmDefinition( node ); + case Token_namespace: + return parseNamespaceAliasDefinition( node ); + } + + int start = lex->index(); + + GroupAST::Node storageSpec; + parseStorageClassSpecifier( storageSpec ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + TypeSpecifierAST::Node spec; + if ( !parseTypeSpecifierOrClassSpec(spec) ) { // replace with simpleTypeSpecifier?!?! + lex->setIndex( start ); + return false; + } + spec->setCvQualify( cv ); + + GroupAST::Node cv2; + parseCvQualify( cv2 ); + spec->setCv2Qualify( cv2 ); + + InitDeclaratorListAST::Node declarators; + parseInitDeclaratorList( declarators ); + + if( lex->lookAhead(0) != ';' ){ + lex->setIndex( start ); + return false; + } + nextToken(); + + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + ast->setTypeSpec( spec ); + ast->setInitDeclaratorList( declarators ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parseNamespaceAliasDefinition( DeclarationAST::Node& /*node*/ ) +{ + if ( lex->lookAhead(0) != Token_namespace ) { + return false; + } + nextToken(); + + ADVANCE( Token_identifier, "identifier" ); + ADVANCE( '=', "=" ); + + NameAST::Node name; + if( !parseName(name) ){ + reportError( i18n("Namespace name expected") ); + } + + ADVANCE( ';', ";" ); + + return true; + +} + +bool Parser::parseDeclarationStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclarationStatement()" << endl; + + int start = lex->index(); + + DeclarationAST::Node decl; + if ( !parseBlockDeclaration(decl) ){ + return false; + } + + DeclarationStatementAST::Node ast = CreateNode<DeclarationStatementAST>(); + ast->setDeclaration( decl ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + ////kdDebug(9007)<< "---------------------> found a block declaration" << endl; + return true; +} + +bool Parser::parseDeclarationInternal( DeclarationAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeclarationInternal()" << endl; + + int start = lex->index(); + + // that is for the case '__declspec(dllexport) int ...' or + // '__declspec(dllexport) inline int ...', etc. + GroupAST::Node winDeclSpec; + parseWinDeclSpec( winDeclSpec ); + + GroupAST::Node funSpec; + bool hasFunSpec = parseFunctionSpecifier( funSpec ); + + GroupAST::Node storageSpec; + bool hasStorageSpec = parseStorageClassSpecifier( storageSpec ); + + if( hasStorageSpec && !hasFunSpec ) + hasFunSpec = parseFunctionSpecifier( funSpec ); + + // that is for the case 'friend __declspec(dllexport) ....' + GroupAST::Node winDeclSpec2; + parseWinDeclSpec( winDeclSpec2 ); + + GroupAST::Node cv; + parseCvQualify( cv ); + + int index = lex->index(); + NameAST::Node name; + if( parseName(name) && lex->lookAhead(0) == '(' ){ + // no type specifier, maybe a constructor or a cast operator?? + + lex->setIndex( index ); + + InitDeclaratorAST::Node declarator; + if( parseInitDeclarator(declarator) ){ + int endSignature = lex->index(); + + switch( lex->lookAhead(0) ){ + case ';': + { + nextToken(); + + InitDeclaratorListAST::Node declarators = CreateNode<InitDeclaratorListAST>(); + + // update declarators position + int startLine, startColumn, endLine, endColumn; + if( declarator.get() ){ + declarator->getStartPosition( &startLine, &startColumn ); + declarator->getEndPosition( &endLine, &endColumn ); + declarators->setStartPosition( startLine, startColumn ); + declarators->setEndPosition( endLine, endColumn ); + } + declarators->addInitDeclarator( declarator ); + + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + ast->setInitDeclaratorList( declarators ); + ast->setText( toString(start, endSignature) ); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + + } + break; + + case ':': + { + AST::Node ctorInit; + StatementListAST::Node funBody; + if( parseCtorInitializer(ctorInit) && parseFunctionBody(funBody) ){ + FunctionDefinitionAST::Node ast = CreateNode<FunctionDefinitionAST>(); + ast->setStorageSpecifier( storageSpec ); + ast->setFunctionSpecifier( funSpec ); + ast->setInitDeclarator( declarator ); + ast->setFunctionBody( funBody ); + ast->setText( toString(start, endSignature) ); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + } + } + break; + + case '{': + { + StatementListAST::Node funBody; + if( parseFunctionBody(funBody) ){ + FunctionDefinitionAST::Node ast = CreateNode<FunctionDefinitionAST>(); + ast->setStorageSpecifier( storageSpec ); + ast->setFunctionSpecifier( funSpec ); + ast->setInitDeclarator( declarator ); + ast->setText( toString(start, endSignature) ); + ast->setFunctionBody( funBody ); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + } + } + break; + + case '(': + case '[': + // ops!! it seems a declarator + goto start_decl; + break; + } + + } + + syntaxError(); + return false; + } + +start_decl: + lex->setIndex( index ); + + if( lex->lookAhead(0) == Token_const && lex->lookAhead(1) == Token_identifier && lex->lookAhead(2) == '=' ){ + // constant definition + nextToken(); + InitDeclaratorListAST::Node declarators; + if( parseInitDeclaratorList(declarators) ){ + ADVANCE( ';', ";" ); + DeclarationAST::Node ast = CreateNode<DeclarationAST>(); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + } + syntaxError(); + return false; + } + + Comment mcomment = comment(); + clearComment(); + + TypeSpecifierAST::Node spec; + if( parseTypeSpecifier(spec) ){ + if ( !hasFunSpec ) + parseFunctionSpecifier( funSpec ); // e.g. "void inline" + spec->setCvQualify( cv ); + + InitDeclaratorListAST::Node declarators; + + InitDeclaratorAST::Node decl; + int startDeclarator = lex->index(); + bool maybeFunctionDefinition = false; + + if( lex->lookAhead(0) != ';' ){ + if( parseInitDeclarator(decl) && lex->lookAhead(0) == '{' ){ + // function definition + maybeFunctionDefinition = true; + } else { + lex->setIndex( startDeclarator ); + if( !parseInitDeclaratorList(declarators) ){ + syntaxError(); + return false; + } + } + } + + int endSignature = lex->index(); + switch( lex->lookAhead(0) ){ + case ';': + { + nextToken(); + SimpleDeclarationAST::Node ast = CreateNode<SimpleDeclarationAST>(); + int line, col; + ast->setComment( mcomment ); + if( &(*decl) ) { + decl->getEndPosition( &line, &col ); + + preparseLineComments( line ); + Comment c = m_commentStore.getCommentInRange( line ); + if( c ) { + ast->addComment( c ); + } + } + + ast->setStorageSpecifier( storageSpec ); + ast->setFunctionSpecifier( funSpec ); + ast->setText( toString(start, endSignature) ); + ast->setTypeSpec( spec ); + ast->setWinDeclSpec( winDeclSpec ); + ast->setInitDeclaratorList( declarators ); + + node = ast; + UPDATE_POS( node, start, lex->index() ); + } + return true; + + case '{': + { + if( !maybeFunctionDefinition ){ + syntaxError(); + return false; + } + StatementListAST::Node funBody; + if ( parseFunctionBody(funBody) ) { + FunctionDefinitionAST::Node ast = CreateNode<FunctionDefinitionAST>(); + + ast->setComment( mcomment ); + + ast->setWinDeclSpec( winDeclSpec ); + ast->setStorageSpecifier( storageSpec ); + ast->setFunctionSpecifier( funSpec ); + ast->setText( toString(start, endSignature) ); + ast->setTypeSpec( spec ); + ast->setFunctionBody( funBody ); + ast->setInitDeclarator( decl ); + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; + } + } + break; + + } + } + + syntaxError(); + return false; +} + +bool Parser::parseFunctionBody( StatementListAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseFunctionBody()" << endl; + + int start = lex->index(); + if( lex->lookAhead(0) != '{' ){ + return false; + } + nextToken(); + + StatementListAST::Node ast = CreateNode<StatementListAST>(); + + while( !lex->lookAhead(0).isNull() ){ + if( lex->lookAhead(0) == '}' ) + break; + + StatementAST::Node stmt; + int startStmt = lex->index(); + if( !parseStatement(stmt) ){ + if( startStmt == lex->index() ) + nextToken(); + skipUntilStatement(); + } else + ast->addStatement( stmt ); + } + + clearComment(); + + if( lex->lookAhead(0) != '}' ){ + reportError( i18n("} expected") ); + } else + nextToken(); + + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +QString Parser::toString( int start, int end, const QString& sep ) const +{ + QStringList l; + + for( int i=start; i<end; ++i ){ + const Token& t = lex->tokenAt(i); + if( t != Token_comment ) + l << t.text(); + } + + return l.join( sep ).stripWhiteSpace(); +} + +bool Parser::parseTypeSpecifierOrClassSpec( TypeSpecifierAST::Node& node ) +{ + if( parseClassSpecifier(node) ) + return true; + else if( parseEnumSpecifier(node) ) + return true; + else if( parseTypeSpecifier(node) ) + return true; + + return false; +} + +bool Parser::parseTryBlockStatement( StatementAST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseTryBlockStatement()" << endl; + + int start = lex->index(); + if( lex->lookAhead(0) != Token_try ){ + return false; + } + nextToken(); + + StatementAST::Node stmt; + if( !parseCompoundStatement(stmt) ){ + syntaxError(); + } + + if( lex->lookAhead(0) != Token_catch ){ + reportError( i18n("catch expected") ); + } + + CatchStatementListAST::Node list = CreateNode<CatchStatementListAST>(); + + while( lex->lookAhead(0) == Token_catch ){ + + nextToken(); + ADVANCE( '(', "(" ); + ConditionAST::Node cond; + if( !parseCondition(cond) ){ + reportError( i18n("condition expected") ); + return false; + } + ADVANCE( ')', ")" ); + + StatementAST::Node body; + if( !parseCompoundStatement(body) ){ + syntaxError(); + } + + CatchStatementAST::Node cstmt = CreateNode<CatchStatementAST>(); + cstmt->setCondition( cond ); + cstmt->setStatement( body ); + int l=0, c=0; + if( cond.get() ) + cond->getStartPosition( &l, &c ); + else if( body.get() ) + body->getStartPosition( &l, &c ); + + cstmt->setStartPosition( l, c ); + if( body.get() ) + body->getEndPosition( &l, &c ); + + cstmt->setEndPosition( l, c ); + list->addStatement( cstmt ); + } + + TryBlockStatementAST::Node ast = CreateNode<TryBlockStatementAST>(); + ast->setStatement( stmt ); + ast->setCatchStatementList( list ); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + + return true; +} + +bool Parser::parsePrimaryExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parsePrimarExpression()" << endl; + + + switch( lex->lookAhead(0) ){ + case Token_string_literal: + { + AST::Node lit; + parseStringLiteral( lit ); + } + return true; + + case Token_number_literal: + case Token_char_literal: + case Token_true: + case Token_false: + nextToken(); + return true; + + case Token_this: + nextToken(); + return true; + + case Token_dynamic_cast: + case Token_static_cast: + case Token_reinterpret_cast: + case Token_const_cast: + { + nextToken(); + + CHECK( '<', "<" ); + AST::Node typeId; + parseTypeId( typeId ); + CHECK( '>', ">" ); + + CHECK( '(', "(" ); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + } + return true; + + case Token_typeid: + { + nextToken(); + CHECK( '(', "(" ); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + } + return true; + + case '(': + { + nextToken(); + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "token = " << lex->lookAhead(0).text() << endl; + AST::Node expr; + if( !parseExpression(expr) ){ + return false; + } + CHECK( ')', ")" ); + } + return true; + + default: + { + int start = lex->index(); + TypeSpecifierAST::Node typeSpec; + if( parseSimpleTypeSpecifier(typeSpec) && lex->lookAhead(0) == '(' ){ + nextToken(); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + return true; + } + + lex->setIndex( start ); + NameAST::Node name; + if( parseName(name) ) + return true; + } + } + + return false; +} + +bool Parser::parsePostfixExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parsePostfixExpression()" << endl; + + AST::Node expr; + if( !parsePrimaryExpression(expr) ) + return false; + + while( true ){ + switch(lex->lookAhead(0)) + { + case '[': + { + nextToken(); + AST::Node e; + parseCommaExpression( e ); + CHECK( ']', "]" ); + } + break; + + case '(': + { + nextToken(); + AST::Node funArgs; + parseCommaExpression( funArgs ); + CHECK( ')', ")" ); + } + break; + + case Token_incr: + case Token_decr: + nextToken(); + break; + + case '.': + case Token_arrow: + { + nextToken(); + if( lex->lookAhead(0) == Token_template ) + nextToken(); + + NameAST::Node name; + if( !parseName(name) ){ + return false; + } + } + break; + + case Token_typename: + { + nextToken(); + + NameAST::Node name; + if( !parseName(name) ){ + return false; + } + + CHECK( '(', "(" ); + AST::Node expr; + parseCommaExpression(expr); + CHECK( ')', ")" ); + } + return true; + + default: + return true; + + } // end switch + + } // end while + + return true; +} + +bool Parser::parseUnaryExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseUnaryExpression()" << endl; + + switch( lex->lookAhead(0) ){ + case Token_incr: + case Token_decr: + case '*': + case '&': + case '+': + case '-': + case '!': + case '~': + { + nextToken(); + AST::Node expr; + return parseCastExpression( expr ); + } + + case Token_sizeof: + { + nextToken(); + int index = lex->index(); + if( lex->lookAhead(0) == '(' ){ + nextToken(); + AST::Node typeId; + if( parseTypeId(typeId) && lex->lookAhead(0) == ')' ){ + nextToken(); + return true; + } + lex->setIndex( index ); + } + AST::Node expr; + return parseUnaryExpression( expr ); + } + + case Token_new: + return parseNewExpression( node ); + + case Token_delete: + return parseDeleteExpression( node ); + } + + return parsePostfixExpression( node ); +} + +bool Parser::parseNewExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNewExpression()" << endl; + if( lex->lookAhead(0) == Token_scope && lex->lookAhead(1) == Token_new ) + nextToken(); + + CHECK( Token_new, "new"); + + if( lex->lookAhead(0) == '(' ){ + nextToken(); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + } + + if( lex->lookAhead(0) == '(' ){ + nextToken(); + AST::Node typeId; + parseTypeId( typeId ); + CHECK( ')', ")" ); + } else { + AST::Node typeId; + parseNewTypeId( typeId ); + } + + AST::Node init; + parseNewInitializer( init ); + return true; +} + +bool Parser::parseNewTypeId( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNewTypeId()" << endl; + TypeSpecifierAST::Node typeSpec; + if( parseTypeSpecifier(typeSpec) ){ + AST::Node declarator; + parseNewDeclarator( declarator ); + return true; + } + + return false; +} + +bool Parser::parseNewDeclarator( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNewDeclarator()" << endl; + AST::Node ptrOp; + if( parsePtrOperator(ptrOp) ){ + AST::Node declarator; + parseNewDeclarator( declarator ); + return true; + } + + if( lex->lookAhead(0) == '[' ){ + while( lex->lookAhead(0) == '[' ){ + nextToken(); + AST::Node expr; + parseExpression( expr ); + ADVANCE( ']', "]" ); + } + return true; + } + + return false; +} + +bool Parser::parseNewInitializer( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseNewInitializer()" << endl; + if( lex->lookAhead(0) != '(' ) + return false; + + nextToken(); + AST::Node expr; + parseCommaExpression( expr ); + CHECK( ')', ")" ); + + return true; +} + +bool Parser::parseDeleteExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseDeleteExpression()" << endl; + if( lex->lookAhead(0) == Token_scope && lex->lookAhead(1) == Token_delete ) + nextToken(); + + CHECK( Token_delete, "delete" ); + + if( lex->lookAhead(0) == '[' ){ + nextToken(); + CHECK( ']', "]" ); + } + + AST::Node expr; + return parseCastExpression( expr ); +} + +bool Parser::parseCastExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCastExpression()" << endl; + + int index = lex->index(); + + if( lex->lookAhead(0) == '(' ){ + nextToken(); + AST::Node typeId; + if ( parseTypeId(typeId) ) { + if ( lex->lookAhead(0) == ')' ) { + nextToken(); + AST::Node expr; + if( parseCastExpression(expr) ) + return true; + } + } + } + + lex->setIndex( index ); + + AST::Node expr; + return parseUnaryExpression( expr ); +} + +bool Parser::parsePmExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser:parsePmExpression()" << endl; + AST::Node expr; + if( !parseCastExpression(expr) ) + return false; + + while( lex->lookAhead(0) == Token_ptrmem ){ + nextToken(); + + if( !parseCastExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseMultiplicativeExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseMultiplicativeExpression()" << endl; + AST::Node expr; + if( !parsePmExpression(expr) ) + return false; + + while( lex->lookAhead(0) == '*' || lex->lookAhead(0) == '/' || lex->lookAhead(0) == '%' ){ + nextToken(); + + if( !parsePmExpression(expr) ) + return false; + } + + return true; +} + + +bool Parser::parseAdditiveExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAdditiveExpression()" << endl; + AST::Node expr; + if( !parseMultiplicativeExpression(expr) ) + return false; + + while( lex->lookAhead(0) == '+' || lex->lookAhead(0) == '-' ){ + nextToken(); + + if( !parseMultiplicativeExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseShiftExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseShiftExpression()" << endl; + AST::Node expr; + if( !parseAdditiveExpression(expr) ) + return false; + + while( lex->lookAhead(0) == Token_shift ){ + nextToken(); + + if( !parseAdditiveExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseRelationalExpression( AST::Node& /*node*/, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseRelationalExpression()" << endl; + AST::Node expr; + if( !parseShiftExpression(expr) ) + return false; + + while( lex->lookAhead(0) == '<' || (lex->lookAhead(0) == '>' && !templArgs) || + lex->lookAhead(0) == Token_leq || lex->lookAhead(0) == Token_geq ){ + nextToken(); + + if( !parseShiftExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseEqualityExpression( AST::Node& /*node*/, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseEqualityExpression()" << endl; + AST::Node expr; + if( !parseRelationalExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == Token_eq || lex->lookAhead(0) == Token_not_eq ){ + nextToken(); + + if( !parseRelationalExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseAndExpression( AST::Node& /*node*/, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAndExpression()" << endl; + AST::Node expr; + if( !parseEqualityExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == '&' ){ + nextToken(); + + if( !parseEqualityExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseExclusiveOrExpression( AST::Node& /*node*/, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseExclusiveOrExpression()" << endl; + AST::Node expr; + if( !parseAndExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == '^' ){ + nextToken(); + + if( !parseAndExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseInclusiveOrExpression( AST::Node& /*node*/, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseInclusiveOrExpression()" << endl; + AST::Node expr; + if( !parseExclusiveOrExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == '|' ){ + nextToken(); + + if( !parseExclusiveOrExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseLogicalAndExpression( AST::Node& /*node*/, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLogicalAndExpression()" << endl; + + AST::Node expr; + if( !parseInclusiveOrExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == Token_and ){ + nextToken(); + + if( !parseInclusiveOrExpression(expr, templArgs) ) + return false; + } + + return true; +} + +bool Parser::parseLogicalOrExpression( AST::Node& node, bool templArgs ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseLogicalOrExpression()" << endl; + + int start = lex->index(); + + AST::Node expr; + if( !parseLogicalAndExpression(expr, templArgs) ) + return false; + + while( lex->lookAhead(0) == Token_or ){ + nextToken(); + + if( !parseLogicalAndExpression(expr, templArgs) ) + return false; + } + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseConditionalExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseConditionalExpression()" << endl; + AST::Node expr; + if( !parseLogicalOrExpression(expr) ) + return false; + + if( lex->lookAhead(0) == '?' ){ + nextToken(); + + if( !parseExpression(expr) ) + return false; + + CHECK( ':', ":" ); + + if( !parseAssignmentExpression(expr) ) + return false; + } + + return true; +} + +bool Parser::parseAssignmentExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseAssignmentExpression()" << endl; + int start = lex->index(); + AST::Node expr; + if( lex->lookAhead(0) == Token_throw && !parseThrowExpression(expr) ) + return false; + else if( !parseConditionalExpression(expr) ) + return false; + + while( lex->lookAhead(0) == Token_assign || lex->lookAhead(0) == '=' ){ + nextToken(); + + if( !parseConditionalExpression(expr) ) + return false; + } + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseConstantExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseConstantExpression()" << endl; + int start = lex->index(); + if( parseConditionalExpression(node) ){ + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; + } + return false; +} + +bool Parser::parseExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseExpression()" << endl; + + int start = lex->index(); + + if( !parseCommaExpression(node) ) + return false; + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseCommaExpression( AST::Node& node ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseCommaExpression()" << endl; + int start = lex->index(); + + AST::Node expr; + if( !parseAssignmentExpression(expr) ) + return false; + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + + if( !parseAssignmentExpression(expr) ) + return false; + } + + AST::Node ast = CreateNode<AST>(); + UPDATE_POS( ast, start, lex->index() ); + node = ast; + return true; +} + +bool Parser::parseThrowExpression( AST::Node& /*node*/ ) +{ + ////kdDebug(9007)<< "--- tok = " << lex->lookAhead(0).text() << " -- " << "Parser::parseThrowExpression()" << endl; + if( lex->lookAhead(0) != Token_throw ) + return false; + + CHECK( Token_throw, "throw" ); + AST::Node expr; + if( !parseAssignmentExpression(expr) ) + return false; + + return true; +} + +bool Parser::parseIvarDeclList( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIvarDecls( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIvarDecl( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIvars( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIvarDeclarator( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseMethodDecl( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseUnarySelector( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordSelector( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseSelector( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordDecl( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseReceiver( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcMessageExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseMessageArgs( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordArgList( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordArg( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseReservedWord( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseMyParms( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseMyParm( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseOptParmList( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcSelectorExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseSelectorArg( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordNameList( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseKeywordName( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcEncodeExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcString( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseProtocolRefs( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseIdentifierList( GroupAST::Node & node ) +{ + int start = lex->index(); + + if( lex->lookAhead(0) != Token_identifier ) + return false; + + GroupAST::Node ast = CreateNode<GroupAST>(); + + AST_FROM_TOKEN( tk, lex->index() ); + ast->addNode( tk ); + nextToken(); + + while( lex->lookAhead(0) == ',' ){ + nextToken(); + if( lex->lookAhead(0) == Token_identifier ){ + AST_FROM_TOKEN( tk, lex->index() ); + ast->addNode( tk ); +// nextToken(); + } + ADVANCE( Token_identifier, "identifier" ); + } + + node = ast; + UPDATE_POS( node, start, lex->index() ); + return true; +} + +bool Parser::parseIdentifierColon( AST::Node & node ) +{ + Q_UNUSED( node ); + + if( lex->lookAhead(0) == Token_identifier && lex->lookAhead(1) == ':' ){ + nextToken(); + nextToken(); + return true; + } // ### else if PTYPENAME -> return true ; + + return false; +} + +bool Parser::parseObjcProtocolExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcOpenBracketExpr( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcCloseBracket( AST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcDef( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcClassDef( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcClassDecl( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + + ADVANCE( OBJC_CLASS, "@class" ); + + GroupAST::Node idList; + if ( !parseIdentifierList( idList ) ) + return false; + + ADVANCE( ';', ";" ); + + return true; +} + +bool Parser::parseObjcProtocolDecl( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + + ADVANCE( OBJC_PROTOCOL, "@protocol" ); + + GroupAST::Node idList; + if ( !parseIdentifierList( idList ) ) + return false; + + ADVANCE( ';', ";" ); + + return true; +} + +bool Parser::parseObjcAliasDecl( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + + ADVANCE( OBJC_ALIAS, "@alias" ); + + GroupAST::Node idList; + if ( !parseIdentifierList( idList ) ) + return false; + + ADVANCE( ';', ";" ); + + return true; +} + +bool Parser::parseObjcProtocolDef( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseObjcMethodDef( DeclarationAST::Node & node ) +{ + Q_UNUSED( node ); + return false; +} + +bool Parser::parseWinDeclSpec( GroupAST::Node & node ) +{ + if( lex->lookAhead(0) == Token_identifier && lex->lookAhead(0).text() == "__declspec" && lex->lookAhead(1) == '(' && lex->lookAhead(2) != ')'){ + int start = lex->index(); + nextToken(); + nextToken(); // skip '(' + + if ( !parseIdentifierList( node ) ) + return false; + + ADVANCE( ')', ")" ); + + UPDATE_POS( node, start, lex->index() ); + return true; + } + + return false; +} + diff --git a/lib/cppparser/parser.h b/lib/cppparser/parser.h new file mode 100644 index 00000000..ff3891ac --- /dev/null +++ b/lib/cppparser/parser.h @@ -0,0 +1,459 @@ +/* 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef PARSER_H +#define PARSER_H + +#include "ast.h" + +#include <qstring.h> +#include <qstringlist.h> +#include <qvaluelist.h> +#include <qvaluestack.h> +#include <set> +#include <iostream> + +struct ParserPrivateData; + +class Driver; +class Lexer; +class Token; +struct Error; + + +class CommentFormatter { + static inline bool isWhite( QChar c ) { + return c.isSpace(); + } + + static void rStrip( QString str, QString& from ) { + if( str.isEmpty() ) return; + + int i = 0; + int ip = from.length(); + int s = from.length(); + + for( int a = s-1; a >= 0; a-- ) { + if( isWhite( from[a] ) ) { + continue; + } else { + if( from[a] == str[i] ) { + i++; + ip = a; + if( i == (int)str.length() ) break; + } else { + break; + } + } + } + + if( ip != (int)from.length() ) from = from.left( ip ); + } + + static void strip( QString str, QString& from ) { + if( str.isEmpty() ) return; + + int i = 0; + int ip = 0; + int s = from.length(); + + for( int a = 0; a < s; a++ ) { + if( isWhite( from[a] ) ) { + continue; + } else { + if( from[a] == str[i] ) { + i++; + ip = a+1; + if( i == (int)str.length() ) break; + } else { + break; + } + } + } + + if( ip ) from = from.mid( ip ); + } + + public: + + static QString formatComment( QString comment ) { + QString ret; + int i = 0; + int s = comment.length(); + while( i < s && comment[i] == '/' ) { + i++; + } + + if( i > 1 ) { + ret = comment.mid( i ); + } else { + ///remove the star in each line + QStringList lines = QStringList::split( "\n", comment ); + + if( lines.isEmpty() ) return ret; + + strip( "/**", lines.front() ); + rStrip( "/**", lines.back() ); + + QStringList::iterator it = lines.begin(); + ++it; + QStringList::iterator eit = lines.end(); + + if( it != lines.end() ) { + --eit; + + for( ; it != eit; ++it ) { + strip( "*", *it ); + } + + if( lines.front().stripWhiteSpace().isEmpty() ) + lines.pop_front(); + + if( lines.back().stripWhiteSpace().isEmpty() ) + lines.pop_back(); + } + + ret = lines.join( "\n" ); + } + + return ret; + } +}; + +class Comment { + QString m_text; + int m_line; + bool m_formatted; + + + void format() { + if( m_formatted ) return; + m_formatted = true; + m_text = CommentFormatter::formatComment( m_text ); + } + + public: + Comment( QString text = "", int line = -1 ) : m_text( text ), m_line( line ), m_formatted(false) { + } + + Comment( int line ) : m_line( line ) { + } + + void operator += ( Comment rhs ) { + format(); + rhs.format(); + m_text += " " + rhs.m_text; + } + + operator bool() const { + return !m_text.isEmpty(); + } + + operator QString() { + format(); + return m_text; + } + + inline int line() const { + return m_line; + } + + bool operator < ( Comment& rhs ) const { + return m_line < rhs.m_line; + } + + bool isSame ( const Comment& rhs ) { + if( rhs.m_formatted ) format(); + return m_text == rhs.m_text; + } + + struct cmp { + bool operator () ( const Comment& c1, const Comment& c2 ) const { + return c1.line() < c2.line(); + } + }; +}; + + +class CommentStore { + private: + typedef std::set< Comment, Comment::cmp > CommentSet; + CommentSet m_comments; + + public: + + ///Returns the comment nearest to "end"(inclusive), and returns & removes it + Comment getCommentInRange( int end, int start = 0 ) { + CommentSet::iterator it = m_comments.lower_bound( end ); + + + while( it != m_comments.begin() && (*it).line() > end ) { + --it; + } + + if( it != m_comments.end() && (*it).line() >= start && (*it).line() <= end ) { + Comment ret = *it; + m_comments.erase( it ); + return ret; + } else { + return Comment(); + } + } + + ///Returns and removes the comment in the line + Comment getComment( int line ) { + CommentSet::iterator it = m_comments.find( line ); + if( it != m_comments.end() ) { + Comment ret = *it; + m_comments.erase( it ); + return ret; + } else { + return Comment(); + } + } + + void addComment( Comment comment ) { + + CommentSet::iterator it = m_comments.find( comment ); + if( it != m_comments.end() ) { + if( comment.isSame( *it ) ) return; + Comment c = *it; + c += comment; + comment = c; + m_comments.erase( it ); + } + + m_comments.insert( comment ); + } + + ///Does not delete the comment + Comment latestComment() { + CommentSet::iterator it = m_comments.end(); + if( it == m_comments.begin() ) return Comment(); + --it; + return *it; + } + + void clear() { + m_comments.clear(); + } +}; + + +class Parser +{ +public: + Parser( Driver* driver, Lexer* lexer ); + virtual ~Parser(); + +private: + virtual bool reportError( const Error& err ); + /** @todo remove*/ virtual bool reportError( const QString& msg ); + /** @todo remove*/ virtual void syntaxError(); + +public /*rules*/ : + + bool parseTranslationUnit( TranslationUnitAST::Node& node ); + + bool parseDeclaration( DeclarationAST::Node& node ); + bool parseBlockDeclaration( DeclarationAST::Node& node ); + bool parseLinkageSpecification( DeclarationAST::Node& node ); + bool parseLinkageBody( LinkageBodyAST::Node& node ); + bool parseNamespace( DeclarationAST::Node& node ); + bool parseNamespaceAliasDefinition( DeclarationAST::Node& node ); + bool parseUsing( DeclarationAST::Node& node ); + bool parseUsingDirective( DeclarationAST::Node& node ); + bool parseTypedef( DeclarationAST::Node& node ); + bool parseAsmDefinition( DeclarationAST::Node& node ); + bool parseTemplateDeclaration( DeclarationAST::Node& node ); + bool parseDeclarationInternal( DeclarationAST::Node& node ); + + bool parseUnqualifiedName( ClassOrNamespaceNameAST::Node& node ); + bool parseStringLiteral( AST::Node& node ); + bool parseName( NameAST::Node& node ); + bool parseOperatorFunctionId( AST::Node& node ); + bool parseTemplateArgumentList( TemplateArgumentListAST::Node& node, bool reportError=true ); + bool parseOperator( AST::Node& node ); + bool parseCvQualify( GroupAST::Node& node ); + bool parseSimpleTypeSpecifier( TypeSpecifierAST::Node& node ); + bool parsePtrOperator( AST::Node& node ); + bool parseTemplateArgument( AST::Node& node ); + bool parseTypeSpecifier( TypeSpecifierAST::Node& node ); + bool parseTypeSpecifierOrClassSpec( TypeSpecifierAST::Node& node ); + bool parseDeclarator( DeclaratorAST::Node& node ); + bool parseTemplateParameterList( TemplateParameterListAST::Node& node ); + bool parseTemplateParameter( TemplateParameterAST::Node& node ); + bool parseStorageClassSpecifier( GroupAST::Node& node ); + bool parseFunctionSpecifier( GroupAST::Node& node ); + bool parseInitDeclaratorList( InitDeclaratorListAST::Node& node ); + bool parseInitDeclarator( InitDeclaratorAST::Node& node ); + bool parseParameterDeclarationClause( ParameterDeclarationClauseAST::Node& node ); + bool parseCtorInitializer( AST::Node& node ); + bool parsePtrToMember( AST::Node& node ); + bool parseEnumSpecifier( TypeSpecifierAST::Node& node ); + bool parseClassSpecifier( TypeSpecifierAST::Node& node ); + bool parseWinDeclSpec( GroupAST::Node& node ); + bool parseElaboratedTypeSpecifier( TypeSpecifierAST::Node& node ); + bool parseDeclaratorId( NameAST::Node& node ); + bool parseExceptionSpecification( GroupAST::Node& node ); + bool parseEnumerator( EnumeratorAST::Node& node ); + bool parseTypeParameter( TypeParameterAST::Node& node ); + bool parseParameterDeclaration( ParameterDeclarationAST::Node& node ); + bool parseTypeId( AST::Node& node ); + bool parseAbstractDeclarator( DeclaratorAST::Node& node ); + bool parseParameterDeclarationList( ParameterDeclarationListAST::Node& node ); + bool parseMemberSpecification( DeclarationAST::Node& node ); + bool parseAccessSpecifier( AST::Node& node ); + bool parseTypeIdList( GroupAST::Node& node ); + bool parseMemInitializerList( AST::Node& node ); + bool parseMemInitializer( AST::Node& node ); + bool parseInitializer( AST::Node& node ); + bool parseBaseClause( BaseClauseAST::Node& node ); + bool parseBaseSpecifier( BaseSpecifierAST::Node& node ); + bool parseInitializerClause( AST::Node& node ); + bool parseMemInitializerId( NameAST::Node& node ); + bool parseFunctionBody( StatementListAST::Node& node ); + + // expression + bool skipExpression( AST::Node& node ); + bool skipCommaExpression( AST::Node& node ); + bool skipExpressionStatement( StatementAST::Node& node ); + + bool parseExpression( AST::Node& node ); + bool parsePrimaryExpression( AST::Node& node ); + bool parsePostfixExpression( AST::Node& node ); + bool parseUnaryExpression( AST::Node& node ); + bool parseNewExpression( AST::Node& node ); + bool parseNewTypeId( AST::Node& node ); + bool parseNewDeclarator( AST::Node& node ); + bool parseNewInitializer( AST::Node& node ); + bool parseDeleteExpression( AST::Node& node ); + bool parseCastExpression( AST::Node& node ); + bool parsePmExpression( AST::Node& node ); + bool parseMultiplicativeExpression( AST::Node& node ); + bool parseAdditiveExpression( AST::Node& node ); + bool parseShiftExpression( AST::Node& node ); + bool parseRelationalExpression( AST::Node& node, bool templArgs=false ); + bool parseEqualityExpression( AST::Node& node, bool templArgs=false ); + bool parseAndExpression( AST::Node& node, bool templArgs=false ); + bool parseExclusiveOrExpression( AST::Node& node, bool templArgs=false ); + bool parseInclusiveOrExpression( AST::Node& node, bool templArgs=false ); + bool parseLogicalAndExpression( AST::Node& node, bool templArgs=false ); + bool parseLogicalOrExpression( AST::Node& node, bool templArgs=false ); + bool parseConditionalExpression( AST::Node& node ); + bool parseAssignmentExpression( AST::Node& node ); + bool parseConstantExpression( AST::Node& node ); + bool parseCommaExpression( AST::Node& node ); + bool parseThrowExpression( AST::Node& node ); + + // statement + bool parseCondition( ConditionAST::Node& node ); + bool parseStatement( StatementAST::Node& node ); + bool parseWhileStatement( StatementAST::Node& node ); + bool parseDoStatement( StatementAST::Node& node ); + bool parseForStatement( StatementAST::Node& node ); + bool parseForEachStatement( StatementAST::Node& node ); // qt4 [erbsland] + bool parseCompoundStatement( StatementAST::Node& node ); + bool parseForInitStatement( StatementAST::Node& node ); + bool parseIfStatement( StatementAST::Node& node ); + bool parseSwitchStatement( StatementAST::Node& node ); + bool parseLabeledStatement( StatementAST::Node& node ); + bool parseDeclarationStatement( StatementAST::Node& node ); + bool parseTryBlockStatement( StatementAST::Node& node ); + + // objective c + bool parseObjcDef( DeclarationAST::Node& node ); + bool parseObjcClassDef( DeclarationAST::Node& node ); + bool parseObjcClassDecl( DeclarationAST::Node& node ); + bool parseObjcProtocolDecl( DeclarationAST::Node& node ); + bool parseObjcAliasDecl( DeclarationAST::Node& node ); + bool parseObjcProtocolDef( DeclarationAST::Node& node ); + bool parseObjcMethodDef( DeclarationAST::Node& node ); + + bool parseIvarDeclList( AST::Node& node ); + bool parseIvarDecls( AST::Node& node ); + bool parseIvarDecl( AST::Node& node ); + bool parseIvars( AST::Node& node ); + bool parseIvarDeclarator( AST::Node& node ); + bool parseMethodDecl( AST::Node& node ); + bool parseUnarySelector( AST::Node& node ); + bool parseKeywordSelector( AST::Node& node ); + bool parseSelector( AST::Node& node ); + bool parseKeywordDecl( AST::Node& node ); + bool parseReceiver( AST::Node& node ); + bool parseObjcMessageExpr( AST::Node& node ); + bool parseMessageArgs( AST::Node& node ); + bool parseKeywordExpr( AST::Node& node ); + bool parseKeywordArgList( AST::Node& node ); + bool parseKeywordArg( AST::Node& node ); + bool parseReservedWord( AST::Node& node ); + bool parseMyParms( AST::Node& node ); + bool parseMyParm( AST::Node& node ); + bool parseOptParmList( AST::Node& node ); + bool parseObjcSelectorExpr( AST::Node& node ); + bool parseSelectorArg( AST::Node& node ); + bool parseKeywordNameList( AST::Node& node ); + bool parseKeywordName( AST::Node& node ); + bool parseObjcEncodeExpr( AST::Node& node ); + bool parseObjcString( AST::Node& node ); + bool parseProtocolRefs( AST::Node& node ); + bool parseIdentifierList( GroupAST::Node& node ); + bool parseIdentifierColon( AST::Node& node ); + bool parseObjcProtocolExpr( AST::Node& node ); + bool parseObjcOpenBracketExpr( AST::Node& node ); + bool parseObjcCloseBracket( AST::Node& node ); + + void nextToken( bool skipComments = true ); + + ///parses all comments until the end of the line + Comment comment(); + void preparseLineComments( int line ); + void processComment( int offset = 0 ); + void clearComment( ); + + bool skipUntil( int token ); + bool skipUntilDeclaration(); + bool skipUntilStatement(); + bool skip( int l, int r ); + QString toString( int start, int end, const QString& sep=" " ) const; + +private: + int currentLine(); + CommentStore m_commentStore; + + template<class Type> + void eventuallyTakeComment( int startLn, int line, Type& ast ); + template<class Type> + void eventuallyTakeComment( Type& ast ); + + ParserPrivateData* d; + Driver* m_driver; + Lexer* lex; + Comment m_currentComment; + int m_problems; + int m_maxProblems; + bool objcp; + +private: + Parser( const Parser& source ); + void operator = ( const Parser& source ); +}; + + +#endif diff --git a/lib/cppparser/tree_parser.cpp b/lib/cppparser/tree_parser.cpp new file mode 100644 index 00000000..780d86a2 --- /dev/null +++ b/lib/cppparser/tree_parser.cpp @@ -0,0 +1,212 @@ +/* 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "tree_parser.h" +#include "driver.h" +#include <kdebug.h> + +TreeParser::TreeParser() +{ +} + +TreeParser::~TreeParser() +{ +} + +void TreeParser::parseTranslationUnit( const ParsedFile& translationUnit ) +{ + ////kdDebug(9007) << "TreeParser::parseTranslationUnit()" << endl; + + QPtrList<DeclarationAST> declarations = translationUnit->declarationList(); + QPtrListIterator<DeclarationAST> it( declarations ); + while( it.current() ){ + if( (it.current() == 0 ) ) { + //kdDebug( 9007 ) << "declaration is zero" << endl; + continue; + } + parseDeclaration( it.current() ); + ++it; + } +} + +void TreeParser::parseDeclaration( DeclarationAST* declaration ) +{ + ////kdDebug(9007) << "TreeParser::parseDeclaration()" << endl; + + if( !declaration ) + return; + + switch( declaration->nodeType() ) + { + case NodeType_LinkageSpecification: + parseLinkageSpecification( static_cast<LinkageSpecificationAST*>(declaration) ); + break; + + case NodeType_Namespace: + parseNamespace( static_cast<NamespaceAST*>(declaration) ); + break; + + case NodeType_NamespaceAlias: + parseNamespaceAlias( static_cast<NamespaceAliasAST*>(declaration) ); + break; + + case NodeType_Using: + parseUsing( static_cast<UsingAST*>(declaration) ); + break; + + case NodeType_UsingDirective: + parseUsingDirective( static_cast<UsingDirectiveAST*>(declaration) ); + break; + + case NodeType_Typedef: + parseTypedef( static_cast<TypedefAST*>(declaration) ); + break; + + case NodeType_TemplateDeclaration: + parseTemplateDeclaration( static_cast<TemplateDeclarationAST*>(declaration) ); + break; + + case NodeType_SimpleDeclaration: + parseSimpleDeclaration( static_cast<SimpleDeclarationAST*>(declaration) ); + break; + + case NodeType_FunctionDefinition: + parseFunctionDefinition( static_cast<FunctionDefinitionAST*>(declaration) ); + break; + + case NodeType_AccessDeclaration: + parseAccessDeclaration( static_cast<AccessDeclarationAST*>(declaration) ); + break; + } +} + +void TreeParser::parseLinkageSpecification( LinkageSpecificationAST* ast ) +{ + ////kdDebug(9007) << "TreeParser::parseLinkageSpecification()" << endl; + if( ast->linkageBody() ) + parseLinkageBody( ast->linkageBody() ); + else if( ast->declaration() ) + parseDeclaration( ast->declaration() ); +} + +void TreeParser::parseNamespace( NamespaceAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseNamespace()" << endl; + if( decl->linkageBody() ) + parseLinkageBody( decl->linkageBody() ); +} + +void TreeParser::parseNamespaceAlias( NamespaceAliasAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseNamespaceAlias()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseUsing( UsingAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseUsing()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseUsingDirective( UsingDirectiveAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseUsingDirective()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseTypedef( TypedefAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseTypedef()" << endl; + if( decl->typeSpec() ) + parseTypeSpecifier( decl->typeSpec() ); +} + +void TreeParser::parseTemplateDeclaration( TemplateDeclarationAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseTemplateDeclaration()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseSimpleDeclaration( SimpleDeclarationAST* decl ) +{ + ////kdDebug(9007) << "TreeParser::parseSimpleDeclaration()" << endl; + Q_UNUSED( decl ); +} + +void TreeParser::parseFunctionDefinition( FunctionDefinitionAST* def ) +{ + ////kdDebug(9007) << "TreeParser::parseFunctionDefinition()" << endl; + Q_UNUSED( def ); +} + +void TreeParser::parseLinkageBody( LinkageBodyAST* linkageBody ) +{ + ////kdDebug(9007) << "TreeParser::parseLinkageBody()" << endl; + QPtrList<DeclarationAST> declarations = linkageBody->declarationList(); + for( QPtrListIterator<DeclarationAST> it(declarations); it.current(); ++it ){ + parseDeclaration( it.current() ); + } +} + +void TreeParser::parseTypeSpecifier( TypeSpecifierAST* typeSpec ) +{ + ////kdDebug(9007) << "TreeParser::parseTypeSpecifier()" << endl; + switch( typeSpec->nodeType() ) + { + case NodeType_ClassSpecifier: + parseClassSpecifier( static_cast<ClassSpecifierAST*>(typeSpec) ); + break; + + case NodeType_EnumSpecifier: + parseEnumSpecifier( static_cast<EnumSpecifierAST*>(typeSpec) ); + break; + + case NodeType_ElaboratedTypeSpecifier: + parseElaboratedTypeSpecifier( static_cast<ElaboratedTypeSpecifierAST*>(typeSpec) ); + break; + } +} + +void TreeParser::parseClassSpecifier( ClassSpecifierAST* classSpec ) +{ + ////kdDebug(9007) << "TreeParser::parseClassSpecifier()" << endl; + QPtrList<DeclarationAST> declarations = classSpec->declarationList(); + for( QPtrListIterator<DeclarationAST> it(declarations); it.current(); ++it ){ + parseDeclaration( it.current() ); + } +} + +void TreeParser::parseEnumSpecifier( EnumSpecifierAST* enumSpec ) +{ + ////kdDebug(9007) << "TreeParser::parseEnumSpecifier()" << endl; + Q_UNUSED( enumSpec ); +} + +void TreeParser::parseElaboratedTypeSpecifier( ElaboratedTypeSpecifierAST* typeSpec ) +{ + ////kdDebug(9007) << "TreeParser::parseElaboratedTypeSpecifier()" << endl; + Q_UNUSED( typeSpec ); +} + +void TreeParser::parseAccessDeclaration ( AccessDeclarationAST * access ) +{ + ////kdDebug(9007) << "TreeParser::parseAccessDeclaration()" << endl; + Q_UNUSED( access ); +} + diff --git a/lib/cppparser/tree_parser.h b/lib/cppparser/tree_parser.h new file mode 100644 index 00000000..a7477a16 --- /dev/null +++ b/lib/cppparser/tree_parser.h @@ -0,0 +1,60 @@ +/* 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef __tree_parser_h +#define __tree_parser_h + +#include "ast.h" +class ParsedFile; + +class TreeParser +{ +public: + TreeParser(); + virtual ~TreeParser(); + + // translation-unit + virtual void parseTranslationUnit( const ParsedFile& ); + + // declarations + virtual void parseDeclaration( DeclarationAST* ); + virtual void parseLinkageSpecification( LinkageSpecificationAST* ); + virtual void parseNamespace( NamespaceAST* ); + virtual void parseNamespaceAlias( NamespaceAliasAST* ); + virtual void parseUsing( UsingAST* ); + virtual void parseUsingDirective( UsingDirectiveAST* ); + virtual void parseTypedef( TypedefAST* ); + virtual void parseTemplateDeclaration( TemplateDeclarationAST* ); + virtual void parseSimpleDeclaration( SimpleDeclarationAST* ); + virtual void parseFunctionDefinition( FunctionDefinitionAST* ); + virtual void parseLinkageBody( LinkageBodyAST* ); + virtual void parseAccessDeclaration( AccessDeclarationAST* ); + + // type-specifier + virtual void parseTypeSpecifier( TypeSpecifierAST* ); + virtual void parseClassSpecifier( ClassSpecifierAST* ); + virtual void parseEnumSpecifier( EnumSpecifierAST* ); + virtual void parseElaboratedTypeSpecifier( ElaboratedTypeSpecifierAST* ); + +private: + TreeParser( const TreeParser& source ); + void operator = ( const TreeParser& source ); +}; + +#endif // __tree_parser_h |