diff --git a/.kdev_include_paths b/.kdev_include_paths index a42a356..93a055e 100644 --- a/.kdev_include_paths +++ b/.kdev_include_paths @@ -1,3 +1,5 @@ -../kdevelop/ -../kdevplatform/ +../kdevelop +../kdevplatform /home/sven/projects/.build/kde4/python/parser +/home/sven/projects/kde4/kdevplatform +/home/sven/projects/kde4/kdevelop diff --git a/CMakeLists.txt b/CMakeLists.txt index fc5af3b..83ee3e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,13 +7,14 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${kdevpython_SOURCE_DIR}/cmake/) #complain about the FindKDevelop-PG.cmake file not findable. find_package(KDE4 REQUIRED) -find_package(KDevPlatform 0.9.80 REQUIRED) +find_package(KDevPlatform 1.0.0 REQUIRED) include_directories( ${KDEVPLATFORM_INCLUDE_DIR} ${KDE4_INCLUDES} ${KDE4_INCLUDE_DIR}/threadweaver ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/duchain ${CMAKE_CURRENT_SOURCE_DIR}/parser ${CMAKE_CURRENT_BINARY_DIR}/parser @@ -30,6 +31,7 @@ include_directories( add_subdirectory(parser) add_subdirectory(duchain) +add_subdirectory(codecompletion) set(kdevpythonlanguagesupport_PART_SRCS pythonlanguagesupport.cpp @@ -45,13 +47,15 @@ target_link_libraries(kdevpythonlanguagesupport ${KDEVPLATFORM_LANGUAGE_LIBRARIES} ${KDE4_THREADWEAVER_LIBRARIES} ${KDE4_KTEXTEDITOR_LIBS} + kdev4pythoncompletion kdev4pythonparser kdev4pythonduchain ) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/parser/parserConfig.h.in" "${CMAKE_CURRENT_SOURCE_DIR}/parser/parserConfig.h" ) + install(TARGETS kdevpythonlanguagesupport DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES kdevpythonsupport.desktop DESTINATION ${SERVICES_INSTALL_DIR}) - - - +install(FILES pythonpythonparser.py DESTINATION ${BIN_INSTALL_DIR}) +install(FILES documentation/pydoc.py DESTINATION ${BIN_INSTALL_DIR}) diff --git a/codecompletion/CMakeLists.txt b/codecompletion/CMakeLists.txt new file mode 100644 index 0000000..2cdef3d --- /dev/null +++ b/codecompletion/CMakeLists.txt @@ -0,0 +1,25 @@ +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} +) + +set(completion_SRCS + keyworditem.cpp + functiondeclarationcompletionitem.cpp + importfileitem.cpp + pythoncodecompletioncontext.cpp + pythoncodecompletionmodel.cpp + pythoncodecompletionworker.cpp +) + +kde4_add_library(kdev4pythoncompletion SHARED ${completion_SRCS}) + +target_link_libraries(kdev4pythoncompletion + ${KDE4_KDECORE_LIBS} + ${KDEVPLATFORM_LANGUAGE_LIBRARIES} + ${KDEVPLATFORM_INTERFACES_LIBRARIES} + ${KDEVPLATFORM_PROJECT_LIBRARIES} + kdev4pythonduchain +) + +install(TARGETS kdev4pythoncompletion DESTINATION ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/codecompletion/functiondeclarationcompletionitem.cpp b/codecompletion/functiondeclarationcompletionitem.cpp new file mode 100644 index 0000000..bd8aecd --- /dev/null +++ b/codecompletion/functiondeclarationcompletionitem.cpp @@ -0,0 +1,38 @@ + +#include +#include +#include + +#include +#include + +#include "functiondeclarationcompletionitem.h" +#include "navigation/navigationwidget.h" + +using namespace KDevelop; +using namespace KTextEditor; + +namespace Python { + +FunctionDeclarationCompletionItem::FunctionDeclarationCompletionItem(DeclarationPointer decl) : NormalDeclarationCompletionItem(decl) { } + +void FunctionDeclarationCompletionItem::executed(KTextEditor::Document* document, const KTextEditor::Range& word) +{ + kDebug() << "FunctionDeclarationCompletionItem executed"; + DUChainPointer decl = declaration().dynamicCast(); + Q_ASSERT(decl.data()); + kDebug() << "declaration data: " << decl.data(); + const QString suffix = "()"; + int skip = 2; // place cursor behind bracktes + if ( decl.data()->defaultParametersSize() != 0 ) { + skip = 1; // place cursor in brackets if there's parameters + } + document->replaceText(word, decl.data()->identifier().toString() + suffix); + if ( View* view = document->activeView() ) { + view->setCursorPosition( Cursor(word.end().line(), word.end().column() + skip) ); + } +} + +FunctionDeclarationCompletionItem::~FunctionDeclarationCompletionItem() { } + +} \ No newline at end of file diff --git a/codecompletion/functiondeclarationcompletionitem.h b/codecompletion/functiondeclarationcompletionitem.h new file mode 100644 index 0000000..2efa1a8 --- /dev/null +++ b/codecompletion/functiondeclarationcompletionitem.h @@ -0,0 +1,23 @@ +#ifndef FUNCTIONDECLARATIONCOMPLETIONITEM_H +#define FUNCTIONDECLARATIONCOMPLETIONITEM_H + +#include +#include + +using namespace KDevelop; + +namespace Python { + +class FunctionDeclarationCompletionItem : public KDevelop::NormalDeclarationCompletionItem +{ + +public: + FunctionDeclarationCompletionItem(DeclarationPointer decl); + virtual ~FunctionDeclarationCompletionItem(); + + virtual void executed(KTextEditor::Document* document, const KTextEditor::Range& word); +}; + +} + +#endif // FUNCTIONDECLARATIONCOMPLETIONITEM_H diff --git a/codecompletion/importfileitem.cpp b/codecompletion/importfileitem.cpp new file mode 100644 index 0000000..34965cc --- /dev/null +++ b/codecompletion/importfileitem.cpp @@ -0,0 +1,27 @@ +#include "importfileitem.h" +#include +#include +#include "navigation/navigationwidget.h" + +using namespace KDevelop; + +namespace Python { + +ImportFileItem::ImportFileItem(const KDevelop::IncludeItem& include): AbstractIncludeFileCompletionItem< NavigationWidget >(include) +{ + +} + +ImportFileItem::~ImportFileItem() +{ + +} + +void ImportFileItem::execute(KTextEditor::Document* document, const KTextEditor::Range& word) +{ + kDebug() << "ImportFileItem executed"; + document->replaceText(word, moduleName); +} + + +} \ No newline at end of file diff --git a/codecompletion/importfileitem.h b/codecompletion/importfileitem.h new file mode 100644 index 0000000..edce9a1 --- /dev/null +++ b/codecompletion/importfileitem.h @@ -0,0 +1,26 @@ +#ifndef IMPORTFILEITEM_H +#define IMPORTFILEITEM_H + +#include +#include "navigation/navigationwidget.h" +#include + +namespace Python { + +typedef KDevelop::AbstractIncludeFileCompletionItem IncludeFileItemBase; + +class ImportFileItem : public IncludeFileItemBase +{ + +public: + ImportFileItem(const KDevelop::IncludeItem& include); + virtual ~ImportFileItem(); + + virtual void execute(KTextEditor::Document* document, const KTextEditor::Range& word); + QString moduleName; + KDevelop::IProject* fromProject; +}; + +} + +#endif // IMPORTFILEITEM_H \ No newline at end of file diff --git a/codecompletion/keyworditem.cpp b/codecompletion/keyworditem.cpp new file mode 100644 index 0000000..0ed0c13 --- /dev/null +++ b/codecompletion/keyworditem.cpp @@ -0,0 +1,47 @@ +#include "keyworditem.h" +#include +#include +#include +#include +#include + +using namespace KDevelop; +using namespace KTextEditor; + +namespace Python { + +Python::KeywordItem::KeywordItem(KDevelop::CodeCompletionContext::Ptr context, QString keyword) : NormalDeclarationCompletionItem ( DeclarationPointer(), context, 0 ) +{ + m_keyword = keyword; +} + +void Python::KeywordItem::execute ( KTextEditor::Document* document, const KTextEditor::Range& word ) +{ + document->replaceText(word, m_keyword); +} + +QVariant KeywordItem::data ( const QModelIndex& index, int role, const KDevelop::CodeCompletionModel* model ) const +{ + switch (role) { + case KDevelop::CodeCompletionModel::IsExpandable: + return QVariant(false); + case Qt::DisplayRole: + if (index.column() == KTextEditor::CodeCompletionModel::Name) { + return QVariant(m_keyword); + } else { + return QVariant(""); + } + break; + case KTextEditor::CodeCompletionModel::ItemSelected: + return QVariant(""); + case KTextEditor::CodeCompletionModel::InheritanceDepth: + return QVariant(0); + default: + //pass + break; + } + + return NormalDeclarationCompletionItem::data(index, role, model); +} + +} diff --git a/codecompletion/keyworditem.h b/codecompletion/keyworditem.h new file mode 100644 index 0000000..488153d --- /dev/null +++ b/codecompletion/keyworditem.h @@ -0,0 +1,22 @@ +#ifndef KEYWORDITEM_H +#define KEYWORDITEM_H + +#include + +using namespace KDevelop; + +namespace Python { + +class KeywordItem : public NormalDeclarationCompletionItem +{ + +public: + KeywordItem(KDevelop::CodeCompletionContext::Ptr context, QString keyword); + virtual void execute ( KTextEditor::Document* document, const KTextEditor::Range& word ); + virtual QVariant data ( const QModelIndex& index, int role, const KDevelop::CodeCompletionModel* model ) const; + QString m_keyword; +}; + +} + +#endif // KEYWORDITEM_H diff --git a/codecompletion/pythoncodecompletioncontext.cpp b/codecompletion/pythoncodecompletioncontext.cpp new file mode 100644 index 0000000..61b0a25 --- /dev/null +++ b/codecompletion/pythoncodecompletioncontext.cpp @@ -0,0 +1,284 @@ +/* + * This file is part of KDevelop + * Copyright 2010 Sven Brauch + * Licensed under the GNU GPL + * */ + +#include "pythoncodecompletioncontext.h" + +#include +#include +#include +#include + +#include +#include + +#include "navigation/navigationwidget.h" +#include "importfileitem.h" +#include "functiondeclarationcompletionitem.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "keyworditem.h" + +using namespace KDevelop; + +typedef QPair DeclarationDepthPair; + +namespace Python { + +QList PythonCodeCompletionContext::completionItems(bool& abort, bool fullCompletion) +{ + if ( abort ) + return QList(); + + QList items; + DUChainReadLocker lock(DUChain::lock()); + + if ( m_operation == PythonCodeCompletionContext::NoCompletion ) { + + } + else if ( m_operation == PythonCodeCompletionContext::ImportFileCompletion ) { + m_maxFolderScanDepth = 1; + foreach ( ImportFileItem* item, includeFileItems() ) { + item->includeItem.name = QString(item->moduleName + " (from " + KUrl::relativeUrl(item->fromProject->folder(), item->includeItem.basePath) + ")"); + items << CompletionTreeItemPointer( item ); + } + } + else if ( m_operation == PythonCodeCompletionContext::ImportSubCompletion ) { + kDebug() << "Stuff found for completion: " << findFilesForName(m_subForModule); + foreach ( ImportFileItem* item, findFilesForName(m_subForModule) ) { + item->includeItem.name = QString(item->moduleName + " (from " + KUrl::relativeUrl(item->fromProject->folder(), item->includeItem.basePath) + ")"); + items << CompletionTreeItemPointer( item ); + } + } + else if ( m_operation == PythonCodeCompletionContext::MemberAccessCompletion ) { + // we don't have type support, so we cannot support completing mebers yet. But we can at least prevent kdevelop from opening a pointless + // popup with completion items you don't want + } + else { + // it's stupid to display a 3-letter completion item on manually invoked code completion and makes everything look crowded + if ( m_operation == PythonCodeCompletionContext::NewStatementCompletion && ! fullCompletion ) { + QStringList keywordItems; + keywordItems << "def" << "class" << "lambda" << "global" << "print"; + foreach ( const QString& current, keywordItems ) { + items << CompletionTreeItemPointer(new KeywordItem(KDevelop::CodeCompletionContext::Ptr(this), current)); + } + } + if ( abort ) { + return QList(); + } + QList declarations = m_duContext->allDeclarations(m_position, m_duContext->topContext()); + + DeclarationPointer currentDeclaration; + int count = declarations.length(); + for ( int i = 0; i < count; i++ ) { + if ( abort ) { + return items; + } + currentDeclaration = DeclarationPointer(declarations.at(i).first); + kDebug() << "Adding item: " << currentDeclaration.data()->identifier().identifier().str(); + NormalDeclarationCompletionItem* item; + if ( currentDeclaration.data()->abstractType() && currentDeclaration.data()->abstractType().constData()->whichType() == AbstractType::TypeFunction ) { + kDebug() << "Adding function declaration item"; + item = new FunctionDeclarationCompletionItem(currentDeclaration); + } + else { + item = new NormalDeclarationCompletionItem(currentDeclaration, KDevelop::CodeCompletionContext::Ptr(this)); + } + kDebug() << item->declaration().data()->identifier().identifier().str(); + items << CompletionTreeItemPointer(item); + } + } + + m_searchingForModule.clear(); + m_subForModule.clear(); + + return items; +} + +QList< ImportFileItem* > PythonCodeCompletionContext::findFilesForName(const QString& name) +{ + kDebug() << "Name: " << name; + QStringList resolvedName = name.split("."); + m_maxFolderScanDepth = resolvedName.length() + 1; + m_searchingForModule = resolvedName; + return includeFileItems(); +} + +QList PythonCodeCompletionContext::includeFileItems() { + QList items; + foreach (IProject* project, ICore::self()->projectController()->projects() ) { + foreach ( KDevelop::ProjectFolderItem* folder, project->foldersForUrl( KUrl(project->folder().url()) ) ) { + m_folderStack.push(folder); + items << fileItemsForFolder(folder, project); + m_folderStack.pop(); + } + } + return items; +} + +QList PythonCodeCompletionContext::fileItemsForFolder(KDevelop::ProjectFolderItem* folder, IProject* project) +{ + kDebug() << " +++++ Processing folder: " << folder->folderName(); + kDebug() << "current folder stack count " << m_folderStack.count(); + if ( ! folder ) { + m_dontAddMe = true; + return QList(); + } + bool continue_recursion = true; + bool do_recursion = true; + + kDebug() << m_maxFolderScanDepth << m_folderStack.count() << m_searchingForModule; + + if ( m_maxFolderScanDepth - 1 < m_folderStack.count() ) continue_recursion = false; // we dont offer foo.bar.baz.bang.bar if there's only one dot in the address by now + + if ( m_searchingForModule.length() > 0 && folder->url() != project->folder().url() ) { + if ( m_searchingForModule.length() >= m_folderStack.count() && m_searchingForModule.at(m_folderStack.count() - 2) != folder->folderName() ) { + kDebug() << "Skip: " << m_searchingForModule.at(m_folderStack.count() - 2) << m_searchingForModule << m_folderStack.count() - 2 << folder->folderName(); + m_dontAddMe = true; + return QList(); + } + else if ( m_searchingForModule.at(m_folderStack.count() - 2) != folder->folderName() ) { + m_dontAddMe = true; + return QList(); + } + kDebug() << "USE: " << m_searchingForModule.at(m_folderStack.count() - 2) << m_searchingForModule << m_folderStack << folder->folderName(); + } + + kDebug() << " >>>>> For directory " << folder->folderName() << " : " << "doing recursion: " << do_recursion << "; continuing downwards: " << continue_recursion; + + QList items; + foreach ( KDevelop::ProjectFolderItem* folder, folder->folderList() ) { + if ( ! folder ) continue; + m_folderStack.push(folder); + if ( continue_recursion ) { + kDebug() << "Scanning for include items: " << folder->folderName(); + items << fileItemsForFolder(folder, project); + if ( m_dontAddMe ) { + m_dontAddMe = false; + m_folderStack.pop(); + continue; + } + } + + // only add items when at right level + if ( m_searchingForModule.length() != 0 && m_maxFolderScanDepth != m_folderStack.count() ) { + kDebug() << "CONTINUE: " << m_maxFolderScanDepth << m_folderStack.count(); + if ( m_searchingForModule.length() < m_folderStack.count() ) do_recursion = false; // don't add items from here, we're too deep + else { + // we're not yet deep enough, so don't even add the folder + m_folderStack.pop(); + continue; + } + } + else { + kDebug() << "ADD: " << m_maxFolderScanDepth << m_folderStack.count(); + kDebug() << "adding files and folders from directory " << folder->folderName(); + } + + // Add the folder + IncludeItem* folderItem = new IncludeItem(); + folderItem->basePath = folder->url(); + folderItem->isDirectory = true; + ImportFileItem* importFolderItem = new ImportFileItem(*folderItem); + importFolderItem->fromProject = project; + importFolderItem->moduleName = folder->folderName(); + items << importFolderItem; + + if ( continue_recursion && do_recursion ) { + // Add all sub-items and folders + foreach ( ProjectFileItem* file, folder->fileList() ) { + if ( ! file->fileName().endsWith(".py") || file->fileName() == "__init__.py" ) continue; + IncludeItem* item = new IncludeItem(); + item->basePath = folder->url(); + ImportFileItem* importItem = new ImportFileItem(*item); + importItem->moduleName = file->fileName().replace(".py", ""); + importItem->fromProject = project; + items << importItem; + } + } + m_folderStack.pop(); + } + return items; +} + +PythonCodeCompletionContext::PythonCodeCompletionContext(DUContextPointer context, const QString& text, const KDevelop::CursorInRevision& position, + int depth): CodeCompletionContext(context, text, position, depth) +{ + QString currentLine = "\n" + text.split("\n").last(); // we'll only look at the last line, as 99% of python statements are limited to one line + kDebug() << "Doing auto-completion context scan for: " << currentLine; + + QRegExp importsub("(.*)\n[\\s]*from(.*)import[\\s]*$"); + importsub.setMinimal(true); + bool is_importSub = importsub.exactMatch(currentLine); + QRegExp importsub2("(.*)\n[\\s]*(from(.*)|import(.*))\\.$"); + importsub2.setMinimal(true); + bool is_importSub2 = importsub2.exactMatch(currentLine); + if ( is_importSub || is_importSub2 ) { + QStringList for_module_match; + if ( is_importSub ) for_module_match = importsub.capturedTexts(); + else for_module_match = importsub2.capturedTexts(); + + kDebug() << for_module_match; + + QString for_module; + if ( is_importSub ) for_module = for_module_match.last().replace(" ", ""); + else for_module = for_module_match[3].replace(" ", ""); + + kDebug() << "Matching against module name: " << for_module_match; + m_operation = PythonCodeCompletionContext::ImportSubCompletion; + m_subForModule = for_module; + return; + } + + QRegExp newStatementCompletion("(.*)\n[\\s]*$"); + newStatementCompletion.setMinimal(true); + bool isNewStatementCompletion = newStatementCompletion.exactMatch(currentLine); + if ( isNewStatementCompletion ) { + m_operation = PythonCodeCompletionContext::NewStatementCompletion; + return; + } + + QRegExp importfile("(.*)\n[\\s]*import[\\s]*$"); + importfile.setMinimal(true); + bool is_importfile = importfile.exactMatch(currentLine); + QRegExp fromimport("(.*)\n[\\s]*from[\\s]*$"); + fromimport.setMinimal(true); + bool is_fromimport = fromimport.exactMatch(currentLine); + if ( is_importfile || is_fromimport ) { + m_operation = PythonCodeCompletionContext::ImportFileCompletion; + return; + } + + QRegExp attributeAccess("(.*)\n[\\s]*(.*)\\.$"); + attributeAccess.setMinimal(true); + bool is_attributeAccess = attributeAccess.exactMatch(currentLine); + if ( is_attributeAccess ) { + m_operation = PythonCodeCompletionContext::MemberAccessCompletion; + return; + } + + QRegExp noCompletionPossible("(.*)\n[\\s]*(class|def)[\\s]*$"); + noCompletionPossible.setMinimal(true); + bool is_noCompletionPossible = noCompletionPossible.exactMatch(currentLine); + if ( is_noCompletionPossible ) { + m_operation = PythonCodeCompletionContext::NoCompletion; + return; + } + + QRegExp memberaccess(""); + kDebug() << "Is import file: " << is_importfile; +// Q_ASSERT(false); +} + + +} diff --git a/codecompletion/pythoncodecompletioncontext.h b/codecompletion/pythoncodecompletioncontext.h new file mode 100644 index 0000000..45123f8 --- /dev/null +++ b/codecompletion/pythoncodecompletioncontext.h @@ -0,0 +1,49 @@ +#ifndef PYTHONCODECOMPLETIONCONTEXT_H +#define PYTHONCODECOMPLETIONCONTEXT_H + +#include +#include "pythoncompletionexport.h" +#include +#include +#include + +using namespace KDevelop; + +namespace KDevelop { + class IProject; + class ProjectFolderItem; +} + +namespace Python { + +class KDEVPYTHONCOMPLETION_EXPORT PythonCodeCompletionContext : public KDevelop::CodeCompletionContext +{ +public: + enum CompletionContextType { + ImportFileCompletion, + MemberAccessCompletion, + DefaultCompletion, + ImportSubCompletion, + NoCompletion, + NewStatementCompletion + }; + + PythonCodeCompletionContext(DUContextPointer context, const QString& text, const KDevelop::CursorInRevision& position, int depth); + virtual QList< KDevelop::CompletionTreeItemPointer > completionItems(bool& abort, bool fullCompletion = true); + QList includeFileItems(); + QList fileItemsForFolder(KDevelop::ProjectFolderItem* folder, KDevelop::IProject* project); + QList findFilesForName(const QString& name); + + CompletionContextType m_operation; + QStack m_folderStack; + int m_maxFolderScanDepth; + QStringList m_searchingForModule; + QString m_subForModule; + +private: + bool m_dontAddMe; +}; + +} + +#endif // PYTHONCODECOMPLETIONCONTEXT_H diff --git a/codecompletion/pythoncodecompletionmodel.cpp b/codecompletion/pythoncodecompletionmodel.cpp new file mode 100644 index 0000000..278a21c --- /dev/null +++ b/codecompletion/pythoncodecompletionmodel.cpp @@ -0,0 +1,32 @@ +/* + * This file is part of KDevelop + * Copyright 2010 Sven Brauch + * Licensed under the GNU GPL + * */ + +#include "pythoncodecompletionmodel.h" +#include "pythoncodecompletionworker.h" +#include "ktexteditor/view.h" + +namespace Python { + +PythonCodeCompletionModel::PythonCodeCompletionModel(QObject* parent) + : CodeCompletionModel(parent) +{ + +} + +PythonCodeCompletionModel::~PythonCodeCompletionModel() { } + + +KTextEditor::Range PythonCodeCompletionModel::completionRange(KTextEditor::View* view, const KTextEditor::Cursor& position) +{ + return KTextEditor::CodeCompletionModelControllerInterface3::completionRange(view, position); +} + +KDevelop::CodeCompletionWorker* PythonCodeCompletionModel::createCompletionWorker() +{ + return new PythonCodeCompletionWorker(this); +} + +} \ No newline at end of file diff --git a/codecompletion/pythoncodecompletionmodel.h b/codecompletion/pythoncodecompletionmodel.h new file mode 100644 index 0000000..77805e5 --- /dev/null +++ b/codecompletion/pythoncodecompletionmodel.h @@ -0,0 +1,23 @@ +#ifndef PYTHONCODECOMPLETIONMODEL_H +#define PYTHONCODECOMPLETIONMODEL_H + +#include +#include +#include "pythoncompletionexport.h" + +namespace Python { + +class KDEVPYTHONCOMPLETION_EXPORT PythonCodeCompletionModel : public KDevelop::CodeCompletionModel +{ + +public: + PythonCodeCompletionModel(QObject* parent); + virtual ~PythonCodeCompletionModel(); + + virtual KDevelop::CodeCompletionWorker* createCompletionWorker(); + KTextEditor::Range completionRange(KTextEditor::View* view, const KTextEditor::Cursor &position); +}; + +} + +#endif // PYTHONCODECOMPLETIONMODEL_H diff --git a/codecompletion/pythoncodecompletionworker.cpp b/codecompletion/pythoncodecompletionworker.cpp new file mode 100644 index 0000000..bd6abbd --- /dev/null +++ b/codecompletion/pythoncodecompletionworker.cpp @@ -0,0 +1,27 @@ +/* + * This file is part of KDevelop + * Copyright 2010 Sven Brauch + * Licensed under the GNU GPL + * */ + +#include "pythoncodecompletionworker.h" +#include "pythoncodecompletionmodel.h" +#include "pythoncodecompletioncontext.h" + + +namespace Python { + +PythonCodeCompletionWorker::PythonCodeCompletionWorker(PythonCodeCompletionModel *parent) + : KDevelop::CodeCompletionWorker(parent) +{ + +} + +KDevelop::CodeCompletionContext* PythonCodeCompletionWorker::createCompletionContext(KDevelop::DUContextPointer context, const QString& contextText, const QString& /*followingText*/, const KDevelop::CursorInRevision& position) const +{ + PythonCodeCompletionContext* completionContext = new PythonCodeCompletionContext(context, contextText, position, 0); + return completionContext; +} + + +} \ No newline at end of file diff --git a/codecompletion/pythoncodecompletionworker.h b/codecompletion/pythoncodecompletionworker.h new file mode 100644 index 0000000..0cef1b0 --- /dev/null +++ b/codecompletion/pythoncodecompletionworker.h @@ -0,0 +1,21 @@ +#ifndef PYTHONCODECOMPLETIONWORKER_H +#define PYTHONCODECOMPLETIONWORKER_H + +#include "pythoncodecompletionmodel.h" +#include +#include +#include "pythoncompletionexport.h" + +namespace Python { + +class KDEVPYTHONCOMPLETION_EXPORT PythonCodeCompletionWorker : public KDevelop::CodeCompletionWorker +{ + +public: + PythonCodeCompletionWorker(PythonCodeCompletionModel *parent); + virtual KDevelop::CodeCompletionContext* createCompletionContext(KDevelop::DUContextPointer context, const QString& contextText, const QString& followingText, const KDevelop::CursorInRevision& position) const; +}; + +#endif // PYTHONCODECOMPLETIONWORKER_H + +} \ No newline at end of file diff --git a/codecompletion/pythoncompletionexport.h b/codecompletion/pythoncompletionexport.h new file mode 100644 index 0000000..c4dc416 --- /dev/null +++ b/codecompletion/pythoncompletionexport.h @@ -0,0 +1,16 @@ +#ifndef PYTHONCOMPLETIONEXPORT_H +#define PYTHONCOMPLETIONEXPORT_H + +/* needed for KDE_EXPORT macros */ +#include + + +#ifndef KDEVPYTHONCOMPLETION_EXPORT +# ifdef MAKE_KDEV4PYTHONCOMPLETION_LIB +# define KDEVPYTHONCOMPLETION_EXPORT KDE_EXPORT +# else +# define KDEVPYTHONCOMPLETION_EXPORT KDE_IMPORT +# endif +#endif + +#endif \ No newline at end of file diff --git a/documentation/pydoc.py b/documentation/pydoc.py new file mode 100755 index 0000000..a197c20 --- /dev/null +++ b/documentation/pydoc.py @@ -0,0 +1,2341 @@ +#!/usr/bin/env python +# -*- coding: latin-1 -*- +"""Generate Python documentation in HTML or text for interactive use. + +In the Python interpreter, do "from pydoc import help" to provide online +help. Calling help(thing) on a Python object documents the object. + +Or, at the shell command line outside of Python: + +Run "pydoc " to show documentation on something. may be +the name of a function, module, package, or a dotted reference to a +class or function within a module or module in a package. If the +argument contains a path segment delimiter (e.g. slash on Unix, +backslash on Windows) it is treated as the path to a Python source file. + +Run "pydoc -k " to search for a keyword in the synopsis lines +of all available modules. + +Run "pydoc -p " to start an HTTP server on a given port on the +local machine to generate documentation web pages. + +For platforms without a command line, "pydoc -g" starts the HTTP server +and also pops up a little window for controlling it. + +Run "pydoc -w " to write out the HTML documentation for a module +to a file named ".html". + +Module docs for core modules are assumed to be in + + http://docs.python.org/library/ + +This can be overridden by setting the PYTHONDOCS environment variable +to a different URL or to a local directory containing the Library +Reference Manual pages. +""" + +__author__ = "Ka-Ping Yee " +__date__ = "26 February 2001" + +__version__ = "$Revision: 79544 $" +__credits__ = """Guido van Rossum, for an excellent programming language. +Tommy Burnette, the original creator of manpy. +Paul Prescod, for all his work on onlinehelp. +Richard Chamberlain, for the first implementation of textdoc. +""" + +# Known bugs that can't be fixed here: +# - imp.load_module() cannot be prevented from clobbering existing +# loaded modules, so calling synopsis() on a binary module file +# changes the contents of any existing module with the same name. +# - If the __file__ attribute on a module is a relative path and +# the current directory is changed with os.chdir(), an incorrect +# path will be displayed. + +import sys, imp, os, re, types, inspect, __builtin__, pkgutil +from repr import Repr +from string import expandtabs, find, join, lower, split, strip, rfind, rstrip +from traceback import extract_tb +try: + from collections import deque +except ImportError: + # Python 2.3 compatibility + class deque(list): + def popleft(self): + return self.pop(0) + +# --------------------------------------------------------- common routines + +def pathdirs(): + """Convert sys.path into a list of absolute, existing, unique paths.""" + dirs = [] + normdirs = [] + for dir in sys.path: + dir = os.path.abspath(dir or '.') + normdir = os.path.normcase(dir) + if normdir not in normdirs and os.path.isdir(dir): + dirs.append(dir) + normdirs.append(normdir) + return dirs + +def getdoc(object): + """Get the doc string or comments for an object.""" + result = inspect.getdoc(object) or inspect.getcomments(object) + return result and re.sub('^ *\n', '', rstrip(result)) or '' + +def splitdoc(doc): + """Split a doc string into a synopsis line (if any) and the rest.""" + lines = split(strip(doc), '\n') + if len(lines) == 1: + return lines[0], '' + elif len(lines) >= 2 and not rstrip(lines[1]): + return lines[0], join(lines[2:], '\n') + return '', join(lines, '\n') + +def classname(object, modname): + """Get a class name and qualify it with a module name if necessary.""" + name = object.__name__ + if object.__module__ != modname: + name = object.__module__ + '.' + name + return name + +def isdata(object): + """Check if an object is of a type that probably means it's data.""" + return not (inspect.ismodule(object) or inspect.isclass(object) or + inspect.isroutine(object) or inspect.isframe(object) or + inspect.istraceback(object) or inspect.iscode(object)) + +def replace(text, *pairs): + """Do a series of global replacements on a string.""" + while pairs: + text = join(split(text, pairs[0]), pairs[1]) + pairs = pairs[2:] + return text + +def cram(text, maxlen): + """Omit part of a string if needed to make it fit in a maximum length.""" + if len(text) > maxlen: + pre = max(0, (maxlen-3)//2) + post = max(0, maxlen-3-pre) + return text[:pre] + '...' + text[len(text)-post:] + return text + +_re_stripid = re.compile(r' at 0x[0-9a-f]{6,16}(>+)$', re.IGNORECASE) +def stripid(text): + """Remove the hexadecimal id from a Python object representation.""" + # The behaviour of %p is implementation-dependent in terms of case. + return _re_stripid.sub(r'\1', text) + +def _is_some_method(obj): + return inspect.ismethod(obj) or inspect.ismethoddescriptor(obj) + +def allmethods(cl): + methods = {} + for key, value in inspect.getmembers(cl, _is_some_method): + methods[key] = 1 + for base in cl.__bases__: + methods.update(allmethods(base)) # all your base are belong to us + for key in methods.keys(): + methods[key] = getattr(cl, key) + return methods + +def _split_list(s, predicate): + """Split sequence s via predicate, and return pair ([true], [false]). + + The return value is a 2-tuple of lists, + ([x for x in s if predicate(x)], + [x for x in s if not predicate(x)]) + """ + + yes = [] + no = [] + for x in s: + if predicate(x): + yes.append(x) + else: + no.append(x) + return yes, no + +def visiblename(name, all=None): + """Decide whether to show documentation on a variable.""" + # Certain special names are redundant. + _hidden_names = ('__builtins__', '__doc__', '__file__', '__path__', + '__module__', '__name__', '__slots__', '__package__') + if name in _hidden_names: return 0 + # Private names are hidden, but special names are displayed. + if name.startswith('__') and name.endswith('__'): return 1 + if all is not None: + # only document that which the programmer exported in __all__ + return name in all + else: + return not name.startswith('_') + +def classify_class_attrs(object): + """Wrap inspect.classify_class_attrs, with fixup for data descriptors.""" + def fixup(data): + name, kind, cls, value = data + if inspect.isdatadescriptor(value): + kind = 'data descriptor' + return name, kind, cls, value + return map(fixup, inspect.classify_class_attrs(object)) + +# ----------------------------------------------------- module manipulation + +def ispackage(path): + """Guess whether a path refers to a package directory.""" + if os.path.isdir(path): + for ext in ('.py', '.pyc', '.pyo'): + if os.path.isfile(os.path.join(path, '__init__' + ext)): + return True + return False + +def source_synopsis(file): + line = file.readline() + while line[:1] == '#' or not strip(line): + line = file.readline() + if not line: break + line = strip(line) + if line[:4] == 'r"""': line = line[1:] + if line[:3] == '"""': + line = line[3:] + if line[-1:] == '\\': line = line[:-1] + while not strip(line): + line = file.readline() + if not line: break + result = strip(split(line, '"""')[0]) + else: result = None + return result + +def synopsis(filename, cache={}): + """Get the one-line summary out of a module file.""" + mtime = os.stat(filename).st_mtime + lastupdate, result = cache.get(filename, (0, None)) + if lastupdate < mtime: + info = inspect.getmoduleinfo(filename) + try: + file = open(filename) + except IOError: + # module can't be opened, so skip it + return None + if info and 'b' in info[2]: # binary modules have to be imported + try: module = imp.load_module('__temp__', file, filename, info[1:]) + except: return None + result = (module.__doc__ or '').splitlines()[0] + del sys.modules['__temp__'] + else: # text modules can be directly examined + result = source_synopsis(file) + file.close() + cache[filename] = (mtime, result) + return result + +class ErrorDuringImport(Exception): + """Errors that occurred while trying to import something to document it.""" + def __init__(self, filename, exc_info): + exc, value, tb = exc_info + self.filename = filename + self.exc = exc + self.value = value + self.tb = tb + + def __str__(self): + exc = self.exc + if type(exc) is types.ClassType: + exc = exc.__name__ + return 'problem in %s - %s: %s' % (self.filename, exc, self.value) + +def importfile(path): + """Import a Python source file or compiled file given its path.""" + magic = imp.get_magic() + file = open(path, 'r') + if file.read(len(magic)) == magic: + kind = imp.PY_COMPILED + else: + kind = imp.PY_SOURCE + file.close() + filename = os.path.basename(path) + name, ext = os.path.splitext(filename) + file = open(path, 'r') + try: + module = imp.load_module(name, file, path, (ext, 'r', kind)) + except: + raise ErrorDuringImport(path, sys.exc_info()) + file.close() + return module + +def safeimport(path, forceload=0, cache={}): + """Import a module; handle errors; return None if the module isn't found. + + If the module *is* found but an exception occurs, it's wrapped in an + ErrorDuringImport exception and reraised. Unlike __import__, if a + package path is specified, the module at the end of the path is returned, + not the package at the beginning. If the optional 'forceload' argument + is 1, we reload the module from disk (unless it's a dynamic extension).""" + try: + # If forceload is 1 and the module has been previously loaded from + # disk, we always have to reload the module. Checking the file's + # mtime isn't good enough (e.g. the module could contain a class + # that inherits from another module that has changed). + if forceload and path in sys.modules: + if path not in sys.builtin_module_names: + # Avoid simply calling reload() because it leaves names in + # the currently loaded module lying around if they're not + # defined in the new source file. Instead, remove the + # module from sys.modules and re-import. Also remove any + # submodules because they won't appear in the newly loaded + # module's namespace if they're already in sys.modules. + subs = [m for m in sys.modules if m.startswith(path + '.')] + for key in [path] + subs: + # Prevent garbage collection. + cache[key] = sys.modules[key] + del sys.modules[key] + module = __import__(path) + except: + # Did the error occur before or after the module was found? + (exc, value, tb) = info = sys.exc_info() + if path in sys.modules: + # An error occurred while executing the imported module. + raise ErrorDuringImport(sys.modules[path].__file__, info) + elif exc is SyntaxError: + # A SyntaxError occurred before we could execute the module. + raise ErrorDuringImport(value.filename, info) + elif exc is ImportError and extract_tb(tb)[-1][2]=='safeimport': + # The import error occurred directly in this function, + # which means there is no such module in the path. + return None + else: + # Some other error occurred during the importing process. + raise ErrorDuringImport(path, sys.exc_info()) + for part in split(path, '.')[1:]: + try: module = getattr(module, part) + except AttributeError: return None + return module + +# ---------------------------------------------------- formatter base class + +class Doc: + def document(self, object, name=None, *args): + """Generate documentation for an object.""" + args = (object, name) + args + # 'try' clause is to attempt to handle the possibility that inspect + # identifies something in a way that pydoc itself has issues handling; + # think 'super' and how it is a descriptor (which raises the exception + # by lacking a __name__ attribute) and an instance. + if inspect.isgetsetdescriptor(object): return self.docdata(*args) + if inspect.ismemberdescriptor(object): return self.docdata(*args) + try: + if inspect.ismodule(object): return self.docmodule(*args) + if inspect.isclass(object): return self.docclass(*args) + if inspect.isroutine(object): return self.docroutine(*args) + except AttributeError: + pass + if isinstance(object, property): return self.docproperty(*args) + return self.docother(*args) + + def fail(self, object, name=None, *args): + """Raise an exception for unimplemented types.""" + message = "don't know how to document object%s of type %s" % ( + name and ' ' + repr(name), type(object).__name__) + raise TypeError, message + + docmodule = docclass = docroutine = docother = docproperty = docdata = fail + + def getdocloc(self, object): + """Return the location of module docs or None""" + + try: + file = inspect.getabsfile(object) + except TypeError: + file = '(built-in)' + + docloc = os.environ.get("PYTHONDOCS", + "http://docs.python.org/library") + basedir = os.path.join(sys.exec_prefix, "lib", + "python"+sys.version[0:3]) + if (isinstance(object, type(os)) and + (object.__name__ in ('errno', 'exceptions', 'gc', 'imp', + 'marshal', 'posix', 'signal', 'sys', + 'thread', 'zipimport') or + (file.startswith(basedir) and + not file.startswith(os.path.join(basedir, 'site-packages')))) and + object.__name__ not in ('xml.etree', 'test.pydoc_mod')): + if docloc.startswith("http://"): + docloc = "%s/%s" % (docloc.rstrip("/"), object.__name__) + else: + docloc = os.path.join(docloc, object.__name__ + ".html") + else: + docloc = None + return docloc + +# -------------------------------------------- HTML documentation generator + +class HTMLRepr(Repr): + """Class for safely making an HTML representation of a Python object.""" + def __init__(self): + Repr.__init__(self) + self.maxlist = self.maxtuple = 20 + self.maxdict = 10 + self.maxstring = self.maxother = 100 + + def escape(self, text): + return replace(text, '&', '&', '<', '<', '>', '>') + + def repr(self, object): + return Repr.repr(self, object) + + def repr1(self, x, level): + if hasattr(type(x), '__name__'): + methodname = 'repr_' + join(split(type(x).__name__), '_') + if hasattr(self, methodname): + return getattr(self, methodname)(x, level) + return self.escape(cram(stripid(repr(x)), self.maxother)) + + def repr_string(self, x, level): + test = cram(x, self.maxstring) + testrepr = repr(test) + if '\\' in test and '\\' not in replace(testrepr, r'\\', ''): + # Backslashes are only literal in the string and are never + # needed to make any special characters, so show a raw string. + return 'r' + testrepr[0] + self.escape(test) + testrepr[0] + return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)', + r'\1', + self.escape(testrepr)) + + repr_str = repr_string + + def repr_instance(self, x, level): + try: + return self.escape(cram(stripid(repr(x)), self.maxstring)) + except: + return self.escape('<%s instance>' % x.__class__.__name__) + + repr_unicode = repr_string + +class HTMLDoc(Doc): + """Formatter class for HTML documentation.""" + + # ------------------------------------------- HTML formatting utilities + + _repr_instance = HTMLRepr() + repr = _repr_instance.repr + escape = _repr_instance.escape + + def page(self, title, contents): + """Format an HTML page.""" + return ''' + +Python: %s + + +%s +''' % (title, contents) + + def heading(self, title, fgcol, bgcol, extras=''): + """Format a page heading.""" + return '''

%s

%s''' % (fgcol, title, extras or ' ') + + def section(self, title, fgcol, bgcol, contents, width=6, + prelude='', marginalia=None, gap=' '): + """Format a section with a heading.""" + if marginalia is None: + marginalia = '' + ' ' * width + '' + result = '''

%s

''' % (bgcol, bgcol, title) + if prelude: + result = result + '''
%s
''' % (prelude) + + return result + '\n%s
' % contents + + def bigsection(self, title, *args): + """Format a section with a big heading.""" + title = '%s' % title + return self.section(title, *args) + + def preformat(self, text): + """Format literal preformatted text.""" + text = self.escape(expandtabs(text)) + return replace(text, '\n\n', '\n \n', '\n\n', '\n \n', + ' ', ' ', '\n', '
\n') + + def multicolumn(self, list, format, cols=3): + """Format a list of items into a multi-column list.""" + result = '' + rows = (len(list)+cols-1)/cols + for col in range(cols): + result = result + '' % (100/cols) + for i in range(rows*col, rows*col+rows): + if i < len(list): + result = result + format(list[i]) + '
\n' + result = result + '' + return '%s
' % result + + def grey(self, text): return '%s' % text + + def namelink(self, name, *dicts): + """Make a link for an identifier, given name-to-URL mappings.""" + for dict in dicts: + if name in dict: + return '%s' % (dict[name], name) + return name + + def classlink(self, object, modname): + """Make a link for a class.""" + name, module = object.__name__, sys.modules.get(object.__module__) + if hasattr(module, name) and getattr(module, name) is object: + return '%s' % ( + module.__name__, name, classname(object, modname)) + return classname(object, modname) + + def modulelink(self, object): + """Make a link for a module.""" + return '%s' % (object.__name__, object.__name__) + + def modpkglink(self, data): + """Make a link for a module or package to display in an index.""" + name, path, ispackage, shadowed = data + if shadowed: + return self.grey(name) + if path: + url = '%s.%s.html' % (path, name) + else: + url = '%s.html' % name + if ispackage: + text = '%s (package)' % name + else: + text = name + return '%s' % (url, text) + + def markup(self, text, escape=None, funcs={}, classes={}, methods={}): + """Mark up some plain text, given a context of symbols to look for. + Each context dictionary maps object names to anchor names.""" + escape = escape or self.escape + results = [] + here = 0 + pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|' + r'RFC[- ]?(\d+)|' + r'PEP[- ]?(\d+)|' + r'(self\.)?(\w+))') + while True: + match = pattern.search(text, here) + if not match: break + start, end = match.span() + results.append(escape(text[here:start])) + + all, scheme, rfc, pep, selfdot, name = match.groups() + if scheme: + url = escape(all).replace('"', '"') + results.append('%s' % (url, url)) + elif rfc: + url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) + results.append('%s' % (url, escape(all))) + elif pep: + url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep) + results.append('%s' % (url, escape(all))) + elif text[end:end+1] == '(': + results.append(self.namelink(name, methods, funcs, classes)) + elif selfdot: + results.append('self.%s' % name) + else: + results.append(self.namelink(name, classes)) + here = end + results.append(escape(text[here:])) + return join(results, '') + + # ---------------------------------------------- type-specific routines + + def formattree(self, tree, modname, parent=None): + """Produce HTML for a class tree as given by inspect.getclasstree().""" + result = '' + for entry in tree: + if type(entry) is type(()): + c, bases = entry + result = result + '' + result = result + self.classlink(c, modname) + if bases and bases != (parent,): + parents = [] + for base in bases: + parents.append(self.classlink(base, modname)) + result = result + '(' + join(parents, ', ') + ')' + result = result + '\n' + elif type(entry) is type([]): + result = result + '
\n%s
\n' % self.formattree( + entry, modname, c) + return '
\n%s
\n' % result + + def docmodule(self, object, name=None, mod=None, *ignored): + """Produce HTML documentation for a module object.""" + name = object.__name__ # ignore the passed-in name + try: + all = object.__all__ + except AttributeError: + all = None + parts = split(name, '.') + links = [] + for i in range(len(parts)-1): + links.append( + '%s' % + (join(parts[:i+1], '.'), parts[i])) + linkedname = join(links + parts[-1:], '.') + head = '%s' % linkedname + try: + path = inspect.getabsfile(object) + url = path + if sys.platform == 'win32': + import nturl2path + url = nturl2path.pathname2url(path) + filelink = '%s' % (url, path) + except TypeError: + filelink = '(built-in)' + info = [] + if hasattr(object, '__version__'): + version = str(object.__version__) + if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': + version = strip(version[11:-1]) + info.append('version %s' % self.escape(version)) + if hasattr(object, '__date__'): + info.append(self.escape(str(object.__date__))) + if info: + head = head + ' (%s)' % join(info, ', ') + docloc = self.getdocloc(object) + if docloc is not None: + docloc = '
Module Docs' % locals() + else: + docloc = '' + result = self.heading( + head, '#ffffff', '#7799ee', + 'index
' + filelink + docloc) + + modules = inspect.getmembers(object, inspect.ismodule) + + classes, cdict = [], {} + for key, value in inspect.getmembers(object, inspect.isclass): + # if __all__ exists, believe it. Otherwise use old heuristic. + if (all is not None or + (inspect.getmodule(value) or object) is object): + if visiblename(key, all): + classes.append((key, value)) + cdict[key] = cdict[value] = '#' + key + for key, value in classes: + for base in value.__bases__: + key, modname = base.__name__, base.__module__ + module = sys.modules.get(modname) + if modname != name and module and hasattr(module, key): + if getattr(module, key) is base: + if not key in cdict: + cdict[key] = cdict[base] = modname + '.html#' + key + funcs, fdict = [], {} + for key, value in inspect.getmembers(object, inspect.isroutine): + # if __all__ exists, believe it. Otherwise use old heuristic. + if (all is not None or + inspect.isbuiltin(value) or inspect.getmodule(value) is object): + if visiblename(key, all): + funcs.append((key, value)) + fdict[key] = '#-' + key + if inspect.isfunction(value): fdict[value] = fdict[key] + data = [] + for key, value in inspect.getmembers(object, isdata): + if visiblename(key, all): + data.append((key, value)) + + doc = self.markup(getdoc(object), self.preformat, fdict, cdict) + doc = doc and '%s' % doc + result = result + '

%s

\n' % doc + + if hasattr(object, '__path__'): + modpkgs = [] + for importer, modname, ispkg in pkgutil.iter_modules(object.__path__): + modpkgs.append((modname, name, ispkg, 0)) + modpkgs.sort() + contents = self.multicolumn(modpkgs, self.modpkglink) + result = result + self.bigsection( + 'Package Contents', '#ffffff', '#aa55cc', contents) + elif modules: + contents = self.multicolumn( + modules, lambda key_value, s=self: s.modulelink(key_value[1])) + result = result + self.bigsection( + 'Modules', '#ffffff', '#aa55cc', contents) + + if classes: + classlist = map(lambda key_value: key_value[1], classes) + contents = [ + self.formattree(inspect.getclasstree(classlist, 1), name)] + for key, value in classes: + contents.append(self.document(value, key, name, fdict, cdict)) + result = result + self.bigsection( + 'Classes', '#ffffff', '#ee77aa', join(contents)) + if funcs: + contents = [] + for key, value in funcs: + contents.append(self.document(value, key, name, fdict, cdict)) + result = result + self.bigsection( + 'Functions', '#ffffff', '#eeaa77', join(contents)) + if data: + contents = [] + for key, value in data: + contents.append(self.document(value, key)) + result = result + self.bigsection( + 'Data', '#ffffff', '#55aa55', join(contents, '
\n')) + if hasattr(object, '__author__'): + contents = self.markup(str(object.__author__), self.preformat) + result = result + self.bigsection( + 'Author', '#ffffff', '#7799ee', contents) + if hasattr(object, '__credits__'): + contents = self.markup(str(object.__credits__), self.preformat) + result = result + self.bigsection( + 'Credits', '#ffffff', '#7799ee', contents) + + return result + + def docclass(self, object, name=None, mod=None, funcs={}, classes={}, + *ignored): + """Produce HTML documentation for a class object.""" + realname = object.__name__ + name = name or realname + bases = object.__bases__ + + contents = [] + push = contents.append + + # Cute little class to pump out a horizontal rule between sections. + class HorizontalRule: + def __init__(self): + self.needone = 0 + def maybe(self): + if self.needone: + push('
\n') + self.needone = 1 + hr = HorizontalRule() + + # List the mro, if non-trivial. + mro = deque(inspect.getmro(object)) + if len(mro) > 2: + hr.maybe() + push('
Method resolution order:
\n') + for base in mro: + push('
%s
\n' % self.classlink(base, + object.__module__)) + push('
\n') + + def spill(msg, attrs, predicate): + ok, attrs = _split_list(attrs, predicate) + if ok: + hr.maybe() + push(msg) + for name, kind, homecls, value in ok: + push(self.document(getattr(object, name), name, mod, + funcs, classes, mdict, object)) + push('\n') + return attrs + + def spilldescriptors(msg, attrs, predicate): + ok, attrs = _split_list(attrs, predicate) + if ok: + hr.maybe() + push(msg) + for name, kind, homecls, value in ok: + push(self._docdescriptor(name, value, mod)) + return attrs + + def spilldata(msg, attrs, predicate): + ok, attrs = _split_list(attrs, predicate) + if ok: + hr.maybe() + push(msg) + for name, kind, homecls, value in ok: + base = self.docother(getattr(object, name), name, mod) + if (hasattr(value, '__call__') or + inspect.isdatadescriptor(value)): + doc = getattr(value, "__doc__", None) + else: + doc = None + if doc is None: + push('
%s
\n' % base) + else: + doc = self.markup(getdoc(value), self.preformat, + funcs, classes, mdict) + doc = '
%s' % doc + push('
%s%s
\n' % (base, doc)) + push('\n') + return attrs + + attrs = filter(lambda data: visiblename(data[0]), + classify_class_attrs(object)) + mdict = {} + for key, kind, homecls, value in attrs: + mdict[key] = anchor = '#' + name + '-' + key + value = getattr(object, key) + try: + # The value may not be hashable (e.g., a data attr with + # a dict or list value). + mdict[value] = anchor + except TypeError: + pass + + while attrs: + if mro: + thisclass = mro.popleft() + else: + thisclass = attrs[0][2] + attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass) + + if thisclass is __builtin__.object: + attrs = inherited + continue + elif thisclass is object: + tag = 'defined here' + else: + tag = 'inherited from %s' % self.classlink(thisclass, + object.__module__) + tag += ':
\n' + + # Sort attrs by name. + try: + attrs.sort(key=lambda t: t[0]) + except TypeError: + attrs.sort(lambda t1, t2: cmp(t1[0], t2[0])) # 2.3 compat + + # Pump out the attrs, segregated by kind. + attrs = spill('Methods %s' % tag, attrs, + lambda t: t[1] == 'method') + attrs = spill('Class methods %s' % tag, attrs, + lambda t: t[1] == 'class method') + attrs = spill('Static methods %s' % tag, attrs, + lambda t: t[1] == 'static method') + attrs = spilldescriptors('Data descriptors %s' % tag, attrs, + lambda t: t[1] == 'data descriptor') + attrs = spilldata('Data and other attributes %s' % tag, attrs, + lambda t: t[1] == 'data') + assert attrs == [] + attrs = inherited + + contents = ''.join(contents) + + if name == realname: + title = 'class %s' % ( + name, realname) + else: + title = '%s = class %s' % ( + name, name, realname) + if bases: + parents = [] + for base in bases: + parents.append(self.classlink(base, object.__module__)) + title = title + '(%s)' % join(parents, ', ') + doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict) + doc = doc and '%s
 
' % doc + + return self.section(title, '#000000', '#ffc8d8', contents, 3, doc) + + def formatvalue(self, object): + """Format an argument default value as text.""" + return self.grey('=' + self.repr(object)) + + def docroutine(self, object, name=None, mod=None, + funcs={}, classes={}, methods={}, cl=None): + """Produce HTML documentation for a function or method object.""" + realname = object.__name__ + name = name or realname + anchor = (cl and cl.__name__ or '') + '-' + name + note = '' + skipdocs = 0 + if inspect.ismethod(object): + imclass = object.im_class + if cl: + if imclass is not cl: + note = ' from ' + self.classlink(imclass, mod) + else: + if object.im_self is not None: + note = ' method of %s instance' % self.classlink( + object.im_self.__class__, mod) + else: + note = ' unbound %s method' % self.classlink(imclass,mod) + object = object.im_func + + if name == realname: + title = '%s' % (anchor, realname) + else: + if (cl and realname in cl.__dict__ and + cl.__dict__[realname] is object): + reallink = '%s' % ( + cl.__name__ + '-' + realname, realname) + skipdocs = 1 + else: + reallink = realname + title = '%s = %s' % ( + anchor, name, reallink) + if inspect.isfunction(object): + args, varargs, varkw, defaults = inspect.getargspec(object) + argspec = inspect.formatargspec( + args, varargs, varkw, defaults, formatvalue=self.formatvalue) + if realname == '': + title = '%s lambda ' % name + argspec = argspec[1:-1] # remove parentheses + else: + argspec = '(...)' + + decl = title + argspec + (note and self.grey( + '%s' % note)) + + if skipdocs: + return '
%s
\n' % decl + else: + doc = self.markup( + getdoc(object), self.preformat, funcs, classes, methods) + doc = doc and '
%s
' % doc + return '
%s
%s
\n' % (decl, doc) + + def _docdescriptor(self, name, value, mod): + results = [] + push = results.append + + if name: + push('
%s
\n' % name) + if value.__doc__ is not None: + doc = self.markup(getdoc(value), self.preformat) + push('
%s
\n' % doc) + push('
\n') + + return ''.join(results) + + def docproperty(self, object, name=None, mod=None, cl=None): + """Produce html documentation for a property.""" + return self._docdescriptor(name, object, mod) + + def docother(self, object, name=None, mod=None, *ignored): + """Produce HTML documentation for a data object.""" + lhs = name and '%s = ' % name or '' + return lhs + self.repr(object) + + def docdata(self, object, name=None, mod=None, cl=None): + """Produce html documentation for a data descriptor.""" + return self._docdescriptor(name, object, mod) + + def index(self, dir, shadowed=None): + """Generate an HTML index for a directory of modules.""" + modpkgs = [] + if shadowed is None: shadowed = {} + for importer, name, ispkg in pkgutil.iter_modules([dir]): + modpkgs.append((name, '', ispkg, name in shadowed)) + shadowed[name] = 1 + + modpkgs.sort() + contents = self.multicolumn(modpkgs, self.modpkglink) + return self.bigsection(dir, '#ffffff', '#ee77aa', contents) + +# -------------------------------------------- text documentation generator + +class TextRepr(Repr): + """Class for safely making a text representation of a Python object.""" + def __init__(self): + Repr.__init__(self) + self.maxlist = self.maxtuple = 20 + self.maxdict = 10 + self.maxstring = self.maxother = 100 + + def repr1(self, x, level): + if hasattr(type(x), '__name__'): + methodname = 'repr_' + join(split(type(x).__name__), '_') + if hasattr(self, methodname): + return getattr(self, methodname)(x, level) + return cram(stripid(repr(x)), self.maxother) + + def repr_string(self, x, level): + test = cram(x, self.maxstring) + testrepr = repr(test) + if '\\' in test and '\\' not in replace(testrepr, r'\\', ''): + # Backslashes are only literal in the string and are never + # needed to make any special characters, so show a raw string. + return 'r' + testrepr[0] + test + testrepr[0] + return testrepr + + repr_str = repr_string + + def repr_instance(self, x, level): + try: + return cram(stripid(repr(x)), self.maxstring) + except: + return '<%s instance>' % x.__class__.__name__ + +class TextDoc(Doc): + """Formatter class for text documentation.""" + + # ------------------------------------------- text formatting utilities + + _repr_instance = TextRepr() + repr = _repr_instance.repr + + def bold(self, text): + """Format a string in bold by overstriking.""" + return join(map(lambda ch: ch + '\b' + ch, text), '') + + def indent(self, text, prefix=' '): + """Indent text by prepending a given prefix to each line.""" + if not text: return '' + lines = split(text, '\n') + lines = map(lambda line, prefix=prefix: prefix + line, lines) + if lines: lines[-1] = rstrip(lines[-1]) + return join(lines, '\n') + + def section(self, title, contents): + """Format a section with a given heading.""" + return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n' + + # ---------------------------------------------- type-specific routines + + def formattree(self, tree, modname, parent=None, prefix=''): + """Render in text a class tree as returned by inspect.getclasstree().""" + result = '' + for entry in tree: + if type(entry) is type(()): + c, bases = entry + result = result + prefix + classname(c, modname) + if bases and bases != (parent,): + parents = map(lambda c, m=modname: classname(c, m), bases) + result = result + '(%s)' % join(parents, ', ') + result = result + '\n' + elif type(entry) is type([]): + result = result + self.formattree( + entry, modname, c, prefix + ' ') + return result + + def docmodule(self, object, name=None, mod=None): + """Produce text documentation for a given module object.""" + name = object.__name__ # ignore the passed-in name + synop, desc = splitdoc(getdoc(object)) + result = self.section('NAME', name + (synop and ' - ' + synop)) + + try: + all = object.__all__ + except AttributeError: + all = None + + try: + file = inspect.getabsfile(object) + except TypeError: + file = '(built-in)' + result = result + self.section('FILE', file) + + docloc = self.getdocloc(object) + if docloc is not None: + result = result + self.section('MODULE DOCS', docloc) + + if desc: + result = result + self.section('DESCRIPTION', desc) + + classes = [] + for key, value in inspect.getmembers(object, inspect.isclass): + # if __all__ exists, believe it. Otherwise use old heuristic. + if (all is not None + or (inspect.getmodule(value) or object) is object): + if visiblename(key, all): + classes.append((key, value)) + funcs = [] + for key, value in inspect.getmembers(object, inspect.isroutine): + # if __all__ exists, believe it. Otherwise use old heuristic. + if (all is not None or + inspect.isbuiltin(value) or inspect.getmodule(value) is object): + if visiblename(key, all): + funcs.append((key, value)) + data = [] + for key, value in inspect.getmembers(object, isdata): + if visiblename(key, all): + data.append((key, value)) + + modpkgs = [] + modpkgs_names = set() + if hasattr(object, '__path__'): + for importer, modname, ispkg in pkgutil.iter_modules(object.__path__): + modpkgs_names.add(modname) + if ispkg: + modpkgs.append(modname + ' (package)') + else: + modpkgs.append(modname) + + modpkgs.sort() + result = result + self.section( + 'PACKAGE CONTENTS', join(modpkgs, '\n')) + + # Detect submodules as sometimes created by C extensions + submodules = [] + for key, value in inspect.getmembers(object, inspect.ismodule): + if value.__name__.startswith(name + '.') and key not in modpkgs_names: + submodules.append(key) + if submodules: + submodules.sort() + result = result + self.section( + 'SUBMODULES', join(submodules, '\n')) + + if classes: + classlist = map(lambda key_value: key_value[1], classes) + contents = [self.formattree( + inspect.getclasstree(classlist, 1), name)] + for key, value in classes: + contents.append(self.document(value, key, name)) + result = result + self.section('CLASSES', join(contents, '\n')) + + if funcs: + contents = [] + for key, value in funcs: + contents.append(self.document(value, key, name)) + result = result + self.section('FUNCTIONS', join(contents, '\n')) + + if data: + contents = [] + for key, value in data: + contents.append(self.docother(value, key, name, maxlen=70)) + result = result + self.section('DATA', join(contents, '\n')) + + if hasattr(object, '__version__'): + version = str(object.__version__) + if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': + version = strip(version[11:-1]) + result = result + self.section('VERSION', version) + if hasattr(object, '__date__'): + result = result + self.section('DATE', str(object.__date__)) + if hasattr(object, '__author__'): + result = result + self.section('AUTHOR', str(object.__author__)) + if hasattr(object, '__credits__'): + result = result + self.section('CREDITS', str(object.__credits__)) + return result + + def docclass(self, object, name=None, mod=None): + """Produce text documentation for a given class object.""" + realname = object.__name__ + name = name or realname + bases = object.__bases__ + + def makename(c, m=object.__module__): + return classname(c, m) + + if name == realname: + title = 'class ' + self.bold(realname) + else: + title = self.bold(name) + ' = class ' + realname + if bases: + parents = map(makename, bases) + title = title + '(%s)' % join(parents, ', ') + + doc = getdoc(object) + contents = doc and [doc + '\n'] or [] + push = contents.append + + # List the mro, if non-trivial. + mro = deque(inspect.getmro(object)) + if len(mro) > 2: + push("Method resolution order:") + for base in mro: + push(' ' + makename(base)) + push('') + + # Cute little class to pump out a horizontal rule between sections. + class HorizontalRule: + def __init__(self): + self.needone = 0 + def maybe(self): + if self.needone: + push('-' * 70) + self.needone = 1 + hr = HorizontalRule() + + def spill(msg, attrs, predicate): + ok, attrs = _split_list(attrs, predicate) + if ok: + hr.maybe() + push(msg) + for name, kind, homecls, value in ok: + push(self.document(getattr(object, name), + name, mod, object)) + return attrs + + def spilldescriptors(msg, attrs, predicate): + ok, attrs = _split_list(attrs, predicate) + if ok: + hr.maybe() + push(msg) + for name, kind, homecls, value in ok: + push(self._docdescriptor(name, value, mod)) + return attrs + + def spilldata(msg, attrs, predicate): + ok, attrs = _split_list(attrs, predicate) + if ok: + hr.maybe() + push(msg) + for name, kind, homecls, value in ok: + if (hasattr(value, '__call__') or + inspect.isdatadescriptor(value)): + doc = getdoc(value) + else: + doc = None + push(self.docother(getattr(object, name), + name, mod, maxlen=70, doc=doc) + '\n') + return attrs + + attrs = filter(lambda data: visiblename(data[0]), + classify_class_attrs(object)) + while attrs: + if mro: + thisclass = mro.popleft() + else: + thisclass = attrs[0][2] + attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass) + + if thisclass is __builtin__.object: + attrs = inherited + continue + elif thisclass is object: + tag = "defined here" + else: + tag = "inherited from %s" % classname(thisclass, + object.__module__) + + # Sort attrs by name. + attrs.sort() + + # Pump out the attrs, segregated by kind. + attrs = spill("Methods %s:\n" % tag, attrs, + lambda t: t[1] == 'method') + attrs = spill("Class methods %s:\n" % tag, attrs, + lambda t: t[1] == 'class method') + attrs = spill("Static methods %s:\n" % tag, attrs, + lambda t: t[1] == 'static method') + attrs = spilldescriptors("Data descriptors %s:\n" % tag, attrs, + lambda t: t[1] == 'data descriptor') + attrs = spilldata("Data and other attributes %s:\n" % tag, attrs, + lambda t: t[1] == 'data') + assert attrs == [] + attrs = inherited + + contents = '\n'.join(contents) + if not contents: + return title + '\n' + return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n' + + def formatvalue(self, object): + """Format an argument default value as text.""" + return '=' + self.repr(object) + + def docroutine(self, object, name=None, mod=None, cl=None): + """Produce text documentation for a function or method object.""" + realname = object.__name__ + name = name or realname + note = '' + skipdocs = 0 + if inspect.ismethod(object): + imclass = object.im_class + if cl: + if imclass is not cl: + note = ' from ' + classname(imclass, mod) + else: + if object.im_self is not None: + note = ' method of %s instance' % classname( + object.im_self.__class__, mod) + else: + note = ' unbound %s method' % classname(imclass,mod) + object = object.im_func + + if name == realname: + title = self.bold(realname) + else: + if (cl and realname in cl.__dict__ and + cl.__dict__[realname] is object): + skipdocs = 1 + title = self.bold(name) + ' = ' + realname + if inspect.isfunction(object): + args, varargs, varkw, defaults = inspect.getargspec(object) + argspec = inspect.formatargspec( + args, varargs, varkw, defaults, formatvalue=self.formatvalue) + if realname == '': + title = self.bold(name) + ' lambda ' + argspec = argspec[1:-1] # remove parentheses + else: + argspec = '(...)' + decl = title + argspec + note + + if skipdocs: + return decl + '\n' + else: + doc = getdoc(object) or '' + return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n') + + def _docdescriptor(self, name, value, mod): + results = [] + push = results.append + + if name: + push(self.bold(name)) + push('\n') + doc = getdoc(value) or '' + if doc: + push(self.indent(doc)) + push('\n') + return ''.join(results) + + def docproperty(self, object, name=None, mod=None, cl=None): + """Produce text documentation for a property.""" + return self._docdescriptor(name, object, mod) + + def docdata(self, object, name=None, mod=None, cl=None): + """Produce text documentation for a data descriptor.""" + return self._docdescriptor(name, object, mod) + + def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=None): + """Produce text documentation for a data object.""" + repr = self.repr(object) + if maxlen: + line = (name and name + ' = ' or '') + repr + chop = maxlen - len(line) + if chop < 0: repr = repr[:chop] + '...' + line = (name and self.bold(name) + ' = ' or '') + repr + if doc is not None: + line += '\n' + self.indent(str(doc)) + return line + +# --------------------------------------------------------- user interfaces + +def pager(text): + """The first time this is called, determine what kind of pager to use.""" + global pager + pager = getpager() + pager(text) + +def getpager(): + """Decide what method to use for paging through text.""" + if type(sys.stdout) is not types.FileType: + return plainpager + if not sys.stdin.isatty() or not sys.stdout.isatty(): + return plainpager + if 'PAGER' in os.environ: + if sys.platform == 'win32': # pipes completely broken in Windows + return lambda text: tempfilepager(plain(text), os.environ['PAGER']) + elif os.environ.get('TERM') in ('dumb', 'emacs'): + return lambda text: pipepager(plain(text), os.environ['PAGER']) + else: + return lambda text: pipepager(text, os.environ['PAGER']) + if os.environ.get('TERM') in ('dumb', 'emacs'): + return plainpager + if sys.platform == 'win32' or sys.platform.startswith('os2'): + return lambda text: tempfilepager(plain(text), 'more <') + if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0: + return lambda text: pipepager(text, 'less') + + import tempfile + (fd, filename) = tempfile.mkstemp() + os.close(fd) + try: + if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0: + return lambda text: pipepager(text, 'more') + else: + return ttypager + finally: + os.unlink(filename) + +def plain(text): + """Remove boldface formatting from text.""" + return re.sub('.\b', '', text) + +def pipepager(text, cmd): + """Page through text by feeding it to another program.""" + pipe = os.popen(cmd, 'w') + try: + pipe.write(text) + pipe.close() + except IOError: + pass # Ignore broken pipes caused by quitting the pager program. + +def tempfilepager(text, cmd): + """Page through text by invoking a program on a temporary file.""" + import tempfile + filename = tempfile.mktemp() + file = open(filename, 'w') + file.write(text) + file.close() + try: + os.system(cmd + ' "' + filename + '"') + finally: + os.unlink(filename) + +def ttypager(text): + """Page through text on a text terminal.""" + lines = split(plain(text), '\n') + try: + import tty + fd = sys.stdin.fileno() + old = tty.tcgetattr(fd) + tty.setcbreak(fd) + getchar = lambda: sys.stdin.read(1) + except (ImportError, AttributeError): + tty = None + getchar = lambda: sys.stdin.readline()[:-1][:1] + + try: + r = inc = os.environ.get('LINES', 25) - 1 + sys.stdout.write(join(lines[:inc], '\n') + '\n') + while lines[r:]: + sys.stdout.write('-- more --') + sys.stdout.flush() + c = getchar() + + if c in ('q', 'Q'): + sys.stdout.write('\r \r') + break + elif c in ('\r', '\n'): + sys.stdout.write('\r \r' + lines[r] + '\n') + r = r + 1 + continue + if c in ('b', 'B', '\x1b'): + r = r - inc - inc + if r < 0: r = 0 + sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n') + r = r + inc + + finally: + if tty: + tty.tcsetattr(fd, tty.TCSAFLUSH, old) + +def plainpager(text): + """Simply print unformatted text. This is the ultimate fallback.""" + sys.stdout.write(plain(text)) + +def describe(thing): + """Produce a short description of the given thing.""" + if inspect.ismodule(thing): + if thing.__name__ in sys.builtin_module_names: + return 'built-in module ' + thing.__name__ + if hasattr(thing, '__path__'): + return 'package ' + thing.__name__ + else: + return 'module ' + thing.__name__ + if inspect.isbuiltin(thing): + return 'built-in function ' + thing.__name__ + if inspect.isgetsetdescriptor(thing): + return 'getset descriptor %s.%s.%s' % ( + thing.__objclass__.__module__, thing.__objclass__.__name__, + thing.__name__) + if inspect.ismemberdescriptor(thing): + return 'member descriptor %s.%s.%s' % ( + thing.__objclass__.__module__, thing.__objclass__.__name__, + thing.__name__) + if inspect.isclass(thing): + return 'class ' + thing.__name__ + if inspect.isfunction(thing): + return 'function ' + thing.__name__ + if inspect.ismethod(thing): + return 'method ' + thing.__name__ + if type(thing) is types.InstanceType: + return 'instance of ' + thing.__class__.__name__ + return type(thing).__name__ + +def locate(path, forceload=0): + """Locate an object by name or dotted path, importing as necessary.""" + parts = [part for part in split(path, '.') if part] + module, n = None, 0 + while n < len(parts): + nextmodule = safeimport(join(parts[:n+1], '.'), forceload) + if nextmodule: module, n = nextmodule, n + 1 + else: break + if module: + object = module + for part in parts[n:]: + try: object = getattr(object, part) + except AttributeError: return None + return object + else: + if hasattr(__builtin__, path): + return getattr(__builtin__, path) + +# --------------------------------------- interactive interpreter interface + +text = TextDoc() +html = HTMLDoc() + +class _OldStyleClass: pass +_OLD_INSTANCE_TYPE = type(_OldStyleClass()) + +def resolve(thing, forceload=0): + """Given an object or a path to an object, get the object and its name.""" + if isinstance(thing, str): + object = locate(thing, forceload) + if not object: + raise ImportError, 'no Python documentation found for %r' % thing + return object, thing + else: + return thing, getattr(thing, '__name__', None) + +def render_doc(thing, title='Python Library Documentation: %s', forceload=0): + """Render text documentation, given an object or a path to an object.""" + object, name = resolve(thing, forceload) + desc = describe(object) + module = inspect.getmodule(object) + if name and '.' in name: + desc += ' in ' + name[:name.rfind('.')] + elif module and module is not object: + desc += ' in module ' + module.__name__ + if type(object) is _OLD_INSTANCE_TYPE: + # If the passed object is an instance of an old-style class, + # document its available methods instead of its value. + object = object.__class__ + elif not (inspect.ismodule(object) or + inspect.isclass(object) or + inspect.isroutine(object) or + inspect.isgetsetdescriptor(object) or + inspect.ismemberdescriptor(object) or + isinstance(object, property)): + # If the passed object is a piece of data or an instance, + # document its available methods instead of its value. + object = type(object) + desc += ' object' + return title % desc + '\n\n' + text.document(object, name) + +def doc(thing, title='Python Library Documentation: %s', forceload=0): + """Display text documentation, given an object or a path to an object.""" + try: + pager(render_doc(thing, title, forceload)) + except (ImportError, ErrorDuringImport), value: + print value + +def writedoc(thing, forceload=0): + """Write HTML documentation to a file in the current directory.""" + try: + object, name = resolve(thing, forceload) + page = html.page(describe(object), html.document(object, name)) + sys.stdout.write(page) + #file = open(name + '.html', 'w') + #file.write(page) + #file.close() + #print 'wrote', name + '.html' + except (ImportError, ErrorDuringImport), value: + print value + +def writedocs(dir, pkgpath='', done=None): + """Write out HTML documentation for all modules in a directory tree.""" + if done is None: done = {} + for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath): + writedoc(modname) + return + +class Helper: + + # These dictionaries map a topic name to either an alias, or a tuple + # (label, seealso-items). The "label" is the label of the corresponding + # section in the .rst file under Doc/ and an index into the dictionary + # in pydoc_topics.py. + # + # CAUTION: if you change one of these dictionaries, be sure to adapt the + # list of needed labels in Doc/tools/sphinxext/pyspecific.py and + # regenerate the pydoc_topics.py file by running + # make pydoc-topics + # in Doc/ and copying the output file into the Lib/ directory. + + keywords = { + 'and': 'BOOLEAN', + 'as': 'with', + 'assert': ('assert', ''), + 'break': ('break', 'while for'), + 'class': ('class', 'CLASSES SPECIALMETHODS'), + 'continue': ('continue', 'while for'), + 'def': ('function', ''), + 'del': ('del', 'BASICMETHODS'), + 'elif': 'if', + 'else': ('else', 'while for'), + 'except': 'try', + 'exec': ('exec', ''), + 'finally': 'try', + 'for': ('for', 'break continue while'), + 'from': 'import', + 'global': ('global', 'NAMESPACES'), + 'if': ('if', 'TRUTHVALUE'), + 'import': ('import', 'MODULES'), + 'in': ('in', 'SEQUENCEMETHODS2'), + 'is': 'COMPARISON', + 'lambda': ('lambda', 'FUNCTIONS'), + 'not': 'BOOLEAN', + 'or': 'BOOLEAN', + 'pass': ('pass', ''), + 'print': ('print', ''), + 'raise': ('raise', 'EXCEPTIONS'), + 'return': ('return', 'FUNCTIONS'), + 'try': ('try', 'EXCEPTIONS'), + 'while': ('while', 'break continue if TRUTHVALUE'), + 'with': ('with', 'CONTEXTMANAGERS EXCEPTIONS yield'), + 'yield': ('yield', ''), + } + # Either add symbols to this dictionary or to the symbols dictionary + # directly: Whichever is easier. They are merged later. + _symbols_inverse = { + 'STRINGS' : ("'", "'''", "r'", "u'", '"""', '"', 'r"', 'u"'), + 'OPERATORS' : ('+', '-', '*', '**', '/', '//', '%', '<<', '>>', '&', + '|', '^', '~', '<', '>', '<=', '>=', '==', '!=', '<>'), + 'COMPARISON' : ('<', '>', '<=', '>=', '==', '!=', '<>'), + 'UNARY' : ('-', '~'), + 'AUGMENTEDASSIGNMENT' : ('+=', '-=', '*=', '/=', '%=', '&=', '|=', + '^=', '<<=', '>>=', '**=', '//='), + 'BITWISE' : ('<<', '>>', '&', '|', '^', '~'), + 'COMPLEX' : ('j', 'J') + } + symbols = { + '%': 'OPERATORS FORMATTING', + '**': 'POWER', + ',': 'TUPLES LISTS FUNCTIONS', + '.': 'ATTRIBUTES FLOAT MODULES OBJECTS', + '...': 'ELLIPSIS', + ':': 'SLICINGS DICTIONARYLITERALS', + '@': 'def class', + '\\': 'STRINGS', + '_': 'PRIVATENAMES', + '__': 'PRIVATENAMES SPECIALMETHODS', + '`': 'BACKQUOTES', + '(': 'TUPLES FUNCTIONS CALLS', + ')': 'TUPLES FUNCTIONS CALLS', + '[': 'LISTS SUBSCRIPTS SLICINGS', + ']': 'LISTS SUBSCRIPTS SLICINGS' + } + for topic, symbols_ in _symbols_inverse.iteritems(): + for symbol in symbols_: + topics = symbols.get(symbol, topic) + if topic not in topics: + topics = topics + ' ' + topic + symbols[symbol] = topics + + topics = { + 'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS ' + 'FUNCTIONS CLASSES MODULES FILES inspect'), + 'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING ' + 'TYPES'), + 'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'), + 'FORMATTING': ('formatstrings', 'OPERATORS'), + 'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS ' + 'FORMATTING TYPES'), + 'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'), + 'INTEGER': ('integers', 'int range'), + 'FLOAT': ('floating', 'float math'), + 'COMPLEX': ('imaginary', 'complex cmath'), + 'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'), + 'MAPPINGS': 'DICTIONARIES', + 'FUNCTIONS': ('typesfunctions', 'def TYPES'), + 'METHODS': ('typesmethods', 'class def CLASSES TYPES'), + 'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'), + 'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'), + 'FRAMEOBJECTS': 'TYPES', + 'TRACEBACKS': 'TYPES', + 'NONE': ('bltin-null-object', ''), + 'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'), + 'FILES': ('bltin-file-objects', ''), + 'SPECIALATTRIBUTES': ('specialattrs', ''), + 'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'), + 'MODULES': ('typesmodules', 'import'), + 'PACKAGES': 'import', + 'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN ' + 'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER ' + 'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES ' + 'LISTS DICTIONARIES BACKQUOTES'), + 'OPERATORS': 'EXPRESSIONS', + 'PRECEDENCE': 'EXPRESSIONS', + 'OBJECTS': ('objects', 'TYPES'), + 'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS ' + 'CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS ' + 'SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'), + 'BASICMETHODS': ('customization', 'cmp hash repr str SPECIALMETHODS'), + 'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'), + 'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'), + 'SEQUENCEMETHODS1': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS2 ' + 'SPECIALMETHODS'), + 'SEQUENCEMETHODS2': ('sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 ' + 'SPECIALMETHODS'), + 'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'), + 'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT ' + 'SPECIALMETHODS'), + 'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'), + 'NAMESPACES': ('naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'), + 'DYNAMICFEATURES': ('dynamic-features', ''), + 'SCOPING': 'NAMESPACES', + 'FRAMES': 'NAMESPACES', + 'EXCEPTIONS': ('exceptions', 'try except finally raise'), + 'COERCIONS': ('coercion-rules','CONVERSIONS'), + 'CONVERSIONS': ('conversions', 'COERCIONS'), + 'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'), + 'SPECIALIDENTIFIERS': ('id-classes', ''), + 'PRIVATENAMES': ('atom-identifiers', ''), + 'LITERALS': ('atom-literals', 'STRINGS BACKQUOTES NUMBERS ' + 'TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'), + 'TUPLES': 'SEQUENCES', + 'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'), + 'LISTS': ('typesseq-mutable', 'LISTLITERALS'), + 'LISTLITERALS': ('lists', 'LISTS LITERALS'), + 'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'), + 'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'), + 'BACKQUOTES': ('string-conversions', 'repr str STRINGS LITERALS'), + 'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr ' + 'ATTRIBUTEMETHODS'), + 'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS1'), + 'SLICINGS': ('slicings', 'SEQUENCEMETHODS2'), + 'CALLS': ('calls', 'EXPRESSIONS'), + 'POWER': ('power', 'EXPRESSIONS'), + 'UNARY': ('unary', 'EXPRESSIONS'), + 'BINARY': ('binary', 'EXPRESSIONS'), + 'SHIFTING': ('shifting', 'EXPRESSIONS'), + 'BITWISE': ('bitwise', 'EXPRESSIONS'), + 'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'), + 'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'), + 'ASSERTION': 'assert', + 'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'), + 'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'), + 'DELETION': 'del', + 'PRINTING': 'print', + 'RETURNING': 'return', + 'IMPORTING': 'import', + 'CONDITIONAL': 'if', + 'LOOPING': ('compound', 'for while break continue'), + 'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'), + 'DEBUGGING': ('debugger', 'pdb'), + 'CONTEXTMANAGERS': ('context-managers', 'with'), + } + + def __init__(self, input, output): + self.input = input + self.output = output + + def __repr__(self): + if inspect.stack()[1][3] == '?': + self() + return '' + return '' + + def __call__(self, request=None): + if request is not None: + self.help(request) + else: + self.intro() + self.interact() + self.output.write(''' +You are now leaving help and returning to the Python interpreter. +If you want to ask for help on a particular object directly from the +interpreter, you can type "help(object)". Executing "help('string')" +has the same effect as typing a particular string at the help> prompt. +''') + + def interact(self): + self.output.write('\n') + while True: + try: + request = self.getline('help> ') + if not request: break + except (KeyboardInterrupt, EOFError): + break + request = strip(replace(request, '"', '', "'", '')) + if lower(request) in ('q', 'quit'): break + self.help(request) + + def getline(self, prompt): + """Read one line, using raw_input when available.""" + if self.input is sys.stdin: + return raw_input(prompt) + else: + self.output.write(prompt) + self.output.flush() + return self.input.readline() + + def help(self, request): + if type(request) is type(''): + request = request.strip() + if request == 'help': self.intro() + elif request == 'keywords': self.listkeywords() + elif request == 'symbols': self.listsymbols() + elif request == 'topics': self.listtopics() + elif request == 'modules': self.listmodules() + elif request[:8] == 'modules ': + self.listmodules(split(request)[1]) + elif request in self.symbols: self.showsymbol(request) + elif request in self.keywords: self.showtopic(request) + elif request in self.topics: self.showtopic(request) + elif request: doc(request, 'Help on %s:') + elif isinstance(request, Helper): self() + else: doc(request, 'Help on %s:') + self.output.write('\n') + + def intro(self): + self.output.write(''' +Welcome to Python %s! This is the online help utility. + +If this is your first time using Python, you should definitely check out +the tutorial on the Internet at http://docs.python.org/tutorial/. + +Enter the name of any module, keyword, or topic to get help on writing +Python programs and using Python modules. To quit this help utility and +return to the interpreter, just type "quit". + +To get a list of available modules, keywords, or topics, type "modules", +"keywords", or "topics". Each module also comes with a one-line summary +of what it does; to list the modules whose summaries contain a given word +such as "spam", type "modules spam". +''' % sys.version[:3]) + + def list(self, items, columns=4, width=80): + items = items[:] + items.sort() + colw = width / columns + rows = (len(items) + columns - 1) / columns + for row in range(rows): + for col in range(columns): + i = col * rows + row + if i < len(items): + self.output.write(items[i]) + if col < columns - 1: + self.output.write(' ' + ' ' * (colw-1 - len(items[i]))) + self.output.write('\n') + + def listkeywords(self): + self.output.write(''' +Here is a list of the Python keywords. Enter any keyword to get more help. + +''') + self.list(self.keywords.keys()) + + def listsymbols(self): + self.output.write(''' +Here is a list of the punctuation symbols which Python assigns special meaning +to. Enter any symbol to get more help. + +''') + self.list(self.symbols.keys()) + + def listtopics(self): + self.output.write(''' +Here is a list of available topics. Enter any topic name to get more help. + +''') + self.list(self.topics.keys()) + + def showtopic(self, topic, more_xrefs=''): + try: + import pydoc_topics + except ImportError: + self.output.write(''' +Sorry, topic and keyword documentation is not available because the +module "pydoc_topics" could not be found. +''') + return + target = self.topics.get(topic, self.keywords.get(topic)) + if not target: + self.output.write('no documentation found for %s\n' % repr(topic)) + return + if type(target) is type(''): + return self.showtopic(target, more_xrefs) + + label, xrefs = target + try: + doc = pydoc_topics.topics[label] + except KeyError: + self.output.write('no documentation found for %s\n' % repr(topic)) + return + pager(strip(doc) + '\n') + if more_xrefs: + xrefs = (xrefs or '') + ' ' + more_xrefs + if xrefs: + import StringIO, formatter + buffer = StringIO.StringIO() + formatter.DumbWriter(buffer).send_flowing_data( + 'Related help topics: ' + join(split(xrefs), ', ') + '\n') + self.output.write('\n%s\n' % buffer.getvalue()) + + def showsymbol(self, symbol): + target = self.symbols[symbol] + topic, _, xrefs = target.partition(' ') + self.showtopic(topic, xrefs) + + def listmodules(self, key=''): + if key: + self.output.write(''' +Here is a list of matching modules. Enter any module name to get more help. + +''') + apropos(key) + else: + self.output.write(''' +Please wait a moment while I gather a list of all available modules... + +''') + modules = {} + def callback(path, modname, desc, modules=modules): + if modname and modname[-9:] == '.__init__': + modname = modname[:-9] + ' (package)' + if find(modname, '.') < 0: + modules[modname] = 1 + def onerror(modname): + callback(None, modname, None) + ModuleScanner().run(callback, onerror=onerror) + self.list(modules.keys()) + self.output.write(''' +Enter any module name to get more help. Or, type "modules spam" to search +for modules whose descriptions contain the word "spam". +''') + +help = Helper(sys.stdin, sys.stdout) + +class Scanner: + """A generic tree iterator.""" + def __init__(self, roots, children, descendp): + self.roots = roots[:] + self.state = [] + self.children = children + self.descendp = descendp + + def next(self): + if not self.state: + if not self.roots: + return None + root = self.roots.pop(0) + self.state = [(root, self.children(root))] + node, children = self.state[-1] + if not children: + self.state.pop() + return self.next() + child = children.pop(0) + if self.descendp(child): + self.state.append((child, self.children(child))) + return child + + +class ModuleScanner: + """An interruptible scanner that searches module synopses.""" + + def run(self, callback, key=None, completer=None, onerror=None): + if key: key = lower(key) + self.quit = False + seen = {} + + for modname in sys.builtin_module_names: + if modname != '__main__': + seen[modname] = 1 + if key is None: + callback(None, modname, '') + else: + desc = split(__import__(modname).__doc__ or '', '\n')[0] + if find(lower(modname + ' - ' + desc), key) >= 0: + callback(None, modname, desc) + + for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror): + if self.quit: + break + if key is None: + callback(None, modname, '') + else: + loader = importer.find_module(modname) + if hasattr(loader,'get_source'): + import StringIO + desc = source_synopsis( + StringIO.StringIO(loader.get_source(modname)) + ) or '' + if hasattr(loader,'get_filename'): + path = loader.get_filename(modname) + else: + path = None + else: + module = loader.load_module(modname) + desc = (module.__doc__ or '').splitlines()[0] + path = getattr(module,'__file__',None) + if find(lower(modname + ' - ' + desc), key) >= 0: + callback(path, modname, desc) + + if completer: + completer() + +def apropos(key): + """Print all the one-line module summaries that contain a substring.""" + def callback(path, modname, desc): + if modname[-9:] == '.__init__': + modname = modname[:-9] + ' (package)' + print modname, desc and '- ' + desc + try: import warnings + except ImportError: pass + else: warnings.filterwarnings('ignore') # ignore problems during import + ModuleScanner().run(callback, key) + +# --------------------------------------------------- web browser interface + +def serve(port, callback=None, completer=None): + import BaseHTTPServer, mimetools, select + + # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded. + class Message(mimetools.Message): + def __init__(self, fp, seekable=1): + Message = self.__class__ + Message.__bases__[0].__bases__[0].__init__(self, fp, seekable) + self.encodingheader = self.getheader('content-transfer-encoding') + self.typeheader = self.getheader('content-type') + self.parsetype() + self.parseplist() + + class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def send_document(self, title, contents): + try: + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.end_headers() + self.wfile.write(html.page(title, contents)) + except IOError: pass + + def do_GET(self): + path = self.path + if path[-5:] == '.html': path = path[:-5] + if path[:1] == '/': path = path[1:] + if path and path != '.': + try: + obj = locate(path, forceload=1) + except ErrorDuringImport, value: + self.send_document(path, html.escape(str(value))) + return + if obj: + self.send_document(describe(obj), html.document(obj, path)) + else: + self.send_document(path, +'no Python documentation found for %s' % repr(path)) + else: + heading = html.heading( +'Python: Index of Modules', +'#ffffff', '#7799ee') + def bltinlink(name): + return '%s' % (name, name) + names = filter(lambda x: x != '__main__', + sys.builtin_module_names) + contents = html.multicolumn(names, bltinlink) + indices = ['

' + html.bigsection( + 'Built-in Modules', '#ffffff', '#ee77aa', contents)] + + seen = {} + for dir in sys.path: + indices.append(html.index(dir, seen)) + contents = heading + join(indices) + '''

+ +pydoc by Ka-Ping Yee <ping@lfw.org>''' + self.send_document('Index of Modules', contents) + + def log_message(self, *args): pass + + class DocServer(BaseHTTPServer.HTTPServer): + def __init__(self, port, callback): + host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost' + self.address = ('', port) + self.url = 'http://%s:%d/' % (host, port) + self.callback = callback + self.base.__init__(self, self.address, self.handler) + + def serve_until_quit(self): + import select + self.quit = False + while not self.quit: + rd, wr, ex = select.select([self.socket.fileno()], [], [], 1) + if rd: self.handle_request() + + def server_activate(self): + self.base.server_activate(self) + if self.callback: self.callback(self) + + DocServer.base = BaseHTTPServer.HTTPServer + DocServer.handler = DocHandler + DocHandler.MessageClass = Message + try: + try: + DocServer(port, callback).serve_until_quit() + except (KeyboardInterrupt, select.error): + pass + finally: + if completer: completer() + +# ----------------------------------------------------- graphical interface + +def gui(): + """Graphical interface (starts web server and pops up a control window).""" + class GUI: + def __init__(self, window, port=7464): + self.window = window + self.server = None + self.scanner = None + + import Tkinter + self.server_frm = Tkinter.Frame(window) + self.title_lbl = Tkinter.Label(self.server_frm, + text='Starting server...\n ') + self.open_btn = Tkinter.Button(self.server_frm, + text='open browser', command=self.open, state='disabled') + self.quit_btn = Tkinter.Button(self.server_frm, + text='quit serving', command=self.quit, state='disabled') + + self.search_frm = Tkinter.Frame(window) + self.search_lbl = Tkinter.Label(self.search_frm, text='Search for') + self.search_ent = Tkinter.Entry(self.search_frm) + self.search_ent.bind('', self.search) + self.stop_btn = Tkinter.Button(self.search_frm, + text='stop', pady=0, command=self.stop, state='disabled') + if sys.platform == 'win32': + # Trying to hide and show this button crashes under Windows. + self.stop_btn.pack(side='right') + + self.window.title('pydoc') + self.window.protocol('WM_DELETE_WINDOW', self.quit) + self.title_lbl.pack(side='top', fill='x') + self.open_btn.pack(side='left', fill='x', expand=1) + self.quit_btn.pack(side='right', fill='x', expand=1) + self.server_frm.pack(side='top', fill='x') + + self.search_lbl.pack(side='left') + self.search_ent.pack(side='right', fill='x', expand=1) + self.search_frm.pack(side='top', fill='x') + self.search_ent.focus_set() + + font = ('helvetica', sys.platform == 'win32' and 8 or 10) + self.result_lst = Tkinter.Listbox(window, font=font, height=6) + self.result_lst.bind('', self.select) + self.result_lst.bind('', self.goto) + self.result_scr = Tkinter.Scrollbar(window, + orient='vertical', command=self.result_lst.yview) + self.result_lst.config(yscrollcommand=self.result_scr.set) + + self.result_frm = Tkinter.Frame(window) + self.goto_btn = Tkinter.Button(self.result_frm, + text='go to selected', command=self.goto) + self.hide_btn = Tkinter.Button(self.result_frm, + text='hide results', command=self.hide) + self.goto_btn.pack(side='left', fill='x', expand=1) + self.hide_btn.pack(side='right', fill='x', expand=1) + + self.window.update() + self.minwidth = self.window.winfo_width() + self.minheight = self.window.winfo_height() + self.bigminheight = (self.server_frm.winfo_reqheight() + + self.search_frm.winfo_reqheight() + + self.result_lst.winfo_reqheight() + + self.result_frm.winfo_reqheight()) + self.bigwidth, self.bigheight = self.minwidth, self.bigminheight + self.expanded = 0 + self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight)) + self.window.wm_minsize(self.minwidth, self.minheight) + self.window.tk.willdispatch() + + import threading + threading.Thread( + target=serve, args=(port, self.ready, self.quit)).start() + + def ready(self, server): + self.server = server + self.title_lbl.config( + text='Python documentation server at\n' + server.url) + self.open_btn.config(state='normal') + self.quit_btn.config(state='normal') + + def open(self, event=None, url=None): + url = url or self.server.url + try: + import webbrowser + webbrowser.open(url) + except ImportError: # pre-webbrowser.py compatibility + if sys.platform == 'win32': + os.system('start "%s"' % url) + elif sys.platform == 'mac': + try: import ic + except ImportError: pass + else: ic.launchurl(url) + else: + rc = os.system('netscape -remote "openURL(%s)" &' % url) + if rc: os.system('netscape "%s" &' % url) + + def quit(self, event=None): + if self.server: + self.server.quit = 1 + self.window.quit() + + def search(self, event=None): + key = self.search_ent.get() + self.stop_btn.pack(side='right') + self.stop_btn.config(state='normal') + self.search_lbl.config(text='Searching for "%s"...' % key) + self.search_ent.forget() + self.search_lbl.pack(side='left') + self.result_lst.delete(0, 'end') + self.goto_btn.config(state='disabled') + self.expand() + + import threading + if self.scanner: + self.scanner.quit = 1 + self.scanner = ModuleScanner() + threading.Thread(target=self.scanner.run, + args=(self.update, key, self.done)).start() + + def update(self, path, modname, desc): + if modname[-9:] == '.__init__': + modname = modname[:-9] + ' (package)' + self.result_lst.insert('end', + modname + ' - ' + (desc or '(no description)')) + + def stop(self, event=None): + if self.scanner: + self.scanner.quit = 1 + self.scanner = None + + def done(self): + self.scanner = None + self.search_lbl.config(text='Search for') + self.search_lbl.pack(side='left') + self.search_ent.pack(side='right', fill='x', expand=1) + if sys.platform != 'win32': self.stop_btn.forget() + self.stop_btn.config(state='disabled') + + def select(self, event=None): + self.goto_btn.config(state='normal') + + def goto(self, event=None): + selection = self.result_lst.curselection() + if selection: + modname = split(self.result_lst.get(selection[0]))[0] + self.open(url=self.server.url + modname + '.html') + + def collapse(self): + if not self.expanded: return + self.result_frm.forget() + self.result_scr.forget() + self.result_lst.forget() + self.bigwidth = self.window.winfo_width() + self.bigheight = self.window.winfo_height() + self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight)) + self.window.wm_minsize(self.minwidth, self.minheight) + self.expanded = 0 + + def expand(self): + if self.expanded: return + self.result_frm.pack(side='bottom', fill='x') + self.result_scr.pack(side='right', fill='y') + self.result_lst.pack(side='top', fill='both', expand=1) + self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight)) + self.window.wm_minsize(self.minwidth, self.bigminheight) + self.expanded = 1 + + def hide(self, event=None): + self.stop() + self.collapse() + + import Tkinter + try: + root = Tkinter.Tk() + # Tk will crash if pythonw.exe has an XP .manifest + # file and the root has is not destroyed explicitly. + # If the problem is ever fixed in Tk, the explicit + # destroy can go. + try: + gui = GUI(root) + root.mainloop() + finally: + root.destroy() + except KeyboardInterrupt: + pass + +# -------------------------------------------------- command-line interface + +def ispath(x): + return isinstance(x, str) and find(x, os.sep) >= 0 + +def cli(): + """Command-line interface (looks at sys.argv to decide what to do).""" + import getopt + class BadUsage: pass + + # Scripts don't get the current directory in their path by default + # unless they are run with the '-m' switch + if '' not in sys.path: + scriptdir = os.path.dirname(sys.argv[0]) + if scriptdir in sys.path: + sys.path.remove(scriptdir) + sys.path.insert(0, '.') + + try: + opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w') + writing = 0 + + for opt, val in opts: + if opt == '-g': + gui() + return + if opt == '-k': + apropos(val) + return + if opt == '-p': + try: + port = int(val) + except ValueError: + raise BadUsage + def ready(server): + print 'pydoc server ready at %s' % server.url + def stopped(): + print 'pydoc server stopped' + serve(port, ready, stopped) + return + if opt == '-w': + writing = 1 + + if not args: raise BadUsage + for arg in args: + if ispath(arg) and not os.path.exists(arg): + print 'file %r does not exist' % arg + break + try: + if ispath(arg) and os.path.isfile(arg): + arg = importfile(arg) + if writing: + if ispath(arg) and os.path.isdir(arg): + writedocs(arg) + else: + writedoc(arg) + else: + help.help(arg) + except ErrorDuringImport, value: + print value + + except (getopt.error, BadUsage): + cmd = os.path.basename(sys.argv[0]) + print """pydoc - the Python documentation tool + +%s ... + Show text documentation on something. may be the name of a + Python keyword, topic, function, module, or package, or a dotted + reference to a class or function within a module or module in a + package. If contains a '%s', it is used as the path to a + Python source file to document. If name is 'keywords', 'topics', + or 'modules', a listing of these things is displayed. + +%s -k + Search for a keyword in the synopsis lines of all available modules. + +%s -p + Start an HTTP server on the given port on the local machine. + +%s -g + Pop up a graphical interface for finding and serving documentation. + +%s -w ... + Write out the HTML documentation for a module to a file in the current + directory. If contains a '%s', it is treated as a filename; if + it names a directory, documentation is written for all the contents. +""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep) + +if __name__ == '__main__': cli() diff --git a/duchain/CMakeLists.txt b/duchain/CMakeLists.txt index 5fe750f..eaeb682 100644 --- a/duchain/CMakeLists.txt +++ b/duchain/CMakeLists.txt @@ -3,12 +3,16 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) -set(duchain_SRCS +set(duchain_SRCS expressionvisitor.cpp declarations/importedmoduledeclaration.cpp + pythonducontext.cpp contextbuilder.cpp pythoneditorintegrator.cpp declarationbuilder.cpp usebuilder.cpp dumpchain.cpp + + navigation/navigationwidget.cpp + navigation/declarationnavigationcontext.cpp # typebuilder.cpp ) @@ -17,10 +21,16 @@ kde4_add_library( kdev4pythonduchain SHARED ${duchain_SRCS} ) target_link_libraries( kdev4pythonduchain ${KDE4_KDECORE_LIBS} ${KDEVPLATFORM_LANGUAGE_LIBRARIES} + ${KDEVPLATFORM_PROJECT_LIBRARIES} ${KDE4_KTEXTEDITOR_LIBS} ${KDEVPLATFORM_INTERFACES_LIBRARIES} + ${QT_QTWEBKIT_LIBRARY} kdev4pythonparser ) install(TARGETS kdev4pythonduchain DESTINATION ${INSTALL_TARGETS_DEFAULT_ARGS}) + +add_subdirectory(navigation) +add_subdirectory(declarations) +add_subdirectory(tests) diff --git a/duchain/contextbuilder.cpp b/duchain/contextbuilder.cpp index 3255d06..da166a9 100644 --- a/duchain/contextbuilder.cpp +++ b/duchain/contextbuilder.cpp @@ -21,7 +21,7 @@ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * *****************************************************************************/ -#include +#include "contextbuilder.h" // #include #include #include @@ -29,43 +29,81 @@ #include #include #include -#include #include "pythoneditorintegrator.h" #include "dumpchain.h" #include #include +#include +#include +#include +#include +#include +#include +#include "usebuilder.h" +#include "pythonducontext.h" using namespace KDevelop; using namespace KTextEditor; +Python::PythonEditorIntegrator* Python::ContextBuilder::m_editor; + namespace Python { + +TopDUContext* ParseJob::m_internalFunctions; + +ReferencedTopDUContext ContextBuilder::build(const IndexedString& url, Ast* node, ReferencedTopDUContext updateContext) +{ + if (!updateContext) { + DUChainReadLocker lock(DUChain::lock()); + updateContext = DUChain::self()->chainForDocument(url); + } + if (updateContext) { + kDebug() << "re-compiling" << url.str(); + DUChainWriteLocker lock(DUChain::lock()); + updateContext->clearImportedParentContexts(); + updateContext->parsingEnvironmentFile()->clearModificationRevisions(); + updateContext->clearProblems(); + } else { + kDebug() << "compiling" << url.str(); + } + + return ContextBuilderBase::build(url, node, updateContext); +} PythonEditorIntegrator* ContextBuilder::editor() const { -// return static_cast(ContextBuilderBase::editor()); - return m_editor; + return ContextBuilder::m_editor; } TopDUContext* ContextBuilder::newTopContext(const RangeInRevision& range, ParsingEnvironmentFile* file) { - IndexedString currentDocumentUrl = m_editor->parseSession()->currentDocument(); + IndexedString currentDocumentUrl = ContextBuilder::m_editor->parseSession()->currentDocument(); + kDebug() << currentDocumentUrl.str(); if ( !file ) { file = new ParsingEnvironmentFile(currentDocumentUrl); file->setLanguage(IndexedString("python")); } - return new TopDUContext(currentDocumentUrl, range, file); + TopDUContext* top = new PythonTopDUContext(currentDocumentUrl, range, file); + ReferencedTopDUContext ref(top); + m_topContext = ref; + return top; +} + +DUContext* ContextBuilder::newContext(const RangeInRevision& range) +{ + return new PythonNormalDUContext(range, currentContext()); } void ContextBuilder::setEditor(PythonEditorIntegrator* editor) { //m_identifierCompiler = new IdentifierCompiler(editor->parseSession()); - m_editor = editor; + ContextBuilder::m_editor = editor; } -void ContextBuilder::setEditor(ParseSession* session) +void ContextBuilder::setEditor(ParseSession* /*session*/) { PythonEditorIntegrator* e = new PythonEditorIntegrator(/*session*/); //m_identifierCompiler = new IdentifierCompiler(e->parseSession()); @@ -92,16 +130,15 @@ RangeInRevision ContextBuilder::editorFindRange( Ast* fromNode, Ast* toNode ) return editor()->findRange(fromNode, toNode); } -QualifiedIdentifier ContextBuilder::identifierForNode( IdentifierAst* node ) +QualifiedIdentifier ContextBuilder::identifierForNode( Python::Identifier* node ) { - return QualifiedIdentifier( node->identifier ); + return QualifiedIdentifier( node->value ); } void ContextBuilder::addImportedContexts() { if ( compilingContexts() && !m_importedParentContexts.isEmpty() ) { - kDebug() << "Adding Imported Contexts"; DUChainWriteLocker lock( DUChain::lock() ); foreach( DUContext* imported, m_importedParentContexts ) currentContext()->addImportedParentContext( imported ); @@ -110,127 +147,152 @@ void ContextBuilder::addImportedContexts() } } -void ContextBuilder::openContextForStatementList( const QList& l ) +void ContextBuilder::openContextForStatementList( const QList& l, DUContext::ContextType /*type*/) { if ( l.count() > 0 ) { - openContext( l.first(), l.last(), DUContext::Other ); + Ast* first = l.first(); + Ast* last = l.last(); + Q_ASSERT(first->hasUsefulRangeInformation); // TODO remove this + RangeInRevision range(RangeInRevision(first->startLine - 1, first->startCol, last->endLine + 1, 10000)); + DUContext* rangectx = openContext(first, range, DUContext::Other ); + kDebug() << " +++ opening context (stmlist): " << range.castToSimpleRange(); addImportedContexts(); visitNodeList( l ); closeContext(); + kDebug() << " --- closed context (stmlist): line " << rangectx->range().castToSimpleRange(); } } +void ContextBuilder::visitAttribute(AttributeAst* node) +{ + Python::AstDefaultVisitor::visitAttribute(node); +} + void ContextBuilder::visitClassDefinition( ClassDefinitionAst* node ) { - kDebug() << "Visiting Class Declaration"; - openContext( node, DUContext::Class, identifierForNode( node->className ) ); + RangeInRevision range(node->body.first()->startLine, node->body.first()->startCol, node->body.last()->endLine, node->body.last()->endCol + 100000); + openContext( node, range, DUContext::Class, identifierForNode( node->name ) ); + kDebug() << " +++ opening CLASS context: " << range.castToSimpleRange(); addImportedContexts(); - visitNodeList( node->inheritance ); - visitNodeList( node->classBody ); + Python::AstDefaultVisitor::visitClassDefinition(node); closeContext(); + kDebug() << " --- closing CLASS context: " << range.castToSimpleRange(); } -void ContextBuilder::visitFunctionDefinition( FunctionDefinitionAst* node ) +void ContextBuilder::visitArguments(ArgumentsAst* node) { - kDebug() << "building function definition context"; - kDebug() << node->startLine; - ClassDefinitionAst* classast = dynamic_cast( node->parent ); + AstDefaultVisitor::visitArguments(node); +} - if ( classast ) - { -// DUChainReadLocker lock( DUChain::lock() ); -// QList classContexts = currentContext()->findContexts( DUContext::Class, QualifiedIdentifier( classast->context->localScopeIdentifier(). ) ); - -// if ( classContexts.count() != 1 ) -// { -// m_importedParentContexts.append( classContexts.first() ); - m_importedParentContexts.append( currentContext() ); -// } - -// if ( classContexts.count() > 1 ) -// { -// kWarning() << "Multiple class contexts for" << classast->className->identifier << classast->context->localScopeIdentifier() << "shouldn't happen!"; -// foreach( DUContext* classContext, classContexts ) -// { -// kDebug() << "Context" << classContext->scopeIdentifier( true ) << "range" << classContext->range().textRange() << "in" << classContext->url().str(); -// } -// } +void ContextBuilder::visitCode(CodeAst* node) { + DUChainWriteLocker lock(DUChain::lock()); + TopDUContext* internal = DUChain::self()->chainForDocument(KUrl("/home/sven/projects/kde4/python/documentation/test.py")); + if ( internal ) { + currentContext()->addImportedParentContext(internal); } + AstDefaultVisitor::visitCode(node); +} - visitNodeList( node->decorators ); - - if ( node->parameters.count() > 0 ) - { - DUContext* funcctx = openContext( node->parameters.first(), node->parameters.last(), DUContext::Function, identifierForNode( node->functionName ) ); - addImportedContexts(); - visitNodeList( node->parameters ); - closeContext(); - m_importedParentContexts.append( funcctx ); +KUrl ContextBuilder::findModulePath(const QString& name) +{ + QStringList modulePath = name.split("."); + + KUrl currentPath = currentContext()->url().toUrl(); + Q_ASSERT(currentPath.url().length()); + kDebug() << " >>>>>>>>> Got URL: " << currentPath.upUrl().url(KUrl::RemoveTrailingSlash); + + IProject* currentProject = ICore::self()->projectController()->findProjectForUrl(currentPath); + if ( ! currentProject ) { + kError() << "Cannot import module contexts without a project opened."; + return KUrl(); } - - openContextForStatementList( node->functionBody ); - m_importedParentContexts.clear(); + + // easiest case: current directory + KUrl filename(currentPath.directory(KUrl::AppendTrailingSlash) + modulePath.first() + ".py"); + kDebug() << "filename url: " << filename; + if ( currentProject->filesForUrl(filename).length() > 0 ) { + ProjectFileItem* result = currentProject->filesForUrl(filename).first(); + kDebug() << "Found! " << result->url(); + return result->url(); + } + + return KUrl(); } -void ContextBuilder::visitFor( ForAst* node ) +void ContextBuilder::visitImportFrom(ImportFromAst* node) { - kDebug() << "Found for, building context"; - DUContext* forctx = openContext( node->assignedTargets.first(), node->assignedTargets.last(), DUContext::Other ); - visitNodeList( node->assignedTargets ); - closeContext(); - - visitNodeList( node->iterable ); + Python::AstDefaultVisitor::visitImportFrom(node); +} - m_importedParentContexts = QList() << forctx; - openContextForStatementList( node->forBody ); - openContextForStatementList( node->elseBody ); - m_importedParentContexts.clear(); +void ContextBuilder::visitImport(ImportAst* node) +{ + foreach ( AliasAst* name, node->names ) { + // for "import ... as", use the as thingy, use the module name otherwise +// Identifier* variableDeclarationName = name->asName ? name->asName->identifier : name->name; # TODO check this + + KUrl moduleFilePath = findModulePath(name->name->value); + if ( ! moduleFilePath.isValid() ) continue; + else { + DUChainWriteLocker lock(DUChain::lock()); + TopDUContext* moduleChain = DUChain::self()->chainForDocument(KUrl(moduleFilePath)); + contextsForModules.insert(name->name->value, TopDUContextPointer(moduleChain)); + kDebug() << "Added " << name->name->value << " to the module chain map"; +// currentContext()->addImportedParentContext(moduleChain); + } + } + Python::AstDefaultVisitor::visitImport(node); } -void ContextBuilder::visitWhile( WhileAst* node ) +void ContextBuilder::visitFunctionDefinition( FunctionDefinitionAst* node ) { - kDebug() << "Creating contexts for while"; - visitNode( node->condition ); - openContextForStatementList( node->whileBody ); - openContextForStatementList( node->elseBody ); + kDebug() << " Building function definition context: " << node->name->value; + DUChainWriteLocker lock(DUChain::lock()); + + visitNodeList( node->decorators ); + + Ast* first = node->body.first(); + Ast* last = node->body.last(); + Q_ASSERT(first->hasUsefulRangeInformation); // TODO remove this + RangeInRevision range(RangeInRevision(first->startLine, first->startCol, last->endLine, last->endCol + 100000)); + + if ( node->arguments && node->arguments->arguments.length() ) + { + int sline, eline, scol, ecol; + sline = node->arguments->arguments.first()->startLine; + eline = node->arguments->arguments.last()->endLine; + scol = node->arguments->arguments.first()->startCol; + ecol = node->arguments->arguments.last()->endCol; + + RangeInRevision range(sline, scol, eline, ecol+100000); + Q_ASSERT(range.isValid()); + DUContext* funcctx = openContext( node->arguments, range, DUContext::Function); + kDebug() << funcctx; + kDebug() << " +++ opening FUNCTION ARGUMENTS context: " << funcctx->range().castToSimpleRange(); + visitNode( node->arguments ); + closeContext(); + m_importedParentContexts.append( funcctx ); + } + + DUContext* ctx = openContext(first, range, DUContext::Function, identifierForNode( node->name ) ); + kDebug() << " +++ opening context (function definition): " << range.castToSimpleRange(); + addImportedContexts(); + + visitNodeList(node->body); + + closeContext(); + kDebug() << " --- closed context (function definition): " << ctx->range().castToSimpleRange(); } void ContextBuilder::visitWith( WithAst * node ) { - kDebug() << "creating contexts for With"; - - m_importedParentContexts = QList() << openContext( node->name, DUContext::Other ); - visitNode( node->name ); + m_importedParentContexts = QList() << openContext( node->contextExpression, DUContext::Other ); + kDebug() << " +++ opening context: " << node->startLine - 1 << ":" << node->startCol << " -- " << node->endLine + 1 << "inf"; + visitNode( node->contextExpression ); closeContext(); openContextForStatementList( node->body ); m_importedParentContexts.clear(); } -void ContextBuilder::visitTry( TryAst* node ) -{ - kDebug() << "creating contexts for try"; - openContextForStatementList( node->tryBody ); - visitNodeList( node->exceptions ); - openContextForStatementList( node->elseBody ); - openContextForStatementList( node->finallyBody ); -} - -void ContextBuilder::visitIf( IfAst* node ) -{ - kDebug() << "creating contexts for if"; - visitNode( node->ifCondition ); - openContextForStatementList( node->ifBody ); - QList< QPair< ExpressionAst*, QList > >::ConstIterator it, end = node->elseIfBodies.end(); - - for ( it = node->elseIfBodies.begin(); it != end; ++it ) - { - visitNode( ( *it ).first ); - openContextForStatementList( ( *it ).second ); - } - - openContextForStatementList( node->elseBody ); -} - } diff --git a/duchain/contextbuilder.h b/duchain/contextbuilder.h index 82f57b4..e605a13 100644 --- a/duchain/contextbuilder.h +++ b/duchain/contextbuilder.h @@ -31,23 +31,31 @@ #include #include "pythonduchainexport.h" +#include "pythonducontext.h" using namespace KDevelop; namespace Python { + +typedef QPair moduleContextTuple; class PythonEditorIntegrator; class ParseSession; -typedef KDevelop::AbstractContextBuilder ContextBuilderBase; +typedef KDevelop::AbstractContextBuilder ContextBuilderBase; class KDEVPYTHONDUCHAIN_EXPORT ContextBuilder: public ContextBuilderBase, public Python::AstDefaultVisitor { public: + virtual ReferencedTopDUContext build(const KDevelop::IndexedString& url, Ast* node, + ReferencedTopDUContext updateContext = ReferencedTopDUContext()); + void setEditor(PythonEditorIntegrator* editor); void setEditor(ParseSession* session); + KUrl findModulePath(const QString& name); + protected: PythonEditorIntegrator* editor() const; @@ -55,21 +63,30 @@ class KDEVPYTHONDUCHAIN_EXPORT ContextBuilder: public ContextBuilderBase, public virtual void setContextOnNode( Ast* node, KDevelop::DUContext* context ); virtual KDevelop::DUContext* contextFromNode( Ast* node ); virtual KDevelop::RangeInRevision editorFindRange( Ast* fromNode, Ast* toNode ); - virtual KDevelop::QualifiedIdentifier identifierForNode( IdentifierAst* node ); + virtual KDevelop::QualifiedIdentifier identifierForNode( Identifier* node ); void addImportedContexts(); virtual void visitFunctionDefinition( FunctionDefinitionAst* ); virtual void visitClassDefinition( ClassDefinitionAst* ); - virtual void visitFor( ForAst* node ); +// virtual void visitFor( ForAst* node ); virtual void visitWith( WithAst* node ); - virtual void visitWhile( WhileAst* node ); - virtual void visitIf( IfAst* node ); - virtual void visitTry( TryAst* node ); +// virtual void visitWhile( WhileAst* node ); +// virtual void visitIf( IfAst* node ); + virtual void visitArguments(ArgumentsAst* node); + virtual void visitCode(CodeAst* node); + virtual void visitImport(ImportAst* node); + virtual void visitImportFrom(ImportFromAst* node); + virtual void visitAttribute(AttributeAst* node); + + DUContext* openSafeContext( Python::Ast* node, RangeInRevision& range, DUContext::ContextType type, Python::Identifier* identifier = 0 ); + + QMap contextsForModules; - PythonEditorIntegrator *m_editor; + static PythonEditorIntegrator* m_editor; TopDUContext* newTopContext(const RangeInRevision& range, ParsingEnvironmentFile* file); + virtual KDevelop::DUContext* newContext(const KDevelop::RangeInRevision& range); template void visitNodeList( const QList& l ) { @@ -82,9 +99,10 @@ class KDEVPYTHONDUCHAIN_EXPORT ContextBuilder: public ContextBuilderBase, public } bool m_mapAst; + ReferencedTopDUContext m_topContext; private: - void openContextForStatementList( const QList& ); + void openContextForStatementList( const QList&, DUContext::ContextType type = DUContext::Other); QList m_importedParentContexts; }; diff --git a/duchain/declarationbuilder.cpp b/duchain/declarationbuilder.cpp index ab4fe16..f77ced2 100644 --- a/duchain/declarationbuilder.cpp +++ b/duchain/declarationbuilder.cpp @@ -1,6 +1,7 @@ /***************************************************************************** * Copyright (c) 2007 Piyush verma * - * Copyright 2007 Andreas Pakulat * + * Copyright 2007 Andreas Pakulat * + * Copyright 2010 Sven Brauch * * * * Permission is hereby granted, free of charge, to any person obtaining * * a copy of this software and associated documentation files (the * @@ -34,12 +35,25 @@ #include #include #include +#include #include #include +#include +#include +#include +#include #include #include +#include + +#include "contextbuilder.h" #include "pythoneditorintegrator.h" +#include "QtGlobal" + +#include +#include <../kdevplatform/language/duchain/declaration.h> +#include "expressionvisitor.h" using namespace KTextEditor; @@ -49,7 +63,6 @@ using namespace KDevelop; namespace Python { - DeclarationBuilder::DeclarationBuilder() : DeclarationBuilderBase() { @@ -67,28 +80,6 @@ DeclarationBuilder:: ~DeclarationBuilder() { } -// This is not used anywhere and causes build errors, so... bye. -/* -template -DeclarationType* DeclarationBuilder::specialDeclaration( KTextEditor::SmartRange* smartRange, - const KDevelop::SimpleRange& range ) -{ - DeclarationType* ret = new DeclarationType( m_editor->currentUrl(), range, currentContext() ); - ret->setSmartRange( smartRange ); - return ret; -} - -template -DeclarationType* DeclarationBuilder::specialDeclaration( KTextEditor::SmartRange* smartRange, - const KDevelop::SimpleRange& range, - int scope ) -{ - DeclarationType* ret = new DeclarationType( m_editor->currentUrl(), range, currentContext() ); - ret->setSmartRange( smartRange ); - return ret; -} -*/ - void DeclarationBuilder::closeDeclaration() { if ( lastContext() ) @@ -102,42 +93,152 @@ void DeclarationBuilder::closeDeclaration() DeclarationBuilderBase::closeDeclaration(); } -void DeclarationBuilder::visitIdentifierTarget(IdentifierTargetAst* node) +template T* DeclarationBuilder::visitVariableDeclaration(Ast* node) +{ + NameAst* currentVariableDefinition = dynamic_cast(node); + Q_ASSERT(currentVariableDefinition); + if ( currentVariableDefinition->context != ExpressionAst::Store + && currentVariableDefinition->context != ExpressionAst::Parameter + && currentVariableDefinition->context != ExpressionAst::AugStore + ) { + return 0; + } + Identifier* id = currentVariableDefinition->identifier; + return visitVariableDeclaration(id, currentVariableDefinition); +} + +/* + * WARNING: This will return a nullpointer if another than the expected type of variable was found! + * */ +template T* DeclarationBuilder::visitVariableDeclaration(Identifier* node, Ast* originalAst) { - Python::AstDefaultVisitor::visitIdentifierTarget(node); + DUChainWriteLocker lock(DUChain::lock()); + Q_ASSERT(node); - QList existingDeclarations; + CursorInRevision until = editorFindRange(node, node).end; - { - DUChainWriteLocker lock( DUChain::lock() ); - RangeInRevision range = editorFindRange(node, node); - CursorInRevision stopSearching = range.start; - QualifiedIdentifier id = identifierForNode(node->identifier); - existingDeclarations = currentContext()->findDeclarations(id, stopSearching); - } - if ( ! existingDeclarations.length() ) { - openDeclaration( node->identifier, node); + Declaration* dec = 0; + + kDebug() << "VARIABLE CONTEXT: " << currentContext()->scopeIdentifier() << currentContext()->range().castToSimpleRange() << currentContext()->type(); + + if ( currentContext() && currentContext()->type() == DUContext::Class ) { + kDebug() << "Creating class member declaration for " << node->value << node->startLine << ":" << node->startCol; + kDebug() << "Context type: " << currentContext()->scopeIdentifier() << currentContext()->range().castToSimpleRange(); + dec = openDeclaration(node, originalAst ? originalAst : node, DeclarationIsDefinition); closeDeclaration(); + } else { + kDebug() << "Creating variable declaration for " << node->value << node->startLine << ":" << node->startCol; + dec = openDeclaration(node, originalAst ? originalAst : node, DeclarationIsDefinition); + closeDeclaration(); + dec->setType(lastType()); + dec->setKind(KDevelop::Declaration::Instance); // everything is an object in python } - else { - kDebug() << "Declaration does already exist, not updating" << node->identifier->identifier.toAscii(); + +// dec->setType<>(); + T* result = dynamic_cast(dec); + if ( ! result ) kError() << "variable declaration does not have the expected type"; + return result; +} + +void DeclarationBuilder::visitExceptionHandler(ExceptionHandlerAst* node) +{ + if ( node->name ) visitVariableDeclaration(node->name); // except Error as + DeclarationBuilderBase::visitExceptionHandler(node); +} + +void DeclarationBuilder::visitFor(ForAst* node) +{ + if ( node->target->astType == Ast::NameAstType ) visitVariableDeclaration(node->target); + else if ( node->target->astType == Ast::TupleAstType ) { + foreach ( ExpressionAst* tupleMember, dynamic_cast(node->target)->elements ) { + if ( tupleMember->astType == Ast::NameAstType ) visitVariableDeclaration(tupleMember); + } } + Python::ContextBuilder::visitFor(node); } +void DeclarationBuilder::visitImport(ImportAst* node) +{ + Python::ContextBuilder::visitImport(node); + foreach ( AliasAst* name, node->names ) { + TopDUContextPointer contextptr = contextsForModules.value(name->asName ? name->asName->identifier->value : name->name->value); + kDebug() << "Chain for document: " << contextptr; + m_importContextsForImportStatement.push(contextptr); + importedModuleDeclaration* dec; + if ( name->asName ) dec = visitVariableDeclaration(name->asName); + else dec = visitVariableDeclaration(name->name); + QString moduleName = name->name->value; + if ( name->asName && name->asName->identifier ) + moduleName += "." + name->asName->identifier->value; + kDebug() << "Module name: " << moduleName; + if ( dec ) { + DUChainWriteLocker lock(DUChain::lock()); + dec->m_moduleIdentifier = moduleName; + dec->setType(IntegralType::Ptr(new IntegralType(IntegralType::TypeMixed))); + } + m_importContextsForImportStatement.clear(); + } +} + +void DeclarationBuilder::visitImportFrom(ImportFromAst* node) +{ + Python::AstDefaultVisitor::visitImportFrom(node); + foreach ( AliasAst* name, node->names ) { + importedModuleDeclaration* dec = 0; + if ( name->asName ) dec = visitVariableDeclaration(name->asName); + else dec = visitVariableDeclaration(name->name); + if ( dec && name->name && node->module ) { + dec->m_moduleIdentifier = node->module->value + "." + name->name->value; + kDebug() << "FromImport module name: " << name->name->value; + } + if ( dec ) { + dec->setType(IntegralType::Ptr(new IntegralType(IntegralType::TypeMixed))); + } + } +} + +void DeclarationBuilder::visitAssignment(AssignmentAst* node) +{ +// visitNode(node->value); + +// qDebug() << "pepeppepe" << node->; + ExpressionVisitor v(currentContext()); + v.visitNode(node->value); + setLastType(v.lastType()); + + foreach ( ExpressionAst* target, node->targets ) { + if ( target->astType == Ast::NameAstType ) { + visitVariableDeclaration(target); + } + } +} void DeclarationBuilder::visitClassDefinition( ClassDefinitionAst* node ) { kDebug() << "opening class definition"; - ContextBuilder::visitClassDefinition( node ); - openDeclaration( node->className, node ); +// ClassDeclaration* classDec = new ClassDeclaration(editorFindRange(node->body.first(), node->body.last()), currentContext()); + + openDeclaration( node->name, node ); eventuallyAssignInternalContext(); closeDeclaration(); + + DeclarationBuilderBase::visitClassDefinition( node ); } void DeclarationBuilder::visitFunctionDefinition( FunctionDefinitionAst* node ) { kDebug() << "opening function definition"; - FunctionDeclaration* dec = openDeclaration( node->functionName, node ); + int decoratorOffset = node->decorators.length(); // adjust the actual range of the functions' name + node->name->startLine += decoratorOffset; node->name->endLine += decoratorOffset; + kDebug() << "Function definition RANGE:" << node->name->startLine << node->name->startCol << node->name->endLine << node->name->endCol; + + // adjust range of arguments, too + if ( node->arguments ) { + node->arguments->startLine += decoratorOffset; + node->arguments->endLine += decoratorOffset; + } + + FunctionDeclaration* dec = openDeclaration( node->name, node ); FunctionType::Ptr type(new FunctionType); @@ -160,43 +261,34 @@ void DeclarationBuilder::visitLambda( LambdaAst* node ) // closeDeclaration(); } -void DeclarationBuilder::visitDefaultParameter( DefaultParameterAst* node ) +void DeclarationBuilder::visitCall(CallAst* node) { - ContextBuilder::visitDefaultParameter( node ); -// AbstractFunctionDeclaration* function = currentDeclaration(); - AbstractFunctionDeclaration* function = dynamic_cast(currentDeclaration()); - - if( function ) - { - if( node->value ) - { - //Not sure what to do here, C++ simply adds the source code as default parameter, but that doesn't sound sane... + foreach ( ExpressionAst* currentArgument, node->arguments ) { + NameAst* realArgument = dynamic_cast(currentArgument); + if ( realArgument ) { + visitVariableDeclaration(realArgument); // some_func(, ) } - //simple case, we have an identifier parameter - if( node->name->astType == Ast::IdentifierParameterPartAst ) - { - function->addDefaultParameter(IndexedString("foo")); - kDebug() << function->defaultParametersSize(); - - Q_ASSERT(hasCurrentType()); - FunctionType::Ptr type = currentType(); - Q_ASSERT(type); - - kDebug() << type->toString(); - - // create a variable definition - IdentifierParameterPartAst* identifierNode = dynamic_cast(node->name); - Declaration* dec = openDeclaration( identifierNode->name, node); - { - DUChainWriteLocker lock(DUChain::lock()); - dec->setType(IntegralType::Ptr(new IntegralType(IntegralType::TypeMixed))); - type->addArgument(dec->abstractType()); - } - closeDeclaration(); + } + Python::AstDefaultVisitor::visitCall(node); +} - } else if( node->name->astType == Ast::ListParameterPartAst ) - { - //complex case, a sublist, what to do?? +void DeclarationBuilder::visitArguments( ArgumentsAst* node ) +{ + AbstractFunctionDeclaration* function = dynamic_cast(currentDeclaration()); + kDebug() << "Current context for parameters: " << currentContext(); + if ( function ) { + NameAst* realParam; + foreach (ExpressionAst* expression, node->arguments) { + realParam = dynamic_cast(expression); + if ( realParam && realParam->context == ExpressionAst::Parameter ) { + Declaration* paramDeclaration = visitVariableDeclaration(realParam); + function->addDefaultParameter(IndexedString(realParam->identifier->value)); + FunctionType::Ptr type = currentType(); + if ( type && paramDeclaration ) type->addArgument(paramDeclaration->abstractType()); + } + else { + DeclarationBuilderBase::visitArguments(node); + } } } } diff --git a/duchain/declarationbuilder.h b/duchain/declarationbuilder.h index 093b5ba..dcf0a0c 100644 --- a/duchain/declarationbuilder.h +++ b/duchain/declarationbuilder.h @@ -32,8 +32,10 @@ namespace Python { + +typedef QPair moduleContextTuple; -typedef KDevelop::AbstractDeclarationBuilder DeclarationBuilderBase; +typedef KDevelop::AbstractDeclarationBuilder DeclarationBuilderBase; class KDEVPYTHONDUCHAIN_EXPORT DeclarationBuilder: public DeclarationBuilderBase { @@ -47,10 +49,21 @@ class KDEVPYTHONDUCHAIN_EXPORT DeclarationBuilder: public DeclarationBuilderBase virtual void visitClassDefinition( ClassDefinitionAst* node ); virtual void visitFunctionDefinition( FunctionDefinitionAst* node ); - virtual void visitDefaultParameter( DefaultParameterAst* node ); virtual void visitLambda( LambdaAst* node ); + virtual void visitAssignment(AssignmentAst* node); + virtual void visitFor(ForAst* node); + virtual void visitImport(ImportAst* node); + virtual void visitImportFrom(ImportFromAst* node); + virtual void visitArguments(ArgumentsAst* node); + virtual void visitExceptionHandler(ExceptionHandlerAst* node); + virtual void visitCall(CallAst* node); + + template T* visitVariableDeclaration(Python::Ast* node); + template T* visitVariableDeclaration(Identifier* node, Ast* originalAst = 0); + + QStack m_importContextsForImportStatement; - virtual void visitIdentifierTarget( IdentifierTargetAst * node ); +// virtual void visitIdentifierTarget( IdentifierTargetAst * node ); private: /* diff --git a/duchain/declarations/CMakeLists.txt b/duchain/declarations/CMakeLists.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/duchain/declarations/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/duchain/declarations/importedmoduledeclaration.cpp b/duchain/declarations/importedmoduledeclaration.cpp new file mode 100644 index 0000000..371e586 --- /dev/null +++ b/duchain/declarations/importedmoduledeclaration.cpp @@ -0,0 +1,46 @@ +#include "importedmoduledeclaration.h" +#include "parser/parserConfig.h" +#include + +namespace Python { + +QString importedModuleDeclaration::generateDocumentationForModule() +{ + QProcess* parser = new QProcess(); + parser->start("/usr/bin/env", QStringList() << "python" << QString(INSTALL_PATH) + QString("/pydoc.py") << QString("-w") << QString(m_moduleIdentifier)); + parser->waitForFinished(); + kDebug() << " ** Reading results..."; + + // TODO this is not clean + if ( parser->exitStatus() != QProcess::NormalExit ) { + kError() << "Error parsing file: " << parser->errorString(); + return "0"; + } + + QString result = parser->readAllStandardOutput(); + + return result; +} + +importedModuleDeclaration::importedModuleDeclaration(DeclarationData& dd): Declaration(dd) +{ + +} + +importedModuleDeclaration::importedModuleDeclaration(const KDevelop::RangeInRevision& range, DUContext* parentContext): Declaration(range, parentContext) +{ + +} + +importedModuleDeclaration::importedModuleDeclaration(const KDevelop::Declaration& rhs): Declaration(rhs) +{ + +} + +importedModuleDeclaration::importedModuleDeclaration(DeclarationData& dd, const KDevelop::RangeInRevision& range): Declaration(dd, range) +{ + +} + +} + diff --git a/duchain/declarations/importedmoduledeclaration.h b/duchain/declarations/importedmoduledeclaration.h new file mode 100644 index 0000000..ed579da --- /dev/null +++ b/duchain/declarations/importedmoduledeclaration.h @@ -0,0 +1,24 @@ +#ifndef IMPORTEDMODULEDECLARATION_H +#define IMPORTEDMODULEDECLARATION_H +#include +#include "pythonduchainexport.h" + +using namespace KDevelop; + +namespace Python { + +class KDEVPYTHONDUCHAIN_EXPORT importedModuleDeclaration : public Declaration +{ + +public: + importedModuleDeclaration(DeclarationData& dd); + importedModuleDeclaration(const KDevelop::RangeInRevision& range, DUContext* parentContext); + importedModuleDeclaration(DeclarationData& dd, const KDevelop::RangeInRevision& range); + importedModuleDeclaration(const KDevelop::Declaration& rhs); + QString m_moduleIdentifier; + QString generateDocumentationForModule(); +}; + +} + +#endif // IMPORTEDMODULEDECLARATION_H diff --git a/duchain/expressionvisitor.cpp b/duchain/expressionvisitor.cpp new file mode 100644 index 0000000..83f0c02 --- /dev/null +++ b/duchain/expressionvisitor.cpp @@ -0,0 +1,111 @@ +#include "expressionvisitor.h" +#include +#include +#include +#include +#include +#include + +using namespace KDevelop; +using namespace Python; + +QHash ExpressionVisitor::s_defaultTypes; + +Python::ExpressionVisitor::ExpressionVisitor(DUContext* ctx) + : m_ctx(ctx) +{ + if(s_defaultTypes.isEmpty()) { + s_defaultTypes.insert(KDevelop::Identifier("True"), AbstractType::Ptr(new IntegralType(IntegralType::TypeBoolean))); + s_defaultTypes.insert(KDevelop::Identifier("False"), AbstractType::Ptr(new IntegralType(IntegralType::TypeBoolean))); + } +} + +void Python::ExpressionVisitor::visitNumber(Python::NumberAst* ) +{ + m_lastType = AbstractType::Ptr(new IntegralType(IntegralType::TypeFloat)); +} + +void Python::ExpressionVisitor::visitString(Python::StringAst* ) +{ + m_lastType = AbstractType::Ptr(new IntegralType(IntegralType::TypeString)); +} + +RangeInRevision nodeRange(Python::Ast* node) +{ + qDebug() << node->endLine; + return RangeInRevision(node->startLine, node->startCol, node->endLine,node->endCol); +} + +void Python::ExpressionVisitor::visitName(Python::NameAst* node) +{ + KDevelop::Identifier id(node->identifier->value); + QHash < KDevelop::Identifier, AbstractType::Ptr >::const_iterator defId = s_defaultTypes.constFind(id); + if(defId!=s_defaultTypes.constEnd()) { + m_lastType = *defId; + return; + } + + QList< Declaration* > d=m_ctx->findDeclarations(id); +// Q_ASSERT(!d.isEmpty()); + + qDebug() << "visitName" << node->identifier->value << d; + if(!d.isEmpty()) + m_lastType = d.last()->abstractType(); + else { + qDebug("VistName type not found"); + RangeInRevision r = nodeRange(node); + + ProblemPointer p(new Problem); + p->setRange(r); + p->setDescription(i18n("undefined variable '%1'", node->identifier->value)); + qDebug() << "adddProblemKiko" << m_ctx->topContext()->url().str(); + p->setFinalLocation(DocumentRange(m_ctx->topContext()->url(), r.castToSimpleRange())); + p->setSeverity(ProblemData::Error); + p->setSource(KDevelop::ProblemData::SemanticAnalysis); + m_ctx->topContext()->addProblem(p); + } +} + +void Python::ExpressionVisitor::visitBinaryOperation(Python::BinaryOperationAst* node) +{ + visitNode(node->lhs); + KDevelop::AbstractType::Ptr leftType = m_lastType; + + visitNode(node->rhs); + KDevelop::AbstractType::Ptr rightType = m_lastType; + + if(leftType->whichType()==AbstractType::TypeIntegral && leftType->whichType()==AbstractType::TypeIntegral) + m_lastType = leftType; + else + m_lastType = AbstractType::Ptr(new UnsureType); +} + +void Python::ExpressionVisitor::visitUnaryOperation(Python::UnaryOperationAst* node) +{ + visitNode(node->operand); + + //FIXME: m_lastValue = m_lastValue; +} + +void Python::ExpressionVisitor::visitBooleanOperation(Python::BooleanOperationAst* node) +{ +// + foreach (ExpressionAst* expression, node->values) { + visitNode(expression); +// if(m_lastType->whichType() != AbstractType::TypeIntegral || m_lastType.cast()->dataType() != IntegralType::TypeBoolean){ +// problem = true; +// qDebug() << "VistBooleanOperation type not match"; +// RangeInRevision r = nodeRange(expression); +// ProblemPointer p(new Problem); +// p->setRange(r); +// p->setDescription(i18n("wrong type '%1'", m_lastType->toString())); +// p->setFinalLocation(DocumentRange(m_ctx->topContext()->url(), r.castToSimpleRange())); +// p->setSeverity(ProblemData::Error); +// p->setSource(KDevelop::ProblemData::SemanticAnalysis); +// m_ctx->topContext()->addProblem(p); +// } + } + + m_lastType = AbstractType::Ptr(new IntegralType(IntegralType::TypeBoolean)); +} + diff --git a/duchain/expressionvisitor.h b/duchain/expressionvisitor.h new file mode 100644 index 0000000..eeeadb7 --- /dev/null +++ b/duchain/expressionvisitor.h @@ -0,0 +1,38 @@ +#ifndef EXPRESSIONVISITOR_H +#define EXPRESSIONVISITOR_H + +#include +#include +#include + +namespace KDevelop { +class Identifier; +} + +namespace Python +{ + +class ExpressionVisitor : public AstDefaultVisitor +{ + public: + ExpressionVisitor(KDevelop::DUContext* ctx); + + virtual void visitBinaryOperation(BinaryOperationAst* node); + virtual void visitUnaryOperation(UnaryOperationAst* node); + virtual void visitBooleanOperation(BooleanOperationAst* node); + + virtual void visitString(StringAst* node); + virtual void visitNumber(NumberAst* node); + virtual void visitName(NameAst* node); + + KDevelop::AbstractType::Ptr lastType() const { return m_lastType; } + private: + static QHash s_defaultTypes; + + KDevelop::AbstractType::Ptr m_lastType; + KDevelop::DUContext* m_ctx; +}; + +} + +#endif // EXPRESSIONVISITOR_H diff --git a/duchain/navigation/CMakeLists.txt b/duchain/navigation/CMakeLists.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/duchain/navigation/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/duchain/navigation/declarationnavigationcontext.cpp b/duchain/navigation/declarationnavigationcontext.cpp new file mode 100644 index 0000000..94e959f --- /dev/null +++ b/duchain/navigation/declarationnavigationcontext.cpp @@ -0,0 +1,79 @@ +/* + Copyright 2007 David Nolden + Copyright 2008 Niko Sams + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "declarationnavigationcontext.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace Python +{ +using namespace KDevelop; + +DeclarationNavigationContext::DeclarationNavigationContext(DeclarationPointer decl, KDevelop::TopDUContextPointer topContext, AbstractNavigationContext* previousContext) + : AbstractDeclarationNavigationContext(decl, topContext, previousContext) +{ + kDebug() << "Generating declaration widget"; + importedModuleDeclaration* import_decl = dynamic_cast(decl.data()); + if ( import_decl ) { + kDebug() << " >> Module declaration found! Building documentation"; + kDebug() << " >> Identifier: " << import_decl->m_moduleIdentifier; + m_fullyQualifiedModuleIdentifier = import_decl->m_moduleIdentifier; + } + else { + kDebug() << "Could not find declaration for this module!" << decl->identifier().identifier().str(); + } +} + +// QString DeclarationNavigationContext::html(bool shorten) { +// QString normalDoc = AbstractDeclarationNavigationContext::html(shorten); +// if ( m_moduleDocumentation.length() ) { +// normalDoc += "



" + m_moduleDocumentation; +// } +// // return normalDoc; +// return QString(); +// } + +NavigationContextPointer DeclarationNavigationContext::registerChild(DeclarationPointer declaration) +{ + return AbstractDeclarationNavigationContext::registerChild(new DeclarationNavigationContext(declaration, m_topContext, this)); +} + +void DeclarationNavigationContext::makeLink(const QString& name, DeclarationPointer declaration, NavigationAction::Type actionType) +{ + AbstractDeclarationNavigationContext::makeLink(name, declaration, actionType); +} + +QString DeclarationNavigationContext::declarationKind(DeclarationPointer decl) +{ + return AbstractDeclarationNavigationContext::declarationKind(decl); +} + +} diff --git a/duchain/navigation/declarationnavigationcontext.h b/duchain/navigation/declarationnavigationcontext.h new file mode 100644 index 0000000..6a8057c --- /dev/null +++ b/duchain/navigation/declarationnavigationcontext.h @@ -0,0 +1,50 @@ +/* + Copyright 2007 David Nolden + Copyright 2008 Niko Sams + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef DECLARATIONNAVIGATIONCONTEXT_H +#define DECLARATIONNAVIGATIONCONTEXT_H + +#include +#include + +namespace Python +{ + +class DeclarationNavigationContext : public KDevelop::AbstractDeclarationNavigationContext +{ +public: + DeclarationNavigationContext(KDevelop::DeclarationPointer decl, KDevelop::TopDUContextPointer topContext, KDevelop::AbstractNavigationContext* previousContext = 0); + + QString m_fullyQualifiedModuleIdentifier; + +protected: + KDevelop::NavigationContextPointer registerChild(KDevelop::DeclarationPointer declaration); +// virtual KDevelop::QualifiedIdentifier prettyQualifiedIdentifier( KDevelop::DeclarationPointer decl ) const; +// virtual void htmlClass(); +// virtual void htmlFunction(); +// QString html(bool shorten = false); + + void makeLink( const QString& name, KDevelop::DeclarationPointer declaration, KDevelop::NavigationAction::Type actionType ); + + virtual QString declarationKind(KDevelop::DeclarationPointer decl); + +}; + +} + +#endif diff --git a/duchain/navigation/navigationwidget.cpp b/duchain/navigation/navigationwidget.cpp new file mode 100644 index 0000000..c0131a5 --- /dev/null +++ b/duchain/navigation/navigationwidget.cpp @@ -0,0 +1,89 @@ +#include "navigationwidget.h" +#include "declarationnavigationcontext.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "parser/parserConfig.h" +#include + +#include + +namespace Python { + +NavigationWidget::NavigationWidget(KDevelop::DeclarationPointer declaration, KDevelop::TopDUContextPointer topContext, const QString& /* htmlPrefix */, const QString& /* htmlSuffix */) +{ + kDebug() << "Navigation widget for Declaration requested"; + m_topContext = topContext; + + initBrowser(400); + + DeclarationNavigationContext* context = new DeclarationNavigationContext(declaration, m_topContext); + m_startContext = context; + setContext(m_startContext); + + m_fullyQualifiedModuleIdentifier = context->m_fullyQualifiedModuleIdentifier; + kDebug() << "Identifier: " << m_fullyQualifiedModuleIdentifier; + if ( m_fullyQualifiedModuleIdentifier.length() ) { + kDebug() << "Checking wether doc server is running"; + QTcpSocket* sock = new QTcpSocket(); + sock->connectToHost(QHostAddress::LocalHost, 1050, QTcpSocket::ReadOnly); + bool running = sock->waitForConnected(300); + if ( ! running ) { + kDebug() << "Not running, starting pydoc server"; + QProcess::startDetached("/usr/bin/env", QStringList() << "python" << QString(INSTALL_PATH) + "/pydoc.py" << "-p" << "1050"); + usleep(100000); // give pydoc server 100ms to start up + } + else { + sock->disconnectFromHost(); + } + delete sock; + + m_documentationWebView = new QWebView(this); + m_documentationWebView->load(QUrl("http://localhost:1050/" + m_fullyQualifiedModuleIdentifier + ".html")); + connect( m_documentationWebView, SIGNAL(loadFinished(bool)), SLOT(addDocumentationData(bool)) ); + } +} + +void NavigationWidget::addDocumentationData(bool finished) +{ + kDebug() << "Done loading!"; + disconnect(m_documentationWebView, SIGNAL(loadFinished(bool))); + if ( finished ) { + QGridLayout* newLayout = new QGridLayout(); + newLayout->setRowMinimumHeight(0, 200); + newLayout->setColumnMinimumWidth(0, 500); + newLayout->addWidget(m_documentationWebView); + layout()->addItem(newLayout); + } + else { + kError() << "Failed to get documentation for " << m_fullyQualifiedModuleIdentifier; + } +// QWebElement document = m_documentationWebView->page()->mainFrame()->documentElement(); +// if ( ! document.isNull() ) { +// kDebug() << " >>> Trying to append documentation... "; +// kDebug() << document.findFirst("body").tagName(); +// document.findFirst("body").findFirst("div").replace(m_originalHtml); +// } +// else { +// kError() << " !!! Could not append documentation to HTML page received!"; +// } +} + +NavigationWidget::NavigationWidget(const KDevelop::IncludeItem& /* includeItem */, KDevelop::TopDUContextPointer /*topContext*/, const QString& /*htmlPrefix*/, const QString& /*htmlSuffix*/) +{ + +} + +} + +#include "navigationwidget.moc" \ No newline at end of file diff --git a/duchain/navigation/navigationwidget.h b/duchain/navigation/navigationwidget.h new file mode 100644 index 0000000..ca0a0c0 --- /dev/null +++ b/duchain/navigation/navigationwidget.h @@ -0,0 +1,32 @@ +#ifndef NAVIGATIONWIDGET_H +#define NAVIGATIONWIDGET_H + +#include +#include +#include "pythonduchainexport.h" +#include + +namespace Python { + +class KDEVPYTHONDUCHAIN_EXPORT NavigationWidget : public KDevelop::AbstractNavigationWidget +{ +Q_OBJECT + +public slots: + void addDocumentationData(bool finished); + +public: + NavigationWidget(KDevelop::DeclarationPointer declaration, KDevelop::TopDUContextPointer topContext, const QString& htmlPrefix = QString(), const QString& htmlSuffix = QString()); + NavigationWidget(const KDevelop::IncludeItem& includeItem, KDevelop::TopDUContextPointer topContext, const QString& htmlPrefix = QString(), const QString& htmlSuffix = QString()); + + static QString shortDescription(KDevelop::Declaration* /*declaration*/) { return "Test"; }; + static QString shortDescription(const KDevelop::IncludeItem& /*includeItem*/) { return "Test"; }; + + QWebView* m_documentationWebView; + QString m_originalHtml; + QString m_fullyQualifiedModuleIdentifier; +}; + +} + +#endif // NAVIGATIONWIDGET_H \ No newline at end of file diff --git a/duchain/pythonducontext.cpp b/duchain/pythonducontext.cpp new file mode 100644 index 0000000..ab33bcb --- /dev/null +++ b/duchain/pythonducontext.cpp @@ -0,0 +1,30 @@ +#include "pythonducontext.h" + +#include +#include +#include +#include + +#include "navigation/navigationwidget.h" + +using namespace KDevelop; + +namespace Python { + +REGISTER_DUCHAIN_ITEM_WITH_DATA(PythonTopDUContext, TopDUContextData); + +REGISTER_DUCHAIN_ITEM_WITH_DATA(PythonNormalDUContext, DUContextData); + +template<> +QWidget* PythonTopDUContext::createNavigationWidget(Declaration* decl, TopDUContext* topContext, const QString& htmlPrefix, const QString& htmlSuffix) const { + if ( ! decl ) return 0; + return new NavigationWidget(DeclarationPointer(decl), TopDUContextPointer(topContext), htmlPrefix, htmlSuffix); +} + +template<> +QWidget* PythonNormalDUContext::createNavigationWidget(Declaration* decl, TopDUContext* topContext, const QString& htmlPrefix, const QString& htmlSuffix) const { + if ( ! decl ) return 0; + return new NavigationWidget(DeclarationPointer(decl), TopDUContextPointer(topContext), htmlPrefix, htmlSuffix); +} + +} diff --git a/duchain/pythonducontext.h b/duchain/pythonducontext.h new file mode 100644 index 0000000..5b17cd2 --- /dev/null +++ b/duchain/pythonducontext.h @@ -0,0 +1,54 @@ +#ifndef PYTHONDUCONTEXT_H +#define PYTHONDUCONTEXT_H + +#include +#include +class QWidget; + +namespace KDevelop +{ + class Declaration; + class TopDUContext; +} + +namespace Python +{ + +template +class PythonDUContext : public BaseContext +{ +public: + template + PythonDUContext(Data& data) : BaseContext(data) { + } + + ///Parameters will be reached to the base-class + template + PythonDUContext(const Param1& p1, const Param2& p2, bool isInstantiationContext) : BaseContext(p1, p2, isInstantiationContext) { + static_cast(this)->d_func_dynamic()->setClassId(this); + } + + ///Both parameters will be reached to the base-class. This fits TopDUContext. + template + PythonDUContext(const Param1& p1, const Param2& p2, const Param3& p3) : BaseContext(p1, p2, p3) { + static_cast(this)->d_func_dynamic()->setClassId(this); + } + template + PythonDUContext(const Param1& p1, const Param2& p2) : BaseContext(p1, p2) { + static_cast(this)->d_func_dynamic()->setClassId(this); + } + + virtual QWidget* createNavigationWidget(KDevelop::Declaration* decl, KDevelop::TopDUContext* topContext, const QString& htmlPrefix, const QString& htmlSuffix) const; + + enum { + Identity = IdentityT + }; +}; + +typedef PythonDUContext PythonTopDUContext; +typedef PythonDUContext PythonNormalDUContext; + +} + + +#endif // PYTHONDUCONTEXT_H diff --git a/duchain/pythoneditorintegrator.cpp b/duchain/pythoneditorintegrator.cpp index 68fc415..ae35f58 100644 --- a/duchain/pythoneditorintegrator.cpp +++ b/duchain/pythoneditorintegrator.cpp @@ -56,6 +56,7 @@ void PythonEditorIntegrator::setParseSession(ParseSession* session) CursorInRevision PythonEditorIntegrator::findPosition( Ast* node , Edge edge ) const { + Q_ASSERT(node); if ( edge == BackEdge ) { // Apparently KTE expects a range to go until _after_ the last character that should be included diff --git a/duchain/tests/CMakeLists.txt b/duchain/tests/CMakeLists.txt new file mode 100644 index 0000000..ac8397d --- /dev/null +++ b/duchain/tests/CMakeLists.txt @@ -0,0 +1,2 @@ +kde4_add_unit_test(pyduchaintest pyduchaintest.cpp ) +target_link_libraries(pyduchaintest kdev4pythonduchain ${QT_QTTEST_LIBRARY} ${KDEVPLATFORM_TESTS_LIBRARIES}) diff --git a/duchain/tests/pyduchaintest.cpp b/duchain/tests/pyduchaintest.cpp new file mode 100644 index 0000000..d50c9fc --- /dev/null +++ b/duchain/tests/pyduchaintest.cpp @@ -0,0 +1,130 @@ +/***************************************************************************** + * Copyright 2010 (c) Miquel Canes Gonzalez * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + *****************************************************************************/ + +#include "pyduchaintest.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QTEST_MAIN(PyDUChainTest) + +using namespace KDevelop; +using namespace Python; + +PyDUChainTest::PyDUChainTest(QObject* parent): QObject(parent) +{ + initShell(); +} + +void PyDUChainTest::initShell() +{ + AutoTestShell::init(); + TestCore* core = new TestCore(); + core->initialize(KDevelop::Core::NoUi); + + DUChain::self()->disablePersistentStorage(); + KDevelop::CodeRepresentation::setDiskChangesForbidden(true); +} + +ReferencedTopDUContext PyDUChainTest::parse(const QByteArray& code) +{ + ParseSession* session = new ParseSession; + session->setContents( code + "\n" ); // append a newline in case the parser doesnt like it without one + + static int mytest=0; + KUrl filename("/test"+QString::number(mytest++)); + session->setCurrentDocument(filename); + + QPair parserResults = session->parse(0); + CodeAst* ast = parserResults.first; + + if(!parserResults.second) + return 0; + + PythonEditorIntegrator editor; + DeclarationBuilder builder( &editor ); + + editor.setParseSession(session); + + ReferencedTopDUContext ret = builder.build(IndexedString(filename), ast); + + { + DUChainWriteLocker lock(DUChain::lock()); +// ParsingEnvironmentFilePointer parsingEnvironmentFile = ret->parsingEnvironmentFile(); +// parsingEnvironmentFile->clearModificationRevisions(); +// parsingEnvironmentFile->setModificationRevision(contents().modification); +// DUChain::self()->updateContextEnvironment(m_duContext, parsingEnvironmentFile.data()); + ret->clearProblems(); + } + + UseBuilder usebuilder( &editor ); + usebuilder.buildUses(ast); + + return ret; +} + +void PyDUChainTest::testSimple() +{ + QFETCH(QString, code); + QFETCH(int, decls); + QFETCH(int, uses); + + ReferencedTopDUContext ctx = parse(code.toLatin1()); + QVERIFY(ctx); + + DUChainReadLocker lock(DUChain::lock()); + QVector< Declaration* > declarations = ctx->localDeclarations(ctx); + + QCOMPARE(declarations.size(), decls); + + int usesCount = 0; + foreach(Declaration* d, declarations) { + usesCount += d->uses().size(); + + QVERIFY(!d->abstractType().isNull()); + } + + QCOMPARE(usesCount, uses); +} + +void PyDUChainTest::testSimple_data() +{ + QTest::addColumn("code"); + QTest::addColumn("decls"); + QTest::addColumn("uses"); + + QTest::newRow("assign") << "b = 2;" << 1 << 0; + QTest::newRow("assign_str") << "b = 'hola';" << 1 << 0; + QTest::newRow("op") << "a = 3; b = a+2;" << 2 << 1; + QTest::newRow("bool") << "a = True" << 1 << 0; + QTest::newRow("op") << "a = True and True;" << 1 << 0; +} diff --git a/duchain/tests/pyduchaintest.h b/duchain/tests/pyduchaintest.h new file mode 100644 index 0000000..c17e04a --- /dev/null +++ b/duchain/tests/pyduchaintest.h @@ -0,0 +1,48 @@ +/***************************************************************************** + * Copyright 2010 (c) Miquel Canes Gonzalez * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + *****************************************************************************/ + +#ifndef PYDUCHAINTEST_H +#define PYDUCHAINTEST_H + +#include + +namespace KDevelop { +class TopDUContext; +class ReferencedTopDUContext; +} + +class PyDUChainTest : public QObject +{ + Q_OBJECT + public: + explicit PyDUChainTest(QObject* parent = 0); + void initShell(); + + KDevelop::ReferencedTopDUContext parse(const QByteArray& code); + + private slots: + void testSimple(); + void testSimple_data(); +}; + +#endif // PYDUCHAINTEST_H diff --git a/duchain/typebuilder.h b/duchain/typebuilder.h index d9efd85..4f2e00e 100644 --- a/duchain/typebuilder.h +++ b/duchain/typebuilder.h @@ -30,7 +30,7 @@ namespace Python { -typedef KDevelop::AbstractTypeBuilder TypeBuilderBase; +typedef KDevelop::AbstractTypeBuilder TypeBuilderBase; class KDEVPYTHONDUCHAIN_EXPORT TypeBuilder: public TypeBuilderBase { diff --git a/duchain/usebuilder.cpp b/duchain/usebuilder.cpp index fd03b39..08c677a 100644 --- a/duchain/usebuilder.cpp +++ b/duchain/usebuilder.cpp @@ -40,51 +40,50 @@ using namespace KDevelop; namespace Python { -UseBuilder::UseBuilder (PythonEditorIntegrator* editor) +UseBuilder::UseBuilder (PythonEditorIntegrator* editor) : m_editor(editor) { } -// void UseBuilder::buildUses(Ast *node) -// { -// supportBuild(node); -// // if (TopDUContext* top = dynamic_cast(m_session->getNode(node))) -// // top->setHasUses(true); -// } - -void UseBuilder::visitIdentifier(IdentifierAst* node) +void UseBuilder::buildUses(Ast* node) { - DUChainWriteLocker lock( DUChain::lock() ); - QualifiedIdentifier id = identifierForNode(node); - RangeInRevision range = editorFindRange(node, node); - CursorInRevision until = range.start; - QList dec = currentContext()->findDeclarations(id, until); - - kDebug() << "-- identifier: " << node->identifier.toAscii(); - kDebug() << "declaration count: " << dec.length(); - kDebug() << "is type: " << node->parent->astType; - - // only highlight the top level properties; maybe we find a way to do the others later - // but it'll be difficult - if ( node->parent->astType == Python::Ast::AtomAst ) { - if ( dec.length() ) { - UseBuilderBase::newUse(node, dec.last()); - } - } + UseBuilderBase::buildUses(node); } -void UseBuilder::openContext(DUContext * newContext) -{ - UseBuilderBase::openContext(newContext); - m_nextUseStack.push(0); -} -void UseBuilder::closeContext() +void UseBuilder::visitName(NameAst* node) { - UseBuilderBase::closeContext(); - m_nextUseStack.pop(); + DUChainWriteLocker lock(DUChain::lock()); + QList declarations = currentContext()->findDeclarations(identifierForNode(node->identifier), editorFindRange(node, node).start); +// QList isDecl = currentContext()->findDeclarations(identifierForNode(node->identifier), editorFindRange(node, node).end); // TODO not so elegant ;D + Declaration* declaration; + if ( declarations.length() ) declaration = declarations.last(); + else declaration = 0; + kDebug() << currentContext()->type() << currentContext()->scopeIdentifier() << currentContext()->range().castToSimpleRange(); + + Q_ASSERT(node->identifier); + Q_ASSERT(node->hasUsefulRangeInformation); // TODO remove this! + RangeInRevision useRange(node->identifier->startLine, node->identifier->startCol, node->identifier->endLine, node->identifier->endCol + 1); + + if ( declaration && declaration->range() == useRange ) return; + + kDebug() << " Registering use for " << node->identifier->value << " at " << useRange.castToSimpleRange() << "with dec" << declaration; + UseBuilderBase::newUse(node, useRange, DeclarationPointer(declaration)); } +// void UseBuilder::openContext(DUContext * newContext) +// { +// UseBuilderBase::openContext(newContext); +// m_nextUseStack.push(0); +// } +// +// void UseBuilder::closeContext() +// { +// UseBuilderBase::closeContext(); +// m_nextUseStack.pop(); +// } + + ParseSession *UseBuilder::parseSession() const { return m_session; diff --git a/duchain/usebuilder.h b/duchain/usebuilder.h index 96f9511..ca51c9c 100644 --- a/duchain/usebuilder.h +++ b/duchain/usebuilder.h @@ -34,7 +34,7 @@ namespace Python { class ParseSession; -typedef KDevelop::AbstractUseBuilder UseBuilderBase; +typedef KDevelop::AbstractUseBuilder UseBuilderBase; class KDEVPYTHONDUCHAIN_EXPORT UseBuilder: public UseBuilderBase { @@ -43,13 +43,16 @@ class KDEVPYTHONDUCHAIN_EXPORT UseBuilder: public UseBuilderBase // UseBuilder(PythonEditorIntegrator* editor, const KUrl &url); UseBuilder(PythonEditorIntegrator *editor); ParseSession* parseSession() const; -// void buildUses(Python::Ast* node); - virtual void openContext(KDevelop::DUContext* newContext); - virtual void closeContext(); - - virtual void visitIdentifier(IdentifierAst *node); + void buildUses(Python::Ast* node); +// virtual void openContext(KDevelop::DUContext* newContext); +// virtual void closeContext(); + +protected: +// virtual void visitIdentifier(Identifier* node); + virtual void visitName(NameAst* node); private: ParseSession* m_session; + PythonEditorIntegrator* m_editor; // void newUse(std::size_t name, Ast *rangenode); inline int& nextUseIndex() { diff --git a/example_ast.py b/example_ast.py new file mode 100644 index 0000000..68b3113 --- /dev/null +++ b/example_ast.py @@ -0,0 +1,180 @@ +a = a and a +a = a or b + +def some_class(foo, bar): + attr1 = 3 + attr2 = 5 + attr3 = 'str' + +some_instance = some_class() +some_instance.attr1 +some_instance.attr2 +some_instance.some_method() + +#comment +""" +multiline comment +foo +bar +""" + +import sys +import random + +import PyQt4.QtCore + +print sys + +def simple_func(foo): + # usage comment, bla, param:foo + pass + +def function(foo): + """ docstring + more + more more + >>> test + >>> test + """ + return foo + +try: + pass +except Exception as e: + print e + +def func(foo, bar, baz, bang, foobang, foobar, foobazbar, foobazbarbang): + return foobang + print foo + print foobazbarbang + + if foobazbar < 5: + pass + +func(sys) +simple_func() + +def func_without_param(): + pass + +func_without_param() + +def another_function(param): + print param + +a = 5 + +bar = a == a +a != a +a < a +a <= a +a > a +a >= a +a is a +a is not a +a not in a +a in a + +a = a + 1 +a = a - 1 +a = a * 1 +a = a / 1 +a = a % 1 +a = a ^ 1 +a = a & 1 +a = a | 1 +a = a ** 1 +a = a >> 1 +a = a << 1 + +a = not a +a = +a +a = -a +a = ~a + +a = b[1:2:3][2] +extended = a[1:2, 2:3] + +i += 3 +i += j + +print 3 if 5 < 7 else 4 + +from random import random + +print random + +random(foo=3) + +a = lambda x: x**2 + +@staticmethod +@classmethod +def genfunc(): + yield foo + +for target1, target2 in some_dict.iteritems(): + print target1, target2 + +pi = 3.1415 + +foo = 1, 2 +bar = (1, 2) + +with open('f') as foo: + pass + +global IMAGLOBALVARIABLE +IMAGLOBALVARIABLE = 0 +try: + a = 3 / 0 +except ZeroDivisionError as err: + raise ValueError +else: + do_something() +finally: + BAM + +if 3 and 5: + pass + +for i in xrange(20): + pass + +while True: + break + continue + +del foo + +import random +random.random(3, 5) + +somelist = [1, 2, 3, 4, 5] +somedict = { 'key1' : 'value1', key2: value2 } + +print somelist[...] +print somelist[1:] +print somelist[:20] +print somelist[1:20] +print somelist[1:20:2] + +class bar(parent): + pass + +if foo in bar and 3 < 5: + pass + +a = [x*2 for x in xrange(20)] + +variable.variable2.variable3 = 15 +def function(param1, param2, param3, *paramstar, **paramdstar): + pass + return param1 * param2 + +assert False +if not 3: + pass + +if 3 * 5 == 7: + pass diff --git a/example_ast.xml b/example_ast.xml new file mode 100644 index 0000000..fb10791 --- /dev/null +++ b/example_ast.xml @@ -0,0 +1,990 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/parser/CMakeLists.txt b/parser/CMakeLists.txt index f0290e3..7118f68 100644 --- a/parser/CMakeLists.txt +++ b/parser/CMakeLists.txt @@ -1,57 +1,41 @@ - - - include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) set(parser_STAT_SRCS - pythondriver.cpp parsesession.cpp - pythonlexer.cpp - kwcheck.cpp - numbercheck.cpp ast.cpp astdefaultvisitor.cpp astvisitor.cpp astbuilder.cpp - astprinter.cpp - ) - -kdevpgqt_generate(_kdevpgList python NAMESPACE PythonParser - "${kdevpython_SOURCE_DIR}/parser/python.g" - "${kdevpython_SOURCE_DIR}/parser/pythonlexer.h" + pythondriver.cpp ) -add_custom_target( debuginfo - ${KDEVPG_EXECUTABLE} --terminals - "${CMAKE_CURRENT_SOURCE_DIR}/parser/python.g" ">terminals" - COMMAND ${KDEVPG_EXECUTABLE} --symbols - "${CMAKE_CURRENT_SOURCE_DIR}/parser/python.g" ">symbols" - COMMAND ${KDEVPG_EXECUTABLE} --rules - "${CMAKE_CURRENT_SOURCE_DIR}/parser/python.g" ">rules" "2>errors" - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" -) -set( parser_SRCS - ${_kdevpgList} -) +# kdevpgqt_generate(_kdevpgList python NAMESPACE PythonParser +# "${kdevpython_SOURCE_DIR}/parser/python.g" +# "${kdevpython_SOURCE_DIR}/parser/pythonlexer.h" +# ) + +# add_custom_target( debuginfo +# ${KDEVPG_EXECUTABLE} --terminals +# "${CMAKE_CURRENT_SOURCE_DIR}/parser/python.g" ">terminals" +# COMMAND ${KDEVPG_EXECUTABLE} --symbols +# "${CMAKE_CURRENT_SOURCE_DIR}/parser/python.g" ">symbols" +# COMMAND ${KDEVPG_EXECUTABLE} --rules +# "${CMAKE_CURRENT_SOURCE_DIR}/parser/python.g" ">rules" "2>errors" +# WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" +# ) +# set( parser_SRCS +# ${_kdevpgList} +# ) #add_subdirectory(tests) kde4_add_library( kdev4pythonparser SHARED ${parser_SRCS} ${parser_STAT_SRCS} ) -target_link_libraries( - kdev4pythonparser +target_link_libraries(kdev4pythonparser ${KDE4_KDECORE_LIBS} ${KDEVPLATFORM_LANGUAGE_LIBRARIES} -) - -kde4_add_executable( python-parser main.cpp ) - -target_link_libraries( - python-parser ${QT_QTCORE_LIBRARY} - kdev4pythonparser ) -install(TARGETS python-parser ${INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS kdev4pythonparser DESTINATION ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/parser/ast.cpp b/parser/ast.cpp index 79a03a6..6eeeca3 100644 --- a/parser/ast.cpp +++ b/parser/ast.cpp @@ -23,330 +23,304 @@ namespace Python { -Ast::Ast( Ast* parent, Ast::AstType type ) - : parent(parent), astType( type ) -{ -} +// We never need actual constructors for AST nodes, but it seems to be required, at least for some platforms +// so we provide pseudo implementations +// there's nothing happening here, don't bother reading the code + +Ast::Ast( Ast* parent, Ast::AstType type ) : parent(parent), astType( type ) { } +Ast::Ast() : parent(0), startCol(0), startLine(0), endCol(0), endLine(0), context(0) { } +Ast::~Ast() { } -Ast::~Ast() +ArgumentsAst::ArgumentsAst(Ast* parent): Ast(parent, Ast::ArgumentsAstType) { + } -CodeAst::CodeAst() - : Ast( 0, Ast::CodeAst ) -{ -} -FunctionDefinitionAst::FunctionDefinitionAst( Ast* parent ) - : StatementAst( parent, Ast::FunctionDefinitionAst ), functionName( 0 ) -{ -} -DecoratorAst::DecoratorAst( Ast* parent ) - : Ast( parent, Ast::DecoratorAst ) -{ -} -ArgumentAst::ArgumentAst( Ast* parent ) - : Ast( parent, Ast::ArgumentAst ), argumentExpression( 0 ), keywordName( 0 ) -{ -} -ParameterAst::ParameterAst( Ast* parent, Ast::AstType type ) - : Ast( parent, type ) -{ -} -StatementAst::StatementAst( Ast* parent, Ast::AstType type ) - : Ast( parent, type ) -{ -} -IfAst::IfAst( Ast* parent ) - : StatementAst( parent, Ast::IfAst ), ifCondition( 0 ) -{ -} -WhileAst::WhileAst( Ast* parent ) - : StatementAst( parent, Ast::WhileAst ), condition( 0 ) -{ -} -ForAst::ForAst( Ast* parent ) - : StatementAst( parent, Ast::ForAst ) -{ -} -ClassDefinitionAst::ClassDefinitionAst( Ast* parent ) - : StatementAst( parent, Ast::ClassDefinitionAst ), className( 0 ) +AssertionAst::AssertionAst(Ast* parent): StatementAst(parent, Ast::AssertionAstType) { + } -TryAst::TryAst( Ast* parent ) - : StatementAst( parent, Ast::TryAst ) -{ -} -ExceptAst::ExceptAst( Ast* parent ) - : Ast( parent, Ast::ExceptAst ), exceptionDeclaration( 0 ), exceptionValue( 0 ) -{ -} -WithAst::WithAst( Ast* parent ) - : StatementAst( parent, Ast::WithAst ), context( 0 ), name( 0 ) -{ -} -ExecAst::ExecAst( Ast* parent ) - : StatementAst( parent, Ast::ExecAst ), executable( 0 ), globalsAndLocals( 0 ), localsOnly( 0 ) -{ -} -GlobalAst::GlobalAst( Ast* parent ) - : StatementAst( parent, Ast::GlobalAst ) -{ -} -ImportAst::ImportAst( Ast* parent, Ast::AstType type ) - : StatementAst( parent, type ) -{ -} -PlainImportAst::PlainImportAst( Ast* parent ) - : ImportAst( parent, Ast::PlainImportAst ) -{ -} -StarImportAst::StarImportAst( Ast* parent ) - : ImportAst( parent, Ast::StarImportAst ) + +AssignmentAst::AssignmentAst(Ast* parent): StatementAst(parent, Ast::AssignmentAstType), value(0) { + } -FromImportAst::FromImportAst( Ast* parent ) - : ImportAst( parent, Ast::FromImportAst ) + +AttributeAst::AttributeAst(Ast* parent): ExpressionAst(parent, Ast::AttributeAstType), value(0) { + } -RaiseAst::RaiseAst( Ast* parent ) - : StatementAst( parent, Ast::RaiseAst ), exceptionType( 0 ), exceptionValue( 0 ), traceback( 0 ) + +AugmentedAssignmentAst::AugmentedAssignmentAst(Ast* parent): StatementAst(parent, Ast::AugmentedAssignmentAstType), value(0) { + } -PrintAst::PrintAst( Ast* parent ) - : StatementAst( parent, Ast::PrintAst ), outfile( 0 ) + +BinaryOperationAst::BinaryOperationAst(Ast* parent): ExpressionAst(parent, Ast::BinaryOperationAstType), lhs(0), rhs(0) { + } -ReturnAst::ReturnAst( Ast* parent ) - : StatementAst( parent, Ast::ReturnAst ) +BooleanOperationAst::BooleanOperationAst(Ast* parent): ExpressionAst(parent, Ast::BooleanOperationAstType) { + } -YieldAst::YieldAst( Ast* parent ) - : StatementAst( parent, Ast::YieldAst ) + +BreakAst::BreakAst(Ast* parent): StatementAst(parent, Ast::BreakAstType) { + } -DelAst::DelAst( Ast* parent ) - : StatementAst( parent, Ast::DelAst ) + +CallAst::CallAst(Ast* parent): ExpressionAst(parent, Ast::CallAstType), function(0), keywordArguments(0), starArguments(0) { + } -AssertAst::AssertAst( Ast* parent ) - : StatementAst( parent, Ast::AssertAst ), assertTest( 0 ), exceptionValue( 0 ) + +ClassDefinitionAst::ClassDefinitionAst(Ast* parent): StatementAst(parent, Ast::ClassDefinitionAstType), name(0) { + } -ExpressionStatementAst::ExpressionStatementAst( Ast* parent ) - : StatementAst( parent, Ast::ExpressionStatementAst ) + +CodeAst::CodeAst() { + astType = Ast::CodeAstType; } -AssignmentAst::AssignmentAst( Ast* parent ) - : StatementAst( parent, Ast::AssignmentAst ), yieldValue( 0 ) + +CompareAst::CompareAst(Ast* parent): ExpressionAst(parent, Ast::CompareAstType), leftmostElement(0) { + } -TargetAst::TargetAst( Ast* parent, Ast::AstType type ) - : Ast( parent, type ) + +ComprehensionAst::ComprehensionAst(Ast* parent): Ast(parent, Ast::ComprehensionAstType), target(0), iterator(0) { + } -AtomAst::AtomAst( Ast* parent ) - : PrimaryAst( parent, Ast::AtomAst ), identifier( 0 ), literal( 0 ), enclosure( 0 ) + +ContinueAst::ContinueAst(Ast* parent): StatementAst(parent, Ast::ContinueAstType) { + } -EnclosureAst::EnclosureAst( Ast* parent ) - : Ast( parent, Ast::EnclosureAst ), list( 0 ), generator( 0 ), dict( 0 ), yield( 0 ) + +DeleteAst::DeleteAst(Ast* parent): StatementAst(parent, Ast::DeleteAstType) { + } -ListAst::ListAst( Ast* parent ) - : Ast( parent, Ast::ListAst ), listGenerator( 0 ) + +DictAst::DictAst(Ast* parent): ExpressionAst(parent, Ast::DictAstType) { + } -ListForAst::ListForAst( Ast* parent ) - : Ast( parent, Ast::ListForAst ), nextGenerator( 0 ), nextCondition( 0 ) + +IndexAst::IndexAst(Ast* parent): SliceAstBase(parent, Ast::IndexAstType), value(0) { + } -ListIfAst::ListIfAst( Ast* parent ) - : Ast( parent, Ast::ListIfAst ), condition( 0 ), nextGenerator( 0 ), nextCondition( 0 ) + +SliceAst::SliceAst(Ast* parent): SliceAstBase(parent, Ast::SliceAstType), lower(0), upper(0), step(0) { + } -GeneratorAst::GeneratorAst( Ast* parent ) - : Ast( parent, Ast::GeneratorAst ), generatedValue( 0 ), generator( 0 ) + +DictionaryComprehensionAst::DictionaryComprehensionAst(Ast* parent): ExpressionAst(parent, Ast::DictionaryComprehensionAstType), key(0), value(0) { + } -GeneratorForAst::GeneratorForAst( Ast* parent ) - : Ast( parent, Ast::GeneratorForAst ), iterableObject( 0 ), - nextGenerator( 0 ), nextCondition( 0 ) + +EllipsisAst::EllipsisAst(Ast* parent): SliceAstBase(parent, Ast::EllipsisAstType) { + } -GeneratorIfAst::GeneratorIfAst( Ast* parent ) - : Ast( parent, Ast::GeneratorIfAst ), condition( 0 ), nextGenerator( 0 ), nextCondition( 0 ) + +ExceptionHandlerAst::ExceptionHandlerAst(Ast* parent): Ast(parent, Ast::ExceptionHandlerAstType), type(0), name(0) { + } -DictionaryAst::DictionaryAst( Ast* parent ) - : Ast( parent, Ast::DictionaryAst ) + +ExecAst::ExecAst(Ast* parent): StatementAst(parent, Ast::ExecAstType), body(0), globals(0), locals(0) { + } -PrimaryAst::PrimaryAst( Ast* parent, Ast::AstType type ) - : ExpressionAst( parent, type ) + +ListComprehensionAst::ListComprehensionAst(Ast* parent): ExpressionAst(parent, Ast::ListComprehensionAstType), element(0) { + } -AttributeReferenceAst::AttributeReferenceAst( Ast* parent ) - : PrimaryAst( parent, Ast::AttributeReferenceAst ), primary( 0 ), identifier( 0 ) + +ExpressionAst::ExpressionAst(Ast* parent, AstType type): Ast(parent, type), value(0) { + } -SubscriptAst::SubscriptAst( Ast* parent ) - : PrimaryAst( parent, Ast::SubscriptAst ), primary( 0 ) + +ExtendedSliceAst::ExtendedSliceAst(Ast* parent): SliceAstBase(parent, Ast::ExtendedSliceAstType) { + } -SliceAst::SliceAst( Ast* parent, Ast::AstType type ) - : PrimaryAst( parent, type ), primary( 0 ) + +ForAst::ForAst(Ast* parent): StatementAst(parent, Ast::ForAstType), target(0), iterator(0) { + } -ExtendedSliceAst::ExtendedSliceAst( Ast* parent ) - : SliceAst( parent, Ast::ExtendedSliceAst ) + +FunctionDefinitionAst::FunctionDefinitionAst(Ast* parent): StatementAst(parent, Ast::FunctionDefinitionAstType), name(0), arguments(0) { + } -SimpleSliceAst::SimpleSliceAst( Ast* parent ) - : SliceAst( parent, Ast::SimpleSliceAst ) + +GeneratorExpressionAst::GeneratorExpressionAst(Ast* parent): ExpressionAst(parent, Ast::GeneratorExpressionAstType), element(0) { + } -SliceItemAst::SliceItemAst( Ast* parent, Ast::AstType type ) - : Ast( parent, type ) + +GlobalAst::GlobalAst(Ast* parent): StatementAst(parent, Ast::GlobalAstType) { + } -ProperSliceItemAst::ProperSliceItemAst( Ast* parent ) - : SliceItemAst( parent, Ast::ProperSliceItemAst ), stride( 0 ) + +Identifier::Identifier(QString value) : value(value) { + } -ExpressionSliceItemAst::ExpressionSliceItemAst( Ast* parent ) - : SliceItemAst( parent, Ast::ExpressionSliceItemAst ), sliceExpression( 0 ) + +IfAst::IfAst(Ast* parent): StatementAst(parent, Ast::IfAstType), condition(0) { + } -EllipsisSliceItemAst::EllipsisSliceItemAst( Ast* parent ) - : SliceItemAst( parent, Ast::EllipsisSliceItemAst ) + +IfExpressionAst::IfExpressionAst(Ast* parent): ExpressionAst(parent, Ast::IfExpressionAstType), condition(0) { + } -CallAst::CallAst( Ast* parent ) - : PrimaryAst( parent, Ast::CallAst ), callable( 0 ), generator( 0 ) + +ImportAst::ImportAst(Ast* parent): StatementAst(parent, Ast::ImportAstType) { + } -ArithmeticExpressionAst::ArithmeticExpressionAst( Ast* parent, Ast::AstType type ) - : ExpressionAst( parent, type ) + +ImportFromAst::ImportFromAst(Ast* parent): StatementAst(parent, Ast::ImportFromAstType), module(0), level(0) { + } -UnaryExpressionAst::UnaryExpressionAst( Ast* parent ) - : ArithmeticExpressionAst( parent, Ast::UnaryExpressionAst ), operand( 0 ) + +KeywordAst::KeywordAst(Ast* parent): Ast(parent, Ast::KeywordAstType), argumentName(0), value(0) { + } -BinaryExpressionAst::BinaryExpressionAst( Ast* parent ) - : ArithmeticExpressionAst( parent, Ast::BinaryExpressionAst ), lhs( 0 ), rhs( 0 ) + +LambdaAst::LambdaAst(Ast* parent): ExpressionAst(parent, Ast::LambdaAstType), arguments(0) { + } -ComparisonAst::ComparisonAst( Ast* parent ) - : BooleanOperationAst( parent, Ast::ComparisonAst ), firstComparator( 0 ) + +ListAst::ListAst(Ast* parent): ExpressionAst(parent, Ast::ListAstType) { + } -BooleanOperationAst::BooleanOperationAst( Ast* parent, Ast::AstType type ) - : ExpressionAst( parent, type ) + +NameAst::NameAst(Ast* parent): ExpressionAst(parent, Ast::NameAstType), identifier(0) { + } -ExpressionAst::ExpressionAst( Ast* parent, Ast::AstType type ) - : Ast( parent, type ) + +NumberAst::NumberAst(Ast* parent): ExpressionAst(parent, Ast::NumberAstType), value("0") { + } -ConditionalExpressionAst::ConditionalExpressionAst( Ast* parent ) - : ExpressionAst( parent, Ast::ConditionalExpressionAst ), - mainExpression( 0 ), condition( 0 ), elseExpression( 0 ) + +PassAst::PassAst(Ast* parent): StatementAst(parent, Ast::PassAstType) { + } -LambdaAst::LambdaAst( Ast* parent ) - : ExpressionAst( parent, Ast::LambdaAst ), expression( 0 ) + +PrintAst::PrintAst(Ast* parent): StatementAst(parent, Ast::PrintAstType), destination(0), newline(0) { + } -DefaultParameterAst::DefaultParameterAst( Ast * parent ) - : ParameterAst( parent, Ast::DefaultParameterAst ), name( 0 ), value( 0 ) +RaiseAst::RaiseAst(Ast* parent): StatementAst(parent, Ast::RaiseAstType), type(0) { + } -ParameterPartAst::ParameterPartAst( Ast * parent, Ast::AstType type ) - : Ast( parent, type ) +ReprAst::ReprAst(Ast* parent): ExpressionAst(parent, Ast::ReprAstType), value(0) { + } -IdentifierParameterPartAst::IdentifierParameterPartAst( Ast * parent ) - : ParameterPartAst( parent, Ast::IdentifierParameterPartAst ), name( 0 ) +ReturnAst::ReturnAst(Ast* parent): StatementAst(parent, Ast::ReturnAstType), value(0) { + } -ListParameterPartAst::ListParameterPartAst( Ast * parent ) - : ParameterPartAst( parent, Ast::ListParameterPartAst ) +SetAst::SetAst(Ast* parent): ExpressionAst(parent, Ast::SetAstType) { + } -DictionaryParameterAst::DictionaryParameterAst( Ast * parent ) - : ParameterAst( parent, Ast::DictionaryParameterAst ), name( 0 ) +SetComprehensionAst::SetComprehensionAst(Ast* parent): ExpressionAst(parent, Ast::SetComprehensionAstType), element(0) { + } -ListParameterAst::ListParameterAst( Ast * parent ) - : ParameterAst( parent, Ast::ListParameterAst ), name( 0 ) +SliceAstBase::SliceAstBase(Ast* parent, AstType type): Ast(parent, type) { + } -BooleanNotOperationAst::BooleanNotOperationAst( Ast * parent ) - : BooleanOperationAst( parent, Ast::BooleanNotOperationAst ), op( 0 ) +StatementAst::StatementAst(Ast* parent, AstType type): Ast(parent, type) { + } -BooleanOrOperationAst::BooleanOrOperationAst( Ast * parent ) - : BooleanOperationAst( parent, Ast::BooleanOrOperationAst ), lhs( 0 ), rhs( 0 ) +StringAst::StringAst(Ast* parent): ExpressionAst(parent, Ast::StringAstType), value("") { + } -BooleanAndOperationAst::BooleanAndOperationAst( Ast * parent ) - : BooleanOperationAst( parent, Ast::BooleanAndOperationAst ), lhs( 0 ), rhs( 0 ) +SubscriptAst::SubscriptAst(Ast* parent): ExpressionAst(parent, Ast::SubscriptAstType), value(0), slice(0) { + } -IdentifierAst::IdentifierAst( Ast * parent ) - : ExpressionAst( parent, Ast::IdentifierAst ) +TryExceptAst::TryExceptAst(Ast* parent): StatementAst(parent, Ast::TryExceptAstType) { + } -LiteralAst::LiteralAst( Ast* parent ) - : Ast( parent, Ast::LiteralAst ) +TryFinallyAst::TryFinallyAst(Ast* parent): StatementAst(parent, Ast::TryFinallyAstType) { + } -IdentifierTargetAst::IdentifierTargetAst( Ast * parent ) - : TargetAst( parent, Ast::IdentifierTargetAst ), identifier( 0 ) +TupleAst::TupleAst(Ast* parent): ExpressionAst(parent, Ast::TupleAstType) { + } -TupleTargetAst::TupleTargetAst( Ast * parent ) - : TargetAst( parent, Ast::TupleTargetAst ) +UnaryOperationAst::UnaryOperationAst(Ast* parent): ExpressionAst(parent, Ast::UnaryOperationAstType), operand(0) { + } -ListTargetAst::ListTargetAst( Ast * parent ) - : TargetAst( parent, Ast::ListTargetAst ) +WhileAst::WhileAst(Ast* parent): StatementAst(parent, Ast::WhileAstType), condition(0) { + } -AttributeReferenceTargetAst::AttributeReferenceTargetAst( Ast * parent ) - : TargetAst( parent, Ast::AttributeReferenceTargetAst ), attribute( 0 ) +WithAst::WithAst(Ast* parent): StatementAst(parent, Ast::WithAstType), contextExpression(0) { + } -SubscriptTargetAst::SubscriptTargetAst( Ast * parent ) - : TargetAst( parent, Ast::SubscriptTargetAst ), subscript( 0 ) +YieldAst::YieldAst(Ast* parent): ExpressionAst(parent, Ast::YieldAstType), value(0) { + } -SliceTargetAst::SliceTargetAst( Ast * parent ) - : TargetAst( parent, Ast::SliceTargetAst ), slice( 0 ) +AliasAst::AliasAst(Ast* parent): Ast(parent, Ast::AliasAstType), name(0), asName(0) { + } -} - -#include "pythonast.h" +} diff --git a/parser/ast.h b/parser/ast.h index 892ae39..b71d9e4 100644 --- a/parser/ast.h +++ b/parser/ast.h @@ -36,892 +36,583 @@ namespace KDevelop class DUContext; } +namespace Python { + class StatementAst; + class FunctionDefinitionAst; + class AssignmentAst; + class PrintAst; + class PassAst; + class ExpressionAst; + class NameAst; + class CallAst; + class AttributeAst; + class ArgumentsAst; + class KeywordAst; + + class ExpressionAst; + class StatementAst; + class Ast; + class ExceptionHandlerAst; + class AliasAst; + class ComprehensionAst; + class SliceAstBase; + class SliceAst; +} + namespace Python { -class Ast; -class CodeAst; -class FunctionDefinitionAst; -class DecoratorAst; -class ArgumentAst; -class StatementAst; -class IfAst; -class WhileAst; -class ForAst; -class ClassDefinitionAst; -class TryAst; -class ExceptAst; -class WithAst; -class ExecAst; -class GlobalAst; -class ImportAst; -class PlainImportAst; -class StarImportAst; -class FromImportAst; -class RaiseAst; -class PrintAst; -class ReturnAst; -class YieldAst; -class DelAst; -class AssertAst; -class ExpressionStatementAst; -class AssignmentAst; -class TargetAst; -class AtomAst; -class EnclosureAst; -class ListAst; -class ListForAst; -class ListIfAst; -class IdentifierAst; -class ListParameterAst; -class GeneratorAst; -class GeneratorForAst; -class GeneratorIfAst; -class DictionaryAst; -class PrimaryAst; -class AttributeReferenceAst; -class SubscriptAst; -class SliceAst; -class ExtendedSliceAst; -class SimpleSliceAst; -class SliceItemAst; -class ProperSliceItemAst; -class ExpressionSliceAst; -class EllipsisSliceAst; -class CallAst; -class ArithmeticExpressionAst; -class UnaryExpressionAst; -class BinaryExpressionAst; -class ComparisonAst; -class BooleanOperationAst; -class ExpressionAst; -class ConditionalExpressionAst; -class LambdaAst; -class ParameterAst; -class ParameterPartAst; -class DefaultParameterAst; -class IdentifierParameterPartAst; -class ListParameterPartAst; -class DictionaryParameterAst; - -class KDEVPYTHONPARSER_EXPORT KDEVPYTHONPARSER_EXPORT Ast +// Base class for all other Abstract Syntax Tree classes +class KDEVPYTHONPARSER_EXPORT Ast { public: enum AstType { - ArgumentAst = 0, - AssertAst = 1, - AssignmentAst = 2, - AtomAst = 3, - AttributeReferenceAst = 4, - AttributeReferenceTargetAst = 5, - BinaryExpressionAst = 6, - BooleanAndOperationAst = 7, - BooleanNotOperationAst = 8, - BooleanOrOperationAst = 9, - BreakAst = 10, - CallAst = 11, - ClassDefinitionAst = 12, - CodeAst = 13, - ComparisonAst = 14, - ConditionalExpressionAst = 15, - ContinueAst = 16, - DecoratorAst = 17, - DefaultParameterAst = 18, - DelAst = 19, - DictionaryAst = 20, - DictionaryParameterAst = 21, - EllipsisSliceItemAst = 22, - EnclosureAst = 23, - ExceptAst = 24, - ExecAst = 25, - ExpressionSliceItemAst = 26, - ExpressionStatementAst = 27, - ExtendedSliceAst = 28, - ForAst = 29, - FromImportAst = 30, - FunctionDefinitionAst = 31, - GeneratorAst = 32, - GeneratorForAst = 33, - GeneratorIfAst = 34, - GlobalAst = 35, - IdentifierAst = 36, - IdentifierParameterPartAst = 37, - IdentifierTargetAst = 38, - IfAst = 39, - LambdaAst = 40, - ListAst = 41, - ListForAst = 42, - ListIfAst = 43, - ListParameterAst = 44, - ListParameterPartAst = 45, - ListTargetAst = 46, - LiteralAst = 47, - PassAst = 48, - PlainImportAst = 49, - PrintAst = 50, - ProperSliceItemAst = 51, - RaiseAst = 52, - ReturnAst = 53, - SimpleSliceAst = 54, - SliceTargetAst = 55, - StarImportAst = 56, - SubscriptAst = 57, - SubscriptTargetAst = 58, - TryAst = 59, - TupleTargetAst = 60, - UnaryExpressionAst = 61, - WhileAst = 62, - WithAst = 63, - YieldAst = 64 + FunctionDefinitionAstType, + AssignmentAstType, + PrintAstType, + PassAstType, + NameAstType, + CallAstType, + AttributeAstType, + ArgumentsAstType, + KeywordAstType, + ClassDefinitionAstType, + ReturnAstType, + DeleteAstType, + ForAstType, + WhileAstType, + IfAstType, + WithAstType, + RaiseAstType, + TryExceptAstType, + TryFinallyAstType, + ImportAstType, + ImportFromAstType, + ExecAstType, + GlobalAstType, + BreakAstType, + ContinueAstType, + AssertionAstType, + AugmentedAssignmentAstType, + DictionaryComprehensionAstType, + ExtendedSliceAstType, + CodeAstType, + StatementAstType, + ExpressionAstType, + + BooleanOperationAstType, + BinaryOperationAstType, + UnaryOperationAstType, + LambdaAstType, + IfExpressionAstType, // the short one, if a then b else c + DictAstType, + SetAstType, + ListComprehensionAstType, + SetComprehensionAstType, + GeneratorExpressionAstType, + YieldAstType, + CompareAstType, + ReprAstType, + NumberAstType, + StringAstType, + SubscriptAstType, + ListAstType, + TupleAstType, + + SliceAstType, + EllipsisAstType, + IndexAstType, + + ComprehensionAstType, + ExceptionHandlerAstType, + AliasAstType // for imports + }; + + enum BooleanOperationTypes { + BooleanAnd, + BooleanOr, + BooleanInvalidOperation + }; + + enum OperatorTypes { + OperatorAdd, + OperatorSub, + OperatorMult, + OperatorDiv, + OperatorMod, + OperatorPow, + OperatorLeftShift, + OperatorRightShift, + OperatorBitwiseOr, + OperatorBitwiseXor, + OperatorBitwiseAnd, + OperatorFloorDivision, + OperatorInvalid + }; + + enum UnaryOperatorTypes { + UnaryOperatorInvert, + UnaryOperatorNot, + UnaryOperatorAdd, + UnaryOperatorSub, + UnaryOperatorInvalid + }; + + enum ComparisonOperatorTypes { + ComparisonOperatorEquals, + ComparisonOperatorNotEquals, + ComparisonOperatorLessThan, + ComparisonOperatorLessThanEqual, + ComparisonOperatorGreaterThan, + ComparisonOperatorGreaterThanEqual, + ComparisonOperatorIs, + ComparisonOperatorIsNot, + ComparisonOperatorIn, + ComparisonOperatorNotIn, + ComparisonOperatorInvalid }; - Ast( Ast* parent, AstType type ); + Ast(Ast* parent, AstType type); + Ast(); virtual ~Ast(); Ast* parent; AstType astType; - /** - * This is the absolute position in the file that this Ast node starts at. - * - * Counting starts with 0. - */ - qint64 start; - - /** - * This is the absolute position in the file that this Ast node ends at. - * - * Counting starts with 0. - */ - qint64 end; - - /** - * This is the column in the starting line where this Ast node starts. - * - * Counting starts with 0. - */ - qint64 startCol; - /** - * This is the line where this Ast node starts. - * - * Counting starts with 0. - */ + qint64 startCol; qint64 startLine; - - /** - * This is the column in the ending line where this Ast node ends. - * - * Counting starts with 0. - */ qint64 endCol; - - /** - * This is the line where this Ast node ends. - * - * Counting starts with 0. - */ qint64 endLine; + + bool hasUsefulRangeInformation; + KDevelop::DUContext* context; }; -class KDEVPYTHONPARSER_EXPORT CodeAst : public Ast -{ - +class KDEVPYTHONPARSER_EXPORT Identifier : public Ast { public: - CodeAst(); - QList statements; + Identifier(QString value); + QString value; }; -class KDEVPYTHONPARSER_EXPORT StatementAst : public Ast -{ - +// this replaces ModuleAst +class KDEVPYTHONPARSER_EXPORT CodeAst : public Ast { public: - StatementAst( Ast*, Ast::AstType type ); + CodeAst(); + QList body; }; -class KDEVPYTHONPARSER_EXPORT ParameterAst : public Ast -{ +/** Statement classes **/ +class KDEVPYTHONPARSER_EXPORT StatementAst : public Ast { public: - ParameterAst( Ast* parent, Ast::AstType type ); + StatementAst(Ast* parent, AstType type); }; -class KDEVPYTHONPARSER_EXPORT ExpressionAst : public Ast -{ - +class KDEVPYTHONPARSER_EXPORT FunctionDefinitionAst : public StatementAst { public: - ExpressionAst( Ast*, Ast::AstType type ); + FunctionDefinitionAst(Ast* parent); + Identifier* name; + ArgumentsAst* arguments; + QList decorators; + QList body; }; -class KDEVPYTHONPARSER_EXPORT IdentifierAst : public ExpressionAst -{ +class KDEVPYTHONPARSER_EXPORT ClassDefinitionAst : public StatementAst { public: - IdentifierAst( Ast* ); - QString identifier; + ClassDefinitionAst(Ast* parent); + Identifier* name; + QList baseClasses; + QList body; + QList decorators; }; - -class KDEVPYTHONPARSER_EXPORT ParameterPartAst : public Ast -{ +class KDEVPYTHONPARSER_EXPORT ReturnAst : public StatementAst { public: - ParameterPartAst( Ast*, Ast::AstType type ); + ReturnAst(Ast* parent); + ExpressionAst* value; }; - -class KDEVPYTHONPARSER_EXPORT ImportAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT DeleteAst : public StatementAst { public: - ImportAst( Ast*, Ast::AstType type ); + DeleteAst(Ast* parent); + QList targets; }; -class KDEVPYTHONPARSER_EXPORT PrimaryAst : public ExpressionAst -{ - +class KDEVPYTHONPARSER_EXPORT AssignmentAst : public StatementAst { public: - PrimaryAst( Ast*, Ast::AstType type ); + AssignmentAst(Ast* parent); + QList targets; + ExpressionAst* value; }; -class KDEVPYTHONPARSER_EXPORT SliceAst : public PrimaryAst -{ - +class KDEVPYTHONPARSER_EXPORT AugmentedAssignmentAst : public StatementAst { public: - SliceAst( Ast*, Ast::AstType type ); - Python::PrimaryAst* primary; + AugmentedAssignmentAst(Ast* parent); + ExpressionAst* target; + Ast::OperatorTypes op; + ExpressionAst* value; }; - -class KDEVPYTHONPARSER_EXPORT SliceItemAst : public Ast -{ - +class KDEVPYTHONPARSER_EXPORT ForAst : public StatementAst { public: - SliceItemAst( Ast*, Ast::AstType type ); + ForAst(Ast* parent); + ExpressionAst* target; + ExpressionAst* iterator; + QList body; + QList orelse; }; - -class KDEVPYTHONPARSER_EXPORT ArithmeticExpressionAst : public ExpressionAst -{ +class KDEVPYTHONPARSER_EXPORT WhileAst : public StatementAst { public: - enum ArithmeticOperation - { - Power, - UnaryPlus, - UnaryMinus, - UnaryTilde, - BinaryPlus, - BinaryMinus, - BinaryMultiply, - BinaryDivide, - BinaryModulo, - BinaryFloor, - BinaryLeftShift, - BinaryRightShift, - BinaryAnd, - BinaryOr, - BinaryXor - }; - - ArithmeticExpressionAst( Ast*, Ast::AstType type ); - ArithmeticOperation opType; + WhileAst(Ast* parent); + ExpressionAst* condition; + QList body; + QList orelse; }; -class KDEVPYTHONPARSER_EXPORT BooleanOperationAst : public ExpressionAst -{ +class KDEVPYTHONPARSER_EXPORT IfAst : public StatementAst { public: - BooleanOperationAst( Ast* parent, Ast::AstType type ); + IfAst(Ast* parent); + ExpressionAst* condition; + QList body; + QList orelse; }; - -class KDEVPYTHONPARSER_EXPORT TargetAst : public Ast -{ +class KDEVPYTHONPARSER_EXPORT WithAst : public StatementAst { public: - TargetAst( Ast*, Ast::AstType ); + WithAst(Ast* parent); + ExpressionAst* contextExpression; + ExpressionAst* optionalVars; + QList body; }; -class KDEVPYTHONPARSER_EXPORT FunctionDefinitionAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT RaiseAst : public StatementAst { public: - FunctionDefinitionAst( Ast* parent ); - Python::IdentifierAst* functionName; - QList parameters; - QList decorators; - QList functionBody; + RaiseAst(Ast* parent); + ExpressionAst* type; + // TODO check what the other things in the grammar actually are and add them }; -class KDEVPYTHONPARSER_EXPORT IdentifierTargetAst : public TargetAst -{ +class KDEVPYTHONPARSER_EXPORT TryExceptAst : public StatementAst { public: - IdentifierTargetAst( Ast* ); - Python::IdentifierAst* identifier; + TryExceptAst(Ast* parent); + QList body; + QList handlers; + QList orelse; }; -class KDEVPYTHONPARSER_EXPORT TupleTargetAst : public TargetAst -{ +class KDEVPYTHONPARSER_EXPORT TryFinallyAst : public StatementAst { public: - TupleTargetAst( Ast* ); - QList items; + TryFinallyAst(Ast* parent); + QList body; + QList finalbody; }; -class KDEVPYTHONPARSER_EXPORT ListTargetAst : public TargetAst -{ +class KDEVPYTHONPARSER_EXPORT AssertionAst : public StatementAst { public: - ListTargetAst( Ast* ); - QList items; + AssertionAst(Ast* parent); + ExpressionAst* condition; + ExpressionAst* message; }; -class KDEVPYTHONPARSER_EXPORT AttributeReferenceTargetAst : public TargetAst -{ +class KDEVPYTHONPARSER_EXPORT ImportAst : public StatementAst { public: - AttributeReferenceTargetAst( Ast* ); - Python::AttributeReferenceAst* attribute; + ImportAst(Ast* parent); + QList names; }; -class KDEVPYTHONPARSER_EXPORT SubscriptTargetAst : public TargetAst -{ +class KDEVPYTHONPARSER_EXPORT ImportFromAst : public StatementAst { public: - SubscriptTargetAst( Ast* ); - Python::SubscriptAst* subscript; + ImportFromAst(Ast* parent); + Identifier* module; + QList names; + int level; }; -class KDEVPYTHONPARSER_EXPORT SliceTargetAst : public TargetAst -{ +class KDEVPYTHONPARSER_EXPORT ExecAst : public StatementAst { public: - SliceTargetAst( Ast* ); - Python::SliceAst* slice; + ExecAst(Ast* parent); + ExpressionAst* body; + ExpressionAst* globals; + ExpressionAst* locals; }; -class KDEVPYTHONPARSER_EXPORT DecoratorAst : public Ast -{ - +class KDEVPYTHONPARSER_EXPORT GlobalAst : public StatementAst { public: - DecoratorAst( Ast* parent ); - QList dottedName; - QList arguments; + GlobalAst(Ast* parent); + QList names; }; -class KDEVPYTHONPARSER_EXPORT ArgumentAst : public Ast -{ - -public: - enum ArgumentType - { - PositionalArgument, - KeywordArgument, - ListArgument, - DictArgument - }; - ArgumentAst( Ast* ); - Python::ExpressionAst* argumentExpression; - Python::IdentifierAst* keywordName; - ArgumentType argumentType; -}; - -class KDEVPYTHONPARSER_EXPORT DefaultParameterAst : public ParameterAst -{ -public: - DefaultParameterAst( Ast* ); - Python::ParameterPartAst* name; - Python::ExpressionAst* value; -}; - - -class KDEVPYTHONPARSER_EXPORT IdentifierParameterPartAst : public ParameterPartAst -{ -public: - IdentifierParameterPartAst( Ast* ); - Python::IdentifierAst* name; -}; - -class KDEVPYTHONPARSER_EXPORT ListParameterPartAst : public ParameterPartAst -{ -public: - ListParameterPartAst( Ast* ); - QList parameternames; -}; - -class KDEVPYTHONPARSER_EXPORT DictionaryParameterAst : public ParameterAst -{ -public: - DictionaryParameterAst( Ast* ); - Python::IdentifierAst* name; -}; - -class KDEVPYTHONPARSER_EXPORT ListParameterAst : public ParameterAst -{ -public: - ListParameterAst( Ast* ); - Python::IdentifierAst* name; -}; - -class KDEVPYTHONPARSER_EXPORT IfAst : public StatementAst -{ +// TODO what's stmt::Expr(expr value) in the grammar and what do we need it for? +class KDEVPYTHONPARSER_EXPORT BreakAst : public StatementAst { public: - IfAst( Ast* ); - ExpressionAst* ifCondition; - QList ifBody; - QList > > elseIfBodies; - QList elseBody; + BreakAst(Ast* parent); }; -class KDEVPYTHONPARSER_EXPORT WhileAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT ContinueAst : public StatementAst { public: - WhileAst( Ast* ); - Python::ExpressionAst* condition; - QList whileBody; - QList elseBody; + ContinueAst(Ast* parent); }; -class KDEVPYTHONPARSER_EXPORT ForAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT PrintAst : public StatementAst { public: - ForAst( Ast* ); - QList assignedTargets; - QList iterable; - QList forBody; - QList elseBody; + PrintAst(Ast* parent); + ExpressionAst* destination; + QList values; + bool newline; }; -class KDEVPYTHONPARSER_EXPORT ClassDefinitionAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT PassAst : public StatementAst { public: - ClassDefinitionAst( Ast* parent ); - Python::IdentifierAst* className; - QList inheritance; - QList classBody; + PassAst(Ast* parent); }; -class KDEVPYTHONPARSER_EXPORT TryAst : public StatementAst -{ +/** Expression classes **/ +class KDEVPYTHONPARSER_EXPORT ExpressionAst : public Ast { public: - TryAst( Ast* ); - QList tryBody; - QList elseBody; - QList finallyBody; - QList exceptions; -}; - -class KDEVPYTHONPARSER_EXPORT ExceptAst : public Ast -{ - -public: - ExceptAst( Ast* ); - Python::ExpressionAst* exceptionDeclaration; - Python::TargetAst* exceptionValue; - QList exceptionBody; + ExpressionAst(Ast* parent, AstType type = Ast::ExpressionAstType); + enum Context { + Load, // the object is read + Store, // the object is written + Delete, // the object is deleted + Parameter, // the object is passed as a parameter + AugLoad, AugStore, // Augmented assignments, like a += 1 + Invalid + }; + ExpressionAst* value; }; -class KDEVPYTHONPARSER_EXPORT WithAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT BooleanOperationAst : public ExpressionAst { public: - WithAst( Ast* ); - Python::ExpressionAst* context; - Python::TargetAst* name; - QList body; + BooleanOperationAst(Ast* parent); + Ast::BooleanOperationTypes type; + QList values; }; -class KDEVPYTHONPARSER_EXPORT ExecAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT BinaryOperationAst : public ExpressionAst { public: - ExecAst( Ast* ); - Python::ArithmeticExpressionAst* executable; - Python::DictionaryAst* globalsAndLocals; - Python::ExpressionAst* localsOnly; + BinaryOperationAst(Ast* parent); + Ast::OperatorTypes type; + ExpressionAst* lhs; + ExpressionAst* rhs; }; -class KDEVPYTHONPARSER_EXPORT GlobalAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT UnaryOperationAst : public ExpressionAst { public: - GlobalAst( Ast* ); - QList identifiers; + UnaryOperationAst(Ast* parent); + Ast::UnaryOperatorTypes type; + ExpressionAst* operand; }; -class KDEVPYTHONPARSER_EXPORT PlainImportAst : public ImportAst -{ - +class KDEVPYTHONPARSER_EXPORT LambdaAst : public ExpressionAst { public: - PlainImportAst( Ast* ); - QList< QPair< QList, Python::IdentifierAst*> > modulesAsName; + LambdaAst(Ast* parent); + ArgumentsAst* arguments; + ExpressionAst* body; }; -class KDEVPYTHONPARSER_EXPORT StarImportAst : public ImportAst -{ - +class KDEVPYTHONPARSER_EXPORT IfExpressionAst : public ExpressionAst { public: - StarImportAst( Ast* ); - QList modulePath; + IfExpressionAst(Ast* parent); + ExpressionAst* condition; + ExpressionAst* body; + ExpressionAst* orelse; }; -class KDEVPYTHONPARSER_EXPORT FromImportAst : public ImportAst -{ - +class KDEVPYTHONPARSER_EXPORT DictAst : public ExpressionAst { public: - FromImportAst( Ast* ); - QList modulePath; - int numLeadingDots; - QList< QPair > identifierAsName; + DictAst(Ast* parent); + QList keys; + QList values; }; -class KDEVPYTHONPARSER_EXPORT RaiseAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT SetAst : public ExpressionAst { public: - RaiseAst( Ast* ); - Python::ExpressionAst* exceptionType; - Python::ExpressionAst* exceptionValue; - Python::ExpressionAst* traceback; + SetAst(Ast* parent); + QList elements; }; -class KDEVPYTHONPARSER_EXPORT PrintAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT ListComprehensionAst : public ExpressionAst { public: - PrintAst( Ast* ); - QList printables; - Python::ExpressionAst* outfile; + ListComprehensionAst(Ast* parent); + ExpressionAst* element; + QList generators; }; -class KDEVPYTHONPARSER_EXPORT ReturnAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT SetComprehensionAst : public ExpressionAst { public: - ReturnAst( Ast* ); - QList returnValues; + SetComprehensionAst(Ast* parent); + ExpressionAst* element; + QList generators; }; -class KDEVPYTHONPARSER_EXPORT YieldAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT DictionaryComprehensionAst : public ExpressionAst { public: - YieldAst( Ast* ); - QList yieldValue; + DictionaryComprehensionAst(Ast* parent); + ExpressionAst* key; + ExpressionAst* value; + QList generators; }; -class KDEVPYTHONPARSER_EXPORT DelAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT GeneratorExpressionAst : public ExpressionAst { public: - DelAst( Ast* ); - QList deleteObjects; + GeneratorExpressionAst(Ast* parent); + ExpressionAst* element; + QList generators; }; -class KDEVPYTHONPARSER_EXPORT AssertAst : public StatementAst -{ - +class KDEVPYTHONPARSER_EXPORT CompareAst : public ExpressionAst { public: - AssertAst( Ast* ); - Python::ExpressionAst* assertTest; - Python::ExpressionAst* exceptionValue; + CompareAst(Ast* parent); + ExpressionAst* leftmostElement; + QList operators; + QList comparands; }; -class KDEVPYTHONPARSER_EXPORT ExpressionStatementAst : public StatementAst -{ - +// TODO whats this exactly? +class KDEVPYTHONPARSER_EXPORT ReprAst : public ExpressionAst { public: - ExpressionStatementAst( Ast* ); - QList expressions; + ReprAst(Ast* parent); + ExpressionAst* value; }; -class KDEVPYTHONPARSER_EXPORT AssignmentAst : public StatementAst -{ +class KDEVPYTHONPARSER_EXPORT NumberAst : public ExpressionAst { public: - - enum OpType - { - AddEqualOp, - SubEqualOp, - MultiplyEqualOp, - DivideEqualOp, - ModuloEqualOp, - PowEqualOp, - LeftShiftEqualOp, - RightShiftEqualOp, - XorEqualOp, - OrEqualOp, - AndEqualOp, - FloorEqualOp, - AssignmentOp - }; - AssignmentAst( Ast* ); - QList, OpType > > targets; - QList value; - Python::YieldAst* yieldValue; + NumberAst(Ast* parent); + QString value; // everything else would be even more strange }; -class KDEVPYTHONPARSER_EXPORT LiteralAst : public Ast -{ +class KDEVPYTHONPARSER_EXPORT StringAst : public ExpressionAst { public: - enum LiteralType - { - String, - Float, - Integer, - ImaginaryNumber - }; - LiteralAst( Ast* ); + StringAst(Ast* parent); QString value; - LiteralType literalType; }; -class KDEVPYTHONPARSER_EXPORT AtomAst : public PrimaryAst -{ - +class KDEVPYTHONPARSER_EXPORT YieldAst : public ExpressionAst { public: - AtomAst( Ast* ); - Python::IdentifierAst* identifier; - Python::LiteralAst* literal; - Python::EnclosureAst* enclosure; + YieldAst(Ast* parent); + ExpressionAst* value; }; -class KDEVPYTHONPARSER_EXPORT EnclosureAst : public Ast -{ - +class KDEVPYTHONPARSER_EXPORT NameAst : public ExpressionAst { public: - enum EnclosureType - { - ParenthesizedForm, - List, - Generator, - Dictionary, - StringConversion, - Yield - }; - EnclosureAst( Ast* ); - QList parenthesizedform; - Python::ListAst* list; - Python::GeneratorAst* generator; - Python::DictionaryAst* dict; - QList stringConversion; - Python::YieldAst* yield; - EnclosureType encType; + NameAst(Ast* parent); + Identifier* identifier; + ExpressionAst::Context context; }; -class KDEVPYTHONPARSER_EXPORT ListAst : public Ast -{ - +class KDEVPYTHONPARSER_EXPORT CallAst : public ExpressionAst { public: - ListAst( Ast* ); - QList plainList; - Python::ListForAst* listGenerator; + CallAst(Ast* parent); + ExpressionAst* function; + QList arguments; + QList keywords; + ExpressionAst* keywordArguments; + ExpressionAst* starArguments; }; -class KDEVPYTHONPARSER_EXPORT ListForAst : public Ast -{ - +class KDEVPYTHONPARSER_EXPORT AttributeAst : public ExpressionAst { public: - ListForAst( Ast* ); - QList assignedTargets; - QList iterableObject; - Python::ListForAst* nextGenerator; - Python::ListIfAst* nextCondition; + AttributeAst(Ast* parent); + ExpressionAst* value; + Identifier* attribute; + ExpressionAst::Context context; }; -class KDEVPYTHONPARSER_EXPORT ListIfAst : public Ast -{ - +class KDEVPYTHONPARSER_EXPORT SubscriptAst : public ExpressionAst { public: - ListIfAst( Ast* ); - Python::ExpressionAst* condition; - Python::ListForAst* nextGenerator; - Python::ListIfAst* nextCondition; + SubscriptAst(Ast* parent); + ExpressionAst* value; + SliceAst* slice; + ExpressionAst::Context context; }; -class KDEVPYTHONPARSER_EXPORT GeneratorAst : public Ast -{ - +class KDEVPYTHONPARSER_EXPORT ListAst : public ExpressionAst { public: - GeneratorAst( Ast* ); - Python::ExpressionAst* generatedValue; - Python::GeneratorForAst* generator; + ListAst(Ast* parent); + QList elements; + ExpressionAst::Context context; }; -class KDEVPYTHONPARSER_EXPORT GeneratorForAst : public Ast -{ - +class KDEVPYTHONPARSER_EXPORT TupleAst : public ExpressionAst { public: - GeneratorForAst( Ast* ); - QList assignedTargets; - Python::ConditionalExpressionAst * iterableObject; - Python::GeneratorForAst* nextGenerator; - Python::GeneratorIfAst* nextCondition; + TupleAst(Ast* parent); + QList elements; + ExpressionAst::Context context; }; -class KDEVPYTHONPARSER_EXPORT GeneratorIfAst : public Ast -{ - +/** Slice classes **/ +class KDEVPYTHONPARSER_EXPORT SliceAstBase : public Ast { public: - GeneratorIfAst( Ast* ); - Python::ExpressionAst* condition; - Python::GeneratorForAst* nextGenerator; - Python::GeneratorIfAst* nextCondition; + SliceAstBase(Ast* parent, AstType type); }; -class KDEVPYTHONPARSER_EXPORT DictionaryAst : public Ast -{ - +class KDEVPYTHONPARSER_EXPORT EllipsisAst : public SliceAstBase { public: - DictionaryAst( Ast* ); - QMap dictionary; + EllipsisAst(Ast* parent); }; -class KDEVPYTHONPARSER_EXPORT AttributeReferenceAst : public PrimaryAst -{ - +class KDEVPYTHONPARSER_EXPORT SliceAst : public SliceAstBase { public: - AttributeReferenceAst( Ast* ); - Python::PrimaryAst* primary; - Python::IdentifierAst* identifier; + SliceAst(Ast* parent); + ExpressionAst* lower; + ExpressionAst* upper; + ExpressionAst* step; }; -class KDEVPYTHONPARSER_EXPORT SubscriptAst : public PrimaryAst -{ - +class KDEVPYTHONPARSER_EXPORT ExtendedSliceAst : public SliceAstBase { public: - SubscriptAst( Ast* ); - Python::PrimaryAst* primary; - QList subscription; + ExtendedSliceAst(Ast* parent); + QList dims; }; -class KDEVPYTHONPARSER_EXPORT ExtendedSliceAst : public SliceAst -{ - +class KDEVPYTHONPARSER_EXPORT IndexAst : public SliceAstBase { public: - ExtendedSliceAst( Ast* ); - QList extendedSliceList; + IndexAst(Ast* parent); + ExpressionAst* value; }; -class KDEVPYTHONPARSER_EXPORT SimpleSliceAst : public SliceAst -{ - -public: - SimpleSliceAst( Ast* ); - QPair simpleSliceBounds; -}; - -class KDEVPYTHONPARSER_EXPORT ProperSliceItemAst : public SliceItemAst -{ - -public: - ProperSliceItemAst( Ast* ); - QPair bounds; - Python::ExpressionAst* stride; -}; - -class KDEVPYTHONPARSER_EXPORT ExpressionSliceItemAst : public SliceItemAst -{ - -public: - ExpressionSliceItemAst( Ast* ); - Python::ExpressionAst* sliceExpression; -}; - -class KDEVPYTHONPARSER_EXPORT EllipsisSliceItemAst : public SliceItemAst -{ - -public: - EllipsisSliceItemAst( Ast* ); -}; - -class KDEVPYTHONPARSER_EXPORT CallAst : public PrimaryAst -{ - -public: - CallAst( Ast* ); - Python::PrimaryAst* callable; - QList arguments; - Python::GeneratorAst* generator; -}; - - -class KDEVPYTHONPARSER_EXPORT UnaryExpressionAst : public ArithmeticExpressionAst -{ - +/** Independent classes **/ +class KDEVPYTHONPARSER_EXPORT ArgumentsAst : public Ast { public: - UnaryExpressionAst( Ast* ); - Python::ExpressionAst* operand; + ArgumentsAst(Ast* parent); + QList arguments; + QList defaultValues; + Identifier* vararg; + Identifier* kwarg; }; -class KDEVPYTHONPARSER_EXPORT BinaryExpressionAst : public ArithmeticExpressionAst -{ - +class KDEVPYTHONPARSER_EXPORT KeywordAst : public Ast { public: - BinaryExpressionAst( Ast* ); - Python::ExpressionAst* lhs; - Python::ExpressionAst* rhs; + KeywordAst(Ast* parent); + Identifier* argumentName; + ExpressionAst* value; }; -class KDEVPYTHONPARSER_EXPORT ComparisonAst : public BooleanOperationAst -{ +class KDEVPYTHONPARSER_EXPORT ComprehensionAst : public Ast { public: - - enum ComparisonOperator - { - LessThanOp, - GreaterThanOp, - EqualOp, - UnequalOp, - LessEqualOp, - GreaterEqualOp, - IsOp, - IsNotOp, - InOp, - NotInOp - }; - ComparisonAst( Ast* ); - Python::ExpressionAst* firstComparator; - QList< QPair > comparatorList; + ComprehensionAst(Ast* parent); + ExpressionAst* target; + ExpressionAst* iterator; + QList conditions; }; -class KDEVPYTHONPARSER_EXPORT BooleanAndOperationAst : public BooleanOperationAst -{ +class KDEVPYTHONPARSER_EXPORT ExceptionHandlerAst : public Ast { public: - BooleanAndOperationAst( Ast* ); - Python::BooleanOperationAst* lhs; - Python::BooleanOperationAst* rhs; + ExceptionHandlerAst(Ast* parent); + ExpressionAst* type; + ExpressionAst* name; + QList body; }; -class KDEVPYTHONPARSER_EXPORT BooleanOrOperationAst : public BooleanOperationAst -{ -public: - BooleanOrOperationAst( Ast* ); - Python::BooleanOperationAst* lhs; - Python::BooleanOperationAst* rhs; -}; - -class KDEVPYTHONPARSER_EXPORT BooleanNotOperationAst : public BooleanOperationAst -{ -public: - BooleanNotOperationAst( Ast* ); - Python::BooleanOperationAst* op; -}; - -class KDEVPYTHONPARSER_EXPORT ConditionalExpressionAst : public ExpressionAst -{ - -public: - ConditionalExpressionAst( Ast* ); - Python::BooleanOperationAst* mainExpression; - Python::BooleanOperationAst* condition; - Python::ExpressionAst* elseExpression; -}; - -class KDEVPYTHONPARSER_EXPORT LambdaAst : public ExpressionAst -{ - +class KDEVPYTHONPARSER_EXPORT AliasAst : public Ast { public: - LambdaAst( Ast* ); - QList parameters; - Python::ExpressionAst* expression; + AliasAst(Ast* parent); + Identifier* name; + NameAst* asName; }; } diff --git a/parser/astbuilder.cpp b/parser/astbuilder.cpp index 55760ea..f5303eb 100644 --- a/parser/astbuilder.cpp +++ b/parser/astbuilder.cpp @@ -22,1850 +22,869 @@ #include -#include "pythonparser.h" #include "ast.h" -#include #include +#include +#include +#include "kurl.h" +#include +#include +#include +#include +#include +#include -namespace Python -{ +#include "parserConfig.h" +#include -//TODO: Check that created AST nodes are pushed onto the stack _before_ visiting subnodes to make sure their parent is correct +using namespace KDevelop; -template static T* safeNodeCast( Ast* node ) +namespace Python { - T* ast = dynamic_cast(node); - Q_ASSERT(ast || !node); - return ast; -} - -static QList targetAstListFromExpressionAstList( const QList& list ) + +CodeAst* AstBuilder::parse(KUrl filename, const QString& contents) { - QList l; - foreach( ExpressionAst* ast, list ) - { - switch( ast->astType ) - { - case Ast::IdentifierAst: - { - IdentifierTargetAst* target = new IdentifierTargetAst( ast->parent ); - target->identifier = safeNodeCast( ast ); - target->start = ast->start; - target->end = ast->end; - target->startCol = ast->startCol; - target->startLine = ast->startLine; - target->endCol = ast->endCol; - target->endLine = ast->endLine; - l << target; - break; - } - case Ast::SubscriptAst: - { - SubscriptTargetAst* target = new SubscriptTargetAst( ast->parent ); - target->subscript = safeNodeCast( ast ); - target->start = ast->start; - target->end = ast->end; - target->startCol = ast->startCol; - target->startLine = ast->startLine; - target->endCol = ast->endCol; - target->endLine = ast->endLine; - l << target; - break; - } - case Ast::AttributeReferenceAst: - { - AttributeReferenceTargetAst* target = new AttributeReferenceTargetAst( ast->parent ); - target->attribute = safeNodeCast( ast ); - target->start = ast->start; - target->end = ast->end; - target->startCol = ast->startCol; - target->startLine = ast->startLine; - target->endCol = ast->endCol; - target->endLine = ast->endLine; - l << target; - break; - } - case Ast::ExtendedSliceAst: - //fall through - case Ast::SimpleSliceAst: - { - SliceTargetAst* target = new SliceTargetAst( ast->parent ); - target->slice = safeNodeCast( ast ); - target->start = ast->start; - target->end = ast->end; - target->startCol = ast->startCol; - target->startLine = ast->startLine; - target->endCol = ast->endCol; - target->endLine = ast->endLine; - l << target; - break; - } - case Ast::AtomAst: - { - AtomAst* atom = dynamic_cast( ast ); - IdentifierTargetAst* target = new IdentifierTargetAst( ast->parent ); - target->identifier = atom->identifier; - target->start = atom->start; - target->end = atom->end; - target->startCol = atom->startCol; - target->endCol = atom->endCol; - target->startLine = atom->startLine; - target->endLine = atom->endLine; - l << target; - delete atom; - break; - } - default: - kDebug() << ast->astType; - Q_ASSERT_X( false, "create_targetlist", "Ooops, found an expression that we can't convert to a target ast, check the code! " ); - } - } - return l; -} - -template static QList generateSpecializedList( const QList& list ) -{ - QList l; - foreach( Ast* ast, list ) - { - T* temp = safeNodeCast( ast ); - l << temp; - } - return l; -} - -IdentifierAst* AstBuilder::createIdentifier( Ast* parent, qint64 idx ) -{ - IdentifierAst* ast = new IdentifierAst( parent ); - ast->start = parser->tokenStream->token( idx ).begin; - ast->end = parser->tokenStream->token( idx ).end; - parser->tokenStream->startPosition( idx, &ast->startLine, &ast->startCol ); - parser->tokenStream->endPosition( idx, &ast->endLine, &ast->endCol ); - ast->identifier = tokenText( idx ); + CodeAst* ast = parseXmlAst(getXmlForFile(filename, contents)); return ast; } - -QList AstBuilder::identifierListFromTokenList( Ast* parent, const KDevPG::ListNode* sequence ) -{ - QList identifiers; - for( int i = 0; i < sequence->count(); i++ ) - { - identifiers << createIdentifier( parent, sequence->at(i)->element ); - } - return identifiers; -} - -void AstBuilder::setStartEnd( Ast* ast, PythonParser::AstNode* node ) -{ - ast->start = parser->tokenStream->token( node->startToken ).begin; - ast->end = parser->tokenStream->token( node->endToken ).end; - parser->tokenStream->startPosition( node->startToken, &ast->startLine, &ast->startCol ); - parser->tokenStream->endPosition( node->endToken, &ast->endLine, &ast->endCol ); -} - -QString AstBuilder::tokenText( qint64 tokenidx ) -{ - // -1 means this is not a valid token idx and thus return an empty string; - if( tokenidx == -1 ) - return ""; - KDevPG::TokenStream::Token token = parser->tokenStream->token( tokenidx ); - return parser->tokenText( token.begin, token.end ); -} - -AstBuilder::AstBuilder(PythonParser::Parser* p) - : parser(p) -{ -} - -void AstBuilder::visitAndExpr(PythonParser::AndExprAst *node) -{ - kDebug() << "visitAndExpr start"; - visitNode( node->andExpr ); - if( node->anddShifExprSequence && node->anddShifExprSequence->count() > 0 ) - { - BinaryExpressionAst* ast = createAst( node ); - ast->opType = ArithmeticExpressionAst::BinaryAnd; - ast->lhs = safeNodeCast( mNodeStack.pop() ); - int count = node->anddShifExprSequence->count(); - BinaryExpressionAst* curast = ast; - for( int i = 0; i < count; i++ ) - { - visitNode( node->anddShifExprSequence->at(i)->element ); - if( i+1 < count ) - { - BinaryExpressionAst* tmp = createAst( - node->anddShifExprSequence->at(i)->element ); - curast->opType = ArithmeticExpressionAst::BinaryAnd; - tmp->lhs = safeNodeCast( mNodeStack.pop() ); - curast->rhs = tmp; - curast = tmp; - }else - { - curast->rhs = safeNodeCast( mNodeStack.pop() ); + +QString AstBuilder::getXmlForFile(KUrl filename, const QString& contents) +{ + QProcess* parser = new QProcess(); + // we call a python script to parse the code for us. It returns an XML string with the AST +// kDebug() << QDir::current(); + kDebug() << "+++ Starting parser for file " << filename.path(); + parser->start("/usr/bin/env", QStringList() << "python" << QString(INSTALL_PATH) + QString("/pythonpythonparser.py")); + qint64 length = contents.length(); + qint64 written = parser->write(contents.toAscii().data(), length); + kDebug() << "Content length: " << length << ", Bytes written: " << written; + parser->closeWriteChannel(); + if ( written != length ) { + Q_ASSERT(false); + } + parser->waitForFinished(); + kDebug() << " ** Reading results..."; + + // TODO this is not clean + if ( parser->exitStatus() != QProcess::NormalExit ) { + kError() << "Error parsing file: " << parser->errorString(); + return "0"; + } + + QString result = parser->readAllStandardOutput(); + kDebug() << " ** XML for " << filename << ": length" << result.length(); + + if ( ! result.length() ) { + result = parser->readAllStandardError(); + QStringList position = result.split(":::"); + + QString additionalExplanation = ""; + if ( position.length() < 4 ) { + kError() << "Could not parse error message! This should not happen."; + kError() << "Raw data was: " << result; + return "0"; + } + + qint64 lineno = position.at(0).toInt() - 1; + qint64 colno = position.at(1).toInt() - 1; + + kDebug() << lineno << colno; + + if ( position.at(2) == "SyntaxError" ) { + additionalExplanation = "Something's wrong with your syntax. Check for missing brackets, commas, and colons."; + } + else if ( position.at(2) == "IndentationError" ) { + additionalExplanation = "You indented your code incorrectly. Also check that you didn't mix tabs and spaces in an incorrect way!"; + } + + KDevelop::ProblemPointer p(new KDevelop::Problem()); + p->setFinalLocation(KDevelop::DocumentRange(KDevelop::IndexedString(filename), KDevelop::SimpleRange(lineno, colno - 5 < 0 ? 0 : colno - 5, lineno, colno + 5))); + p->setSource(KDevelop::ProblemData::Parser); + p->setDescription(position.at(2)); + p->setExplanation(position.at(3) + "

" + additionalExplanation); + p->setSeverity(KDevelop::ProblemData::Error); + { + DUChainWriteLocker lock(DUChain::lock()); + m_problems.clear(); + m_problems.append(p); + } + kWarning() << "Parse Error: " << result; + return "0"; + } + delete parser; + return result; +} + +CodeAst* AstBuilder::parseXmlAst(QString xml) +{ + Q_ASSERT(xml.length()); + + if ( xml == "0" ) { + return 0; + } + + QXmlStreamReader* xmlast = new QXmlStreamReader(); + xmlast->addData(xml); + + m_nodeMap.clear(); + + parseXmlAstNode(xmlast, QXmlStreamReader::Invalid); + + populateAst(); + + CodeAst* codeAst = dynamic_cast(m_currentNode); + Q_ASSERT(codeAst); + return codeAst; +} + +void AstBuilder::parseXmlAstNode(QXmlStreamReader* xmlast, QXmlStreamReader::TokenType /*token = QXmlStreamReader::Invalid*/) { + bool nodeAdded = false; + + while ( ! xmlast->atEnd() && ! xmlast->hasError() ) { + // Advance to the next (first) token + QXmlStreamReader::TokenType token = xmlast->readNext(); + + // Store everything we need later into local variables + QString currentElementName = xmlast->name().toString(); + QString currentElementText = xmlast->text().toString(); + QList currentElementAttributes = xmlast->attributes().toList(); + + // We ignore startDocument and EndDocument + if ( token == QXmlStreamReader::StartDocument || token == QXmlStreamReader::EndDocument ) { + continue; + } + // We recursively continue parsing if we find another element + else if ( token == QXmlStreamReader::StartElement ) { + // Here we can now assemble an actual node with the attributes extracted above + + // Skip the document root element + if ( currentElementName == "pythonast" ) { + parseXmlAstNode(xmlast, token); + continue; } - } - mNodeStack.push( ast ); - } - kDebug() << "visitAndExpr end"; -} - -void AstBuilder::visitAndTest(PythonParser::AndTestAst *node) -{ - kDebug() << "visitAndTest start"; - visitNode( node->notTestSequence->at(0)->element ); - if( node->notTestSequence->count() > 1 ) - { - BooleanAndOperationAst* ast = createAst( node ); - ast->lhs = safeNodeCast( mNodeStack.pop() ); - int count = node->notTestSequence->count(); - BooleanAndOperationAst* curast = ast; - for( int i = 1; i < count; i++ ) - { - visitNode( node->notTestSequence->at(i)->element ); - if( i+1 < count ) - { - BooleanAndOperationAst* tmp = createAst( - node->notTestSequence->at(i)->element ); - tmp->lhs = safeNodeCast( mNodeStack.pop() ); - curast->rhs = tmp; - curast = tmp; - }else - { - curast->rhs = safeNodeCast( mNodeStack.pop() ); - } - } - mNodeStack.push( ast ); - } - kDebug() << "visitAndTest end"; -} - -void AstBuilder::visitArglist(PythonParser::ArglistAst *node) -{ - kDebug() << "visitArglist start"; - QList args; - visitNode( node->argListBegin ); - if( dynamic_cast( mNodeStack.top() ) ) - { - args << mNodeStack.pop(); - mListStack.push( args ); - // Early return because a Generator expression was found, thats the only - // thing in this "argumentlist" then - return; - } - if( node->argListBegin ) - { - args += mListStack.pop(); - } - if( node->arglistStar ) - { - ArgumentAst* ast = createAst( node->arglistStar ); - ast->argumentType = ArgumentAst::ListArgument; - visitNode( node->arglistStar ); - ast->argumentExpression = safeNodeCast( mNodeStack.pop() ); - args << ast; - } - if( node->arglistDoublestar ) - { - ArgumentAst* ast = createAst( node->arglistDoublestar ); - ast->argumentType = ArgumentAst::DictArgument; - visitNode( node->arglistDoublestar ); - ast->argumentExpression = safeNodeCast( mNodeStack.pop() ); - args << ast; - } - mListStack.push( args ); - kDebug() << "visitArglist end"; -} - -void AstBuilder::visitArgument(PythonParser::ArgumentAst *node) -{ - kDebug() << "visitArgument start"; - visitNode( node->argumentTest ); - if( node->argumentEqualTest ) - { - ArgumentAst* ast = createAst( node ); - ast->argumentType = ArgumentAst::KeywordArgument; - AtomAst *argumentName = safeNodeCast( mNodeStack.pop() ); - ast->keywordName = safeNodeCast( argumentName->identifier ); - visitNode( node->argumentEqualTest ); - ast->argumentExpression = safeNodeCast( mNodeStack.pop() ); - mNodeStack.push( ast ); - }else if( node->genFor ) - { - GeneratorAst* ast = createAst( node ); - ast->generatedValue = safeNodeCast( mNodeStack.pop() ); - visitNode( node->genFor ); - ast->generator = safeNodeCast( mNodeStack.pop() ); - mNodeStack.push( ast ); - }else - { - ArgumentAst* ast = createAst( node ); - ast->argumentType = ArgumentAst::PositionalArgument; - ast->argumentExpression = safeNodeCast( mNodeStack.pop() ); - mNodeStack.push( ast ); - } - kDebug() << "visitArgument end"; -} - -void AstBuilder::visitArithExpr(PythonParser::ArithExprAst *node) -{ - kDebug() << "visitArithExpr start"; - visitNode( node->arithTerm ); - if( node->arithOpListSequence && node->arithOpListSequence->count() > 0 && node->arithTermListSequence->count() > 0 ) - { - Q_ASSERT_X( node->arithOpListSequence->count() == node->arithTermListSequence->count(), - "visitArithExpr", "different number of operators and operands" ); - BinaryExpressionAst* ast = createAst( node ); - Ast *dbg_node = mNodeStack.pop(); - ast->lhs = safeNodeCast( dbg_node ); - BinaryExpressionAst* cur = ast; - int count = node->arithOpListSequence->count(); - for( int i = 0; i < count; i++ ) - { - switch( node->arithOpListSequence->at(i)->element->arithOp ) - { - case PythonParser::PlusOp: - cur->opType = BinaryExpressionAst::BinaryPlus; - break; - case PythonParser::MinusOp: - cur->opType = BinaryExpressionAst::BinaryMinus; - break; - default: - //Should never reach here, unless somebody changed the grammer and not the builder - Q_ASSERT(false); + + // this will push a parent onto the stack + nodeAdded = parseAstNode(currentElementName, /*currentElementText,*/ currentElementAttributes); // we might need ElementText some day + if ( ! nodeAdded ) { + m_isRealNodeMap.append(false); + continue; } - visitNode( node->arithTermListSequence->at(i)->element ); - if( i+1 < count ) - { - BinaryExpressionAst* tmp = createAst( node->arithTermListSequence->at(i)->element ); - cur->rhs = tmp; - cur = tmp; - cur->lhs = safeNodeCast( mNodeStack.pop() ); - }else - { - cur->rhs = safeNodeCast( mNodeStack.pop() ); + m_isRealNodeMap.append(true); + + m_currentNode = m_nodeStack.last(); + + parseXmlAstNode(xmlast, token); + } + else if ( token == QXmlStreamReader::EndElement ) { + if ( currentElementName == "pythonast" ) continue; + + // now we pop the parent off + bool isreal = m_isRealNodeMap.last(); + m_isRealNodeMap.removeLast(); + + if ( isreal ) { + m_currentNode = m_nodeStack.last(); + m_nodeStack.removeLast(); } } - mNodeStack.push( ast ); - } - kDebug() << "visitArithExpr end"; -} - -void AstBuilder::visitAssertStmt(PythonParser::AssertStmtAst *node) + // Everything else (stuff between tags, comments...) is ignored + else continue; + } + if ( xmlast->hasError() ) { + kWarning() << "Invalid XML file: " << xmlast->errorString(); + kWarning() << "Aborting!"; + Q_ASSERT(false); + } +} + +bool AstBuilder::parseAstNode(QString name, /*QString text, */ const QList< QXmlStreamAttribute >& attributes) +{ + Ast* ast; + + QMap attributeDict; + + for ( int i=0; istartLine = -5; + + m_nodeMap.insert(node_id, ast); + m_attributeStore.insert(node_id, attributeDict); + + m_nodeStack.append(ast); +// kDebug() << "Stack size: " << m_nodeStack.length(); + return true; +} + +template T* AstBuilder::resolveNode(const QString& identifier) +{ + if ( ! identifier.length() ) return 0; + int id = identifier.toInt(); + Ast* found = m_nodeMap.value(id); + T* ret = dynamic_cast(found); + Q_ASSERT(found || ! ret); + return found ? ret : 0; +} + +template QList AstBuilder::resolveNodeList(const QString& commaSeperatedIdentifiers) +{ + QList items; + items.clear(); + QStringList identifiers = commaSeperatedIdentifiers.split(","); + T* found; + for ( int i=0; i(identifiers.at(i)); + if ( found ) items << found; + } + return items; +} + +Identifier* AstBuilder::createIdentifier(const QString& name, Ast* range) +{ + Identifier* ident = new Identifier(name); + ident->startCol = range->startCol; + ident->endCol = range->startCol + name.length() - 1; + ident->startLine = range->startLine; + ident->endLine = range->endLine; + ident->parent = range; + return ident; +} + +ExpressionAst::Context AstBuilder::resolveContext(const QString& identifier) { - kDebug() << "visitAssertStmt start"; - AssertAst* ast = createAst( node ); - visitNode( node->assertNotTest ); - ast->assertTest = safeNodeCast(mNodeStack.pop()); - if( node->assertRaiseTest ) - { - visitNode( node->assertRaiseTest ); - ast->exceptionValue = safeNodeCast(mNodeStack.pop()); - } - mNodeStack.push(ast); - kDebug() << "visitAssertStmt end"; + int id = identifier.toInt(); + if ( ! id ) return ExpressionAst::Invalid; + return m_contextNodeMap.value(id); } -void AstBuilder::visitAtom(PythonParser::AtomAst *node) +Ast::BooleanOperationTypes AstBuilder::resolveBooleanOperator(const QString& identifier) { - kDebug() << "visitAtom start"; - AtomAst* ast = createAst( node ); - if( node->atomIdentifierName >= 0 || node->number || (node->stringliteralSequence && node->stringliteralSequence->count() > 0) ) - { - if( node->atomIdentifierName >= 0 ) - { - IdentifierAst* id = createIdentifier( ast, node->atomIdentifierName ); - ast->identifier = id; - }else if( node->number ) - { - visitNode( node->number ); - ast->literal = safeNodeCast( mNodeStack.pop() ); - }else if ( node->stringliteralSequence ) - { - LiteralAst* lit = createAst( node ); - lit->parent = ast; - lit->literalType = LiteralAst::String; - for( int i = 0; i < node->stringliteralSequence->count(); i++ ) - { - lit->value += tokenText( node->stringliteralSequence->at(i)->element ); - } - ast->literal = lit; - } - }else if( node->listmaker ) - { - EnclosureAst* enc = createAst( node->listmaker ); - enc->parent = ast; - visitNode( node->listmaker ); - enc->encType = EnclosureAst::List; - enc->list = safeNodeCast( mNodeStack.pop() ); - ast->enclosure = enc; - }else if( node->codeexpr ) - { - EnclosureAst* enc = createAst( node->codeexpr ); - visitNode( node->codeexpr ); - enc->parent = ast; - enc->encType = EnclosureAst::StringConversion; - enc->stringConversion = generateSpecializedList( mListStack.pop() ); - ast->enclosure = enc; - }else if( node->dictmaker ) - { - EnclosureAst* enc = createAst( node->dictmaker ); - enc->parent = ast; - visitNode( node->dictmaker ); - enc->encType = EnclosureAst::Dictionary; - enc->dict = safeNodeCast( mNodeStack.pop() ); - ast->enclosure = enc; - }else if( node->yield ) - { - visitNode( node->yield ); - EnclosureAst* enc = createAst( node->yield ); - enc->parent = ast; - enc->encType = EnclosureAst::Yield; - enc->yield = safeNodeCast( mNodeStack.pop() ); - ast->enclosure = enc; - }else - { - EnclosureAst* enc; - visitNode( node->testlist ); - if( node->genFor ) - { - enc = createAst( node ); - enc->encType = EnclosureAst::Generator; - QList l = generateSpecializedList( mListStack.pop() ); - GeneratorAst* gen = createAst( node ); - gen->generatedValue = l.first(); - visitNode( node->genFor ); - gen->generator = safeNodeCast( mNodeStack.pop() ); - enc->generator = gen; - enc->parent = ast; - ast->enclosure = enc; - }else - { - enc = createAst( node ); - enc->encType = EnclosureAst::ParenthesizedForm; - enc->parent = ast; - QList dbg_node = mListStack.top(); - enc->parenthesizedform = generateSpecializedList( mListStack.pop() ); - ast->enclosure = enc; - } - } - mNodeStack.push( ast ); - kDebug() << "visitAtom end"; + int id = identifier.toInt(); + if ( ! id ) return Ast::BooleanInvalidOperation; + return m_boolOpNodeMap.value(id); } -void AstBuilder::visitBreakStmt(PythonParser::BreakStmtAst *node) +Ast::OperatorTypes AstBuilder::resolveOperator(const QString& identifier) { - kDebug() << "visitBreakStmt start"; - StatementAst* ast = createAst( node, Ast::BreakAst ); - mNodeStack.push( ast ); - kDebug() << "visitBreakStmt end"; -} - -void AstBuilder::visitClassdef(PythonParser::ClassdefAst *node) -{ - kDebug() << "visitClassdef start"; - ClassDefinitionAst* ast = createAst( node ); - ast->className = createIdentifier( ast, node->className ); - if( node->testlist ) - { - visitNode( node->testlist ); - ast->inheritance = generateSpecializedList( mListStack.pop() ); - } - visitNode( node->classSuite ); - ast->classBody = generateSpecializedList( mListStack.pop() ); - mNodeStack.push(ast); - kDebug() << "visitClassdef end"; -} - -void AstBuilder::visitComparison(PythonParser::ComparisonAst *node) -{ - kDebug() << "visitComparison start"; - visitNode( node->compExpr ); - if( node->compOpSequence && node->compOpSequence->count() > 0 && node->compOpExprSequence->count() > 0 ) - { - ComparisonAst* ast = createAst( node ); - ast->firstComparator = safeNodeCast( mNodeStack.pop() ); - mNodeStack.push( ast ); - Q_ASSERT( node->compOpSequence->count() == node->compOpExprSequence->count() ); - int count = node->compOpSequence->count(); - for( int i = 0; i < count; i++ ) - { - QPair pair; - switch( node->compOpSequence->at(i)->element->compOp ) - { - case PythonParser::LessOp: - pair.first = ComparisonAst::LessThanOp; - break; - case PythonParser::GreaterOp: - pair.first = ComparisonAst::GreaterThanOp; - break; - case PythonParser::IsEqualOp: - pair.first = ComparisonAst::EqualOp; - break; - case PythonParser::GreaterEqOp: - pair.first = ComparisonAst::GreaterEqualOp; - break; - case PythonParser::LessEqOp: - pair.first = ComparisonAst::LessEqualOp; - break; - case PythonParser::UnEqualOp: - pair.first = ComparisonAst::UnequalOp; - break; - case PythonParser::InOp: - pair.first = ComparisonAst::InOp; - break; - case PythonParser::NotInOp: - pair.first = ComparisonAst::NotInOp; - break; - case PythonParser::IsNotOp: - pair.first = ComparisonAst::IsNotOp; - break; - case PythonParser::IsOp: - pair.first = ComparisonAst::IsOp; - break; - default: - //Should never reach here, unless somebody changed the grammer and not the builder - Q_ASSERT(false); - } - visitNode( node->compOpExprSequence->at(i)->element ); - pair.second = safeNodeCast( mNodeStack.pop() ); - ast->comparatorList << pair; - } - } - kDebug() << "visitComparison end"; + int id = identifier.toInt(); + if ( ! id ) return Ast::OperatorInvalid; + return m_opNodeMap.value(id); } -void AstBuilder::visitCompoundStmt(PythonParser::CompoundStmtAst *node) +Ast::UnaryOperatorTypes AstBuilder::resolveUnaryOperator(const QString& identifier) { - kDebug() << "visitCompoundStmt start"; - PythonParser::DefaultVisitor::visitCompoundStmt( node ); - kDebug() << "visitCompoundStmt end"; + int id = identifier.toInt(); + if ( ! id ) return Ast::UnaryOperatorInvalid; + return m_unaryOpNodeMap.value(id); } -void AstBuilder::visitContinueStmt(PythonParser::ContinueStmtAst *node) +Ast::ComparisonOperatorTypes AstBuilder::resolveComparisonOperator(const QString& identifier) { - kDebug() << "visitContinueStmt start"; - StatementAst* ast = createAst( node, Ast::ContinueAst ); - mNodeStack.push( ast ); - kDebug() << "visitContinueStmt end"; -} - -void AstBuilder::visitDottedName(PythonParser::DottedNameAst *node) { - // no idea why this is meant to be a list, - // i've never seen something like "qualified decorators" in python... TODO check this - IdentifierAst *ast = createAst( node ); - QList l; - l << ast; - mListStack.push(l); + int id = identifier.toInt(); + if ( ! id ) return Ast::ComparisonOperatorInvalid; + return m_compOpNodeMap.value(id); } -void AstBuilder::visitDecorator(PythonParser::DecoratorAst *node) +QList< Ast::ComparisonOperatorTypes > AstBuilder::resolveComparisonOperatorList(const QString& identifiers) { - kDebug() << "visitDecorator start"; - DecoratorAst* ast = createAst( node ); - visitNode( node->decoratorName ); - ast->dottedName = generateSpecializedList( mListStack.pop() ); - if( node->arguments ) - { - visitNode( node->arguments ); - ast->arguments = generateSpecializedList( mListStack.pop() ); + QList items; + QList ids = identifiers.split(","); + for ( int i=0; i < ids.length(); i++ ) { + items << resolveComparisonOperator(ids.at(i)); } - mNodeStack.push( ast ); - kDebug() << "visitDecorator end"; + return items; } -void AstBuilder::visitDecorators(PythonParser::DecoratorsAst *node) +ExecAst* AstBuilder::populateExecAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitDecorators start"; - QList l; - int count = node->decoratorSequence->count(); - for( int i = 0; i < count; i++ ) - { - visitNode( node->decoratorSequence->at(i)->element ); - l << safeNodeCast( mNodeStack.pop() ); - } - mListStack.push( l ); - kDebug() << "visitDecorators end"; + ExecAst* currentNode = dynamic_cast(ast); + currentNode->body = resolveNode(currentAttributes.value("NR_body")); + currentNode->locals = resolveNode(currentAttributes.value("NR_locals")); + currentNode->globals = resolveNode(currentAttributes.value("NR_globals")); + return currentNode; } -void AstBuilder::visitDefparam(PythonParser::DefparamAst *node) +NameAst* AstBuilder::populateNameAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - - kDebug() << "visitDefparam start"; - if( node->paramname != -1 ) - { - IdentifierParameterPartAst* ast = createAst( node ); - ast->name = createIdentifier( ast, node->paramname ); - mNodeStack.push( ast ); - }else - { - ListParameterPartAst* ast = createAst( node ); - mNodeStack.push( ast ); - visitNode( node->fplist ); - ast->parameternames = generateSpecializedList( mListStack.pop() ); - } - kDebug() << "visitDefparam start"; + NameAst* currentNode = dynamic_cast(ast); + currentNode->context = resolveContext(currentAttributes.value("NR_ctx")); + currentNode->identifier = createIdentifier(currentAttributes.value("id"), currentNode); +// kDebug() << "Processing NameAst" << currentNode->identifier->value; + return currentNode; } -void AstBuilder::visitDelStmt(PythonParser::DelStmtAst *node) +ClassDefinitionAst* AstBuilder::populateClassDefinitonAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitDelStmt start"; - DelAst* ast = createAst( node ); - visitNode( node->delList ); - ast->deleteObjects = generateSpecializedList( mListStack.pop() ); - mNodeStack.push( ast ); - kDebug() << "visitDelStmt end"; + ClassDefinitionAst* currentNode = dynamic_cast(ast); + currentNode->baseClasses = resolveNodeList(currentAttributes.value("NRLST_bases")); + currentNode->body = resolveNodeList(currentAttributes.value("NRLST_body")); + currentNode->decorators = resolveNodeList(currentAttributes.value("NRLST_decorator_list")); + currentNode->name = createIdentifier(currentAttributes.value("name"), currentNode); + currentNode->name->startCol += 6; // TODO fix this! ;D + currentNode->name->endCol += 6; + return currentNode; } -void AstBuilder::visitDictmaker(PythonParser::DictmakerAst *node) +FunctionDefinitionAst* AstBuilder::populateFunctionDefinitionAst(Ast* ast, const stringDictionary& currentAttributes) { - kDebug() << "visitDictmaker start"; - DictionaryAst* ast = createAst( node ); - mNodeStack.push(ast); - int count = node->keyListSequence ? node->keyListSequence->count() : 0; - Q_ASSERT( count == (node->valueListSequence ? node->valueListSequence->count() : 0) ); - for( int i = 0; i < count; i++ ) - { - visitNode( node->keyListSequence->at(i)->element ); - ExpressionAst* key = safeNodeCast( mNodeStack.pop() ); - visitNode( node->valueListSequence->at(i)->element ); - ast->dictionary.insert( key, safeNodeCast( mNodeStack.pop() ) ); - } - - kDebug() << "visitDictmaker end"; + FunctionDefinitionAst* currentNode = dynamic_cast(ast); + currentNode->arguments = resolveNode(currentAttributes.value("NR_args")); + currentNode->body = resolveNodeList(currentAttributes.value("NRLST_body")); + currentNode->decorators = resolveNodeList(currentAttributes.value("NRLST_decorator_list")); + currentNode->name = createIdentifier(currentAttributes.value("name"), currentNode); + currentNode->name->startCol += 4; // TODO fix this! ;D + currentNode->name->endCol += 4; + return currentNode; } -void AstBuilder::visitExceptClause(PythonParser::ExceptClauseAst *node) +AssignmentAst* AstBuilder::populateAssignmentAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitExceptClause start"; - ExceptAst* ast = createAst( node ); - mNodeStack.push( ast ); - visitNode( node->exceptTest ); - ast->exceptionDeclaration = safeNodeCast( mNodeStack.pop() ); - visitNode( node->exceptTargetTest ); - ast->exceptionValue = safeNodeCast( mNodeStack.pop() ); - kDebug() << "visitExceptClause end"; + AssignmentAst* currentNode = dynamic_cast(ast); + currentNode->value = resolveNode(currentAttributes.value("NR_value")); + currentNode->targets = resolveNodeList(currentAttributes.value("NRLST_targets")); + return currentNode; } -void AstBuilder::visitExecStmt(PythonParser::ExecStmtAst *node) +CodeAst* AstBuilder::populateCodeAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitExecStmt start"; - ExecAst* ast = createAst( node ); - visitNode( node->execCode ); - ast->executable = safeNodeCast( mNodeStack.pop() ); - if( node->globalDictExec ) - { - visitNode( node->globalDictExec ); - ast->globalsAndLocals = safeNodeCast( mNodeStack.pop() ); - } - if( node->localDictExec ) - { - visitNode( node->localDictExec ); - ast->localsOnly = safeNodeCast( mNodeStack.pop() ); - } - mNodeStack.push( ast ); - kDebug() << "visitExecStmt end"; + CodeAst* currentNode = dynamic_cast(ast); + currentNode->body = resolveNodeList(currentAttributes.value("NRLST_body")); + return currentNode; } -void AstBuilder::visitExpr(PythonParser::ExprAst *node) +DeleteAst* AstBuilder::populateDeleteAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitExpr start"; - visitNode( node->expr ); - if( node->orrExprSequence && node->orrExprSequence->count() > 0 ) - { - BinaryExpressionAst* ast = createAst( node ); - ast->opType = ArithmeticExpressionAst::BinaryOr; - ast->lhs = safeNodeCast( mNodeStack.pop() ); - int count = node->orrExprSequence->count(); - BinaryExpressionAst* curast = ast; - for( int i = 0; i < count; i++ ) - { - visitNode( node->orrExprSequence->at(i)->element ); - if( i+1 < count ) - { - BinaryExpressionAst* tmp = createAst( - node->orrExprSequence->at(i)->element ); - curast->opType = ArithmeticExpressionAst::BinaryOr; - tmp->lhs = safeNodeCast( mNodeStack.pop() ); - curast->rhs = tmp; - curast = tmp; - }else - { - curast->rhs = safeNodeCast( mNodeStack.pop() ); - } - } - mNodeStack.push( ast ); - } - kDebug() << "visitExpr end"; + DeleteAst* currentNode = dynamic_cast(ast); + currentNode->targets = resolveNodeList(currentAttributes.value("NRLST_targets")); + return currentNode; } -void AstBuilder::visitExprStmt(PythonParser::ExprStmtAst *node) +ForAst* AstBuilder::populateForAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitExprStmt start"; - visitNode( node->testlist ); - if( node->augassign ) - { - // Augmented assignments cannot have multiple targets, so the testlist needs to contain only 1 element - Q_ASSERT( mListStack.top().count() == 1 ); - AssignmentAst* a = createAst( node ); - QList l = targetAstListFromExpressionAstList( - generateSpecializedList( mListStack.pop() ) ); - AssignmentAst::OpType op; - switch( node->augassign->assignOp ) - { - case PythonParser::PlusEqOp: - op = AssignmentAst::AddEqualOp; - break; - case PythonParser::MinusEqOp: - op = AssignmentAst::SubEqualOp; - break; - case PythonParser::StarEqOp: - op = AssignmentAst::MultiplyEqualOp; - break; - case PythonParser::SlashEqOp: - op = AssignmentAst::DivideEqualOp; - break; - case PythonParser::ModuloEqOp: - op = AssignmentAst::ModuloEqualOp; - break; - case PythonParser::AndEqOp: - op = AssignmentAst::AndEqualOp; - break; - case PythonParser::OrEqOp: - op = AssignmentAst::OrEqualOp; - break; - case PythonParser::HatEqOp: - op = AssignmentAst::XorEqualOp; - break; - case PythonParser::LeftShiftEqOp: - op = AssignmentAst::LeftShiftEqualOp; - break; - case PythonParser::RightShiftEqOp: - op = AssignmentAst::RightShiftEqualOp; - break; - case PythonParser::DoublestarEqOp: - op = AssignmentAst::PowEqualOp; - break; - case PythonParser::DoubleslashEqOp: - op = AssignmentAst::FloorEqualOp; - break; - default: - //Should never reach here, unless somebody changed the grammer and not the builder - Q_ASSERT(false); - } - a->targets.append( qMakePair( l, op ) ); - if( node->yield ) - { - visitNode( node->yield ); - a->yieldValue = safeNodeCast( mNodeStack.pop() ); - }else - { - visitNode( node->anugassignTestlist ); - a->value = generateSpecializedList( mListStack.pop() ); - } - mNodeStack.push( a ); - }else if( node->yield || ( node->equalTestlistSequence && node->equalTestlistSequence->count() ) > 0 ) - { - AssignmentAst* a = createAst( node ); - QList l = targetAstListFromExpressionAstList( - generateSpecializedList( mListStack.pop() ) ); - a->targets.append( qMakePair( l, AssignmentAst::AssignmentOp) ); - - int count = node->equalTestlistSequence->count(); - if( count > 0 ) - { - for( int i = 0; i < count; i++ ) - { - if( !node->yield && i == count-1 ) - { - // We have no yield statement, so the last element in the - // list is the actual expression for the assignment - break; - } - visitNode( node->equalTestlistSequence->at(i)->element ); - l = targetAstListFromExpressionAstList( - generateSpecializedList( mListStack.pop() ) ); - a->targets.append( qMakePair( l, AssignmentAst::AssignmentOp ) ); - } - } - if( node->yield ) - { - visitNode( node->yield ); - a->yieldValue = safeNodeCast( mNodeStack.pop() ); - }else - { - visitNode( node->equalTestlistSequence->at( count-1 )->element ); - a->value = generateSpecializedList( mListStack.pop() ); - } - mNodeStack.push( a ); - }else - { - ExpressionStatementAst *ast = createAst( node ); - ast->expressions = generateSpecializedList( mListStack.pop() ); - mNodeStack.push( ast ); - } - kDebug() << "visitExprStmt end"; + ForAst* currentNode = dynamic_cast(ast); + currentNode->body = resolveNodeList(currentAttributes.value("NRLST_body")); + currentNode->orelse = resolveNodeList(currentAttributes.value("NRLST_orelse")); + currentNode->iterator = resolveNode(currentAttributes.value("NR_iter")); + currentNode->target = resolveNode(currentAttributes.value("NR_target")); + return currentNode; } -void AstBuilder::visitExprlist(PythonParser::ExprlistAst *node) +PrintAst* AstBuilder::populatePrintAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitExprlist start"; - QList l; - int count = node->exprSequence->count(); - for( int i = 0; i < count; i++ ) - { - visitNode( node->exprSequence->at(i)->element ); - l << safeNodeCast( mNodeStack.pop() ); - } - mListStack.push( l ); - kDebug() << "visitExprlist end"; + PrintAst* currentNode = dynamic_cast(ast); + currentNode->destination = resolveNode(currentAttributes.value("NR_dest")); + currentNode->newline = currentAttributes.value("nl") == "True" ? true : false; + currentNode->values = resolveNodeList(currentAttributes.value("NRLST_values")); + return currentNode; } -void AstBuilder::visitFactor(PythonParser::FactorAst *node) +ReturnAst* AstBuilder::populateReturnAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitFactor start"; - if( node->power ) - { - visitNode( node->power ); - }else - { - UnaryExpressionAst* ast = createAst( node ); - mNodeStack.push( ast ); - visitNode( node->factor ); - switch( node->factOp->op ) - { - case PythonParser::UnaryPlusOp: - ast->opType = ArithmeticExpressionAst::UnaryPlus; - break; - case PythonParser::UnaryTildeOp: - ast->opType = ArithmeticExpressionAst::UnaryTilde; - break; - case PythonParser::UnaryMinusOp: - ast->opType = ArithmeticExpressionAst::UnaryMinus; - break; - default: - //Shouldn't reach this, unless someone changes the grammar and didn't update here - Q_ASSERT(false); - } - ast->operand = safeNodeCast( mNodeStack.pop() ); - } - kDebug() << "visitFactor end"; + ReturnAst* currentNode = dynamic_cast(ast); + currentNode->value = resolveNode(currentAttributes.value("NR_value")); + return currentNode; } -void AstBuilder::visitFlowStmt(PythonParser::FlowStmtAst *node) +IfAst* AstBuilder::populateIfAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitFlowStmt start"; - PythonParser::DefaultVisitor::visitFlowStmt( node ); - kDebug() << "visitFlowStmt end"; + IfAst* currentNode = dynamic_cast(ast); + currentNode->body = resolveNodeList(currentAttributes.value("NRLST_body")); + currentNode->condition = resolveNode(currentAttributes.value("NR_test")); + currentNode->orelse = resolveNodeList(currentAttributes.value("NRLST_orelse")); + return currentNode; } -void AstBuilder::visitForStmt(PythonParser::ForStmtAst *node) +BooleanOperationAst* AstBuilder::populateBooleanOperationAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitForStmt start"; - ForAst* ast = createAst( node ); - visitNode( node->forExpr ); - ast->assignedTargets = targetAstListFromExpressionAstList( generateSpecializedList( mListStack.pop() ) ); - visitNode( node->forTestlist ); - ast->iterable = generateSpecializedList( mListStack.pop() ); - visitNode( node->forSuite ); - ast->forBody = generateSpecializedList( mListStack.pop() ); - if( node->forElseSuite ) - { - visitNode( node->forElseSuite ); - ast->elseBody = generateSpecializedList( mListStack.pop() ); - } - mNodeStack.push( ast ); - kDebug() << "visitForStmt end"; + BooleanOperationAst* currentNode = dynamic_cast(ast); + currentNode->values = resolveNodeList(currentAttributes.value("NRLST_values")); + currentNode->type = resolveBooleanOperator(currentAttributes.value("NR_op")); + return currentNode; } -void AstBuilder::visitFpDef(PythonParser::FpDefAst *node) +CallAst* AstBuilder::populateCallAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitFpDef start"; - DefaultParameterAst* ast = createAst( node ); - - mNodeStack.push( ast ); - visitNode( node->defparam ); - ast->name = safeNodeCast( mNodeStack.pop() ); - if( node->fpDefTest ) - { - visitNode( node->fpDefTest ); - ast->value = safeNodeCast( mNodeStack.pop() ); - } - kDebug() << "visitFpDef end"; + CallAst* currentNode = dynamic_cast(ast); + currentNode->arguments = resolveNodeList(currentAttributes.value("NRLST_args")); + currentNode->function = resolveNode(currentAttributes.value("NR_func")); + currentNode->keywordArguments = resolveNode(currentAttributes.value("NR_kwargs")); + currentNode->keywords = resolveNodeList(currentAttributes.value("NRLST_keywords")); + currentNode->starArguments = resolveNode(currentAttributes.value("NR_starargs")); + return currentNode; } -void AstBuilder::visitFplist(PythonParser::FplistAst *node) +LambdaAst* AstBuilder::populateLambdaAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitFplist start"; - int count = node->fplistFpdefSequence->count(); - QList l; - for( int i = 0; i < count; i++ ) - { - visitNode( node->fplistFpdefSequence->at(i)->element ); - l << safeNodeCast( mNodeStack.pop() ); - } - mListStack.push( l ); - kDebug() << "visitFplist end"; + LambdaAst* currentNode = dynamic_cast(ast); + currentNode->arguments = resolveNode(currentAttributes.value("NR_args")); + currentNode->body = resolveNode(currentAttributes.value("NR_body")); + return currentNode; } -void AstBuilder::visitFuncdecl(PythonParser::FuncdeclAst *node) +WhileAst* AstBuilder::populateWhileAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitFuncdecl start"; - FunctionDefinitionAst* ast = createAst( node ); - if( node->decorators ) - { - visitNode( node->decorators ); - ast->decorators = generateSpecializedList( mListStack.pop() ); - } - ast->functionName = createIdentifier( ast, node->funcName ); - if( node->funArgs ) - { - visitNode( node->funArgs ); - ast->parameters = generateSpecializedList( mListStack.pop() ); - } - visitNode( node->funSuite ); - ast->functionBody = generateSpecializedList( mListStack.pop() ); - mNodeStack.push( ast ); - kDebug() << "visitFuncdecl end"; + WhileAst* currentNode = dynamic_cast(ast); + currentNode->body = resolveNodeList(currentAttributes.value("NRLST_body")); + currentNode->orelse = resolveNodeList(currentAttributes.value("NRLST_orelse")); + currentNode->condition = resolveNode(currentAttributes.value("NR_test")); + return currentNode; } -void AstBuilder::visitFuncDef(PythonParser::FuncDefAst *node) +DictAst* AstBuilder::populateDictAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitFuncDef start"; - QList l; - int count = node->fpDefSequence->count(); - for( int i = 0; i < count; i++ ) - { - visitNode( node->fpDefSequence->at(i)->element ); - l << safeNodeCast( mNodeStack.pop() ); - } - mListStack.push( l ); - kDebug() << "visitFuncDef end"; + DictAst* currentNode = dynamic_cast(ast); + currentNode->keys = resolveNodeList(currentAttributes.value("NRLST_keys")); + currentNode->values = resolveNodeList(currentAttributes.value("NRLST_values")); + return currentNode; } -void AstBuilder::visitGenFor(PythonParser::GenForAst *node) +ListAst* AstBuilder::populateListAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitGenFor start"; - GeneratorForAst* ast = createAst( node ); - mNodeStack.push( ast ); - visitNode( node->exprlist ); - ast->assignedTargets = generateSpecializedList( mListStack.pop() ); - visitNode( node->test ); - ast->iterableObject = safeNodeCast( mNodeStack.pop() ); - if( node->genIter ) - { - visitNode( node->genIter ); - if( node->genIter->genFor ) - { - ast->nextGenerator = safeNodeCast( mNodeStack.pop() ); - }else - { - ast->nextCondition = safeNodeCast( mNodeStack.pop() ); - } - } - kDebug() << "visitGenFor end"; + ListAst* currentNode = dynamic_cast(ast); + currentNode->elements = resolveNodeList(currentAttributes.value("NRLST_elts")); + currentNode->context = resolveContext(currentAttributes.value("NR_ctx")); + return currentNode; } -void AstBuilder::visitGenIf(PythonParser::GenIfAst *node) +TupleAst* AstBuilder::populateTupleAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitGenIf start"; - GeneratorIfAst* ast = createAst( node ); - mNodeStack.push( ast ); - visitNode( node->test ); - ast->condition = safeNodeCast( mNodeStack.pop() ); - if( node->genIter ) - { - visitNode( node->genIter ); - if( node->genIter->genFor ) - { - ast->nextGenerator = safeNodeCast( mNodeStack.pop() ); - }else - { - ast->nextCondition = safeNodeCast( mNodeStack.pop() ); - } - } - kDebug() << "visitGenIf end"; + TupleAst* currentNode = dynamic_cast(ast); + currentNode->context = resolveContext(currentAttributes.value("NR_ctx")); + currentNode->elements = resolveNodeList(currentAttributes.value("NRLST_elts")); + return currentNode; } -void AstBuilder::visitGenIter(PythonParser::GenIterAst *node) +AugmentedAssignmentAst* AstBuilder::populateAugmentedAssignmentAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitGenIter start"; - PythonParser::DefaultVisitor::visitGenIter(node); - kDebug() << "visitGenIter end"; + AugmentedAssignmentAst* currentNode = dynamic_cast(ast); + currentNode->op = resolveOperator(currentAttributes.value("NR_op")); + currentNode->target = resolveNode(currentAttributes.value("NR_target")); + currentNode->value = resolveNode(currentAttributes.value("NR_value")); + return currentNode; } -void AstBuilder::visitGlobalStmt(PythonParser::GlobalStmtAst *node) +RaiseAst* AstBuilder::populateRaiseAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitGlobalStmt start"; - GlobalAst* ast = createAst( node ); - ast->identifiers = identifierListFromTokenList( ast, node->globalNameSequence ); - mNodeStack.push( ast ); - kDebug() << "visitGlobalStmt end"; + RaiseAst* currentNode = dynamic_cast(ast); + currentNode->type = resolveNode(currentAttributes.value("NR_type")); + return currentNode; } -void AstBuilder::visitIfStmt(PythonParser::IfStmtAst *node) +TryExceptAst* AstBuilder::populateTryExceptAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitIfStmt start"; - IfAst* ast = createAst( node ); - visitNode( node->ifTest ); - ast->ifCondition = safeNodeCast( mNodeStack.pop() ); - visitNode( node->ifSuite ); - ast->ifBody = generateSpecializedList( mListStack.pop() ); - if (node->elifTestSequence && node->elifSuiteSequence) { - Q_ASSERT( node->elifTestSequence->count() == node->elifSuiteSequence->count() ); - int count = node->elifTestSequence->count(); - for( int i = 0; i < count; i++) - { - visitNode( node->elifTestSequence->at(i)->element ); - ExpressionAst* expr = safeNodeCast( mNodeStack.pop() ); - visitNode( node->elifSuiteSequence->at(i)->element ); - ast->elseIfBodies.append( - qMakePair( expr , - generateSpecializedList( - mListStack.pop() ) ) ); - } - } - if( node->ifElseSuite ) - { - visitNode( node->ifElseSuite ); - ast->elseBody = generateSpecializedList( mListStack.pop() ); - } - mNodeStack.push( ast ); - kDebug() << "visitIfStmt end"; + TryExceptAst* currentNode = dynamic_cast(ast); + currentNode->body = resolveNodeList(currentAttributes.value("NRLST_body")); + currentNode->handlers = resolveNodeList(currentAttributes.value("NRLST_handlers")); + currentNode->orelse = resolveNodeList(currentAttributes.value("NRLST_orelse")); + return currentNode; } -void AstBuilder::visitImportFrom(PythonParser::ImportFromAst *node) +TryFinallyAst* AstBuilder::populateTryFinallyAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitImportFrom start"; - if( !node->importAsNames ) - { - StarImportAst* ast = createAst( node ); - ast->modulePath = identifierListFromTokenList( ast, node->importFromName->dottedNameSequence ); - mNodeStack.push( ast ); - }else - { - FromImportAst* ast = createAst( node ); - ast->modulePath = identifierListFromTokenList( ast, node->importFromName->dottedNameSequence ); - const KDevPG::ListNode* idNames; - idNames = node->importAsNames->importAsNameSequence; - int count = idNames->count(); - for(int i = 0; i < count; i++) - { - PythonParser::ImportAsNameAst* namenode = idNames->at(i)->element; - kDebug() << "Fetching from-as:" << tokenText( namenode->importedName ); - ast->identifierAsName.append( qMakePair( - createIdentifier( ast, namenode->importedName ), - createIdentifier( ast, namenode->importedAs ) ) ); - } - mNodeStack.push( ast ); - } - kDebug() << "visitImportFrom end"; + TryFinallyAst* currentNode = dynamic_cast(ast); + currentNode->body = resolveNodeList(currentAttributes.value("NRLST_body")); + currentNode->finalbody = resolveNodeList(currentAttributes.value("NRLST_finalbody")); + return currentNode; } -void AstBuilder::visitImportName(PythonParser::ImportNameAst *node) +AssertionAst* AstBuilder::populateAssertionAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitImportName start"; - PlainImportAst* ast = createAst( node ); - const KDevPG::ListNode* importedmodules; - importedmodules = node->importName->dottedAsNameSequence; - int count = importedmodules->count(); - for( int i = 0; i < count ; i++ ) - { - PythonParser::DottedAsNameAst* import = importedmodules->at(i)->element; - QList modulepath = identifierListFromTokenList( ast, import->importDottedName->dottedNameSequence ); - ast->modulesAsName.append( qMakePair( modulepath, createIdentifier( ast, import->importedAs ) ) ); - } - mNodeStack.push( ast ); - kDebug() << "visitImportName end"; + AssertionAst* currentNode = dynamic_cast(ast); + currentNode->condition = resolveNode(currentAttributes.value("NR_test")); + currentNode->message = resolveNode(currentAttributes.value("NR_msg")); + return currentNode; } -void AstBuilder::visitImportStmt(PythonParser::ImportStmtAst *node) +BinaryOperationAst* AstBuilder::populateBinaryOperationAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitImportStmt start"; - PythonParser::DefaultVisitor::visitImportStmt( node ); - kDebug() << "visitImportStmt end"; + BinaryOperationAst* currentNode = dynamic_cast(ast); + currentNode->rhs = resolveNode(currentAttributes.value("NR_right")); + currentNode->lhs = resolveNode(currentAttributes.value("NR_left")); + currentNode->type = resolveOperator(currentAttributes.value("NR_op")); + return currentNode; } -void AstBuilder::visitLambdaDef(PythonParser::LambdaDefAst *node) +ImportAst* AstBuilder::populateImportAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitLambdaDef start"; - LambdaAst* ast = createAst( node ); - if( node->lambdaVarargslist ) - { - visitNode( node->lambdaVarargslist ); - ast->parameters = generateSpecializedList( mListStack.pop() ); - } - visitNode( node->lambdaTest ); - ast->expression = safeNodeCast( mNodeStack.pop() ); - kDebug() << "visitLambdaDef end"; + ImportAst* currentNode = dynamic_cast(ast); + currentNode->names = resolveNodeList(currentAttributes.value("NRLST_names")); + return currentNode; } -void AstBuilder::visitListFor(PythonParser::ListForAst *node) +ImportFromAst* AstBuilder::populateImportFromAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitListFor start"; - ListForAst* ast = createAst( node ); - mNodeStack.push( ast ); - visitNode( node->exprlist ); - ast->assignedTargets = generateSpecializedList( mListStack.pop() ); - visitNode( node->testlistSafe ); - ast->iterableObject = generateSpecializedList( mListStack.pop() ); - if( node->listIter ) - { - visitNode( node->listIter ); - if( node->listIter->listFor ) - { - ast->nextGenerator = safeNodeCast( mNodeStack.pop() ); - }else - { - ast->nextCondition = safeNodeCast( mNodeStack.pop() ); - } - } - kDebug() << "visitListFor end"; + ImportFromAst* currentNode = dynamic_cast(ast); + currentNode->level = currentAttributes.value("level").toInt(); + currentNode->module = createIdentifier(currentAttributes.value("module"), currentNode); + currentNode->names = resolveNodeList(currentAttributes.value("NRLST_names")); + return currentNode; } -void AstBuilder::visitListIf(PythonParser::ListIfAst *node) +AliasAst* AstBuilder::populateAliasAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitListIf start"; - ListIfAst* ast = createAst( node ); - mNodeStack.push( ast ); - visitNode( node->test ); - ast->condition = safeNodeCast( mNodeStack.pop() ); - if( node->listIter ) - { - visitNode( node->listIter ); - if( node->listIter->listFor ) - { - ast->nextGenerator = safeNodeCast( mNodeStack.pop() ); - }else - { - ast->nextCondition = safeNodeCast( mNodeStack.pop() ); - } - } - - kDebug() << "visitListIf end"; -} - -void AstBuilder::visitListIter(PythonParser::ListIterAst *node) -{ - kDebug() << "visitListIter start"; - PythonParser::DefaultVisitor::visitListIter( node ); - kDebug() << "visitListIter end"; -} - -void AstBuilder::visitListmaker(PythonParser::ListmakerAst *node) -{ - kDebug() << "visitListmaker start"; - ListAst* ast = createAst( node ); - mNodeStack.push( ast ); - visitNode( node->listMakerTest ); - if ( node->listMakerTest ) { - ast->plainList = generateSpecializedList( mListStack.pop() ); - } - if( node->listFor ) - { - //We should have only 1 expression in the listMakerTest as we're having a list_comprehension - Q_ASSERT( node->listMakerTest->listTestSequence->count() == 1 ); - visitNode( node->listFor ); - ast->listGenerator = safeNodeCast( mNodeStack.pop() ); - } - kDebug() << "visitListmaker end"; -} - -void AstBuilder::visitListMakerTest(PythonParser::ListMakerTestAst *node) -{ - kDebug() << "visitListMakerTest start"; - QList l; - int count = node->listTestSequence->count(); - kDebug() << "Elements in list cnt: " << count; - for( int i = 0; i < count; i++ ) - { - visitNode( node->listTestSequence->at(i)->element ); - l << mNodeStack.pop(); - } - mListStack.push( l ); - kDebug() << "visitListMakerTest end"; -} - -void AstBuilder::visitNotTest(PythonParser::NotTestAst *node) -{ - kDebug() << "visitNotTest start"; - if( node->notTest ) - { - BooleanNotOperationAst* ast = createAst( node ); - mNodeStack.push( ast ); - visitNode( node->notTest ); - ast->op = safeNodeCast( mNodeStack.pop() ); - }else - { - visitNode( node->comparison ); - } - kDebug() << "visitNotTest end"; + AliasAst* currentNode = dynamic_cast(ast); + currentNode->asName = resolveNode(currentAttributes.value("NR_asname")); + currentNode->name = createIdentifier(currentAttributes.value("name"), currentNode); + return currentNode; } -void AstBuilder::visitNumber(PythonParser::NumberAst *node) +GlobalAst* AstBuilder::populateGlobalAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitNumber start"; - LiteralAst* ast = createAst( node ); - switch( node->numType ) - { - case PythonParser::IntegerNumeric: - ast->literalType = LiteralAst::Integer; - break; - case PythonParser::ImaginaryNumeric: - ast->literalType = LiteralAst::ImaginaryNumber; - break; - case PythonParser::FloatNumeric: - ast->literalType = LiteralAst::Float; - break; - } - ast->value = tokenText( node->value ); - mNodeStack.push( ast ); - kDebug() << "visitNumber end"; -} - -void AstBuilder::visitPassStmt(PythonParser::PassStmtAst *node) -{ - kDebug() << "visitPassStmt start"; - StatementAst* ast = createAst( node, Ast::PassAst ); - mNodeStack.push( ast ); - kDebug() << "visitPassStmt end"; -} - -void AstBuilder::visitPower(PythonParser::PowerAst *node) -{ - kDebug() << "visitPower start"; - visitNode( node->atom ); - if( node->trailerSequence ) - { - int count = node->trailerSequence->count(); - if( count > 0 ) - { - for( int i = 0; i < count; i++ ) - { - visitTrailer( node->trailerSequence->at( i )->element ); - PrimaryAst* ast = safeNodeCast( mNodeStack.pop() ); - PrimaryAst* prim = safeNodeCast( mNodeStack.pop() ); - switch( ast->astType ) - { - case Ast::CallAst: - static_cast( ast )->callable = prim; - break; - case Ast::ExtendedSliceAst: - case Ast::SimpleSliceAst: - static_cast( ast )->primary = prim; - break; - case Ast::AttributeReferenceAst: - static_cast( ast )->primary = prim; - break; - case Ast::SubscriptAst: - static_cast( ast )->primary = prim; - break; - default: - Q_ASSERT_X(false, "visitTrailer", "OOOPS visitTrailer returned a PrimaryAst that is not known to have a primary in front of it, like an AtomAst or something new."); - break; - } - mNodeStack.push( ast ); - } - } - } - if( node->factor ) - { - BinaryExpressionAst* bast = createAst( node ); - bast->opType = ArithmeticExpressionAst::Power; - bast->lhs = safeNodeCast( mNodeStack.pop() ); - visitNode( node->factor ); - bast->rhs = safeNodeCast( mNodeStack.pop() ); - mNodeStack.push( bast ); - } - kDebug() << "visitPower end"; -} - -void AstBuilder::visitPlainArgumentsList(PythonParser::PlainArgumentsListAst *node) -{ - kDebug() << "visitPlainArgumentsList start"; - QList l; - int count = node->argumentsSequence->count(); - for( int i = 0; i < count; i++ ) - { - visitNode( node->argumentsSequence->at(i)->element ); - if( dynamic_cast( mNodeStack.top() ) ) - { - l << safeNodeCast( mNodeStack.pop() ); - }else if( dynamic_cast( mNodeStack.top() ) ) - { - //Early return, we found a generator expression on the stack - return; - } - } - mListStack.push( l ); - kDebug() << "visitPlainArgumentsList end"; + GlobalAst* currentNode = dynamic_cast(ast); +// currentNode->names = resolveNodeList(currentAttributes.value("NRLST_names")); // TODO the parser does not write this correctly! also, need to fix resolve + return currentNode; } -void AstBuilder::visitPrintStmt(PythonParser::PrintStmtAst *node) +UnaryOperationAst* AstBuilder::populateUnaryOperationAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitPrintStmt start"; - PrintAst* ast = createAst( node ); - if( node->printArgsSequence->count() > 0 ) - { - int count = node->printArgsSequence->count(); - for( int i = 0; i < count; i++ ) - { - visitNode( node->printArgsSequence->at(i)->element ); - ast->printables.append( safeNodeCast( mNodeStack.pop() ) ); - } - }else - { - visitNode( node->rshiftArgsSequence->at(0)->element ); - ast->outfile = safeNodeCast( mNodeStack.pop() ); - int count = node->rshiftArgsSequence->count(); - for( int i = 1; i < count; i++ ) - { - visitNode( node->printArgsSequence->at(i)->element ); - ast->printables.append( safeNodeCast( mNodeStack.pop() ) ); - } - } - mNodeStack.push( ast ); - kDebug() << "visitPrintStmt end"; + UnaryOperationAst* currentNode = dynamic_cast(ast); + currentNode->operand = resolveNode(currentAttributes.value("NR_operand")); + currentNode->type = resolveUnaryOperator(currentAttributes.value("NR_op")); + return currentNode; } -void AstBuilder::visitProject(PythonParser::ProjectAst *node) +IfExpressionAst* AstBuilder::populateIfExpressionAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitProject start"; - CodeAst* code = new CodeAst(); - setStartEnd( code, node ); - mNodeStack.push( code ); - kDebug() << "Node stack count: " << mNodeStack.count(); - if( node->stmtSequence ) - { - int count = node->stmtSequence->count(); - for( int i = 0; i < count; i++ ) - { - visitNode( node->stmtSequence->at(i)->element ); - Ast* a = mNodeStack.pop(); - if( a ) - code->statements << safeNodeCast( a ); - kDebug() << "Node stack count: " << mNodeStack.count(); - } - } - kDebug() << "Node stack count: " << mNodeStack.count(); - kDebug() << "visitProject end"; + IfExpressionAst* currentNode = dynamic_cast(ast); + currentNode->body = resolveNode(currentAttributes.value("NR_body")); + currentNode->orelse = resolveNode(currentAttributes.value("NR_orelse")); + currentNode->condition = resolveNode(currentAttributes.value("NR_test")); + return currentNode; } -void AstBuilder::visitRaiseStmt(PythonParser::RaiseStmtAst *node) +ListComprehensionAst* AstBuilder::populateListComprehensionAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitRaiseStmt start"; - RaiseAst* ast = createAst( node ); - if( node->type ) - { - visitNode( node->type ); - ast->exceptionType = safeNodeCast( mNodeStack.pop() ); - } - if( node->value ) - { - visitNode( node->value ); - ast->exceptionValue = safeNodeCast( mNodeStack.pop() ); - } - if( node->traceback ) - { - visitNode( node->traceback ); - ast->traceback = safeNodeCast( mNodeStack.pop() ); - } - mNodeStack.push( ast ); - kDebug() << "visitRaiseStmt end"; + ListComprehensionAst* currentNode = dynamic_cast(ast); + currentNode->generators = resolveNodeList(currentAttributes.value("NRLST_generators")); + currentNode->element = resolveNode(currentAttributes.value("NR_elt")); + return currentNode; } -void AstBuilder::visitReturnStmt(PythonParser::ReturnStmtAst *node) +WithAst* AstBuilder::populateWithAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitReturnStmt start"; - ReturnAst* ast = createAst( node ); - visitNode( node->returnExpr ); - ast->returnValues = generateSpecializedList( mListStack.pop() ); - mNodeStack.push( ast ); - kDebug() << "visitReturnStmt end"; + WithAst* currentNode = dynamic_cast(ast); + currentNode->body = resolveNodeList(currentAttributes.value("NRLST_body")); + currentNode->contextExpression = resolveNode(currentAttributes.value("NR_context_expr")); + currentNode->optionalVars = resolveNode(currentAttributes.value("NR_optional_vars")); + return currentNode; } -void AstBuilder::visitShiftExpr(PythonParser::ShiftExprAst *node) +ComprehensionAst* AstBuilder::populateComprehensionAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitShiftExpr start"; - visitNode( node->arithExpr ); - if( node->shiftOpListSequence ) - { - int count = node->shiftOpListSequence->count(); - if( count > 0 ) - { - Q_ASSERT( count == node->arithExprListSequence->count() ); - BinaryExpressionAst* ast = createAst( node ); - ast->lhs = safeNodeCast( mNodeStack.pop() ); - BinaryExpressionAst* cur = ast; - for( int i = 0; i < count; i++ ) - { - switch( node->shiftOpListSequence->at( i )->element->shiftOp ) - { - case PythonParser::LeftShiftOp: - cur->opType = ArithmeticExpressionAst::BinaryLeftShift; - break; - case PythonParser::RightShiftOp: - cur->opType = ArithmeticExpressionAst::BinaryRightShift; - break; - default: - Q_ASSERT_X(false, "visitShiftExpr", "OOOPS, shift operator was something other than left or right shifting!"); - } - visitNode( node->arithExprListSequence->at( i )->element ); - if( i == count - 1 ) - { - cur->rhs = safeNodeCast( mNodeStack.pop() ); - }else - { - cur->rhs = createAst( node->arithExprListSequence->at( i )->element ); - cur = safeNodeCast( cur->rhs ); - cur->lhs = safeNodeCast( mNodeStack.pop() ); - } - } - mNodeStack.push( ast ); - } - } - kDebug() << "visitShiftExpr end"; + ComprehensionAst* currentNode = dynamic_cast(ast); + currentNode->conditions = resolveNodeList(currentAttributes.value("NRLST_ifs")); + currentNode->iterator = resolveNode(currentAttributes.value("NR_iter")); + currentNode->target = resolveNode(currentAttributes.value("NR_target")); + return currentNode; } -void AstBuilder::visitSimpleStmt(PythonParser::SimpleStmtAst *node) +CompareAst* AstBuilder::populateCompareAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitSimpleStmt start"; - PythonParser::DefaultVisitor::visitSimpleStmt( node ); - kDebug() << "visitSimpleStmt end"; + CompareAst* currentNode = dynamic_cast(ast); + currentNode->comparands = resolveNodeList(currentAttributes.value("NRLST_comparators")); + currentNode->operators = resolveComparisonOperatorList(currentAttributes.value("NRLST_ops")); + currentNode->leftmostElement = resolveNode(currentAttributes.value("NR_left")); + return currentNode; } -void AstBuilder::visitSmallStmt(PythonParser::SmallStmtAst *node) +NumberAst* AstBuilder::populateNumberAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitSmallStmt start"; - PythonParser::DefaultVisitor::visitSmallStmt( node ); - kDebug() << "visitSmallStmt end"; + NumberAst* currentNode = dynamic_cast(ast); + currentNode->value = currentAttributes.value("n"); // save this as a QString to aviod problems with python number formats like 3j+2 (complex), 3L, 3.35, etc. + return currentNode; } -void AstBuilder::visitStmt(PythonParser::StmtAst *node) +StringAst* AstBuilder::populateStringAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitStmt start"; - if( node->simpleStmt || node->compoundStmt ) - { - PythonParser::DefaultVisitor::visitStmt( node ); - }else - { - // Pushing a 0 onto the stack so that visitProject and visitSuite can - // test for this case - mNodeStack.push( 0 ); - kDebug() << "Found linebreak"; - } - kDebug() << "visitStmt end"; + StringAst* currentNode = dynamic_cast(ast); + currentNode->value = currentAttributes.value("s"); + return currentNode; } -void AstBuilder::visitSubscript(PythonParser::SubscriptAst *node) +AttributeAst* AstBuilder::populateAttributeAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitSubscript start"; - if( node->isEllipsis || node->hasColon ) - { - if( node->isEllipsis ) - { - EllipsisSliceItemAst* ast = createAst( node ); - mNodeStack.push( ast ); - }else - { - ProperSliceItemAst* ast = createAst( node ); - mNodeStack.push( ast ); - if( node->begin ) - { - visitNode( node->begin ); - ast->bounds.first = safeNodeCast( mNodeStack.pop() ); - } - if( node->end ) - { - visitNode( node->end ); - ast->bounds.second = safeNodeCast( mNodeStack.pop() ); - } - if( node->step ) - { - visitNode( node->step ); - ast->stride = safeNodeCast( mNodeStack.pop() ); - } - } - }else if( node->begin ) - { - visitNode( node->begin ); - } - kDebug() << "visitSubscript end"; + AttributeAst* currentNode = dynamic_cast(ast); + currentNode->value = resolveNode(currentAttributes.value("NR_value")); + currentNode->attribute = createIdentifier(currentAttributes.value("attr"), currentNode); + currentNode->context = resolveContext(currentAttributes.value("NR_ctx")); + return currentNode; } -void AstBuilder::visitSubscriptlist(PythonParser::SubscriptlistAst *node) +SubscriptAst* AstBuilder::populateSubscriptAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitSubscriptlist start"; - - if( node->hasComma ) - { - int count = node->subscriptSequence->count(); - PrimaryAst* curast = createAst( node ); - mNodeStack.push( curast ); - for( int i = 0; i < count; i++ ) - { - visitNode( node->subscriptSequence->at( i )->element ); - if( dynamic_cast( mNodeStack.top() ) == 0 - && curast->astType != Ast::ExtendedSliceAst ) - { - SubscriptAst* sast = safeNodeCast( curast ); - curast = createAst( node ); - ExtendedSliceAst* esast = safeNodeCast( curast ); - for( int j = 0; j < sast->subscription.count(); j++ ) - { - ExpressionSliceItemAst* esiast = createAst( - node->subscriptSequence->at(j)->element ); - esiast->sliceExpression = sast->subscription.at( j ); - esast->extendedSliceList << esiast; - } - delete sast; - } - - if( curast->astType == Ast::ExtendedSliceAst ) - { - if( dynamic_cast( mNodeStack.top() ) != 0 ) - { - ExpressionSliceItemAst* itemast = createAst( - node->subscriptSequence->at(i)->element ); - itemast->sliceExpression = safeNodeCast( mNodeStack.pop() ); - safeNodeCast( curast )->extendedSliceList << itemast; - }else - { - safeNodeCast( curast )->extendedSliceList << - safeNodeCast( mNodeStack.pop() ); - } - }else - { - safeNodeCast( curast )->subscription - << safeNodeCast( mNodeStack.pop() ); - } - } - }else - { - visitNode( node->subscriptSequence->at(0)->element ); - if( dynamic_cast( mNodeStack.top() ) ) - { - SubscriptAst* ast = createAst( node ); - ast->subscription << safeNodeCast( mNodeStack.pop() ); - mNodeStack.push( ast ); - }else - { - SimpleSliceAst* ast = createAst( node ); - ProperSliceItemAst* extslice = safeNodeCast( mNodeStack.pop() ); - ast->simpleSliceBounds.first = extslice->bounds.first; - ast->simpleSliceBounds.second = extslice->bounds.second; - delete extslice; - mNodeStack.push( ast ); - } - } - - kDebug() << "visitSubscriptlist end"; + SubscriptAst* currentNode = dynamic_cast(ast); + currentNode->context = resolveContext("NR_ctx"); + currentNode->slice = resolveNode(currentAttributes.value("NR_slice")); + currentNode->value = resolveNode(currentAttributes.value("NR_value")); + return currentNode; } -void AstBuilder::visitSuite(PythonParser::SuiteAst *node) +SliceAst* AstBuilder::populateSliceAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitSuite start"; - QList l; - if( node->simpleStmt ) - { - visitNode( node->simpleStmt ); - l << mNodeStack.pop(); - } else - { - int count = node->stmtSequence->count(); - for( int i = 0; i < count; i++ ) - { - visitNode( node->stmtSequence->at(i)->element ); - Ast* a = mNodeStack.pop(); - if( a ) - l << a; - } - } - mListStack.push( l ); - kDebug() << "visitSuite end"; + SliceAst* currentNode = dynamic_cast(ast); + currentNode->lower = resolveNode(currentAttributes.value("NR_lower")); + currentNode->upper = resolveNode(currentAttributes.value("NR_upper")); + currentNode->step = resolveNode(currentAttributes.value("NR_step")); + return currentNode; } -void AstBuilder::visitTerm(PythonParser::TermAst *node) +ArgumentsAst* AstBuilder::populateArgumentsAst(Ast* ast, const Python::stringDictionary& currentAttributes) { - kDebug() << "visitTerm start"; - visitNode( node->factor ); - if( node->factorsSequence ) - { - int count = node->factorsSequence->count(); - if( count > 0 ) - { - Q_ASSERT( count == node->termOpSequence->count() ); - BinaryExpressionAst* curast = createAst( node ); - curast->lhs = safeNodeCast( mNodeStack.pop() ); - // put the binary expression onto the stack now, so its still on the stack - // after the loop finishes - mNodeStack.push( curast ); - for( int i = 0; i < count; i++ ) - { - //Push current bin-expr on stack to be used as parent - mNodeStack.push( curast ); - visitNode( node->factorsSequence->at(i)->element ); - if( i == count-1 ) - { - curast->rhs = safeNodeCast( mNodeStack.pop() ); - }else - { - curast->rhs = createAst( node ); - switch( node->termOpSequence->at(i)->element->op ) - { - case PythonParser::StarOp: - curast->opType = ArithmeticExpressionAst::BinaryMultiply; - break; - case PythonParser::ModuloOp: - curast->opType = ArithmeticExpressionAst::BinaryModulo; - break; - case PythonParser::SlashOp: - curast->opType = ArithmeticExpressionAst::BinaryDivide; - break; - case PythonParser::DoubleSlashOp: - curast->opType = ArithmeticExpressionAst::BinaryFloor; - break; - default: - Q_ASSERT_X( false, "visitTerm", "OOPS, termop has an unknown value" ); - } - curast->lhs = safeNodeCast( mNodeStack.pop() ); - curast = safeNodeCast( curast->rhs ); + ArgumentsAst* currentNode = dynamic_cast(ast); + currentNode->arguments = resolveNodeList(currentAttributes.value("NRLST_args")); + currentNode->defaultValues = resolveNodeList(currentAttributes.value("NRLST_defaults")); + currentNode->kwarg = createIdentifier(currentAttributes.value("kwarg"), currentNode); + currentNode->vararg = createIdentifier(currentAttributes.value("paramstar"), currentNode); + return currentNode; +} + +ExceptionHandlerAst* AstBuilder::populateExceptionHandlerAst(Ast* ast, const Python::stringDictionary& currentAttributes) +{ + ExceptionHandlerAst* currentNode = dynamic_cast(ast); + currentNode->body = resolveNodeList(currentAttributes.value("NRLST_body")); + currentNode->name = resolveNode(currentAttributes.value("NR_name")); + currentNode->type = resolveNode(currentAttributes.value("NR_type")); + return currentNode; +} + +IndexAst* AstBuilder::populateIndexAst(Ast* ast, const Python::stringDictionary& currentAttributes) +{ + IndexAst* currentNode = dynamic_cast(ast); + currentNode->value = resolveNode(currentAttributes.value("NR_value")); + return currentNode; +} + +KeywordAst* AstBuilder::populateKeywordAst(Ast* ast, const Python::stringDictionary& currentAttributes) +{ + KeywordAst* currentNode = dynamic_cast(ast); + currentNode->argumentName = createIdentifier(currentAttributes.value("arg"), currentNode); + currentNode->value = resolveNode(currentAttributes.value("NR_value")); + return currentNode; +} + +ExpressionAst* AstBuilder::populateExpressionAst(Ast* ast, const stringDictionary& currentAttributes) +{ + ExpressionAst* currentNode = dynamic_cast(ast); + currentNode->value = resolveNode(currentAttributes.value("NR_value")); + return currentNode; +} + +void AstBuilder::populateAst() +{ + Ast* currentAbstractNode; + stringDictionary currentAttributes; + QMapIterator i(m_nodeMap); + while ( i.hasNext() ) { + i.next(); + currentAbstractNode = i.value(); + currentAttributes = m_attributeStore.value(i.key()); + +// kDebug() << "Processing AST node ID " << i.key(); +// kDebug() << "Amount of attributes: " << currentAttributes.size(); + + stringDictionary::const_iterator i = currentAttributes.begin(); +// while ( i != currentAttributes.end() ) { +// kDebug() << i.key() << i.value(); +// ++i; +// } + if ( currentAttributes.value("lineno").length() > 0 && currentAttributes.value("col_offset").length() > 0 ) + currentAbstractNode->hasUsefulRangeInformation = true; + else currentAbstractNode->hasUsefulRangeInformation = false; + + int startLine = currentAttributes.value("lineno").toInt() - 1; // start = 0 <> start = 1 + currentAbstractNode->startLine = startLine; + currentAbstractNode->endLine = startLine; + int startCol = currentAttributes.value("col_offset").toInt(); + currentAbstractNode->startCol = startCol; + currentAbstractNode->endCol = startCol; // this is justified if necessary (only an AST with an actual value or identifier will know the true range) + + switch ( currentAbstractNode->astType ) { + case Ast::CodeAstType: currentAbstractNode = populateCodeAst(currentAbstractNode, currentAttributes); break; + case Ast::FunctionDefinitionAstType: currentAbstractNode = populateFunctionDefinitionAst(currentAbstractNode, currentAttributes); break; + case Ast::ClassDefinitionAstType: currentAbstractNode = populateClassDefinitonAst(currentAbstractNode, currentAttributes); break; + case Ast::ReturnAstType: currentAbstractNode = populateReturnAst(currentAbstractNode, currentAttributes); break; + case Ast::DeleteAstType: currentAbstractNode = populateDeleteAst(currentAbstractNode, currentAttributes); break; + case Ast::AssignmentAstType: currentAbstractNode = populateAssignmentAst(currentAbstractNode, currentAttributes); break; + case Ast::AugmentedAssignmentAstType: currentAbstractNode = populateAugmentedAssignmentAst(currentAbstractNode, currentAttributes); break; + case Ast::ForAstType: currentAbstractNode = populateForAst(currentAbstractNode, currentAttributes); break; + case Ast::WhileAstType: currentAbstractNode = populateWhileAst(currentAbstractNode, currentAttributes); break; + case Ast::IfAstType: currentAbstractNode = populateIfAst(currentAbstractNode, currentAttributes); break; + case Ast::WithAstType: currentAbstractNode = populateWithAst(currentAbstractNode, currentAttributes); break; + case Ast::RaiseAstType: currentAbstractNode = populateRaiseAst(currentAbstractNode, currentAttributes); break; + case Ast::TryExceptAstType: currentAbstractNode = populateTryExceptAst(currentAbstractNode, currentAttributes); break; + case Ast::TryFinallyAstType: currentAbstractNode = populateTryFinallyAst(currentAbstractNode, currentAttributes); break; + case Ast::AssertionAstType: currentAbstractNode = populateAssertionAst(currentAbstractNode, currentAttributes); break; + case Ast::ImportAstType: currentAbstractNode = populateImportAst(currentAbstractNode, currentAttributes); break; + case Ast::ImportFromAstType: currentAbstractNode = populateImportFromAst(currentAbstractNode, currentAttributes); break; +// case Ast::ExecAstType: break; // TODO support this? or better not? :] + case Ast::GlobalAstType: currentAbstractNode = populateGlobalAst(currentAbstractNode, currentAttributes); break; + case Ast::BreakAstType: break; // ok + case Ast::ContinueAstType: break; // ok + case Ast::PrintAstType: currentAbstractNode = populatePrintAst(currentAbstractNode, currentAttributes); break; + case Ast::PassAstType: break; // ok + case Ast::BooleanOperationAstType: currentAbstractNode = populateBooleanOperationAst(currentAbstractNode, currentAttributes); break; + case Ast::BinaryOperationAstType: currentAbstractNode = populateBinaryOperationAst(currentAbstractNode, currentAttributes); break; + case Ast::UnaryOperationAstType: currentAbstractNode = populateUnaryOperationAst(currentAbstractNode, currentAttributes); break; + case Ast::LambdaAstType: currentAbstractNode = populateLambdaAst(currentAbstractNode, currentAttributes); break; + case Ast::IfExpressionAstType: currentAbstractNode = populateIfExpressionAst(currentAbstractNode, currentAttributes); break; + case Ast::DictAstType: currentAbstractNode = populateDictAst(currentAbstractNode, currentAttributes); break; +// case Ast::SetAstType: break; // TODO support this (read about sets) + case Ast::ListComprehensionAstType: currentAbstractNode = populateListComprehensionAst(currentAbstractNode, currentAttributes); break; +// case Ast::SetComprehensionAstType: break; // TODO support this +// case Ast::DictionaryComprehensionAstType: break; // TODO fix this for python 2.7+ +// case Ast::GeneratorExpressionAstType: break; // TODO read about this + case Ast::CompareAstType: currentAbstractNode = populateCompareAst(currentAbstractNode, currentAttributes); break; +// case Ast::ReprAstType: break; // TODO support this + case Ast::NumberAstType: currentAbstractNode = populateNumberAst(currentAbstractNode, currentAttributes); break; + case Ast::StringAstType: currentAbstractNode = populateStringAst(currentAbstractNode, currentAttributes); break; +// case Ast::YieldAstType: break; // TODO TODO + case Ast::NameAstType: currentAbstractNode = populateNameAst(currentAbstractNode, currentAttributes); break; + case Ast::CallAstType: currentAbstractNode = populateCallAst(currentAbstractNode, currentAttributes); break; + case Ast::AttributeAstType: currentAbstractNode = populateAttributeAst(currentAbstractNode, currentAttributes); break; + case Ast::SubscriptAstType: currentAbstractNode = populateSubscriptAst(currentAbstractNode, currentAttributes); break; + case Ast::ListAstType: currentAbstractNode = populateListAst(currentAbstractNode, currentAttributes); break; + case Ast::TupleAstType: currentAbstractNode = populateTupleAst(currentAbstractNode, currentAttributes); break; +// case Ast::EllipsisAstType: break; // TODO TODO + case Ast::SliceAstType: currentAbstractNode = populateSliceAst(currentAbstractNode, currentAttributes); break; +// case Ast::ExtendedSliceAstType: break; // TODO TODO + case Ast::IndexAstType: currentAbstractNode = populateIndexAst(currentAbstractNode, currentAttributes); break; + case Ast::ArgumentsAstType: currentAbstractNode = populateArgumentsAst(currentAbstractNode, currentAttributes); break; + case Ast::KeywordAstType: currentAbstractNode = populateKeywordAst(currentAbstractNode, currentAttributes); break; + case Ast::ComprehensionAstType: currentAbstractNode = populateComprehensionAst(currentAbstractNode, currentAttributes); break; + case Ast::ExceptionHandlerAstType: currentAbstractNode = populateExceptionHandlerAst(currentAbstractNode, currentAttributes); break; + case Ast::AliasAstType: currentAbstractNode = populateAliasAst(currentAbstractNode, currentAttributes); break; + case Ast::ExpressionAstType: currentAbstractNode = populateExpressionAst(currentAbstractNode, currentAttributes); break; + case Ast::StatementAstType: break; // ok + default: kWarning() << "Unsupported AST type: " << currentAbstractNode->astType; break; + } + + // Walk through the tree and set proper end columns and lines, as the python parser sadly does not do this for us + if ( currentAbstractNode->hasUsefulRangeInformation ) { + Ast* parent = currentAbstractNode->parent; + while ( parent ) { + if ( parent->endLine < currentAbstractNode->endLine ) { + parent->endLine = currentAbstractNode->endLine; + parent->endCol = currentAbstractNode->endCol; } - //pop parent from stack - mNodeStack.pop(); - } - } - } - kDebug() << "visitTerm end"; -} - -void AstBuilder::visitTest(PythonParser::TestAst *node) -{ - kDebug() << "visitTest start"; - if( node->lambdaDef ) - { - visitNode( node->lambdaDef ); - }else - { - visitNode( node->andTestSequence->at(0)->element ); - if( node->andTestSequence->count() > 1 ) - { - BooleanOrOperationAst* ast = createAst( node ); - ast->lhs = safeNodeCast( mNodeStack.pop() ); - int count = node->andTestSequence->count(); - mNodeStack.push( ast ); - for( int i = 1; i < count; i++ ) - { - visitNode( node->andTestSequence->at(i)->element ); - if( i+1 < count ) - { - BooleanOrOperationAst* tmp = createAst( - node->andTestSequence->at(i)->element ); - tmp->lhs = safeNodeCast( mNodeStack.pop() ); - ast->rhs = tmp; - ast = tmp; - }else - { - ast->rhs = safeNodeCast( mNodeStack.pop() ); + if ( ! parent->hasUsefulRangeInformation && parent->startLine == -5 ) { + parent->startLine = currentAbstractNode->startLine; + parent->startCol = currentAbstractNode->startCol; } + parent = parent->parent; } } } - kDebug() << "visitTest end"; -} - -void AstBuilder::visitTestlist(PythonParser::TestlistAst *node) -{ - kDebug() << "visitTestlist start"; - QList expressions; - int count = node->testsSequence->count(); - for( int i = 0; i < count; i++ ) - { - visitNode( node->testsSequence->at( i )->element ); - expressions << safeNodeCast( mNodeStack.pop() ); - } - mListStack.push( expressions ); - kDebug() << "visitTestlist end"; -} - -void AstBuilder::visitCodeexpr(PythonParser::CodeexprAst *node) -{ - kDebug() << "visitCodeexpr start"; - QList l; - int count = node->testSequence->count(); - for( int i = 0; i < count; i++ ) - { - visitNode( node->testSequence->at(i)->element ); - l << safeNodeCast( mNodeStack.pop() ); - } - mListStack.push( l ); - kDebug() << "visitCodeexpr end"; -} - -void AstBuilder::visitTestlistSafe(PythonParser::TestlistSafeAst *node) -{ - kDebug() << "visitTestlistSafe start"; - QList expressions; - int count = node->testSequence->count(); - for( int i = 0; i < count; i++ ) - { - visitNode( node->testSequence->at( i )->element ); - expressions << safeNodeCast( mNodeStack.pop() ); - } - mListStack.push( expressions ); - kDebug() << "visitTestlistSafe end"; -} - -void AstBuilder::visitTrailer(PythonParser::TrailerAst *node) -{ - kDebug() << "visitTrailer start"; - if( node->trailerArglist ) - { - CallAst* ast = createAst( node ); - visitNode( node->trailerArglist ); - QList tmp = mListStack.pop(); - if( tmp.count() == 1 && tmp.at( 0 )->astType == Ast::GeneratorAst ) - { - //generator in the call - ast->generator = safeNodeCast( tmp.at( 0 ) ); - }else - { - ast->arguments = generateSpecializedList( tmp ); - } - mNodeStack.push( ast ); - }else if( node->trailerDotName ) - { - AttributeReferenceAst* ast = createAst( node ); - ast->identifier = createIdentifier( ast, node->trailerDotName ); - mNodeStack.push( ast ); - } - kDebug() << "visitTrailer end"; -} - -void AstBuilder::visitTryStmt(PythonParser::TryStmtAst *node) -{ - kDebug() << "visitTryStmt start"; - TryAst* ast = createAst( node ); - visitNode( node->trySuite ); - ast->tryBody = generateSpecializedList( mListStack.pop() ); - if( node->finallySuite ) - { - ast->finallyBody = generateSpecializedList( mListStack.pop() ); - }else - { - int count = node->exceptClauseSequence->count(); - for( int i = 1; i < count; i++ ) - { - visitNode( node->exceptClauseSequence->at(i)->element ); - ExceptAst* ex = safeNodeCast( mNodeStack.pop() ); - visitNode( node->exceptSuiteSequence->at(i)->element ); - ex->exceptionBody = generateSpecializedList( mListStack.pop() ); - ast->exceptions.append( ex ); - } - if( node->tryElseSuite ) - { - visitNode( node->tryElseSuite ); - ast->elseBody = generateSpecializedList( mListStack.pop() ); - } - } - mNodeStack.push( ast ); - kDebug() << "visitTryStmt end"; -} - -void AstBuilder::visitVarargslist(PythonParser::VarargslistAst *node) -{ - kDebug() << "visitVarargslist start"; - QList l; - if( node->funcDef ) - { - visitNode( node->funcDef ); - l += mListStack.pop(); - } - if( node->funPosParam ) - { - if( node->funPosParam->listParam ) - { - ListParameterAst* ast = createAst( node->funPosParam->listParam ); - ast->name = createIdentifier( ast, node->funPosParam->listParam->starId ); - l << ast; - } - if( node->funPosParam->dictParam ) - { - DictionaryParameterAst* ast = createAst( node->funPosParam->dictParam ); - ast->name = createIdentifier( ast, node->funPosParam->dictParam->doubleStarId ); - l << ast; - } - } - mListStack.push( l ); - kDebug() << "visitVarargslist end"; -} - -void AstBuilder::visitWhileStmt(PythonParser::WhileStmtAst *node) -{ - kDebug() << "visitWhileStmt start"; - WhileAst* ast = createAst( node ); - visitNode( node->whileTest ); - ast->condition = safeNodeCast( mNodeStack.pop() ); - visitNode( node->whileSuite ); - ast->whileBody = generateSpecializedList( mListStack.pop() );; - if( node->whileElseSuite ) - { - visitNode( node->whileElseSuite ); - ast->elseBody = generateSpecializedList( mListStack.pop() ); - } - mNodeStack.push( ast ); - kDebug() << "visitWhileStmt end"; -} - -void AstBuilder::visitXorExpr(PythonParser::XorExprAst *node) -{ - kDebug() << "visitXorExpr start"; - visitNode( node->xorExpr ); - if( node->hatXorExprSequence && node->hatXorExprSequence->count() > 0 ) - { - BinaryExpressionAst* curast = createAst( node ); - mNodeStack.push( curast ); - int count = node->hatXorExprSequence->count(); - for( int i = 0; i < count; i++ ) - { - visitNode( node->hatXorExprSequence->at(i)->element ); - if( i == count - 1 ) - { - curast->rhs = safeNodeCast( mNodeStack.pop() ); - }else - { - BinaryExpressionAst* bin = createAst( node->hatXorExprSequence->at(i)->element ); - bin->lhs = safeNodeCast( mNodeStack.pop() ); - curast->rhs = bin; - curast = bin; - } - } - } - kDebug() << "visitXorExpr end"; -} - -void AstBuilder::visitYieldExpr( PythonParser::YieldExprAst * node ) -{ - kDebug() << "visitYieldExpr start"; - YieldAst* ast = createAst( node ); - visitNode( node->expr ); - ast->yieldValue = generateSpecializedList( mListStack.pop() ); - mNodeStack.push( ast ); - kDebug() << "visitYieldExpr end"; } - -void AstBuilder::visitYieldStmt(PythonParser::YieldStmtAst *node) -{ - kDebug() << "visitYieldStmt start"; - visitNode( node->yield ); - kDebug() << "visitYieldStmt end"; -} - -CodeAst* AstBuilder::codeAst() -{ - return safeNodeCast( mNodeStack.top() ); -} - - + } diff --git a/parser/astbuilder.h b/parser/astbuilder.h index 827e9c2..478d444 100644 --- a/parser/astbuilder.h +++ b/parser/astbuilder.h @@ -23,10 +23,12 @@ #include -#include - -#include "pythondefaultvisitor.h" #include "ast.h" +#include +#include +#include "kdebug.h" +#include "QXmlStreamReader" +#include namespace PythonParser { @@ -39,111 +41,104 @@ namespace Python class Ast; class CodeAst; +typedef QMap stringDictionary; -class AstBuilder : public PythonParser::DefaultVisitor +class AstBuilder { + public: - AstBuilder(PythonParser::Parser*); - CodeAst* codeAst(); - - virtual void visitAndExpr(PythonParser::AndExprAst *node); - virtual void visitAndTest(PythonParser::AndTestAst *node); - virtual void visitArglist(PythonParser::ArglistAst *node); - virtual void visitArgument(PythonParser::ArgumentAst *node); - virtual void visitArithExpr(PythonParser::ArithExprAst *node); - virtual void visitAssertStmt(PythonParser::AssertStmtAst *node); - virtual void visitAtom(PythonParser::AtomAst *node); - virtual void visitBreakStmt(PythonParser::BreakStmtAst *node); - virtual void visitClassdef(PythonParser::ClassdefAst *node); - virtual void visitCodeexpr(PythonParser::CodeexprAst *node); - virtual void visitComparison(PythonParser::ComparisonAst *node); - virtual void visitCompoundStmt(PythonParser::CompoundStmtAst *node); - virtual void visitContinueStmt(PythonParser::ContinueStmtAst *node); - virtual void visitDottedName(PythonParser::DottedNameAst *node); - virtual void visitDecorator(PythonParser::DecoratorAst *node); - virtual void visitDecorators(PythonParser::DecoratorsAst *node); - virtual void visitDefparam(PythonParser::DefparamAst *node); - virtual void visitDelStmt(PythonParser::DelStmtAst *node); - virtual void visitDictmaker(PythonParser::DictmakerAst *node); - virtual void visitExceptClause(PythonParser::ExceptClauseAst *node); - virtual void visitExecStmt(PythonParser::ExecStmtAst *node); - virtual void visitExpr(PythonParser::ExprAst *node); - virtual void visitExprStmt(PythonParser::ExprStmtAst *node); - virtual void visitExprlist(PythonParser::ExprlistAst *node); - virtual void visitFactor(PythonParser::FactorAst *node); - virtual void visitFlowStmt(PythonParser::FlowStmtAst *node); - virtual void visitForStmt(PythonParser::ForStmtAst *node); - virtual void visitFpDef(PythonParser::FpDefAst *node); - virtual void visitFplist(PythonParser::FplistAst *node); - virtual void visitFuncdecl(PythonParser::FuncdeclAst *node); - virtual void visitFuncDef(PythonParser::FuncDefAst *node); - virtual void visitGenFor(PythonParser::GenForAst *node); - virtual void visitGenIf(PythonParser::GenIfAst *node); - virtual void visitGenIter(PythonParser::GenIterAst *node); - virtual void visitGlobalStmt(PythonParser::GlobalStmtAst *node); - virtual void visitIfStmt(PythonParser::IfStmtAst *node); - virtual void visitImportFrom(PythonParser::ImportFromAst *node); - virtual void visitImportName(PythonParser::ImportNameAst *node); - virtual void visitImportStmt(PythonParser::ImportStmtAst *node); - virtual void visitLambdaDef(PythonParser::LambdaDefAst *node); - virtual void visitListFor(PythonParser::ListForAst *node); - virtual void visitListIf(PythonParser::ListIfAst *node); - virtual void visitListIter(PythonParser::ListIterAst *node); - virtual void visitListmaker(PythonParser::ListmakerAst *node); - virtual void visitListMakerTest(PythonParser::ListMakerTestAst *node); - virtual void visitNotTest(PythonParser::NotTestAst *node); - virtual void visitNumber(PythonParser::NumberAst *node); - virtual void visitPassStmt(PythonParser::PassStmtAst *node); - virtual void visitPlainArgumentsList(PythonParser::PlainArgumentsListAst *node); - virtual void visitPower(PythonParser::PowerAst *node); - virtual void visitPrintStmt(PythonParser::PrintStmtAst *node); - virtual void visitProject(PythonParser::ProjectAst *node); - virtual void visitRaiseStmt(PythonParser::RaiseStmtAst *node); - virtual void visitReturnStmt(PythonParser::ReturnStmtAst *node); - virtual void visitShiftExpr(PythonParser::ShiftExprAst *node); - virtual void visitSimpleStmt(PythonParser::SimpleStmtAst *node); - virtual void visitSmallStmt(PythonParser::SmallStmtAst *node); - virtual void visitStmt(PythonParser::StmtAst *node); - virtual void visitSubscript(PythonParser::SubscriptAst *node); - virtual void visitSubscriptlist(PythonParser::SubscriptlistAst *node); - virtual void visitSuite(PythonParser::SuiteAst *node); - virtual void visitTerm(PythonParser::TermAst *node); - virtual void visitTest(PythonParser::TestAst *node); - virtual void visitTestlist(PythonParser::TestlistAst *node); - virtual void visitTestlistSafe(PythonParser::TestlistSafeAst *node); - virtual void visitTrailer(PythonParser::TrailerAst *node); - virtual void visitTryStmt(PythonParser::TryStmtAst *node); - virtual void visitVarargslist(PythonParser::VarargslistAst *node); - virtual void visitWhileStmt(PythonParser::WhileStmtAst *node); - virtual void visitXorExpr(PythonParser::XorExprAst *node); - virtual void visitYieldStmt(PythonParser::YieldStmtAst *node); - virtual void visitYieldExpr(PythonParser::YieldExprAst *node); - - QStack mNodeStack; + CodeAst* parse(KUrl filename, const QString& contents); + QList m_problems; private: - QStack > mListStack; - PythonParser::Parser* parser; - void setStartEnd( Ast* ast, PythonParser::AstNode* node ); - QString tokenText( qint64 tokenidx ); - - template T* createAst( PythonParser::AstNode* node, Ast::AstType t ) - { - T* ast = new T( mNodeStack.top(), t ); - setStartEnd( ast, node ); - return ast; - } - - template T* createAst( PythonParser::AstNode* node ) - { - T* ast = new T( mNodeStack.top() ); - setStartEnd( ast, node ); - return ast; - } - QList identifierListFromTokenList( Ast* parent, const KDevPG::ListNode* sequence ); - IdentifierAst* createIdentifier( Ast* parent, qint64 idx ); + CodeAst* parseXmlAst(QString xml); + QString getXmlForFile(KUrl filename, const QString& contents); + void parseXmlAstNode(QXmlStreamReader* xmlast, QXmlStreamReader::TokenType token); + bool parseAstNode(QString name, /*QString text, */const QList& attributes); + + KDevelop::TopDUContext* m_topContext; + + QList m_nodeStack; + + // one map for regular ast nodes, and the others for primitive nodes of different types + QMap m_nodeMap; + QList m_isRealNodeMap; + + QMap m_contextNodeMap; + QMap m_boolOpNodeMap; + QMap m_compOpNodeMap; + QMap m_opNodeMap; + QMap m_unaryOpNodeMap; + + QStack m_astStack; + QMap m_attributeStore; + Ast* m_currentNode; + + Ast::BooleanOperationTypes resolveBooleanOperator(const QString& identifier); + Ast::ComparisonOperatorTypes resolveComparisonOperator(const QString& identifier); + Ast::OperatorTypes resolveOperator(const QString& identifier); + Ast::UnaryOperatorTypes resolveUnaryOperator(const QString& identifier); + ExpressionAst::Context resolveContext(const QString& identifier); + + QList resolveBooleanOperatorList(const QString& identifiers); + QList resolveComparisonOperatorList(const QString& identifiers); + QList resolveOperatorList(const QString& identifiers); + QList resolveUnaryOperatorList(const QString& identifiers); + QList resolveContextList(const QString& identifiers); + + void populateAst(); + + template QList resolveNodeList(const QString& commaSeperatedIdentifiers); + template T* resolveNode(const QString& identifier); + + Identifier* createIdentifier(const QString& name, Ast* range); + + FunctionDefinitionAst* populateFunctionDefinitionAst(Ast* ast, const stringDictionary& currentAttributes); + AssignmentAst* populateAssignmentAst(Ast* ast, const stringDictionary& currentAttributes); + CodeAst* populateCodeAst(Ast* ast, const stringDictionary& currentAttributes); + ClassDefinitionAst* populateClassDefinitonAst(Ast* ast, const stringDictionary& currentAttributes); + NameAst* populateNameAst(Ast* ast, const stringDictionary& currentAttributes); + ReturnAst* populateReturnAst(Ast* ast, const stringDictionary& currentAttributes); + DeleteAst* populateDeleteAst(Ast* ast, const stringDictionary& currentAttributes); + ForAst* populateForAst(Ast* ast, const stringDictionary& currentAttributes); + WhileAst* populateWhileAst(Ast* ast, const stringDictionary& currentAttributes); + PrintAst* populatePrintAst(Ast* ast, const stringDictionary& currentAttributes); + IfAst* populateIfAst(Ast* ast, const stringDictionary& currentAttributes); + LambdaAst* populateLambdaAst(Ast* ast, const stringDictionary& currentAttributes); + BooleanOperationAst* populateBooleanOperationAst(Ast* ast, const stringDictionary& currentAttributes); + CallAst* populateCallAst(Ast* ast, const stringDictionary& currentAttributes); + DictAst* populateDictAst(Ast* ast, const stringDictionary& currentAttributes); + ListAst* populateListAst(Ast* ast, const stringDictionary& currentAttributes); + TupleAst* populateTupleAst(Ast* ast, const stringDictionary& currentAttributes); + AugmentedAssignmentAst* populateAugmentedAssignmentAst(Ast* ast, const stringDictionary& currentAttributes); + WithAst* populateWithAst(Ast* ast, const stringDictionary& currentAttributes); + RaiseAst* populateRaiseAst(Ast* ast, const stringDictionary& currentAttributes); + TryExceptAst* populateTryExceptAst(Ast* ast, const stringDictionary& currentAttributes); + TryFinallyAst* populateTryFinallyAst(Ast* ast, const stringDictionary& currentAttributes); + AssertionAst* populateAssertionAst(Ast* ast, const stringDictionary& currentAttributes); + ImportAst* populateImportAst(Ast* ast, const stringDictionary& currentAttributes); + ImportFromAst* populateImportFromAst(Ast* ast, const stringDictionary& currentAttributes); + ExecAst* populateExecAst(Ast* ast, const stringDictionary& currentAttributes); + GlobalAst* populateGlobalAst(Ast* ast, const stringDictionary& currentAttributes); + BinaryOperationAst* populateBinaryOperationAst(Ast* ast, const stringDictionary& currentAttributes); + AliasAst* populateAliasAst(Ast* ast, const stringDictionary& currentAttributes); + UnaryOperationAst* populateUnaryOperationAst(Ast* ast, const stringDictionary& currentAttributes); + IfExpressionAst* populateIfExpressionAst(Ast* ast, const stringDictionary& currentAttributes); + ListComprehensionAst* populateListComprehensionAst(Ast* ast, const stringDictionary& currentAttributes); + GeneratorExpressionAst* populateGeneratorExpressionAst(Ast* ast, const stringDictionary& currentAttributes); + ComprehensionAst* populateComprehensionAst(Ast* ast, const stringDictionary& currentAttributes); + CompareAst* populateCompareAst(Ast* ast, const stringDictionary& currentAttributes); + NumberAst* populateNumberAst(Ast* ast, const stringDictionary& currentAttributes); + StringAst* populateStringAst(Ast* ast, const stringDictionary& currentAttributes); + AttributeAst* populateAttributeAst(Ast* ast, const stringDictionary& currentAttributes); + SubscriptAst* populateSubscriptAst(Ast* ast, const stringDictionary& currentAttributes); + SliceAst* populateSliceAst(Ast* ast, const stringDictionary& currentAttributes); + KeywordAst* populateKeywordAst(Ast* ast, const stringDictionary& currentAttributes); + ArgumentsAst* populateArgumentsAst(Ast* ast, const stringDictionary& currentAttributes); + IndexAst* populateIndexAst(Ast* ast, const stringDictionary& currentAttributes); + ExceptionHandlerAst* populateExceptionHandlerAst(Ast* ast, const stringDictionary& currentAttributes); + ExpressionAst* populateExpressionAst(Ast* ast, const stringDictionary& currentAttributes); }; } -#endif - +#endif \ No newline at end of file diff --git a/parser/astdefaultvisitor.cpp b/parser/astdefaultvisitor.cpp index 03c4840..97bba97 100644 --- a/parser/astdefaultvisitor.cpp +++ b/parser/astdefaultvisitor.cpp @@ -1,6 +1,7 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * + * Copyright 2010 Sven Brauch * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * @@ -19,610 +20,371 @@ ***************************************************************************/ #include "astdefaultvisitor.h" +#include "ast.h" +#include namespace Python { -// TODO: Check which visitXX need a visitNode( node->someIdentifierAst ); - -AstDefaultVisitor::AstDefaultVisitor() - : AstVisitor() -{ -} - -AstDefaultVisitor::~AstDefaultVisitor() -{ -} - -void AstDefaultVisitor::visitCode( CodeAst* code ) -{ - foreach( StatementAst* stmt, code->statements ) - { - visitNode( stmt ); - } -} +AstDefaultVisitor::AstDefaultVisitor() { } +AstDefaultVisitor::~AstDefaultVisitor() { } -void AstDefaultVisitor::visitFunctionDefinition( FunctionDefinitionAst* node ) -{ - foreach( DecoratorAst* ast, node->decorators ) - { - visitNode( ast ); - } - - visitNode( node->functionName ); - - foreach( ParameterAst* ast, node->parameters ) - { - visitNode( ast ); - } - - foreach( StatementAst* ast, node->functionBody ) - { - visitNode( ast ); - } -} +// The Ast "ends" here, those dont have child nodes +// note that Identifier is not a node in this Ast +void AstDefaultVisitor::visitName(NameAst* node) { Q_UNUSED(node); } +void AstDefaultVisitor::visitPass(PassAst* node) { Q_UNUSED(node); } +void AstDefaultVisitor::visitAlias(AliasAst* node) { Q_UNUSED(node); } +void AstDefaultVisitor::visitBreak(BreakAst* node) { Q_UNUSED(node); } +void AstDefaultVisitor::visitContinue(ContinueAst* node) { Q_UNUSED(node); } +void AstDefaultVisitor::visitEllipsis(EllipsisAst* node) { Q_UNUSED(node); } +void AstDefaultVisitor::visitGlobal(GlobalAst* node) { Q_UNUSED(node); } +void AstDefaultVisitor::visitNumber(NumberAst* node) { Q_UNUSED(node); } +void AstDefaultVisitor::visitString(StringAst* node) { Q_UNUSED(node); } -void AstDefaultVisitor::visitDecorator( DecoratorAst* node ) +void AstDefaultVisitor::visitCode(CodeAst* node) { - foreach( IdentifierAst* a, node->dottedName ) - { - visitNode( a ); - } - foreach( ArgumentAst* a, node->arguments ) - { - visitNode( a ); + kDebug() << "Visiting code"; + foreach (Ast* statement, node->body) { + visitNode(statement); } } -void AstDefaultVisitor::visitArgument( ArgumentAst* node ) -{ - visitNode( node->keywordName ); - visitNode( node->argumentExpression ); -} - -void AstDefaultVisitor::visitDefaultParameter( DefaultParameterAst* node ) +void AstDefaultVisitor::visitExpression(ExpressionAst* node) { - visitNode( node->name ); - visitNode( node->value ); + visitNode(node->value); } -void AstDefaultVisitor::visitIdentifierParameterPart( IdentifierParameterPartAst* node ) +void AstDefaultVisitor::visitAssertion(AssertionAst* node) { - visitNode( node->name ); + visitNode(node->condition); + visitNode(node->message); } -void AstDefaultVisitor::visitListParameterPart( ListParameterPartAst* node ) +void AstDefaultVisitor::visitDelete(DeleteAst* node) { - foreach( ParameterPartAst* a, node->parameternames ) - { - visitNode( a ); + foreach (ExpressionAst* expression, node->targets) { + visitNode(expression); } } -void AstDefaultVisitor::visitDictionaryParameter( DictionaryParameterAst* node ) +void AstDefaultVisitor::visitExec(ExecAst* node) { - visitNode( node->name ); + visitNode(node->body); + visitNode(node->globals); + visitNode(node->locals); } -void AstDefaultVisitor::visitListParameter( ListParameterAst* node ) +void AstDefaultVisitor::visitExtendedSlice(ExtendedSliceAst* node) { - visitNode( node->name ); -} - -void AstDefaultVisitor::visitIf( IfAst* node ) -{ - visitNode( node->ifCondition ); - foreach( StatementAst* a, node->ifBody ) - { - visitNode( a ); - } - QList > >::const_iterator it = - node->elseIfBodies.begin(); - QList > >::const_iterator end = - node->elseIfBodies.end(); - for( ; it != end; ++it ) - { - QPair > p = *it; - visitNode( p.first ); - foreach( StatementAst* a, p.second ) - { - visitNode( a ); - } - } - foreach( StatementAst* a, node->elseBody ) - { - visitNode( a ); + foreach (SliceAst* slice, node->dims) { + visitNode(slice); } } -void AstDefaultVisitor::visitWhile( WhileAst* node ) +void AstDefaultVisitor::visitFor(ForAst* node) { - visitNode( node->condition ); - - foreach( StatementAst* a, node->whileBody ) - { - visitNode( a ); + visitNode(node->target); + visitNode(node->iterator); + foreach (Ast* statement, node->body) { + visitNode(statement); } - - foreach( StatementAst* a, node->elseBody ) - { - visitNode( a ); + foreach (Ast* statement, node->orelse) { + visitNode(statement); } } -void AstDefaultVisitor::visitFor( ForAst* node ) +void AstDefaultVisitor::visitGeneratorExpression(GeneratorExpressionAst* node) { - - foreach( TargetAst* a, node->assignedTargets ) - { - visitNode( a ); - } - - foreach( ExpressionAst* a, node->iterable ) - { - visitNode( a ); - } - - foreach( StatementAst* a, node->forBody ) - { - visitNode( a ); - } - - - foreach( StatementAst* a, node->elseBody ) - { - visitNode( a ); + visitNode(node->element); + foreach (ComprehensionAst* comp, node->generators) { + visitNode(comp); } } -void AstDefaultVisitor::visitClassDefinition( ClassDefinitionAst* node ) +void AstDefaultVisitor::visitIf(IfAst* node) { - visitNode( node->className ); - foreach( ExpressionAst* a, node->inheritance ) - { - visitNode( a ); + visitNode(node->condition); + foreach (Ast* statement, node->body) { + visitNode(statement); } - foreach( StatementAst* a, node->classBody ) - { - visitNode( a ); + foreach (Ast* statement, node->orelse) { + visitNode(statement); } } -void AstDefaultVisitor::visitTry( TryAst* node ) +void AstDefaultVisitor::visitIfExpression(IfExpressionAst* node) { - - foreach( StatementAst* a, node->tryBody ) - { - visitNode( a ); - } - foreach( ExceptAst* a, node->exceptions ) - { - visitNode( a ); - } - - foreach( StatementAst* a, node->elseBody ) - { - visitNode( a ); - } - foreach( StatementAst* a, node->finallyBody ) - { - visitNode( a ); - } + visitNode(node->condition); + visitNode(node->body); + visitNode(node->orelse); } -void AstDefaultVisitor::visitExcept( ExceptAst* node ) +void AstDefaultVisitor::visitImport(ImportAst* node) { - visitNode( node->exceptionDeclaration ); - visitNode( node->exceptionValue ); - foreach( StatementAst* a, node->exceptionBody ) - { - visitNode( a ); + foreach (AliasAst* alias, node->names) { + visitNode(alias); } } -void AstDefaultVisitor::visitWith( WithAst* node ) +void AstDefaultVisitor::visitImportFrom(ImportFromAst* node) { - visitNode( node->context ); - visitNode( node->name ); - foreach( StatementAst* a, node->body ) - { - visitNode( a ); + foreach (AliasAst* alias, node->names) { + visitNode(alias); } } -void AstDefaultVisitor::visitExec( ExecAst* node ) +void AstDefaultVisitor::visitIndex(IndexAst* node) { - visitNode( node->executable ); - visitNode( node->globalsAndLocals ); - visitNode( node->localsOnly ); + visitNode(node->value); } -void AstDefaultVisitor::visitGlobal( GlobalAst* node ) +void AstDefaultVisitor::visitLambda(LambdaAst* node) { - foreach( IdentifierAst* a, node->identifiers ) - { - visitNode( a ); - } + visitNode(node->arguments); + visitNode(node->body); } -void AstDefaultVisitor::visitPlainImport( PlainImportAst* node ) +void AstDefaultVisitor::visitRaise(RaiseAst* node) { - for( int i = 0; i < node->modulesAsName.count(); i++ ) - { - QPair< QList, Python::IdentifierAst*> pair = - node->modulesAsName.at(i); - for( int j = 0; j < pair.first.count(); j++ ) - { - visitNode( pair.first.at(j) ); - } - visitNode( pair.second ); - } + visitNode(node->type); } -void AstDefaultVisitor::visitStarImport( StarImportAst* node ) +void AstDefaultVisitor::visitRepr(ReprAst* node) { - foreach( IdentifierAst* a, node->modulePath ) - { - visitNode( a ); - } + visitNode(node->value); } -void AstDefaultVisitor::visitFromImport( FromImportAst* node ) +void AstDefaultVisitor::visitReturn(ReturnAst* node) { - foreach( IdentifierAst* a, node->modulePath ) - { - visitNode( a ); - } - for( int i = 0; i < node->identifierAsName.count(); i++ ) - { - visitNode( node->identifierAsName.at(i).first ); - visitNode( node->identifierAsName.at(i).second ); - } + visitNode(node->value); } -void AstDefaultVisitor::visitRaise( RaiseAst* node ) +void AstDefaultVisitor::visitSet(SetAst* node) { - visitNode( node->exceptionType ); - visitNode( node->exceptionValue ); - visitNode( node->traceback ); -} - -void AstDefaultVisitor::visitPrint( PrintAst* node ) -{ - visitNode( node->outfile ); - foreach( ExpressionAst* a, node->printables ) - { - visitNode( a ); + foreach (ExpressionAst* expression, node->elements) { + visitNode(expression); } } -void AstDefaultVisitor::visitReturn( ReturnAst* node ) +void AstDefaultVisitor::visitSetComprehension(SetComprehensionAst* node) { - foreach( ExpressionAst* e, node->returnValues ) - { - visitNode( e ); + visitNode(node->element); + foreach (ComprehensionAst* comp, node->generators) { + visitNode(comp); } } -void AstDefaultVisitor::visitYield( YieldAst* node ) +void AstDefaultVisitor::visitSlice(SliceAst* node) { - foreach( ExpressionAst* e, node->yieldValue ) - { - visitNode( e ); - } + visitNode(node->lower); + visitNode(node->upper); + visitNode(node->step); } -void AstDefaultVisitor::visitDel( DelAst* node ) +void AstDefaultVisitor::visitSubscript(SubscriptAst* node) { - foreach( TargetAst* t, node->deleteObjects ) - { - visitNode( t ); - } + visitNode(node->value); + visitNode(node->slice); } -void AstDefaultVisitor::visitAssert( AssertAst* node ) +void AstDefaultVisitor::visitTryExcept(TryExceptAst* node) { - visitNode( node->assertTest ); - visitNode( node->exceptionValue ); -} - -void AstDefaultVisitor::visitExpressionStatement( ExpressionStatementAst* node ) -{ - foreach( ExpressionAst* e, node->expressions ) - { - visitNode( e ); + foreach (Ast* statement, node->body) { + visitNode(statement); } -} - -void AstDefaultVisitor::visitAssignment( AssignmentAst* node ) -{ - QList, AssignmentAst::OpType > >::const_iterator it; - QList, AssignmentAst::OpType > >::const_iterator end; - it = node->targets.begin(); - end = node->targets.end(); - for( ; it != end; ++it ) - { - QList tl = (*it).first; - foreach( TargetAst* t, tl ) - { - visitNode( t ); - } + foreach (ExceptionHandlerAst* handler, node->handlers) { + visitNode(handler); } - - foreach( ExpressionAst* e, node->value ) - { - visitNode( e ); + foreach (Ast* statement, node->orelse) { + visitNode(statement); } - visitNode( node->yieldValue ); -} - -void AstDefaultVisitor::visitAtom( AtomAst* node ) -{ - visitNode( node->identifier ); - visitNode( node->enclosure ); - visitNode( node->literal ); } -void AstDefaultVisitor::visitEnclosure( EnclosureAst* node ) +void AstDefaultVisitor::visitTryFinally(TryFinallyAst* node) { - switch( node->encType ) - { - case EnclosureAst::Dictionary: - visitNode( node->dict ); - break; - case EnclosureAst::Generator: - visitNode( node->generator ); - break; - case EnclosureAst::List: - visitNode( node->list ); - break; - case EnclosureAst::Yield: - visitNode( node->yield ); - break; - case EnclosureAst::ParenthesizedForm: - foreach( ExpressionAst* a, node->parenthesizedform ) - { - visitNode( a ); - } - break; - case EnclosureAst::StringConversion: - foreach( ExpressionAst* a, node->stringConversion ) - { - visitNode( a ); - } - break; + foreach (Ast* statement, node->body) { + visitNode(statement); } -} - -void AstDefaultVisitor::visitList( ListAst* node ) -{ - foreach( ExpressionAst* a, node->plainList ) - { - visitNode( a ); + foreach (Ast* statement, node->finalbody) { + visitNode(statement); } - visitNode( node->listGenerator ); } -void AstDefaultVisitor::visitListFor( ListForAst* node ) +void AstDefaultVisitor::visitTuple(TupleAst* node) { - foreach( TargetAst* t, node->assignedTargets ) - { - visitNode( t ); - } - foreach( ExpressionAst* e, node->iterableObject ) - { - visitNode( e ); + foreach (ExpressionAst* expression, node->elements) { + visitNode(expression); } - visitNode( node->nextGenerator ); - visitNode( node->nextCondition ); } -void AstDefaultVisitor::visitListIf( ListIfAst* node ) +void AstDefaultVisitor::visitUnaryOperation(UnaryOperationAst* node) { - visitNode( node->condition ); - visitNode( node->nextGenerator ); - visitNode( node->nextCondition ); + visitNode(node->operand); } -void AstDefaultVisitor::visitGenerator( GeneratorAst* node ) +void AstDefaultVisitor::visitWhile(WhileAst* node) { - visitNode( node->generatedValue ); - visitNode( node->generator ); -} - -void AstDefaultVisitor::visitGeneratorFor( GeneratorForAst* node ) -{ - foreach( TargetAst* t, node->assignedTargets ) - { - visitNode( t ); + visitNode(node->condition); + foreach (Ast* statement, node->body) { + visitNode(statement); + } + foreach (Ast* statement, node->orelse) { + visitNode(statement); } - visitNode( node->iterableObject ); - visitNode( node->nextGenerator ); - visitNode( node->nextCondition ); -} - -void AstDefaultVisitor::visitGeneratorIf( GeneratorIfAst* node ) -{ - visitNode( node->condition ); - visitNode( node->nextGenerator ); - visitNode( node->nextCondition ); } -void AstDefaultVisitor::visitDictionary( DictionaryAst* node ) +void AstDefaultVisitor::visitWith(WithAst* node) { - foreach( ExpressionAst* key, node->dictionary.keys() ) - { - visitNode( key ); - visitNode( node->dictionary[key] ); + visitNode(node->contextExpression); + visitNode(node->optionalVars); + foreach (Ast* statement, node->body) { + visitNode(statement); } } -void AstDefaultVisitor::visitAttributeReference( AttributeReferenceAst* node ) +void AstDefaultVisitor::visitYield(YieldAst* node) { - visitNode( node->primary ); - visitNode( node->identifier ); + visitNode(node->value); } -void AstDefaultVisitor::visitSubscript( SubscriptAst* node ) +void AstDefaultVisitor::visitList(ListAst* node) { - visitNode( node->primary ); - foreach( ExpressionAst* e, node->subscription ) - { - visitNode( e ); + foreach (ExpressionAst* expression, node->elements) { + visitNode(expression); } } -void AstDefaultVisitor::visitExtendedSlice( ExtendedSliceAst* node ) +void AstDefaultVisitor::visitListComprehension(ListComprehensionAst* node) { - visitNode( node->primary ); - foreach( SliceItemAst* s, node->extendedSliceList ) - { - visitNode( s ); + visitNode(node->element); + foreach (ComprehensionAst* comp, node->generators) { + visitNode(comp); } } -void AstDefaultVisitor::visitSimpleSlice( SimpleSliceAst* node ) -{ - visitNode( node->primary ); - visitNode( node->simpleSliceBounds.first ); - visitNode( node->simpleSliceBounds.second ); -} - -void AstDefaultVisitor::visitProperSliceItem( ProperSliceItemAst* node ) -{ - visitNode( node->bounds.first ); - visitNode( node->bounds.second ); - visitNode( node->stride ); -} - -void AstDefaultVisitor::visitExpressionSliceItem( ExpressionSliceItemAst* node ) -{ - visitNode( node->sliceExpression ); -} - -void AstDefaultVisitor::visitEllipsisSliceItem( EllipsisSliceItemAst* ) +void AstDefaultVisitor::visitExceptionHandler(ExceptionHandlerAst* node) { -} - -void AstDefaultVisitor::visitCall( CallAst* node ) -{ - visitNode( node->callable ); - foreach( ArgumentAst* a, node->arguments ) - { - visitNode( a ); + visitNode(node->type); + visitNode(node->name); + foreach (Ast* statement, node->body) { + visitNode(statement); } - visitNode( node->generator ); -} - -void AstDefaultVisitor::visitUnaryExpression( UnaryExpressionAst* node ) -{ - visitNode( node->operand ); -} - -void AstDefaultVisitor::visitBinaryExpression( BinaryExpressionAst* node ) -{ - visitNode( node->lhs ); - visitNode( node->rhs ); } -void AstDefaultVisitor::visitComparison( ComparisonAst* node ) +void AstDefaultVisitor::visitDict(DictAst* node) { - visitNode( node->firstComparator ); - QList< QPair< ComparisonAst::ComparisonOperator, ExpressionAst*> >::iterator it; - QList< QPair< ComparisonAst::ComparisonOperator, ExpressionAst*> >::iterator end = node->comparatorList.end(); - for( it = node->comparatorList.begin(); it != end; ++it ) - { - visitNode( (*it).second ); + foreach (ExpressionAst* expression, node->keys) { + visitNode(expression); + } + foreach (ExpressionAst* expression, node->values) { + visitNode(expression); } } -void AstDefaultVisitor::visitBooleanNotOperation( BooleanNotOperationAst* node ) -{ - visitNode( node->op ); -} - -void AstDefaultVisitor::visitBooleanOrOperation( BooleanOrOperationAst* node ) +void AstDefaultVisitor::visitDictionaryComprehension(DictionaryComprehensionAst* node) { - visitNode( node->lhs ); - visitNode( node->rhs ); + visitNode(node->key); + visitNode(node->value); + foreach (ComprehensionAst* comp, node->generators) { + visitNode(comp); + } } -void AstDefaultVisitor::visitBooleanAndOperation( BooleanAndOperationAst* node ) +void AstDefaultVisitor::visitAugmentedAssignment(AugmentedAssignmentAst* node) { - visitNode( node->lhs ); - visitNode( node->rhs ); + visitNode(node->target); + visitNode(node->value); } -void AstDefaultVisitor::visitConditionalExpression( ConditionalExpressionAst* node ) +void AstDefaultVisitor::visitBinaryOperation(BinaryOperationAst* node) { - visitNode( node->mainExpression ); - visitNode( node->condition ); - visitNode( node->elseExpression ); + visitNode(node->lhs); + visitNode(node->rhs); } -void AstDefaultVisitor::visitLambda( LambdaAst* node ) +void AstDefaultVisitor::visitBooleanOperation(BooleanOperationAst* node) { - foreach( ParameterAst* p, node->parameters ) - { - visitNode( p ); + foreach (ExpressionAst* expression, node->values) { + visitNode(expression); } - visitNode( node->expression ); } -void AstDefaultVisitor::visitPass( StatementAst* ) -{ -} - -void AstDefaultVisitor::visitContinue( StatementAst* ) +void AstDefaultVisitor::visitClassDefinition(ClassDefinitionAst* node) { + foreach (ExpressionAst* expression, node->baseClasses) { + visitNode(expression); + } + foreach (Ast* statement, node->body) { + visitNode(statement); + } + foreach (ExpressionAst* expression, node->decorators) { + visitNode(expression); + } } -void AstDefaultVisitor::visitBreak( StatementAst* ) +void AstDefaultVisitor::visitCompare(CompareAst* node) { + visitNode(node->leftmostElement); + foreach (ExpressionAst* expression, node->comparands) { + visitNode(expression); + } } -void AstDefaultVisitor::visitIdentifier( IdentifierAst * ) +void AstDefaultVisitor::visitComprehension(ComprehensionAst* node) { + visitNode(node->target); + visitNode(node->iterator); + foreach (ExpressionAst* expression, node->conditions) { + visitNode(expression); + } } -void AstDefaultVisitor::visitLiteral( LiteralAst * ) +void AstDefaultVisitor::visitAssignment(AssignmentAst* node) { + foreach (ExpressionAst* expression, node->targets) { + visitNode(expression); + }; + visitNode(node->value); } -void AstDefaultVisitor::visitIdentifierTarget( IdentifierTargetAst * ast ) +void AstDefaultVisitor::visitPrint(PrintAst* node) { - visitNode( ast->identifier ); + visitNode(node->destination); + foreach (ExpressionAst* expression, node->values) { + visitNode(expression); + } } -void AstDefaultVisitor::visitListTarget( ListTargetAst * ast ) +void AstDefaultVisitor::visitCall(CallAst* node) { - foreach( Python::TargetAst* t, ast->items ) - { - visitNode( t ); + visitNode(node->function); + visitNode(node->keywordArguments); + visitNode(node->starArguments); + foreach (ExpressionAst* argument, node->arguments) { + visitNode(argument); } } -void AstDefaultVisitor::visitTupleTarget( TupleTargetAst * ast ) +void AstDefaultVisitor::visitFunctionDefinition(FunctionDefinitionAst* node) { - foreach( Python::TargetAst* t, ast->items ) - { - visitNode( t ); - } + visitNode(node->arguments); } -void AstDefaultVisitor::visitAttributeReferenceTarget( AttributeReferenceTargetAst * ast ) +void AstDefaultVisitor::visitAttribute(AttributeAst* node) { - visitNode( ast->attribute ); + visitNode(node->value); } -void AstDefaultVisitor::visitSubscriptTarget( SubscriptTargetAst * ast ) +void AstDefaultVisitor::visitKeyword(KeywordAst* node) { - visitNode( ast->subscript ); + visitNode(node->value); } -void AstDefaultVisitor::visitSliceTarget( SliceTargetAst * ast ) +void AstDefaultVisitor::visitArguments(ArgumentsAst* node) { - visitNode( ast->slice ); + foreach (ExpressionAst* expression, node->arguments) { + visitNode(expression); + } } } diff --git a/parser/astdefaultvisitor.h b/parser/astdefaultvisitor.h index 451d47a..891c285 100644 --- a/parser/astdefaultvisitor.h +++ b/parser/astdefaultvisitor.h @@ -24,6 +24,11 @@ #include "astvisitor.h" #include "parserexport.h" +/** + * Note: This has been generated using utilities/generate.py + * but you can modifiy it, it's not regenerated automatically + */ + namespace Python { @@ -33,72 +38,61 @@ class KDEVPYTHONPARSER_EXPORT AstDefaultVisitor : public AstVisitor AstDefaultVisitor(); virtual ~AstDefaultVisitor(); - virtual void visitCode( CodeAst* ); - virtual void visitFunctionDefinition( FunctionDefinitionAst* ); - virtual void visitDecorator( DecoratorAst* ); - virtual void visitArgument( ArgumentAst* ); - virtual void visitDefaultParameter( DefaultParameterAst* ); - virtual void visitIdentifierParameterPart( IdentifierParameterPartAst* ); - virtual void visitListParameterPart( ListParameterPartAst* ); - virtual void visitDictionaryParameter( DictionaryParameterAst* ); - virtual void visitListParameter( ListParameterAst* ); - virtual void visitIf( IfAst* ); - virtual void visitWhile( WhileAst* ); - virtual void visitFor( ForAst* ); - virtual void visitClassDefinition( ClassDefinitionAst* ); - virtual void visitTry( TryAst* ); - virtual void visitExcept( ExceptAst* ); - virtual void visitWith( WithAst* ); - virtual void visitExec( ExecAst* ); - virtual void visitGlobal( GlobalAst* ); - virtual void visitPlainImport( PlainImportAst* ); - virtual void visitStarImport( StarImportAst* ); - virtual void visitFromImport( FromImportAst* ); - virtual void visitRaise( RaiseAst* ); - virtual void visitPrint( PrintAst* ); - virtual void visitReturn( ReturnAst* ); - virtual void visitYield( YieldAst* ); - virtual void visitDel( DelAst* ); - virtual void visitAssert( AssertAst* ); - virtual void visitExpressionStatement( ExpressionStatementAst* ); - virtual void visitAssignment( AssignmentAst* ); - virtual void visitAtom( AtomAst* ); - virtual void visitEnclosure( EnclosureAst* ); - virtual void visitList( ListAst* ); - virtual void visitListFor( ListForAst* ); - virtual void visitListIf( ListIfAst* ); - virtual void visitLiteral( LiteralAst* ); - virtual void visitGenerator( GeneratorAst* ); - virtual void visitGeneratorFor( GeneratorForAst* ); - virtual void visitGeneratorIf( GeneratorIfAst* ); - virtual void visitDictionary( DictionaryAst* ); - virtual void visitAttributeReference( AttributeReferenceAst* ); - virtual void visitSubscript( SubscriptAst* ); - virtual void visitExtendedSlice( ExtendedSliceAst* ); - virtual void visitSimpleSlice( SimpleSliceAst* ); - virtual void visitProperSliceItem( ProperSliceItemAst* ); - virtual void visitExpressionSliceItem( ExpressionSliceItemAst* ); - virtual void visitEllipsisSliceItem( EllipsisSliceItemAst* ); - virtual void visitCall( CallAst* ); - virtual void visitUnaryExpression( UnaryExpressionAst* ); - virtual void visitBinaryExpression( BinaryExpressionAst* ); - virtual void visitComparison( ComparisonAst* ); - virtual void visitBooleanNotOperation( BooleanNotOperationAst* ); - virtual void visitBooleanAndOperation( BooleanAndOperationAst* ); - virtual void visitBooleanOrOperation( BooleanOrOperationAst* ); - virtual void visitConditionalExpression( ConditionalExpressionAst* ); - virtual void visitLambda( LambdaAst* ); - virtual void visitBreak( StatementAst* ); - virtual void visitContinue( StatementAst* ); - virtual void visitPass( StatementAst* ); - virtual void visitIdentifier( IdentifierAst* ); - virtual void visitIdentifierTarget( IdentifierTargetAst* ); - virtual void visitListTarget( ListTargetAst* ); - virtual void visitTupleTarget( TupleTargetAst* ); - virtual void visitAttributeReferenceTarget( AttributeReferenceTargetAst* ); - virtual void visitSubscriptTarget( SubscriptTargetAst* ); - virtual void visitSliceTarget( SliceTargetAst* ); - + virtual void visitCode(CodeAst* node); + virtual void visitFunctionDefinition(FunctionDefinitionAst* node); + virtual void visitClassDefinition(ClassDefinitionAst* node); + virtual void visitReturn(ReturnAst* node); + virtual void visitDelete(DeleteAst* node); + virtual void visitAssignment(AssignmentAst* node); + virtual void visitAugmentedAssignment(AugmentedAssignmentAst* node); + virtual void visitFor(ForAst* node); + virtual void visitWhile(WhileAst* node); + virtual void visitIf(IfAst* node); + virtual void visitWith(WithAst* node); + virtual void visitRaise(RaiseAst* node); + virtual void visitTryExcept(TryExceptAst* node); + virtual void visitTryFinally(TryFinallyAst* node); + virtual void visitAssertion(AssertionAst* node); + virtual void visitImport(ImportAst* node); + virtual void visitImportFrom(ImportFromAst* node); + virtual void visitExec(ExecAst* node); + virtual void visitGlobal(GlobalAst* node); + virtual void visitBreak(BreakAst* node); + virtual void visitContinue(ContinueAst* node); + virtual void visitPrint(PrintAst* node); + virtual void visitPass(PassAst* node); + virtual void visitBooleanOperation(BooleanOperationAst* node); + virtual void visitBinaryOperation(BinaryOperationAst* node); + virtual void visitUnaryOperation(UnaryOperationAst* node); + virtual void visitLambda(LambdaAst* node); + virtual void visitIfExpression(IfExpressionAst* node); + virtual void visitDict(DictAst* node); + virtual void visitSet(SetAst* node); + virtual void visitListComprehension(ListComprehensionAst* node); + virtual void visitSetComprehension(SetComprehensionAst* node); + virtual void visitDictionaryComprehension(DictionaryComprehensionAst* node); + virtual void visitGeneratorExpression(GeneratorExpressionAst* node); + virtual void visitCompare(CompareAst* node); + virtual void visitRepr(ReprAst* node); + virtual void visitNumber(NumberAst* node); + virtual void visitString(StringAst* node); + virtual void visitYield(YieldAst* node); + virtual void visitName(NameAst* node); + virtual void visitCall(CallAst* node); + virtual void visitAttribute(AttributeAst* node); + virtual void visitSubscript(SubscriptAst* node); + virtual void visitList(ListAst* node); + virtual void visitTuple(TupleAst* node); + virtual void visitEllipsis(EllipsisAst* node); + virtual void visitSlice(SliceAst* node); + virtual void visitExtendedSlice(ExtendedSliceAst* node); + virtual void visitIndex(IndexAst* node); + virtual void visitArguments(ArgumentsAst* node); + virtual void visitKeyword(KeywordAst* node); + virtual void visitComprehension(ComprehensionAst* node); + virtual void visitExceptionHandler(ExceptionHandlerAst* node); + virtual void visitAlias(AliasAst* node); + virtual void visitExpression(ExpressionAst* node); }; } diff --git a/parser/astprinter.cpp b/parser/astprinter.cpp deleted file mode 100644 index 64b2a0e..0000000 --- a/parser/astprinter.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/*************************************************************************** - * This file is part of KDevelop * - * Copyright 2008 Andreas Pakulat * - * * - * This program 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 program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#include "astprinter.h" - -#include - -namespace Python -{ - -static QString names[] = { -"Argument", -"Assert", -"Assignment", -"Atom", -"AttributeReference", -"AttributeReferenceTarget", -"BinaryExpression", -"BooleanNotOperation", -"BooleanAndOperation", -"BooleanOrOperation", -"Break", -"Call", -"ClassDefinition", -"Code", -"Comparison", -"ConditionalExpression", -"Continue", -"Decorator", -"DefaultParameter", -"Del", -"Dictionary", -"DictionaryParameter", -"EllipsisSliceItem", -"Enclosure", -"Except", -"Exec", -"ExpressionSliceItem", -"ExpressionStatement", -"ExtendedSlice", -"For", -"FromImport", -"FunctionDefinition", -"Generator", -"GeneratorFor", -"GeneratorIf", -"Global", -"Identifier", -"IdentifierParameterPart", -"IdentifierTarget", -"If", -"Lambda", -"List", -"ListFor", -"ListIf", -"ListParameter", -"ListParameterPart", -"ListTarget", -"Literal", -"Pass", -"PlainImport", -"Print", -"ProperSliceItem", -"Raise", -"Return", -"SimpleSlice", -"SliceTarget", -"StarImport", -"Subscript", -"SubscriptTarget", -"TupleTarget", -"Try", -"UnaryExpression", -"While", -"With", -"Yield", -}; - -AstPrinter::AstPrinter() - : AstDefaultVisitor(), indent( 0 ) -{ -} - -AstPrinter::~AstPrinter() -{ -} - -void AstPrinter::visitNode( Ast* node ) -{ - if( node ) - { - kDebug() << indentation() + "\\ " + names[node->astType] + "[(" + QString::number( node->start ) + ")]"; - ++indent; - } - AstDefaultVisitor::visitNode( node ); - if(node) - { - --indent; - kDebug() << indentation() + "/ " + names[node->astType] + "[(" + QString::number( node->end ) + ")]"; - } -} - -QString AstPrinter::indentation() const -{ - QString s; - for( int a = 0; a < indent; a++ ) - s += "| "; - return s; -} - -void AstPrinter::visitIdentifier( IdentifierAst * ast ) -{ - kDebug() << indentation() + " Identifier ==" << ast->identifier; -} - -void AstPrinter::visitLiteral( LiteralAst * ast ) -{ - kDebug() << indentation() + " literal type ==" << ast->literalType; - kDebug() << indentation() + " Literal ==" << ast->value; -} - -void AstPrinter::visitArgument( ArgumentAst* node ) -{ - kDebug() << indentation() + " ArgumentType ==" << node->argumentType; - AstDefaultVisitor::visitArgument( node ); -} - -void AstPrinter::visitFromImport( FromImportAst* node ) -{ - kDebug() << indentation() + " leading dots ==" << node->numLeadingDots; - AstDefaultVisitor::visitFromImport( node ); -} - -void AstPrinter::visitAssignment( AssignmentAst* node ) -{ - QList, AssignmentAst::OpType > >::const_iterator it; - QList, AssignmentAst::OpType > >::const_iterator end; - it = node->targets.begin(); - end = node->targets.end(); - for( ; it != end; ++it ) - { - QList tl = (*it).first; - foreach( TargetAst* t, tl ) - { - visitNode( t ); - } - kDebug() << indentation() + " op type ==" << (*it).second; - } - - foreach( ExpressionAst* e, node->value ) - { - visitNode( e ); - } - visitNode( node->yieldValue ); -} - -void AstPrinter::visitEnclosure( EnclosureAst* node ) -{ - kDebug() << indentation() + " enclosure type ==" << node->encType; - AstDefaultVisitor::visitEnclosure( node ); -} - -void AstPrinter::visitComparison( ComparisonAst* node ) -{ - visitNode( node->firstComparator ); - QList< QPair< ComparisonAst::ComparisonOperator, ExpressionAst*> >::iterator it; - QList< QPair< ComparisonAst::ComparisonOperator, ExpressionAst*> >::iterator end = node->comparatorList.end(); - for( it = node->comparatorList.begin(); it != end; ++it ) - { - kDebug() << indentation() + " comparison type" << (*it).first; - visitNode( (*it).second ); - } -} - -void AstPrinter::visitBinaryExpression( BinaryExpressionAst* node ) -{ - kDebug() << indentation() + " binary op ==" << node->opType; - AstDefaultVisitor::visitBinaryExpression( node ); -} - -void AstPrinter::visitUnaryExpression( UnaryExpressionAst* node ) -{ - kDebug() << indentation() + " binary op ==" << node->opType; - AstDefaultVisitor::visitUnaryExpression( node ); -} - -} diff --git a/parser/astprinter.h b/parser/astprinter.h deleted file mode 100644 index 34c7945..0000000 --- a/parser/astprinter.h +++ /dev/null @@ -1,53 +0,0 @@ -/*************************************************************************** - * This file is part of KDevelop * - * Copyright 2008 Andreas Pakulat * - * * - * This program 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 program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU Library General Public * - * License along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#ifndef ASTPRINTER_H -#define ASTPRINTER_H - -#include "astdefaultvisitor.h" -#include "parserexport.h" - -namespace Python -{ - -class KDEVPYTHONPARSER_EXPORT AstPrinter : public AstDefaultVisitor -{ -public: - AstPrinter(); - ~AstPrinter(); - virtual void visitIdentifier( IdentifierAst* ast ); - virtual void visitLiteral( LiteralAst* ast ); - virtual void visitNode( Ast* ast ); - virtual void visitUnaryExpression( UnaryExpressionAst* node ); - virtual void visitBinaryExpression( BinaryExpressionAst* node ); - virtual void visitComparison( ComparisonAst* node ); - virtual void visitEnclosure( EnclosureAst* node ); - virtual void visitAssignment( AssignmentAst* node ); - virtual void visitFromImport( FromImportAst* node ); - virtual void visitArgument( ArgumentAst* node ); - -private: - QString indentation() const; - int indent; -}; - -} - -#endif diff --git a/parser/astvisitor.cpp b/parser/astvisitor.cpp index f02bd87..a108723 100644 --- a/parser/astvisitor.cpp +++ b/parser/astvisitor.cpp @@ -20,84 +20,79 @@ #include "astvisitor.h" +/** + * Note: This has been generated using utilities/generate.py + * but you can modifiy it, it's not regenerated automatically + */ + namespace Python { + +AstVisitor::AstVisitor() { } +AstVisitor::~AstVisitor() { } -AstVisitor::visitFunc _S_parser_table[] = { - - reinterpret_cast(&AstVisitor::visitArgument), - reinterpret_cast(&AstVisitor::visitAssert), - reinterpret_cast(&AstVisitor::visitAssignment), - reinterpret_cast(&AstVisitor::visitAtom), - reinterpret_cast(&AstVisitor::visitAttributeReference), - reinterpret_cast(&AstVisitor::visitAttributeReferenceTarget), - reinterpret_cast(&AstVisitor::visitBinaryExpression), - reinterpret_cast(&AstVisitor::visitBooleanNotOperation), - reinterpret_cast(&AstVisitor::visitBooleanAndOperation), - reinterpret_cast(&AstVisitor::visitBooleanOrOperation), - reinterpret_cast(&AstVisitor::visitBreak), - reinterpret_cast(&AstVisitor::visitCall), - reinterpret_cast(&AstVisitor::visitClassDefinition), - reinterpret_cast(&AstVisitor::visitCode), - reinterpret_cast(&AstVisitor::visitComparison), - reinterpret_cast(&AstVisitor::visitConditionalExpression), - reinterpret_cast(&AstVisitor::visitContinue), - reinterpret_cast(&AstVisitor::visitDecorator), - reinterpret_cast(&AstVisitor::visitDefaultParameter), - reinterpret_cast(&AstVisitor::visitDel), - reinterpret_cast(&AstVisitor::visitDictionary), - reinterpret_cast(&AstVisitor::visitDictionaryParameter), - reinterpret_cast(&AstVisitor::visitEllipsisSliceItem), - reinterpret_cast(&AstVisitor::visitEnclosure), - reinterpret_cast(&AstVisitor::visitExcept), - reinterpret_cast(&AstVisitor::visitExec), - reinterpret_cast(&AstVisitor::visitExpressionSliceItem), - reinterpret_cast(&AstVisitor::visitExpressionStatement), - reinterpret_cast(&AstVisitor::visitExtendedSlice), - reinterpret_cast(&AstVisitor::visitFor), - reinterpret_cast(&AstVisitor::visitFromImport), - reinterpret_cast(&AstVisitor::visitFunctionDefinition), - reinterpret_cast(&AstVisitor::visitGenerator), - reinterpret_cast(&AstVisitor::visitGeneratorFor), - reinterpret_cast(&AstVisitor::visitGeneratorIf), - reinterpret_cast(&AstVisitor::visitGlobal), - reinterpret_cast(&AstVisitor::visitIdentifier), - reinterpret_cast(&AstVisitor::visitIdentifierParameterPart), - reinterpret_cast(&AstVisitor::visitIdentifierTarget), - reinterpret_cast(&AstVisitor::visitIf), - reinterpret_cast(&AstVisitor::visitLambda), - reinterpret_cast(&AstVisitor::visitList), - reinterpret_cast(&AstVisitor::visitListFor), - reinterpret_cast(&AstVisitor::visitListIf), - reinterpret_cast(&AstVisitor::visitListParameter), - reinterpret_cast(&AstVisitor::visitListParameterPart), - reinterpret_cast(&AstVisitor::visitListTarget), - reinterpret_cast(&AstVisitor::visitLiteral), - reinterpret_cast(&AstVisitor::visitPass), - reinterpret_cast(&AstVisitor::visitPlainImport), - reinterpret_cast(&AstVisitor::visitPrint), - reinterpret_cast(&AstVisitor::visitProperSliceItem), - reinterpret_cast(&AstVisitor::visitRaise), - reinterpret_cast(&AstVisitor::visitReturn), - reinterpret_cast(&AstVisitor::visitSimpleSlice), - reinterpret_cast(&AstVisitor::visitSliceTarget), - reinterpret_cast(&AstVisitor::visitStarImport), - reinterpret_cast(&AstVisitor::visitSubscript), - reinterpret_cast(&AstVisitor::visitSubscriptTarget), - reinterpret_cast(&AstVisitor::visitTupleTarget), - reinterpret_cast(&AstVisitor::visitTry), - reinterpret_cast(&AstVisitor::visitUnaryExpression), - reinterpret_cast(&AstVisitor::visitWhile), - reinterpret_cast(&AstVisitor::visitWith), - reinterpret_cast(&AstVisitor::visitYield), - - -}; -void AstVisitor::visitNode( Ast* node ) +void AstVisitor::visitNode(Ast* node) { - if (node) - (this->*_S_parser_table[node->astType])(node); + if ( ! node ) return; + switch ( node->astType ) { + case Ast::CodeAstType: this->visitCode(dynamic_cast(node)); break; + case Ast::FunctionDefinitionAstType: this->visitFunctionDefinition(dynamic_cast(node)); break; + case Ast::ClassDefinitionAstType: this->visitClassDefinition(dynamic_cast(node)); break; + case Ast::ReturnAstType: this->visitReturn(dynamic_cast(node)); break; + case Ast::DeleteAstType: this->visitDelete(dynamic_cast(node)); break; + case Ast::AssignmentAstType: this->visitAssignment(dynamic_cast(node)); break; + case Ast::AugmentedAssignmentAstType: this->visitAugmentedAssignment(dynamic_cast(node)); break; + case Ast::ForAstType: this->visitFor(dynamic_cast(node)); break; + case Ast::WhileAstType: this->visitWhile(dynamic_cast(node)); break; + case Ast::IfAstType: this->visitIf(dynamic_cast(node)); break; + case Ast::WithAstType: this->visitWith(dynamic_cast(node)); break; + case Ast::RaiseAstType: this->visitRaise(dynamic_cast(node)); break; + case Ast::TryExceptAstType: this->visitTryExcept(dynamic_cast(node)); break; + case Ast::TryFinallyAstType: this->visitTryFinally(dynamic_cast(node)); break; + case Ast::AssertionAstType: this->visitAssertion(dynamic_cast(node)); break; + case Ast::ImportAstType: this->visitImport(dynamic_cast(node)); break; + case Ast::ImportFromAstType: this->visitImportFrom(dynamic_cast(node)); break; + case Ast::ExecAstType: this->visitExec(dynamic_cast(node)); break; + case Ast::GlobalAstType: this->visitGlobal(dynamic_cast(node)); break; + case Ast::BreakAstType: this->visitBreak(dynamic_cast(node)); break; + case Ast::ContinueAstType: this->visitContinue(dynamic_cast(node)); break; + case Ast::PrintAstType: this->visitPrint(dynamic_cast(node)); break; + case Ast::PassAstType: this->visitPass(dynamic_cast(node)); break; + case Ast::BooleanOperationAstType: this->visitBooleanOperation(dynamic_cast(node)); break; + case Ast::BinaryOperationAstType: this->visitBinaryOperation(dynamic_cast(node)); break; + case Ast::UnaryOperationAstType: this->visitUnaryOperation(dynamic_cast(node)); break; + case Ast::LambdaAstType: this->visitLambda(dynamic_cast(node)); break; + case Ast::IfExpressionAstType: this->visitIfExpression(dynamic_cast(node)); break; + case Ast::DictAstType: this->visitDict(dynamic_cast(node)); break; + case Ast::SetAstType: this->visitSet(dynamic_cast(node)); break; + case Ast::ListComprehensionAstType: this->visitListComprehension(dynamic_cast(node)); break; + case Ast::SetComprehensionAstType: this->visitSetComprehension(dynamic_cast(node)); break; + case Ast::DictionaryComprehensionAstType: this->visitDictionaryComprehension(dynamic_cast(node)); break; + case Ast::GeneratorExpressionAstType: this->visitGeneratorExpression(dynamic_cast(node)); break; + case Ast::CompareAstType: this->visitCompare(dynamic_cast(node)); break; + case Ast::ReprAstType: this->visitRepr(dynamic_cast(node)); break; + case Ast::NumberAstType: this->visitNumber(dynamic_cast(node)); break; + case Ast::StringAstType: this->visitString(dynamic_cast(node)); break; + case Ast::YieldAstType: this->visitYield(dynamic_cast(node)); break; + case Ast::NameAstType: this->visitName(dynamic_cast(node)); break; + case Ast::CallAstType: this->visitCall(dynamic_cast(node)); break; + case Ast::AttributeAstType: this->visitAttribute(dynamic_cast(node)); break; + case Ast::SubscriptAstType: this->visitSubscript(dynamic_cast(node)); break; + case Ast::ListAstType: this->visitList(dynamic_cast(node)); break; + case Ast::TupleAstType: this->visitTuple(dynamic_cast(node)); break; + case Ast::EllipsisAstType: this->visitEllipsis(dynamic_cast(node)); break; + case Ast::SliceAstType: this->visitSlice(dynamic_cast(node)); break; + case Ast::ExtendedSliceAstType: this->visitExtendedSlice(dynamic_cast(node)); break; + case Ast::IndexAstType: this->visitIndex(dynamic_cast(node)); break; + case Ast::ArgumentsAstType: this->visitArguments(dynamic_cast(node)); break; + case Ast::KeywordAstType: this->visitKeyword(dynamic_cast(node)); break; + case Ast::ComprehensionAstType: this->visitComprehension(dynamic_cast(node)); break; + case Ast::ExceptionHandlerAstType: this->visitExceptionHandler(dynamic_cast(node)); break; + case Ast::AliasAstType: this->visitAlias(dynamic_cast(node)); break; + case Ast::ExpressionAstType: this->visitExpression(dynamic_cast(node)); break; + case Ast::StatementAstType: break; + } } } diff --git a/parser/astvisitor.h b/parser/astvisitor.h index 04b27ec..cf18daa 100644 --- a/parser/astvisitor.h +++ b/parser/astvisitor.h @@ -24,83 +24,81 @@ #include "ast.h" #include "parserexport.h" +/** + * Note: This has been generated using utilities/generate.py + * but you can modifiy it, it's not regenerated automatically + */ + namespace Python { class KDEVPYTHONPARSER_EXPORT AstVisitor { public: - virtual ~AstVisitor() {} + AstVisitor(); + virtual ~AstVisitor(); typedef void (AstVisitor::*visitFunc)(Ast *); + + void visitNode(Ast* node); - virtual void visitNode( Ast* ); + virtual void visitCode(CodeAst* node) { Q_UNUSED(node); }; + virtual void visitStatement(StatementAst* node) { Q_UNUSED(node); }; + virtual void visitFunctionDefinition(FunctionDefinitionAst* node) { Q_UNUSED(node); }; + virtual void visitClassDefinition(ClassDefinitionAst* node) { Q_UNUSED(node); }; + virtual void visitReturn(ReturnAst* node) { Q_UNUSED(node); }; + virtual void visitDelete(DeleteAst* node) { Q_UNUSED(node); }; + virtual void visitAssignment(AssignmentAst* node) { Q_UNUSED(node); }; + virtual void visitAugmentedAssignment(AugmentedAssignmentAst* node) { Q_UNUSED(node); }; + virtual void visitFor(ForAst* node) { Q_UNUSED(node); }; + virtual void visitWhile(WhileAst* node) { Q_UNUSED(node); }; + virtual void visitIf(IfAst* node) { Q_UNUSED(node); }; + virtual void visitWith(WithAst* node) { Q_UNUSED(node); }; + virtual void visitRaise(RaiseAst* node) { Q_UNUSED(node); }; + virtual void visitTryExcept(TryExceptAst* node) { Q_UNUSED(node); }; + virtual void visitTryFinally(TryFinallyAst* node) { Q_UNUSED(node); }; + virtual void visitAssertion(AssertionAst* node) { Q_UNUSED(node); }; + virtual void visitImport(ImportAst* node) { Q_UNUSED(node); }; + virtual void visitImportFrom(ImportFromAst* node) { Q_UNUSED(node); }; + virtual void visitExec(ExecAst* node) { Q_UNUSED(node); }; + virtual void visitGlobal(GlobalAst* node) { Q_UNUSED(node); }; + virtual void visitBreak(BreakAst* node) { Q_UNUSED(node); }; + virtual void visitContinue(ContinueAst* node) { Q_UNUSED(node); }; + virtual void visitPrint(PrintAst* node) { Q_UNUSED(node); }; + virtual void visitPass(PassAst* node) { Q_UNUSED(node); }; + virtual void visitExpression(ExpressionAst* node) { Q_UNUSED(node); }; + virtual void visitBooleanOperation(BooleanOperationAst* node) { Q_UNUSED(node); }; + virtual void visitBinaryOperation(BinaryOperationAst* node) { Q_UNUSED(node); }; + virtual void visitUnaryOperation(UnaryOperationAst* node) { Q_UNUSED(node); }; + virtual void visitLambda(LambdaAst* node) { Q_UNUSED(node); }; + virtual void visitIfExpression(IfExpressionAst* node) { Q_UNUSED(node); }; + virtual void visitDict(DictAst* node) { Q_UNUSED(node); }; + virtual void visitSet(SetAst* node) { Q_UNUSED(node); }; + virtual void visitListComprehension(ListComprehensionAst* node) { Q_UNUSED(node); }; + virtual void visitSetComprehension(SetComprehensionAst* node) { Q_UNUSED(node); }; + virtual void visitDictionaryComprehension(DictionaryComprehensionAst* node) { Q_UNUSED(node); }; + virtual void visitGeneratorExpression(GeneratorExpressionAst* node) { Q_UNUSED(node); }; + virtual void visitCompare(CompareAst* node) { Q_UNUSED(node); }; + virtual void visitRepr(ReprAst* node) { Q_UNUSED(node); }; + virtual void visitNumber(NumberAst* node) { Q_UNUSED(node); }; + virtual void visitString(StringAst* node) { Q_UNUSED(node); }; + virtual void visitYield(YieldAst* node) { Q_UNUSED(node); }; + virtual void visitName(NameAst* node) { Q_UNUSED(node); }; + virtual void visitCall(CallAst* node) { Q_UNUSED(node); }; + virtual void visitAttribute(AttributeAst* node) { Q_UNUSED(node); }; + virtual void visitSubscript(SubscriptAst* node) { Q_UNUSED(node); }; + virtual void visitList(ListAst* node) { Q_UNUSED(node); }; + virtual void visitTuple(TupleAst* node) { Q_UNUSED(node); }; + virtual void visitEllipsis(EllipsisAst* node) { Q_UNUSED(node); }; + virtual void visitSlice(SliceAst* node) { Q_UNUSED(node); }; + virtual void visitExtendedSlice(ExtendedSliceAst* node) { Q_UNUSED(node); }; + virtual void visitIndex(IndexAst* node) { Q_UNUSED(node); }; + virtual void visitArguments(ArgumentsAst* node) { Q_UNUSED(node); }; + virtual void visitKeyword(KeywordAst* node) { Q_UNUSED(node); }; + virtual void visitComprehension(ComprehensionAst* node) { Q_UNUSED(node); }; + virtual void visitExceptionHandler(ExceptionHandlerAst* node) { Q_UNUSED(node); }; + virtual void visitAlias(AliasAst* node) { Q_UNUSED(node); }; - virtual void visitCode( CodeAst* ) = 0; - virtual void visitFunctionDefinition( FunctionDefinitionAst* ) = 0; - virtual void visitDecorator( DecoratorAst* ) = 0; - virtual void visitArgument( ArgumentAst* ) = 0; - virtual void visitDefaultParameter( DefaultParameterAst* ) = 0; - virtual void visitIdentifierParameterPart( IdentifierParameterPartAst* ) = 0; - virtual void visitListParameterPart( ListParameterPartAst* ) = 0; - virtual void visitDictionaryParameter( DictionaryParameterAst* ) = 0; - virtual void visitListParameter( ListParameterAst* ) = 0; - virtual void visitIf( IfAst* ) = 0; - virtual void visitWhile( WhileAst* ) = 0; - virtual void visitFor( ForAst* ) = 0; - virtual void visitClassDefinition( ClassDefinitionAst* ) = 0; - virtual void visitTry( TryAst* ) = 0; - virtual void visitExcept( ExceptAst* ) = 0; - virtual void visitWith( WithAst* ) = 0; - virtual void visitExec( ExecAst* ) = 0; - virtual void visitGlobal( GlobalAst* ) = 0; - virtual void visitPlainImport( PlainImportAst* ) = 0; - virtual void visitStarImport( StarImportAst* ) = 0; - virtual void visitFromImport( FromImportAst* ) = 0; - virtual void visitRaise( RaiseAst* ) = 0; - virtual void visitPrint( PrintAst* ) = 0; - virtual void visitReturn( ReturnAst* ) = 0; - virtual void visitYield( YieldAst* ) = 0; - virtual void visitDel( DelAst* ) = 0; - virtual void visitAssert( AssertAst* ) = 0; - virtual void visitExpressionStatement( ExpressionStatementAst* ) = 0; - virtual void visitAssignment( AssignmentAst* ) = 0; - virtual void visitAtom( AtomAst* ) = 0; - virtual void visitEnclosure( EnclosureAst* ) = 0; - virtual void visitList( ListAst* ) = 0; - virtual void visitListFor( ListForAst* ) = 0; - virtual void visitListIf( ListIfAst* ) = 0; - virtual void visitLiteral( LiteralAst* ) = 0; - virtual void visitGenerator( GeneratorAst* ) = 0; - virtual void visitGeneratorFor( GeneratorForAst* ) = 0; - virtual void visitGeneratorIf( GeneratorIfAst* ) = 0; - virtual void visitDictionary( DictionaryAst* ) = 0; - virtual void visitAttributeReference( AttributeReferenceAst* ) = 0; - virtual void visitSubscript( SubscriptAst* ) = 0; - virtual void visitExtendedSlice( ExtendedSliceAst* ) = 0; - virtual void visitSimpleSlice( SimpleSliceAst* ) = 0; - virtual void visitProperSliceItem( ProperSliceItemAst* ) = 0; - virtual void visitExpressionSliceItem( ExpressionSliceItemAst* ) = 0; - virtual void visitEllipsisSliceItem( EllipsisSliceItemAst* ) = 0; - virtual void visitCall( CallAst* ) = 0; - virtual void visitUnaryExpression( UnaryExpressionAst* ) = 0; - virtual void visitBinaryExpression( BinaryExpressionAst* ) = 0; - virtual void visitComparison( ComparisonAst* ) = 0; - virtual void visitBooleanNotOperation( BooleanNotOperationAst* ) = 0; - virtual void visitBooleanAndOperation( BooleanAndOperationAst* ) = 0; - virtual void visitBooleanOrOperation( BooleanOrOperationAst* ) = 0; - virtual void visitConditionalExpression( ConditionalExpressionAst* ) = 0; - virtual void visitLambda( LambdaAst* ) = 0; - virtual void visitBreak( StatementAst* ) = 0; - virtual void visitContinue( StatementAst* ) = 0; - virtual void visitPass( StatementAst* ) = 0; - virtual void visitIdentifier( IdentifierAst* ) = 0; - virtual void visitIdentifierTarget( IdentifierTargetAst* ) = 0; - virtual void visitListTarget( ListTargetAst* ) = 0; - virtual void visitTupleTarget( TupleTargetAst* ) = 0; - virtual void visitAttributeReferenceTarget( AttributeReferenceTargetAst* ) = 0; - virtual void visitSubscriptTarget( SubscriptTargetAst* ) = 0; - virtual void visitSliceTarget( SliceTargetAst* ) = 0; }; } diff --git a/parser/kwcheck.cpp b/parser/kwcheck.cpp deleted file mode 100644 index c77a576..0000000 --- a/parser/kwcheck.cpp +++ /dev/null @@ -1,459 +0,0 @@ -/* KDevelop Python Support - * - * Copyright 2007 Andreas Pakulat - * - * 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. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#include "kwcheck.h" -#include "pythonparser.h" - -namespace PythonParser -{ - -int checkForKeyword( const QChar* txt, int len ) -{ - switch( len ) - { - case 2: - return kwcheck2(txt); - case 3: - return kwcheck3(txt); - case 4: - return kwcheck4(txt); - case 5: - return kwcheck5(txt); - case 6: - return kwcheck6(txt); - case 7: - return kwcheck7(txt); - case 8: - return kwcheck8(txt); - default: - return Parser::Token_IDENTIFIER; - } -} - -int kwcheck2( const QChar* s ) -{ - if( s[0] == 'a' ) - { - if( s[1] == 's' ) - { - return Parser::Token_AS; - } - }else if( s[0] == 'i' ) - { - if( s[1] == 's' ) - { - return Parser::Token_IS; - }else if( s[1] == 'n' ) - { - return Parser::Token_IN; - }else if( s[1] == 'f' ) - { - return Parser::Token_IF; - } - }else if( s[0] == 'o' ) - { - if( s[1] == 'r' ) - return Parser::Token_OR; - } - return Parser::Token_IDENTIFIER; -} - -int kwcheck3( const QChar* s ) -{ - if( s[0] == 'a' ) - { - if( s[1] == 'n' ) - { - if( s[2] == 'd' ) - { - return Parser::Token_AND; - } - } - } else if( s[0] == 'd' ) - { - if( s[1] == 'e' ) - { - if( s[2] == 'l' ) - { - return Parser::Token_DEL; - }else if( s[2] == 'f' ) - { - return Parser::Token_DEF; - } - } - } else if( s[0] == 'f' ) - { - if( s[1] == 'o' ) - { - if( s[2] == 'r' ) - { - return Parser::Token_FOR; - } - } - }else if( s[0] == 'n' ) - { - if( s[1] == 'o' ) - { - if( s[2] == 't' ) - { - return Parser::Token_NOT; - } - } - }else if( s[0] == 't' ) - { - if( s[1] == 'r' ) - { - if( s[2] == 'y' ) - { - return Parser::Token_TRY; - } - } - } - return Parser::Token_IDENTIFIER; -} - - -int kwcheck4( const QChar* s ) -{ - if( s[0] == 'e' ) - { - if( s[1] == 'l' ) - { - if( s[2] == 's' ) - { - if( s[3] == 'e' ) - { - return Parser::Token_ELSE; - } - }else if( s[2] == 'i' ) - { - if( s[3] == 'f' ) - { - return Parser::Token_ELIF; - } - } - }else if( s[1] == 'x' ) - { - if( s[2] == 'e' ) - { - if( s[3] == 'c' ) - { - return Parser::Token_EXEC; - } - } - } - }else if( s[0] == 'f' ) - { - if( s[1] == 'r' ) - { - if( s[2] == 'o' ) - { - if( s[3] == 'm' ) - { - return Parser::Token_FROM; - } - } - } - }else if( s[0] == 'p' ) - { - if( s[1] == 'a' ) - { - if( s[2] == 's' ) - { - if( s[3] == 's' ) - { - return Parser::Token_PASS; - } - } - } - } - return Parser::Token_IDENTIFIER; -} - -int kwcheck5( const QChar* s ) -{ - if( s[0] == 'b' ) - { - if( s[1] == 'r' ) - { - if( s[2] == 'e' ) - { - if( s[3] == 'a' ) - { - if( s[4] == 'k' ) - { - return Parser::Token_BREAK; - } - } - } - } - }else if( s[0] == 'c' ) - { - if( s[1] == 'l' ) - { - if( s[2] == 'a' ) - { - if( s[3] == 's' ) - { - if( s[4] == 's' ) - { - return Parser::Token_CLASS; - } - } - } - } - }else if( s[0] == 'p' ) - { - if( s[1] == 'r' ) - { - if( s[2] == 'i' ) - { - if( s[3] == 'n' ) - { - if( s[4] == 't' ) - { - return Parser::Token_PRINT; - } - } - } - } - }else if( s[0] == 'r' ) - { - if( s[1] == 'a' ) - { - if( s[2] == 'i' ) - { - if( s[3] == 's' ) - { - if( s[4] == 'e' ) - { - return Parser::Token_RAISE; - } - } - } - } - }else if( s[0] == 'w' ) - { - if( s[1] == 'h' ) - { - if( s[2] == 'i' ) - { - if( s[3] == 'l' ) - { - if( s[4] == 'e' ) - { - return Parser::Token_WHILE; - } - } - } - } - }else if( s[0] == 'y' ) - { - if( s[1] == 'i' ) - { - if( s[2] == 'e' ) - { - if( s[3] == 'l' ) - { - if( s[4] == 'd' ) - { - return Parser::Token_YIELD; - } - } - } - } - } - return Parser::Token_IDENTIFIER; -} - -int kwcheck6( const QChar* s ) -{ - if( s[0] == 'a' ) - { - if( s[1] == 's' ) - { - if( s[2] == 's' ) - { - if( s[3] == 'e' ) - { - if( s[4] == 'r' ) - { - if( s[5] == 't' ) - { - return Parser::Token_ASSERT; - } - } - } - } - } - }else if( s[0] == 'e' ) - { - if( s[1] == 'x' ) - { - if( s[2] == 'c' ) - { - if( s[3] == 'e' ) - { - if( s[4] == 'p' ) - { - if( s[5] == 't' ) - { - return Parser::Token_EXCEPT; - } - } - } - } - } - }else if( s[0] == 'g' ) - { - if( s[1] == 'l' ) - { - if( s[2] == 'o' ) - { - if( s[3] == 'b' ) - { - if( s[4] == 'a' ) - { - if( s[5] == 'l' ) - { - return Parser::Token_GLOBAL; - } - } - } - } - } - }else if( s[0] == 'i' ) - { - if( s[1] == 'm' ) - { - if( s[2] == 'p' ) - { - if( s[3] == 'o' ) - { - if( s[4] == 'r' ) - { - if( s[5] == 't' ) - { - return Parser::Token_IMPORT; - } - } - } - } - } - }else if( s[0] == 'l' ) - { - if( s[1] == 'a' ) - { - if( s[2] == 'm' ) - { - if( s[3] == 'b' ) - { - if( s[4] == 'd' ) - { - if( s[5] == 'a' ) - { - return Parser::Token_LAMBDA; - } - } - } - } - } - }else if( s[0] == 'r' ) - { - if( s[1] == 'e' ) - { - if( s[2] == 't' ) - { - if( s[3] == 'u' ) - { - if( s[4] == 'r' ) - { - if( s[5] == 'n' ) - { - return Parser::Token_RETURN; - } - } - } - } - } - } - return Parser::Token_IDENTIFIER; -} - -int kwcheck7( const QChar* s ) -{ - if( s[0] == 'f' ) - { - if( s[1] == 'i' ) - { - if( s[2] == 'n' ) - { - if( s[3] == 'a' ) - { - if( s[4] == 'l' ) - { - if( s[5] == 'l' ) - { - if( s[6] == 'y' ) - { - return Parser::Token_FINALLY; - } - } - } - } - } - } - } - return Parser::Token_IDENTIFIER; -} - -int kwcheck8( const QChar* s ) -{ - if( s[0] == 'c' ) - { - if( s[1] == 'o' ) - { - if( s[2] == 'n' ) - { - if( s[3] == 't' ) - { - if( s[4] == 'i' ) - { - if( s[5] == 'n' ) - { - if( s[6] == 'u' ) - { - if( s[7] == 'e' ) - { - return Parser::Token_CONTINUE; - } - } - } - } - } - } - } - } - return Parser::Token_IDENTIFIER; -} - -} - -// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/parser/kwcheck.h b/parser/kwcheck.h deleted file mode 100644 index b4a54ca..0000000 --- a/parser/kwcheck.h +++ /dev/null @@ -1,42 +0,0 @@ -/* KDevelop Python Support - * - * Copyright 2007 Andreas Pakulat - * - * 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. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#ifndef KWCHECK_H -#define KWCHECK_H - -class QChar; - -namespace PythonParser -{ - -int kwcheck2( const QChar* s ); -int kwcheck3( const QChar* s ); -int kwcheck4( const QChar* s ); -int kwcheck5( const QChar* s ); -int kwcheck6( const QChar* s ); -int kwcheck7( const QChar* s ); -int kwcheck8( const QChar* s ); -int checkForKeyword( const QChar* txt, int len ); - -} - -#endif - -// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/parser/numbercheck.cpp b/parser/numbercheck.cpp deleted file mode 100644 index fb07025..0000000 --- a/parser/numbercheck.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* KDevelop Python Support - * - * Copyright 2007 Andreas Pakulat - * - * 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. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#include "numbercheck.h" -#include "pythonparser.h" - -#include -#include -#include - -namespace PythonParser -{ -QRegExp intnum("((0|[1-9][0-9]*)|0[0-7]+|0(x|X)[0-9a-fA-F]+)(l|L)?"); -QRegExp floatnum("(([0-9]+)?\\.[0-9]+|[0-9]+\\.)|(([0-9]+|(([0-9]+)?\\.[0-9]+|[0-9]+\\.))(e|E)[-+]?[0-9]+)"); -QRegExp imagnum("("+floatnum.pattern()+"|[0-9]+)(j|J)"); - -int getTokenForNumberString( const QString& s ) -{ - if( intnum.exactMatch( s ) ) - { - return Parser::Token_INTEGER; - }else if( floatnum.exactMatch( s ) ) - { - return Parser::Token_FLOAT; - }else if( imagnum.exactMatch( s ) ) - { - return Parser::Token_IMAGNUM; - } - return Parser::Token_INVALID; -} -} - -// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/parser/numbercheck.h b/parser/numbercheck.h deleted file mode 100644 index e8271c2..0000000 --- a/parser/numbercheck.h +++ /dev/null @@ -1,35 +0,0 @@ -/* KDevelop Python Support - * - * Copyright 2007 Andreas Pakulat - * - * 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. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#ifndef NUMBERCHECK_H -#define NUMBERCHECK_H - -class QString; - -namespace PythonParser -{ - -int getTokenForNumberString( const QString& ); - -} - -#endif - -// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/parser/parserConfig.h.in b/parser/parserConfig.h.in new file mode 100644 index 0000000..783dac2 --- /dev/null +++ b/parser/parserConfig.h.in @@ -0,0 +1 @@ +#define INSTALL_PATH "@BIN_INSTALL_DIR@" \ No newline at end of file diff --git a/parser/parsesession.cpp b/parser/parsesession.cpp index b8b715b..afc00d4 100644 --- a/parser/parsesession.cpp +++ b/parser/parsesession.cpp @@ -1,6 +1,7 @@ /***************************************************************************** * Copyright (c) 2007 Andreas Pakulat * * Copyright (c) 2007 Piyush verma * + * Copyright 2010 Sven Brauch * * * * Permission is hereby granted, free of charge, to any person obtaining * * a copy of this software and associated documentation files (the * @@ -26,6 +27,7 @@ #include "pythondriver.h" #include +#include "astbuilder.h" using namespace KDevelop; @@ -39,9 +41,9 @@ ParseSession::~ParseSession() { } -void ParseSession::setCurrentDocument(IndexedString& filename) +void ParseSession::setCurrentDocument(KUrl& filename) { - m_currentDocument = filename; + m_currentDocument = KDevelop::IndexedString(filename); } IndexedString ParseSession::currentDocument() @@ -60,12 +62,14 @@ void ParseSession::setContents( const QString& contents ) m_contents = contents; } -bool ParseSession::parse( Python::CodeAst** ast ) +QPair ParseSession::parse( Python::CodeAst* ast ) { - Python::Driver d; - d.setContent( m_contents ); - kDebug() << m_contents; - return d.parse( ast ); + Driver driver; + driver.setCurrentDocument(m_currentDocument.toUrl()); + driver.setContent(m_contents); + QPair result = driver.parse(ast); + m_problems = driver.m_problems; + return result; } } diff --git a/parser/parsesession.h b/parser/parsesession.h index 7942a6f..91f9b8f 100644 --- a/parser/parsesession.h +++ b/parser/parsesession.h @@ -25,12 +25,14 @@ #define PYTHON_PARSESESSION_H #include #include "parserexport.h" -#include "pythonparser.h" #include #include #include #include #include "ast.h" +#include "kurl.h" + +#include using namespace KDevelop; @@ -50,10 +52,12 @@ class KDEVPYTHONPARSER_EXPORT ParseSession void setContents( const QString& contents ); QString contents() const; - void setCurrentDocument(IndexedString& filename); + void setCurrentDocument(KUrl& filename); IndexedString currentDocument(); - bool parse( Python::CodeAst** ); + QPair parse( Python::CodeAst* ast ); + + QList m_problems; void mapAstUse(Ast* node, const SimpleUse& use) { @@ -63,7 +67,7 @@ class KDEVPYTHONPARSER_EXPORT ParseSession private: QString m_contents; - IndexedString m_currentDocument; + KDevelop::IndexedString m_currentDocument; }; diff --git a/parser/python.g b/parser/python.g deleted file mode 100644 index 2afc5a4..0000000 --- a/parser/python.g +++ /dev/null @@ -1,675 +0,0 @@ -------------------------------------------------------------------------------- --- Copyright (c) 2006 Andreas Pakulat --- Copyright (c) 2007 Piyush Verma --- --- Permission is hereby granted, free of charge, to any person obtaining --- a copy of this software and associated documentation files (the --- "Software"), to deal in the Software without restriction, including --- without limitation the rights to use, copy, modify, merge, publish, --- distribute, sublicense, and/or sell copies of the Software, and to --- permit persons to whom the Software is furnished to do so, subject to --- the following conditions: --- --- The above copyright notice and this permission notice shall be --- included in all copies or substantial portions of the Software. --- --- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, --- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF --- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND --- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE --- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION --- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION --- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- - ------------------------------------------------------------ --- Grammar for Python2.5 --- Modelled after the Grammar files shipped with Python2.4 --- source, the Python Language Reference documentation, --- also included with Python --- And after the Python 2.3.3 Antlr grammar found on the --- antlr grammar list page ------------------------------------------------------------ - ------------------------------------------------------------ --- TODO: Error recovery --- %parserclass (private declaration) --- [: --- parser::pythonCompatibilityMode m_compatibilityMode; --- --- struct ParserState { --- }; --- ParserState m_state; --- :] --- Parser::parserState *Parser::copyCurrentState() --- { --- ParserState *state = new ParserState(); --- return state; --- } --- --- void Parser::restoreState( Parser::ParserState *state ) --- { --- } --- --- Then a rule like (stmt)* -> project can be written --- as try/recover(stmt)* -> project and the parser --- will skip any errornous statements - ------------------------------------------------------------ --- Global declarations ------------------------------------------------------------ - -[: - -#include -#include - -namespace PythonParser -{ - class Lexer; - - enum OperatorType { - LeftShiftOp, - RightShiftOp, - PlusOp, - MinusOp, - StarOp, - SlashOp, - ModuloOp, - DoubleSlashOp, - UnaryPlusOp, - UnaryMinusOp, - UnaryTildeOp, - PlusEqOp, - MinusEqOp, - StarEqOp, - SlashEqOp, - ModuloEqOp, - AndEqOp, - OrEqOp, - HatEqOp, - LeftShiftEqOp, - RightShiftEqOp, - DoublestarEqOp, - DoubleslashEqOp, - LessOp, - GreaterOp, - IsEqualOp, - GreaterEqOp, - LessEqOp, - UnEqualOp, - InOp, - NotInOp, - IsOp, - IsNotOp - }; - enum NumericType { - IntegerNumeric, - FloatNumeric, - ImaginaryNumeric - }; -} -:] - - ------------------------------------------------------------- --- Export macro to use the parser in a shared lib ------------------------------------------------------------- -%export_macro "KDEVPYTHONPARSER_EXPORT" -%export_macro_header "parserexport.h" - ------------------------------------------------------------- --- Parser class members ------------------------------------------------------------- - -%parserclass (public declaration) -[: - /** - * Transform the raw input into tokens. - * When this method returns, the parser's token stream has been filled - * and any parse_*() method can be called. - */ - void tokenize( const QString& contents ); - - enum ProblemType { - Error, - Warning, - Info - }; - void reportProblem( Parser::ProblemType type, const QString& message ); - QString tokenText(qint64 begin, qint64 end); - void setDebug(bool debug); - -:] - -%parserclass (private declaration) -[: - QString mContents; - bool mDebug; -:] - ------------------------------------------------------------ --- List of defined tokens ------------------------------------------------------------ - --- keywords -%token AND ("and"), DEL ("del"), FOR ("for"), IS ("is"), RAISE ("raise"), - ASSERT ("assert"), ELIF ("elif"), FROM ("from"), LAMBDA ("lambda"), - RETURN ("returns"), BREAK ("break"), ELSE ("else"), GLOBAL ("global"), - NOT ("not"), TRY ("try"), CLASS ("class"), EXCEPT ("except"), IF ("if"), - OR ("or"), WHILE ("while"), CONTINUE ("continue"), EXEC ("exec"), - IMPORT ("import"), PASS ("pass"), YIELD ("yield"), DEF ("def"), IN ("in"), - PRINT ("print"), FINALLY ("finally"), AS ("as") ;; - --- indentation which is important in python and linebreak -%token INDENT ("indent"), DEDENT ("dedent"), LINEBREAK ("linebreak") ;; - --- Identifiers, Strings and numbers -%token STRINGLITERAL ("stringliteral"), IDENTIFIER ("identifier"), - INTEGER ("integer"), FLOAT ("float"), IMAGNUM ("imagnum") ;; - --- separators -%token LPAREN ("lparen"), RPAREN ("rparen"), LBRACE ("lbrace"), RBRACE ("rbrace"), - LBRACKET ("lbracket"), RBRACKET ("rbracket"), COMMA ("comma"), AT ("at"), - SEMICOLON ("semicolon"), COLON ("colon"), DOT ("dot"), BACKTICK ("backtick") ;; - --- operators -%token ELLIPSIS ("ellipsis"), STAR ("star"), DOUBLESTAR ("doublestar"), EQUAL ("equal"), - PLUS ("plus"), MINUS ("minus"), TILDE ("tilde"), SLASH ("slash"), - DOUBLESLASH ("doubleslash"), MODULO ("modulo"), AND ("and"), LSHIFT ("lshift"), - RSHIFT ("rshift"), PLUSEQ ("pluseq"), MINUSEQ ("minuseq"), SLASHEQ ("slasheq"), - DOUBLESLASHEQ ("doubleslasheq"), MODULOEQ ("moduloeq"), ANDEQ ("andeq"), - STAREQ ("stareq"), DOUBLESTAREQ ("doublestareq"), LSHIFTEQ ("lshifteq"), - RSHIFTEQ ("rshifteq"), LESS ("less"), GREATER ("greater"), GREATEREQ ("greatereq"), - LESSEQ ("lesseq"), UNEQUAL ("unequal"), OR ("or"), BITXOR ("bitxor"), ISEQUAL ("isequal"), - TILDEEQ ("tildeeq"), OREQ ("oreq"), BITAND ("bitand") , BITOR ("bitor");; - - --- token that makes the parser fail in any case: -%token INVALID ("invalid token") ;; - --- The actual grammar starts here. - - ( #stmt = stmt )* --> project ;; - - AT decoratorName = dottedName ( LPAREN ( arguments=arglist | 0) RPAREN | 0 ) LINEBREAK --> decorator ;; - - (#decorator = decorator )+ --> decorators ;; - --- Function Definition: Can start with Decorators. --- varargslist defines the Function Variable Arguements - ( decorators = decorators | 0 ) - DEF funcName=IDENTIFIER LPAREN ( ?[: LA(1).kind != Token_RPAREN :] ( funArgs = varargslist ) - | 0 ) - RPAREN COLON funSuite=suite --> funcdecl ;; - --- Function variable Arguement List - ( funcDef=funcDef | 0 ) ( - ?[: yytoken != Token_RPAREN && LA(2).kind == Token_IDENTIFIER:] (funPosParam = funPosParam ) - | 0 - ) --> varargslist ;; - --- The Vararguement trailer, defines *args and **args - ( listParam=listParam ( COMMA dictParam=dictParam | 0 ) - | dictParam=dictParam ) --> funPosParam;; - - DOUBLESTAR doubleStarId=IDENTIFIER --> dictParam;; - - STAR starId=IDENTIFIER --> listParam;; - --- Function Definition - #fpDef=fpDef ( COMMA [:if(yytoken == Token_RPAREN || yytoken == Token_STAR || yytoken == Token_DOUBLESTAR ) { break; } :] #fpDef=fpDef )* --> funcDef ;; - - --- Function parameter Defintion - defparam=defparam ( EQUAL fpDefTest=test | 0 ) --> fpDef ;; - --- Function Parameter Definition - LPAREN (fplist = fplist) RPAREN - | paramname=IDENTIFIER --> defparam ;; - - --- Function parameter List - #fplistFpdef=defparam - ( COMMA [: if ( yytoken == Token_RPAREN ) - { break; } :] - #fplistFpdef=defparam )* --> fplist ;; - --- A statement could be simple statement, a compound statement OR just a Linebreak - simpleStmt = simpleStmt - | compoundStmt = compoundStmt - | LINEBREAK --> stmt ;; - --- simple statement, TODO this needs more work for simpleStmts at the enf of files - #smallStmt = smallStmt - ( SEMICOLON [: if( yytoken == Token_LINEBREAK || yytoken == Token_DEDENT) { break;} :] #smallStmt = smallStmt )* LINEBREAK --> simpleStmt ;; - --- a small statement could be of any such kinds - exprStmt = exprStmt - | printStmt = printStmt - | delStmt = delStmt - | passStmt = passStmt - | flowStmt = flowStmt - | importStmt = importStmt - | globalStmt= globalStmt - | execStmt = execStmt - | assertStmt = assertStmt --> smallStmt ;; - - (testlist = testlist) ( augassign = augassign ( anugassignTestlist = testlist | yield=yieldExpr ) - | ( EQUAL ( yield=yieldExpr | #equalTestlist = testlist ) )+ - | ?[: yytoken == Token_SEMICOLON || yytoken == Token_LINEBREAK :] 0 ) --> exprStmt ;; - - PLUSEQ [: (*yynode)->assignOp = PythonParser::PlusEqOp; :] - | MINUSEQ [: (*yynode)->assignOp = PythonParser::MinusEqOp; :] - | STAREQ [: (*yynode)->assignOp = PythonParser::StarEqOp; :] - | SLASHEQ [: (*yynode)->assignOp = PythonParser::SlashEqOp; :] - | MODULOEQ [: (*yynode)->assignOp = PythonParser::ModuloEqOp; :] - | ANDEQ [: (*yynode)->assignOp = PythonParser::AndEqOp; :] - | OREQ [: (*yynode)->assignOp = PythonParser::OrEqOp; :] - | TILDEEQ [: (*yynode)->assignOp = PythonParser::HatEqOp; :] - | LSHIFTEQ [: (*yynode)->assignOp = PythonParser::LeftShiftEqOp; :] - | RSHIFTEQ [: (*yynode)->assignOp = PythonParser::RightShiftEqOp; :] - | DOUBLESTAREQ [: (*yynode)->assignOp = PythonParser::DoublestarEqOp; :] - | DOUBLESLASHEQ [: (*yynode)->assignOp = PythonParser::DoubleslashEqOp;:] --> augassign [ - member variable assignOp : PythonParser::OperatorType; ];; - - PRINT - ( - (#printArgs=test ( COMMA [: if(yytoken == Token_SEMICOLON || yytoken == Token_LINEBREAK) {break; } :]#printArgs=test )*) - | RSHIFT #rshiftArgs=test ( ( COMMA [: if(yytoken == Token_SEMICOLON || yytoken == Token_LINEBREAK) {break; } :]#rshiftArgs=test )*) - | 0 - ) --> printStmt ;; - - DEL delList=exprlist --> delStmt ;; - - PASS --> passStmt ;; - - breakStmt=breakStmt - | continueStmt=continueStmt - | returnStmt=returnStmt - | raiseStmt=raiseStmt - | yieldStmt=yieldStmt --> flowStmt ;; - - BREAK --> breakStmt ;; - - CONTINUE --> continueStmt ;; - - RETURN ( returnExpr=testlist | 0 ) --> returnStmt ;; - - YIELD ( expr=testlist | 0 ) --> yieldExpr ;; - - yield=yieldExpr --> yieldStmt ;; - - RAISE ( type=test ( COMMA value=test ( COMMA traceback=test | 0 ) | 0 ) | 0 ) --> raiseStmt ;; - - importImport=importName - | importFrom=importFrom --> importStmt ;; - - IMPORT importName=dottedAsNames --> importName ;; - - FROM importFromName=dottedName IMPORT ( STAR | LPAREN importAsNames=importAsNames RPAREN | importAsNames=importAsNames ) --> importFrom ;; - - importedName=IDENTIFIER ( AS importedAs=IDENTIFIER | 0 ) --> importAsName ;; - - importDottedName=dottedName ( AS importedAs=IDENTIFIER | 0 ) --> dottedAsName ;; - - #importAsName=importAsName - ( COMMA [: if( yytoken == Token_RPAREN || yytoken == Token_LINEBREAK || yytoken == Token_SEMICOLON ) { break;} :] #importAsName=importAsName)* --> importAsNames ;; - - #dottedAsName=dottedAsName ( COMMA #dottedAsName=dottedAsName )* --> dottedAsNames ;; - - #dottedName=IDENTIFIER ( DOT #dottedName=IDENTIFIER )* --> dottedName ;; - - GLOBAL #globalName=IDENTIFIER ( COMMA #globalName=IDENTIFIER )* --> globalStmt ;; - - EXEC execCode=expr ( IN globalDictExec=test ( COMMA localDictExec=test | 0 ) | 0 ) --> execStmt ;; - - ASSERT assertNotTest=test ( COMMA assertRaiseTest=test | 0 ) --> assertStmt ;; - - ifStmt=ifStmt - | whileStmt=whileStmt - | forStmt=forStmt - | tryStmt=tryStmt - | funcdecl=funcdecl - | classdef=classdef --> compoundStmt ;; - - IF ifTest=test COLON ifSuite=suite ( ELIF #elifTest=test COLON #elifSuite=suite )* ( ELSE COLON ifElseSuite=suite | 0 ) --> ifStmt ;; - - WHILE whileTest=test COLON whileSuite=suite ( ELSE COLON whileElseSuite=suite | 0 ) --> whileStmt ;; - - FOR forExpr=exprlist IN forTestlist=testlist COLON forSuite=suite ( ELSE COLON forElseSuite=suite | 0 ) --> forStmt ;; - - TRY COLON trySuite=suite - ( ( #exceptClause=exceptClause COLON #exceptSuite=suite )+ ( ELSE COLON tryElseSuite=suite | 0 ) | FINALLY COLON finallySuite=suite ) --> tryStmt ;; - - EXCEPT ( exceptTest=test ( COMMA exceptTargetTest=test | 0 ) | 0 ) --> exceptClause ;; - - simpleStmt=simpleStmt | (LINEBREAK)+ INDENT (#stmt=stmt)+ DEDENT --> suite ;; - - #andTest=andTest ( OR #andTest=andTest )* | lambdaDef=lambdaDef --> test ;; - - #notTest=notTest ( AND #notTest=notTest )* --> andTest ;; - - NOT notTest=notTest | comparison=comparison --> notTest ;; - - compExpr=expr ( #compOp=compOp #compOpExpr=expr )* --> comparison ;; - - LESS [: (*yynode)->compOp = PythonParser::LessOp; :] - | GREATER [: (*yynode)->compOp = PythonParser::GreaterOp; :] - | ISEQUAL [: (*yynode)->compOp = PythonParser::IsEqualOp; :] - | GREATEREQ [: (*yynode)->compOp = PythonParser::GreaterEqOp; :] - | LESSEQ [: (*yynode)->compOp = PythonParser::LessEqOp; :] - | UNEQUAL [: (*yynode)->compOp = PythonParser::UnEqualOp; :] - | IN [: (*yynode)->compOp = PythonParser::InOp; :] - | NOT IN [: (*yynode)->compOp = PythonParser::NotInOp; :] - | IS (NOT [: (*yynode)->compOp = PythonParser::IsNotOp; :] - | 0 [: (*yynode)->compOp = PythonParser::IsOp; :] - ) --> compOp [ - member variable compOp : PythonParser::OperatorType; ];; - - expr=xorExpr ( BITOR #orrExpr=xorExpr )* --> expr ;; - - xorExpr=andExpr ( BITXOR #hatXorExpr=andExpr )* --> xorExpr ;; - - andExpr=shiftExpr ( BITAND #anddShifExpr=shiftExpr )* --> andExpr ;; - - arithExpr=arithExpr - ( #shiftOpList=shiftOp #arithExprList=arithExpr )* --> shiftExpr ;; - - LSHIFT [: (*yynode)->shiftOp = PythonParser::LeftShiftOp; :] - | RSHIFT [: (*yynode)->shiftOp = PythonParser::RightShiftOp; :] --> shiftOp [ - member variable shiftOp : PythonParser::OperatorType; ];; - - arithTerm=term - ((#arithOpList = arithOp #arithTermList=term )+ | 0) --> arithExpr ;; - - PLUS [: (*yynode)->arithOp = PythonParser::PlusOp; :] - | MINUS [: (*yynode)->arithOp = PythonParser::MinusOp; :] --> arithOp [ - member variable arithOp: PythonParser::OperatorType; ] ;; - - factor=factor - ((#termOp = termOp #factors=factor )+ | 0) --> term ;; - - STAR [: (*yynode)->op = PythonParser::StarOp; :] - | SLASH [: (*yynode)->op = PythonParser::SlashOp; :] - | MODULO [: (*yynode)->op = PythonParser::ModuloOp; :] - | DOUBLESLASH [: (*yynode)->op = PythonParser::DoubleSlashOp; :] --> termOp [ - member variable op : PythonParser::OperatorType; ];; - - ( factOp=factOp) factor=factor | power=power --> factor ;; - - PLUS [: (*yynode)->op = PythonParser::UnaryPlusOp; :] - | MINUS [: (*yynode)->op = PythonParser::UnaryMinusOp; :] - | TILDE [: (*yynode)->op = PythonParser::UnaryTildeOp ; :] --> factOp [ - member variable op : PythonParser::OperatorType; ];; - - ( atom=atom ) - (#trailer=trailer)* ( DOUBLESTAR factor=factor | 0 ) --> power ;; - - LPAREN ( yield=yieldExpr | - ( testlist=testlist ( genFor=genFor | 0 ) ) | 0 [: (*yynode)->listmaker = create(); :] ) RPAREN - | LBRACKET ( listmaker=listmaker | 0 [: (*yynode)->listmaker = create(); :] ) RBRACKET - | LBRACE dictmaker=dictmaker RBRACE - | BACKTICK codeexpr=codeexpr BACKTICK - | atomIdentifierName=IDENTIFIER - | number=number - | (#stringliteral=STRINGLITERAL)+ --> atom ;; - - value=INTEGER [: (*yynode)->numType = PythonParser::IntegerNumeric; :] - | value=FLOAT [: (*yynode)->numType = PythonParser::FloatNumeric; :] - | value=IMAGNUM [: (*yynode)->numType = PythonParser::ImaginaryNumeric; :] --> number [ - member variable numType: PythonParser::NumericType; ];; - - ( #listTest=test ( COMMA [: if (yytoken == Token_RBRACKET) { break; } :] #listTest=test )* | 0) --> listMakerTest ;; - - listMakerTest=listMakerTest (listFor=listFor | 0) --> listmaker ;; - - LAMBDA ( lambdaVarargslist=varargslist | 0 ) COLON lambdaTest=test --> lambdaDef ;; - - LPAREN ( trailerArglist=arglist | 0 ) RPAREN | LBRACKET subscriptlist=subscriptlist RBRACKET | DOT trailerDotName=IDENTIFIER --> trailer ;; - - #subscript=subscript ( COMMA [: (*yynode)->hasComma = true; if (yytoken == Token_RBRACKET) { break; } :] - #subscript=subscript )* --> subscriptlist [ - member variable hasComma: bool; ] ;; - --- Sub Scripts Check if the curent token is not a COLON it should be a test --- If a COLON it skips the 'test'. if the next token is not RBRACKET or COMMA after test it can be a COLON. --- Else it ends. - ELLIPSIS [: (*yynode)->isEllipsis = true; :] - | ( ?[: yytoken != Token_COLON :] begin=test | 0 ) - ( ?[: yytoken == Token_RBRACKET || yytoken == Token_COMMA :] 0 - | COLON [: (*yynode)->hasColon = true; :] ( end=test | 0 ) - ( COLON [: (*yynode)->hasColon = true; :] - ( step=test | 0 ) | 0 ) ) --> subscript - [: (*yynode)->isEllipsis = false; - (*yynode)->hasColon = false; :] - [ member variable isEllipsis: bool; - member variable hasColon: bool; ] ;; - - #expr=expr - ( COMMA [: if (yytoken == Token_IN || yytoken == Token_SEMICOLON || yytoken == Token_LINEBREAK ) { break; } :] - #expr=expr )* --> exprlist ;; - - #tests=test ( COMMA [: if( yytoken == Token_COLON || yytoken == Token_SEMICOLON || yytoken == Token_RPAREN || yytoken == Token_LINEBREAK) {break;} :] - #tests=test )* --> testlist ;; - - #test=test ( ( COMMA #test=test )+ ( COMMA | 0 ) | 0 ) --> testlistSafe ;; - - (#keyList=test COLON #valueList=test | 0) ( COMMA [: if (yytoken == Token_RBRACE) { break; } :] - #keyList=test COLON #valueList=test )* --> dictmaker ;; - - CLASS className=IDENTIFIER ( ( LPAREN (testlist=testlist | 0) RPAREN ) | 0 ) COLON classSuite=suite --> classdef ;; - - #arguments=argument - ( COMMA [: if(yytoken == Token_RPAREN || yytoken == Token_STAR || yytoken == Token_DOUBLESTAR) { break; } :] #arguments=argument)* --> plainArgumentsList ;; - - (argListBegin=plainArgumentsList | 0) - ( - ( STAR arglistStar=test (?[: LA(1).kind != Token_RPAREN :] COMMA DOUBLESTAR arglistDoublestar=test | 0) - | DOUBLESTAR arglistDoublestar=test) - | 0 ) --> arglist ;; - - argumentTest=test ( EQUAL argumentEqualTest=test - | ?[: yytoken == Token_FOR :] genFor=genFor - | ?[: yytoken == Token_RPAREN || yytoken == Token_STAR || yytoken == Token_DOUBLESTAR || yytoken == Token_COMMA :] 0 ) --> argument ;; - - listFor=listFor | listIf=listIf --> listIter ;; - - FOR exprlist=exprlist IN testlistSafe=testlistSafe ( listIter=listIter | 0 ) --> listFor ;; - - IF test=test ( listIter=listIter | 0 ) --> listIf ;; - - genFor=genFor - | genIf=genIf --> genIter ;; - - FOR exprlist=exprlist IN test=test ( genIter=genIter | 0 ) --> genFor ;; - - IF test=test ( genIter=genIter | 0 ) --> genIf ;; - - #test=test ( COMMA #test=test )* --> codeexpr ;; - ------------------------------------------------------------------ --- Code segments copied to the implementation (.cpp) file. --- If existent, kdevelop-pg's current syntax requires this block --- to occur at the end of the file. ------------------------------------------------------------------ - -[: -#include "pythonlexer.h" -#include -#include - -namespace PythonParser -{ - -void Parser::tokenize( const QString& contents ) -{ - mContents = contents; - kDebug() << mContents; - Lexer lexer( this, contents ); - int kind = Parser::Token_EOF; - - do - { - kind = lexer.nextTokenKind(); - if ( !kind ) // when the lexer returns 0, the end of file is reached - { - //Parser::Token &tt = tokenStream->next(); - //tt.kind = Parser::Token_LINEBREAK; - //tt.begin = lexer.tokenBegin(); - //tt.end = lexer.tokenEnd(); - kind = Parser::Token_EOF; - } - Parser::Token &t = tokenStream->next(); - t.begin = lexer.tokenBegin(); - t.end = lexer.tokenEnd(); - t.kind = kind; -// if( mDebug ) - kDebug() << kind << tokenText(t.begin,t.end) << t.begin << t.end; - } - while ( kind != Parser::Token_EOF ); - - yylex(); // produce the look ahead token -} - - -QString Parser::tokenText(qint64 begin, qint64 end) -{ - return mContents.mid(begin,end-begin+1); -} - - -void Parser::reportProblem( Parser::ProblemType type, const QString& message ) -{ - if (type == Error) - kDebug() << "** ERROR:" << message; - else if (type == Warning) - kDebug() << "** WARNING:" << message; - else if (type == Info) - kDebug() << "** Info:" << message; -} - - -// custom error recovery -void Parser::expectedToken(int /*expected*/, qint64 /*where*/, const QString& name) -{ - reportProblem( Parser::Error, QString("Expected token \"%1\"").arg(name)); -} - -void Parser::expectedSymbol(int /*expectedSymbol*/, const QString& name) -{ - qint64 line; - qint64 col; - qint64 index = tokenStream->index()-1; - Token &token = tokenStream->token(index); - kDebug() << "token starts at:" << token.begin; - kDebug() << "index is:" << index; - tokenStream->startPosition(index, &line, &col); - QString tokenValue = tokenText(token.begin, token.end); - reportProblem( Parser::Error, - QString("Expected symbol \"%1\" (current token: \"%2\" [%3] at line: %4 col: %5)") - .arg(name) - .arg(token.kind != 0 ? tokenValue : "EOF") - .arg(token.kind) - .arg(line) - .arg(col)); -} - -void Parser::setDebug( bool debug ) -{ - mDebug = debug; -} - - - -} // end of namespace cool - -:] - --- kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; auto-insert-doxygen on - diff --git a/parser/pythondriver.cpp b/parser/pythondriver.cpp index 223d595..9bfe9c4 100644 --- a/parser/pythondriver.cpp +++ b/parser/pythondriver.cpp @@ -20,7 +20,6 @@ #include "pythondriver.h" -#include "pythonparser.h" #include #include @@ -29,6 +28,12 @@ #include "astbuilder.h" +#include +#include + + +using namespace KDevelop; + namespace Python { @@ -60,41 +65,26 @@ void Driver::setDebug( bool debug ) m_debug = debug; } -bool Driver::parse( Python::CodeAst** ast ) +void Driver::setCurrentDocument(KUrl url) +{ + m_currentDocument = url; +} + +QPair Driver::parse( Python::CodeAst* /* ast */) { - if(!m_tokenstream) - m_tokenstream = new KDevPG::TokenStream(); - if(!m_pool) - m_pool = new KDevPG::MemoryPool(); - - PythonParser::Parser pythonparser; - pythonparser.setTokenStream( m_tokenstream ); - pythonparser.setMemoryPool( m_pool ); - pythonparser.setDebug( m_debug ); - - pythonparser.tokenize(m_content); - PythonParser::ProjectAst* srcast; - bool matched = pythonparser.parseProject( &srcast ); - if( matched ) + AstBuilder pythonparser; + QPair matched; + matched.first = pythonparser.parse(m_currentDocument, m_content); + matched.second = matched.first ? true : false; // check wether an AST was returned and react accordingly + + m_problems = pythonparser.m_problems; + + if( matched.second ) { kDebug() << "Sucessfully parsed"; -// if( m_debug ) -// { -// PythonParser::DebugVisitor d( pythonparser.tokenStream ); -// d.visitProject(*srcast); -// } - Python::AstBuilder builder(&pythonparser); - builder.visitProject( srcast ); - for ( int i=0; i < builder.mNodeStack.count(); i++ ) { - Ast* dbg_node = builder.mNodeStack.at(i); - kDebug() << dbg_node; - } - *ast = builder.codeAst(); - }else { - *ast = 0; - pythonparser.expectedSymbol(PythonParser::AstNode::ProjectKind, "project"); + matched.first = 0; kDebug() << "Couldn't parse content"; } return matched; diff --git a/parser/pythondriver.h b/parser/pythondriver.h index 9104d55..af463f2 100644 --- a/parser/pythondriver.h +++ b/parser/pythondriver.h @@ -23,6 +23,11 @@ #include #include "parserexport.h" +#include + +#include + + namespace KDevPG { class MemoryPool; @@ -45,15 +50,19 @@ class KDEVPYTHONPARSER_EXPORT Driver bool readFile( const QString&, const char* = 0 ); void setContent( const QString& ); void setDebug( bool ); - bool parse( Python::CodeAst** ast ); + QPair parse( Python::CodeAst* ast ); void setTokenStream( KDevPG::TokenStream* ); void setMemoryPool( KDevPG::MemoryPool* ); + void setCurrentDocument(KUrl url); + + QList m_problems; + private: QString m_content; bool m_debug; KDevPG::MemoryPool* m_pool; KDevPG::TokenStream* m_tokenstream; - + KUrl m_currentDocument; }; } diff --git a/parser/pythonlexer.cpp b/parser/pythonlexer.cpp deleted file mode 100644 index c951aac..0000000 --- a/parser/pythonlexer.cpp +++ /dev/null @@ -1,651 +0,0 @@ -/* KDevelop QMake Support - * - * Copyright 2007 Andreas Pakulat - * - * 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. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#include "pythonlexer.h" - -#include -#include -#include -#include -#include "pythonparser.h" -#include -#include - -#include "kwcheck.h" -#include "numbercheck.h" - -namespace PythonParser -{ - -/* - * @TODO: COMMENT all the stuff, really needed - */ - -Lexer::Lexer( Parser* _parser, const QString& content ): - m_content( content ), m_parser( _parser ), - m_curpos( 0 ), m_contentSize( m_content.size() ), - m_tokenBegin( 0 ), m_tokenEnd( 0 ), m_openParenNum( 0 ) -{ - pushState( ErrorState ); - pushState( DefaultState ); - pushState( IndentState ); - pushIndentation( 0 ); -} - -int Lexer::state() const -{ - return m_state.top(); -} - -void Lexer::pushState( int state ) -{ - m_state.push( state ); -} - -void Lexer::popState() -{ - m_state.pop(); -} - -int Lexer::indentation() const -{ - return m_indentation.top(); -} - -void Lexer::pushIndentation( int indentation ) -{ - m_indentation.push( indentation ); -} - -void Lexer::popIndentation() -{ - m_indentation.pop(); -} - -int Lexer::nextTokenKind() -{ - kDebug() << "nextTokenKind called"; - int token = Parser::Token_INVALID; - if ( m_curpos >= m_contentSize ) - { - if( indentation() > 0 ) - { - popIndentation(); - m_tokenEnd = m_curpos; - return Parser::Token_DEDENT; - } - m_tokenBegin = -1; - m_tokenEnd = -1; - return 0; - } - QChar* it = m_content.data(); - it += m_curpos; - if( state() == IndentState ) - { - // Check wether we need to indent or dedent, this is quite complicated. - // Especially because it contains 3 exit points and one point that resets - // the current position and it and then lets the rest of the code run - if( !it->isSpace() && it->unicode() != '#' && indentation() > 0 ) - { - // No whitespace at the start of the line, so we need to create - // as many DEDENT tokens as we have indenations in the stack - token = Parser::Token_DEDENT; - m_tokenBegin = m_curpos; - m_tokenEnd = m_curpos; - popIndentation(); - return token; - }else if( it->isSpace() ) - { - // We've got indentation, lets see how much - m_tokenBegin = m_curpos; - int spacecount = 0; - while( it->isSpace() && it->unicode() != '\n' && m_curpos < m_contentSize ) - { - if( it->unicode() == '\t' ) - { - spacecount += 8-( spacecount % 8 ); - } else if( it->unicode() != '\f' && it->unicode() != '\n' ) - { - spacecount++; - }else if( it->unicode() == '#' ) - { - // find the next newline position and return a linebreak token for it - do - { - it++; - m_curpos++; - }while( it->unicode() != '\n' && m_curpos < m_contentSize ); - m_tokenBegin = m_curpos; - m_tokenEnd = m_curpos; - createNewline( m_curpos ); - m_curpos++; - return Parser::Token_LINEBREAK; - } - it++; - m_curpos++; - } - if( it->unicode() == '#' ) - { - // Ooops, whitespace and then a #, this is a plain comment line, only create - // a newline token for this - do - { - it++; - m_curpos++; - }while( it->unicode() != '\n' && m_curpos < m_contentSize ); - m_tokenBegin = m_curpos; - m_tokenEnd = m_curpos; - createNewline( m_curpos ); - m_curpos++; - return Parser::Token_LINEBREAK; - }else if( it->unicode() == '\n' ) - { - m_tokenBegin = m_curpos; - m_tokenEnd = m_curpos; - createNewline( m_curpos ); - m_curpos++; - return Parser::Token_LINEBREAK; - } - it--; - m_tokenEnd = m_curpos-1; - - if( spacecount > indentation() ) - { - // We have more indentation, so we need to create an INDENT token - pushIndentation( spacecount ); - popState(); - token = Parser::Token_INDENT; - return token; - }else if( spacecount == indentation() ) - { - // Don't do anything, same indentation level so we ignore the whitespace and move on with lexing the forthcoming text - popState(); - m_curpos = m_tokenBegin; - it = m_content.data() + m_curpos; - }else - { - // We've got a dedentation, so create a DEDENT token - // If the next indentation level is still larger than what we've - // counted we'll do another try on the next call, else we'll go into - // usual parsing next time we enter this function - popIndentation(); - token = Parser::Token_DEDENT; - if( spacecount < indentation() ) - { - m_curpos = m_tokenBegin; - }else - { - popState(); - } - return token; - } - }else - { - // Either we have a comment, which will be ignored in the next part - // or we have a non-whitespace character and no more indentation - // level to create a DEDENT token for. Thus get out of IndentState - // and do normal parsing - popState(); - } - } - switch ( state() ) - { - case DefaultState: - it = ignoreWhitespaceAndComments( it ); - if( it->unicode() == '\\' && (it+1)->unicode() == '\n' ) - { - createNewline(m_curpos+1); - m_curpos += 2; - it += 2; - } - m_tokenBegin = m_curpos; - if( isStringStart( it ) ) - { - if( it->toLower().unicode() == 'u' ) - { - it++; - m_curpos++; - } - if( it->toLower().unicode() == 'r' ) - { - it++; - m_curpos++; - } - QChar* quotestart = it++; - m_curpos++; - if( it->unicode() == quotestart->unicode() && (it+1)->unicode() == quotestart->unicode() ) - { - it += 2; - m_curpos += 2; - // read everything until we find 3 consecutive chars that are - // equal to quotestart - while( m_curpos < m_contentSize - 3 && !( it->unicode() == quotestart->unicode() - && (it+1)->unicode() == quotestart->unicode() - && (it+2)->unicode() == quotestart->unicode()) ) - { - if( it->unicode() == '\\' ) - { - it += 2; - m_curpos += 2; - }else - { - it++; - m_curpos++; - } - } - // We checked these 2 characters and they're either the last 2 chars - // or they're the closing '' or "" and thus we always consume them too - it += 2; - m_curpos += 2; - token = Parser::Token_STRINGLITERAL; - }else - { - // single quote so read until the end of line or closing quote - // if eol is found return invalid token - while( it->unicode() != quotestart->unicode() && it->unicode() != '\n' && m_curpos < m_contentSize ) - { - if( it->unicode() == '\\' ) - { - it += 2; - m_curpos += 2; - }else - { - it++; - m_curpos++; - } - } - if( it->unicode() == '\n' ) - { - // We've read to the newline, now return to the last string character - m_curpos--; - } - token = Parser::Token_STRINGLITERAL; - } - //Go to the next character, it still points to the end of the literal - it++; - if( it->isSpace() ) - { - // read more space until we find a non-space character - // if the non-space char is a quote again set the current position - // to the char before this non-space character, so next time - // we lex the next string. This allows for string concatenation - QChar* space = it; - int count = 0; - while( space->isSpace() && (m_curpos+count) < m_contentSize ) - { - space++; - count++; - } - // Now check wether this is a string begin, if so we immediately - // return the token and set its endPos, we also set the parsing - // position to the next quote and ignore the newline that might exist - // between the strings - if( space->unicode() == '\'' || space->unicode() == '"' ) - { - m_tokenEnd = m_curpos; - m_curpos += count; - return token; - } - } - }else if( it->isLetter() || it->unicode() == '_' ) - { - QChar* start = it; - do{ - it++; - m_curpos++; - }while( m_curpos < m_contentSize - && ( it->isLetterOrNumber() || it->unicode() == '_' ) ); - //Adjust current position to the last character that belongs to the identifier/keyword - m_curpos--; - token = checkForKeyword( start, m_curpos-m_tokenBegin+1 ); - }else if( it->isNumber() || ( it->unicode() == '.' && (it+1)->isNumber() ) ) - { - // all numeric literals start with a number or a . followed by a number - do{ - it++; - m_curpos++; - }while( m_curpos < m_contentSize - && ( it->isNumber() || it->unicode() == 'e' - || it->unicode() == 'E' || it->unicode() == 'j' - || it->unicode() == 'J' || it->unicode() == '.' - || it->unicode() == 'A' || it->unicode() == 'B' - || it->unicode() == 'C' || it->unicode() == 'D' - || it->unicode() == 'F' || it->unicode() == 'a' - || it->unicode() == 'b' || it->unicode() == 'c' - || it->unicode() == 'd' || it->unicode() == 'f' - || it->unicode() == 'l' || it->unicode() == 'L' - || it->unicode() == 'X' || it->unicode() == 'x' ) ); - //Adjust position to last character that belongs to the number - m_curpos--; - token = getTokenForNumberString( m_content.mid( m_tokenBegin, m_curpos-m_tokenBegin+1 ) ); - }else - { - QChar* ch2 = m_curpos < m_contentSize ? it + 1 : 0; - QChar* ch3 = m_curpos < m_contentSize ? it + 2 : 0; - kDebug() << "it: " << it->toAscii(); - switch ( it->unicode() ) - { - case '\n': - pushState( IndentState ); - createNewline( m_curpos ); - token = Parser::Token_LINEBREAK; - break; - case '(': - m_openParenNum++; - token = Parser::Token_LPAREN; - break; - case ')': - m_openParenNum--; - token = Parser::Token_RPAREN; - break; - case '{': - m_openParenNum++; - token = Parser::Token_LBRACE; - break; - case '}': - m_openParenNum--; - token = Parser::Token_RBRACE; - break; - case '[': - m_openParenNum++; - token = Parser::Token_LBRACKET; - break; - case ']': - m_openParenNum--; - token = Parser::Token_RBRACKET; - break; - case ',': - token = Parser::Token_COMMA; - break; - case ';': - token = Parser::Token_SEMICOLON; - break; - case ':': - token = Parser::Token_COLON; - break; - case '.': - if( ch2 && ch3 && ch2->unicode() == '.' && ch3->unicode() == '.' ) - { - m_curpos += 2; - token = Parser::Token_ELLIPSIS; - }else - { - token = Parser::Token_DOT; - } - break; - case '`': - token = Parser::Token_BACKTICK; - break; - case '@': - token = Parser::Token_AT; - break; - case '*': - if( ch2 && ch3 && ch2->unicode() == '*' && ch3->unicode() == '=') - { - m_curpos += 2; - token = Parser::Token_DOUBLESTAREQ; - }else if( ch2 && ch2->unicode() == '*' ) - { - m_curpos++; - token = Parser::Token_DOUBLESTAR; - }else if( ch2 && ch2->unicode() == '=') - { - m_curpos++; - token = Parser::Token_STAREQ; - }else - { - token = Parser::Token_STAR; - } - break; - case '=': - if( ch2 && ch2->unicode() == '=' ) - { - m_curpos++; - token = Parser::Token_ISEQUAL; - }else - { - token = Parser::Token_EQUAL; - } - break; - case '+': - if( ch2 && ch2->unicode() == '=' ) - { - m_curpos++; - token = Parser::Token_PLUSEQ; - }else - { - token = Parser::Token_PLUS; - } - break; - case '-': - if( ch2 && ch2->unicode() == '=' ) - { - m_curpos++; - token = Parser::Token_MINUSEQ; - }else - { - token = Parser::Token_MINUS; - } - break; - case '~': - if( ch2 && ch2->unicode() == '=' ) - { - m_curpos++; - token = Parser::Token_TILDEEQ; - }else - { - token = Parser::Token_TILDE; - } - break; - case '/': - if( ch2 && ch2->unicode() == '=' ) - { - m_curpos++; - token = Parser::Token_SLASHEQ; - }else if( ch2 && ch3 && ch2->unicode() == '/' && ch3->unicode() == '=' ) - { - m_curpos += 2; - token = Parser::Token_DOUBLESLASHEQ; - }else if( ch2 && ch2->unicode() == '/' ) - { - m_curpos++; - token = Parser::Token_DOUBLESLASH; - }else - { - token = Parser::Token_SLASH; - } - break; - case '%': - if( ch2 && ch2->unicode() == '=' ) - { - m_curpos++; - token = Parser::Token_MODULOEQ; - }else - { - token = Parser::Token_MODULO; - } - break; - case '&': - if( ch2 && ch2->unicode() == '=' ) - { - m_curpos++; - token = Parser::Token_ANDEQ; - }else - { - token = Parser::Token_BITAND; - } - break; - case '<': - if( ch2 && ch2->unicode() == '=' ) - { - m_curpos++; - token = Parser::Token_LESSEQ; - }else if( ch2 && ch3 && ch2->unicode() == '<' && ch3->unicode() == '=' ) - { - m_curpos += 2; - token = Parser::Token_LSHIFTEQ; - }else if( ch2 && ch2->unicode() == '<' ) - { - m_curpos++; - token = Parser::Token_LSHIFT; - }else if( ch2 && ch2->unicode() == '>' ) - { - m_curpos++; - token = Parser::Token_UNEQUAL; - }else - { - token = Parser::Token_LESS; - } - break; - case '>': - if( ch2 && ch2->unicode() == '=' ) - { - m_curpos++; - token = Parser::Token_GREATEREQ; - }else if( ch2 && ch3 && ch2->unicode() == '>' && ch3->unicode() == '=' ) - { - m_curpos += 2; - token = Parser::Token_RSHIFTEQ; - }else if( ch2 && ch2->unicode() == '>' ) - { - m_curpos++; - token = Parser::Token_RSHIFT; - }else - { - token = Parser::Token_GREATER; - } - break; - case '|': - if( ch2 && ch2->unicode() == '=' ) - { - m_curpos++; - token = Parser::Token_OREQ; - }else - { - token = Parser::Token_BITOR; - }; - break; - case '^': - token = Parser::Token_BITXOR; - break; - case '!': - if( ch2 && ch2->unicode() == '=' ) - { - m_curpos++; - token = Parser::Token_UNEQUAL; - } - break; - default: - break; - } - } - m_tokenEnd = m_curpos; - break; - default: - token = Parser::Token_INVALID; - break; - } - if ( m_curpos >= m_contentSize ) - { - if( indentation() > 0 ) - { - popIndentation(); - m_tokenEnd = m_curpos; - return Parser::Token_DEDENT; - } - m_tokenBegin = -1; - m_tokenEnd = -1; - return 0; - } - m_tokenEnd = m_curpos; - m_curpos++; - return token; -} - -qint64 Lexer::tokenBegin() const -{ - return m_tokenBegin; -} - -qint64 Lexer::tokenEnd() const -{ - return m_tokenEnd; -} - -QChar* Lexer::ignoreWhitespaceAndComments( QChar* it ) -{ - // Ignore whitespace and comments, but preserve the newline if we're not inside a parenthesis - bool isComment = false; - while ( m_curpos < m_contentSize - && ( it->isSpace() || isComment || it->unicode() == '#' || - ( it->unicode() == '\\' && m_openParenNum && (it+1)->unicode() == '\n' ) - ) - && ( it->unicode() != '\n' || m_openParenNum > 0 ) ) - { - if( it->unicode() == '#' ) - { - isComment = true; - }else if( it->unicode() == '\n' && m_openParenNum > 0 ) - { - isComment = false; - createNewline( m_curpos ); - } - ++it; - ++m_curpos; - } - return it; -} - -void Lexer::createNewline( int curpos ) -{ - if( m_parser ) - m_parser->tokenStream->locationTable()->newline( curpos ); -} - -bool Lexer::isStringStart( QChar* it ) -{ - if( it->unicode() == '\'' || it->unicode() == '"' ) - { - return true; - } - if( it->toLower().unicode() == 'u' && m_curpos < m_contentSize - 1 ) - { - if( (it+1)->unicode() == '\'' || (it+1)->unicode() == '"' ) - { - return true; - }else if( (it+1)->toLower().unicode() == 'r' && m_curpos < m_contentSize - 2 ) - { - if( (it+2)->unicode() == '\'' || (it+2)->unicode() == '"' ) - { - return true; - } - } - } - if( it->toLower().unicode() == 'r' && m_curpos < m_contentSize - 1 ) - { - if( (it+1)->unicode() == '\'' || (it+1)->unicode() == '"' ) - { - return true; - } - } - return false; -} - -} diff --git a/parser/pythonlexer.h b/parser/pythonlexer.h deleted file mode 100644 index d3a7b27..0000000 --- a/parser/pythonlexer.h +++ /dev/null @@ -1,81 +0,0 @@ -/* KDevelop Python Support - * - * Copyright 2007 Andreas Pakulat - * - * 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. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#ifndef PYTHONLEXER_H -#define PYTHONLEXER_H - -#include -#include - -#include "parserexport.h" - -class QString; - -namespace PythonParser -{ - -class Parser; - -class KDEVPYTHONPARSER_EXPORT Lexer { -public: - Lexer(Parser* _parser, const QString& contents); - - int nextTokenKind(); - qint64 tokenBegin() const; - qint64 tokenEnd() const; - -private: - QString m_content; - Parser* m_parser; - int m_curpos; - int m_contentSize; - qint64 m_tokenBegin; - qint64 m_tokenEnd; - unsigned int m_openParenNum; - - int state() const; - void pushState(int state); - void popState(); - - int indentation() const; - void pushIndentation( int indentation ); - void popIndentation(); - - QChar* ignoreWhitespaceAndComments( QChar* it ); - void createNewline( int curpos ); - bool isStringStart( QChar* ); - - QStack m_state; - QStack m_indentation; - enum State - { - ErrorState = -1, - DefaultState = 0, - IndentState = 1 - }; - - -}; - -} - -#endif - -// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/python_helpers/documentationgenerator.py b/python_helpers/documentationgenerator.py new file mode 100644 index 0000000..5899844 --- /dev/null +++ b/python_helpers/documentationgenerator.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# -*- Coding: utf-8 -*- + +import types +from PyQt4.QtCore import pyqtWrapperType + +import subprocess + +import os, sys + +import re + +def dbg(*args): + for arg in args: + sys.stdout.write(str(arg) + ' ') + sys.stdout.write('\n') + +def removeIndent(line): + m = re.search(r'^ *', line) + return line[:m.start()] + line[m.end():] + +class DocumentationGenerator(): + validMethodTypes = [types.UnboundMethodType, types.FunctionType, types.BuiltinFunctionType] + validModuleTypes = [pyqtWrapperType, types.FunctionType, types.ModuleType, types.ClassType] + + current_file = None + open_files = dict() + + exclude_packages = ['_sane', 'sane', 'this', 'Pyrex.Plex.test_tm'] + + def walk_directory(self, path): + modules_found = [] + for root, dirs, files in os.walk(path): + for current_file in files: + if not ( current_file.endswith('.so') or current_file.endswith('.py') ) or current_file.startswith('__init__'): + continue + module_path = root.replace(path, '').replace('/', '.') + '.' + current_file.replace('.so', '').replace('.py', '') + module_path = module_path.replace('site-packages.', '') + + if module_path.startswith('.'): + module_path = module_path[1:] + + if module_path in self.exclude_packages or module_path.find('test') != -1: + continue + + modules_found.append(module_path) + dbg("Modules found: " + str(modules_found)) + return modules_found + + def run(self, basepath): + for module_name in self.walk_directory(basepath): + try: + current_m = __import__(module_name) + except: + dbg("Could not import module " + module_name) + continue + self.walk_module(current_m, module_name) + + def walk_module(self, module, module_name): + dbg("CHECKMODULE> ", module_name, module, type(module)) + if type(module) in self.validModuleTypes: + properties = dir(module) + for current_property_name in properties: + if current_property_name.startswith('_'): + continue + + current_property = getattr(module, current_property_name) + current_type = type(current_property) + dbg("CHECK> ", module_name, module, current_property, current_type) + if current_type in self.validModuleTypes: + dbg("RECURSIVE_CHECK> ", module_name, current_property_name) + self.walk_module(current_property_name, module_name + '.' + current_property_name) + if current_type in self.validMethodTypes: + self.generate_documentation_for(module_name + '.' + current_property_name) + self.generate_documentation_for(module_name) + + def write_docfile(self, *args): + for item in args: + self.current_file.write(str(item)) + self.current_file.write('\n') + + def get_docfile(self, module_name): + pathspec = module_name.split('.')[:-1] + relative_path = 'results/' + '/'.join(pathspec) + '.py' + dbg("PATH> ", relative_path, " (from ", module_name, ")") + try: + self.current_file = self.open_files[relative_path] + return self.current_file + except KeyError: + pass + try: + self.current_file = open(relative_path, 'w') + self.open_files[relative_path] = self.current_file + return self.current_file + except IOError: + pass + try: + path = 'results/' + for part in pathspec: + path += part + '/' + if not os.path.exists(path): + dbg("CREATE> ", path) + os.mkdir(path) + dbg("CREATE> ", relative_path) + self.current_file = open(relative_path, 'w') + self.open_files[relative_path] = self.current_file + return self.current_file + except IOError: + raise IOError('Could not create valid docfile') + + def close_files(self): + for key, f in self.open_files.iteritems(): + f.close() + + def indent(self, moar = 0): + ret = '' + for i in xrange(0, moar): + ret += " " + return ret + + def generate_documentation_for(self, module_name): + dbg("PYDOC> ", module_name) + documentation = subprocess.Popen(['/usr/bin/pydoc', module_name], stdout = subprocess.PIPE).stdout.read() + lines = documentation.split("\n") + + docfile = self.get_docfile(module_name) + + split = lines[2].split("=") + try: + if split[1][:2] == ' _': + return + except: + pass + if not '='.join(split[1:]): + dbg("SKIP> Skipping invalid function") + dbg("SKIP> Failed to get documentation for", module_name) + return + if '='.join(split[1:]).find('class ') != -1: + dbg("SKIP_CLS> skipping class", module_name) + return + lines[2] = "def" + '='.join(split[1:]) + + split = lines[2].split('method of') + lines[2] = 'method of'.join(split[:1]) + ":" + + documentation = "\n".join(lines[3:]) + + lines[2] = lines[2].replace('{', "''' ").replace('}', " '''").replace('function ', 'lambda_func').replace('<', '"').replace('>', '"').replace('...', "args=''") + + self.write_docfile(self.indent() + "# Generated Documentation for ", ''.join(split[:1])) + self.write_docfile(self.indent() + lines[2]) + self.write_docfile(self.indent(1) + '"""') + for line in lines[3:]: + self.write_docfile(self.indent(1) + removeIndent(line)) + self.write_docfile(self.indent(1) + '"""') + self.write_docfile(self.indent(1) + "pass \n\n") + +try: + generator = DocumentationGenerator() + generator.run('/usr/lib/python2.6/') +except Exception as e: + print e +finally: + generator.close_files() \ No newline at end of file diff --git a/python_helpers/generate_docs.py b/python_helpers/generate_docs.py new file mode 100644 index 0000000..dd498ce --- /dev/null +++ b/python_helpers/generate_docs.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python + +import pydoc + +modules = ['random'] +code = '' + +current_name = [] + +import types +import pydoc +import subprocess + +import re + +import os + +modules_done = [] + +import sys + +from PyQt4.QtCore import pyqtWrapperType + +def dbg(s): + sys.stderr.write(s) + sys.stderr.write("\n") + +def walk_directory(path): + modules_found = [] + for root, dirs, files in os.walk(path): + for current_file in files: + if not ( current_file.endswith('.so') or current_file.endswith('.py') ) or current_file.startswith('__init__'): + continue + module_path = root.replace(path, '').replace('/', '.') + '.' + current_file.replace('.so', '').replace('.py', '') + if module_path.startswith('.'): + module_path = module_path[1:] + modules_found.append(module_path) + dbg("Modules found: " + str(modules_found)) + return modules_found + +def indent(moar = 0): + ret = '' + for i in xrange(0, len(current_name) + moar - 1): + ret += " " + return ret + +def removeIndent(line): + m = re.search(r'^ *', line) + return line[:m.start()] + line[m.end():] + +validMethodTypes = [types.UnboundMethodType, types.FunctionType, types.BuiltinFunctionType] +validModuleTypes = [pyqtWrapperType, types.FunctionType] + +def process(obj): + try: + if obj.__name__.startswith('_'): + raise AttributeError + except: + #dbg("Aborting, name starts with __") + return + + try: + current_name.append(obj.__name__) + except: + current_name.append('') + #dbg(" ++ Process called with argument " + str(obj)) + + if type(obj) == types.ModuleType or type(obj) in validModuleTypes: + properties = dir(obj) + #print obj, properties, type(obj) + if type(obj) == types.ModuleType: + print indent() + "class " + obj.__name__.replace('<','').replace('>','') + "():" + print indent(1) + "pass" + + for current_property in properties: + current_property = getattr(obj, current_property) + if type(current_property) in validMethodTypes: + #dbg(" >> Processing property: " + str(current_property)) + process(current_property) + if type(current_property) in validModuleTypes: + dbg(" MODULE: " + str(current_property)) + for item in dir(current_property): + if type(current_property) in validMethodTypes: + process(getattr(current_property, item)) + else: + pass + #dbg(" -- Skipped property of invalid type " + str(type(current_property))) + + if type(obj) in validMethodTypes: + dbg(" ++ Generating documentation for " + str(obj)) + dbg(" ++ [" + '.'.join(current_name) + "]") + documentation = subprocess.Popen(['/usr/bin/pydoc', '.'.join(current_name)], stdout = subprocess.PIPE).stdout.read() + lines = documentation.split("\n") + + split = lines[2].split("=") + try: + if split[1][:2] == ' _': + current_name.pop() + return + except: + pass + print indent() + "# Generated Documentation for ", ''.join(split[:1]) + if not '='.join(split[1:]): + dbg("Skipping invalid function") + current_name.pop() + return + lines[2] = "def" + '='.join(split[1:]) + + split = lines[2].split('method of') + lines[2] = 'method of'.join(split[:1]) + ":" + + documentation = "\n".join(lines[3:]) + + lines[2] = lines[2].replace('{', "''' ").replace('}', " '''").replace('function ', 'lambda_func').replace('<', '"').replace('>', '"').replace('...', "args=''") + + print indent() + lines[2] + print indent(1) + '"""' + for line in lines[3:]: + print indent(1) + removeIndent(line) + print indent(1) + '"""' + print indent(1) + "pass \n\n" + + current_name.pop() + +root_path = '' +exclude_packages = ['_sane', 'sane', 'this', 'Pyrex.Plex.test_tm'] +current_file = None +result_basepath = 'results/' +for module in walk_directory('/usr/lib/python2.6/'): + module = module.replace("\n", "").replace('site-packages.', '') + module_parts = module.split('.') + try: + current_file = open(result_basepath + module.replace('.', '/'), 'w') + except IOError: + path = result_basepath + for part in module_parts: + path += part + '/' + if not os.path.exists(path): + os.mkdir(path) + current_file = open(result_basepath + module.replace('.', '/'), 'w') + + dbg(" >> Processing MODULE: " + module) + if module in exclude_packages or module.find('test') != -1: # exclude tests and stuff + continue + try: + current_m = __import__(root_path + module) + except: + dbg("Could not import module " + module) + continue + + get_attributes = module.split('.') + + for attrib in get_attributes: + try: + current_m = getattr(current_m, attrib) + dbg(" ##### " + str(root_path + module) + " > " + str(current_m) + " (" + str(attrib) + ")") + except AttributeError: + continue + + dbg(" >>> CALL :: " + module) + process(current_m) + dbg(" <<< RETURN") diff --git a/python_helpers/get_builtins.py b/python_helpers/get_builtins.py new file mode 100755 index 0000000..23e60ca --- /dev/null +++ b/python_helpers/get_builtins.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +import __builtin__ +for item in dir(__builtin__): + print item diff --git a/python_helpers/pydocparser.py b/python_helpers/pydocparser.py new file mode 100644 index 0000000..b350e22 --- /dev/null +++ b/python_helpers/pydocparser.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python2.6 + +import re +import os +import sys +import subprocess + +modules = ['random'] +state = None + +PackageScanState, ClassScanState = 'pkg', 'cls' +ClassScanState_Outline, ClassScanState_MethodResolution, ClassScanState_MethodDefinition, ClassScanState_DataDescriptors, ClassScanState_Attributes, ClassScanState_InheritedMethods = 'c_otl', 'c_res', 'c_mdf', 'c_dat', 'c_att', 'c_inh' +ClassScanState_InheritedAttributes, ClassScanState_MethodDocumentation = 'c_iat', 'c_mdc' + +ClassMatchRegex = re.compile(r' (class .*?\(.*?\).*?)') +InClassMatchRegex = re.compile(r' \| ') +ResolvedMethodMatchRegex = re.compile(r' \| ([A-Za-z_]*?\(.*?\))') +UnresolvedMethodMatchRegex = re.compile(r' \| (.*? = \)') +CouldBeMethodDocLineRegex = re.compile(r' \| (.*)') + +class method(): + documentation = '' + name = '' + parameters = [] + + def __repr__(self): + return '<'+self.name+'>' + +for module in modules: + doctext = subprocess.Popen(['/usr/bin/pydoc', module], stdout = subprocess.PIPE).stdout.read() + doctext_lines = doctext.split('\n') + methods = [] + currentMethod = None + state = None + classState = None + + for line in doctext_lines: + print state, classState, line[:70] + if state == None: + if re.match(ClassMatchRegex, line): + state = ClassScanState + classState = ClassScanState_Outline + if re.match(ResolvedMethodMatchRegex, line): pass + elif state == ClassScanState: + if not re.match(InClassMatchRegex, line): + state = None + classState = None + continue + if line == ' | Method resolution order:': + classState = ClassScanState_MethodResolution + continue + if line == ' | Methods defined here:': + classState = ClassScanState_MethodDefinition + continue + + if classState == ClassScanState_MethodDocumentation: + if re.match(CouldBeMethodDocLineRegex, line): + currentMethod.documentation += line + else: + classState = ClassScanState_MethodDefinition + + if classState == ClassScanState_MethodDefinition: + if re.match(ResolvedMethodMatchRegex, line): + if currentMethod is not None: + methods.append(currentMethod) + currentMethod = method() + currentMethod.name = line + classState = ClassScanState_MethodDocumentation + +for m in methods: + print m diff --git a/pythonhighlighting.h b/pythonhighlighting.h index 2459001..354fa84 100644 --- a/pythonhighlighting.h +++ b/pythonhighlighting.h @@ -38,7 +38,6 @@ class Highlighting : public KDevelop::CodeHighlighting Q_OBJECT public: Highlighting( QObject* parent ); - }; } diff --git a/pythonlanguagesupport.cpp b/pythonlanguagesupport.cpp index b421039..0b8353d 100644 --- a/pythonlanguagesupport.cpp +++ b/pythonlanguagesupport.cpp @@ -43,11 +43,19 @@ #include #include +#include +#include + #include "pythonparsejob.h" #include "pythonhighlighting.h" #include "duchain/pythoneditorintegrator.h" +#include "codecompletion/pythoncodecompletionmodel.h" #include +#include +#include +#include +#include using namespace KDevelop; @@ -56,14 +64,20 @@ K_EXPORT_PLUGIN( KDevPythonSupportFactory( "kdevpythonsupport" ) ) namespace Python { + +LanguageSupport* LanguageSupport::m_self = 0; LanguageSupport::LanguageSupport( QObject* parent, const QVariantList& /*args*/ ) : KDevelop::IPlugin( KDevPythonSupportFactory::componentData(), parent ), KDevelop::ILanguageSupport() { KDEV_USE_EXTENSION_INTERFACE( KDevelop::ILanguageSupport ) + + m_self = this; m_highlighting = new Highlighting( this ); + PythonCodeCompletionModel* codeCompletion = new PythonCodeCompletionModel(this); + new KDevelop::CodeCompletion(this, codeCompletion, "Python"); } LanguageSupport::~LanguageSupport() @@ -82,6 +96,11 @@ QString LanguageSupport::name() const return "Python"; } +LanguageSupport* LanguageSupport::self() +{ + return m_self; +} + KDevelop::ILanguage *LanguageSupport::language() { kDebug() << core()->languageController()->language( name() ); @@ -93,6 +112,19 @@ KDevelop::ICodeHighlighting* LanguageSupport::codeHighlighting() const return m_highlighting; } +// QWidget* LanguageSupport::specialLanguageObjectNavigationWidget(const KUrl& url, const KDevelop::SimpleCursor& position) +// { +// kDebug() << "Navigation widget requested *** "; +// QFrame* frame = new QFrame(); +// QLabel* label = new QLabel(); +// QHBoxLayout *layout = new QHBoxLayout(); +// label->setText("Foo"); +// frame->setLayout(layout); +// layout->addWidget(label); +// return frame; +// } + + } #include "pythonlanguagesupport.moc" diff --git a/pythonlanguagesupport.h b/pythonlanguagesupport.h index a7d8291..54662c5 100644 --- a/pythonlanguagesupport.h +++ b/pythonlanguagesupport.h @@ -61,9 +61,14 @@ class LanguageSupport : public KDevelop::IPlugin, public KDevelop::ILanguageSupp KDevelop::ILanguage *language(); /*the code highlighter*/ KDevelop::ICodeHighlighting* codeHighlighting() const; + + static LanguageSupport* self(); + +// virtual QWidget* specialLanguageObjectNavigationWidget(const KUrl& url, const KDevelop::SimpleCursor& position); private: Highlighting* m_highlighting; + static LanguageSupport* m_self; }; } diff --git a/pythonparsejob.cpp b/pythonparsejob.cpp index 47347b4..2d12b63 100644 --- a/pythonparsejob.cpp +++ b/pythonparsejob.cpp @@ -47,14 +47,19 @@ // #include "contextbuilder.h" #include "declarationbuilder.h" #include "usebuilder.h" -#include "astprinter.h" +// #include "astprinter.h" // #include "usebuilder.h" +#include +#include +#include +#include using namespace KDevelop; namespace Python { +TopDUContext* ParseJob::m_internalFunctions; ParseJob::ParseJob(LanguageSupport* parent, const KUrl &url ) : KDevelop::ParseJob( url ) @@ -66,6 +71,8 @@ ParseJob::ParseJob(LanguageSupport* parent, const KUrl &url ) { kDebug(); m_parent = parent; + ParseJob::internalFunctionsFile = new KUrl("/home/sven/projects/kde4/python/documentation/test.py"); + ParseJob::m_internalFunctions = 0; } ParseJob::~ParseJob() @@ -74,8 +81,7 @@ ParseJob::~ParseJob() LanguageSupport *ParseJob::python() const { - kDebug() << "language requested"; - return qobject_cast( const_cast( parent() ) ); + return LanguageSupport::self(); } @@ -90,75 +96,108 @@ bool ParseJob::wasReadFromDisk() const return m_readFromDisk; } +void ParseJob::checkInternalFunctionsParsed() +{ + if ( ! ParseJob::m_internalFunctions ) { + DUChain::self()->updateContextForUrl(IndexedString(*internalFunctionsFile), minimumFeatures()); + } +} + void ParseJob::run() { kDebug(); + + LanguageSupport* lang = python(); + ILanguage* ilang = lang->language(); + QReadLocker parselock(ilang->parseLock()); + UrlParseLock urlLock(document()); + + if ( m_url != *internalFunctionsFile ) checkInternalFunctionsParsed(); - if ( abortRequested() ) + if (abortRequested() || !python() || !python()->language()) { + kWarning() << "Language support is NULL"; return abortJob(); - -// QReadLocker lock(python()->language()->parseLock()); - UrlParseLock urlLock(document()); + } readContents(); - m_session->setContents( QString::fromUtf8(contents().contents) + "\n" ); + m_session->setContents( QString::fromUtf8(contents().contents) + "\n" ); // append a newline in case the parser doesnt like it without one + m_session->setCurrentDocument(m_url); if ( abortRequested() ) return abortJob(); - + + IndexedString filename = KDevelop::IndexedString(m_url.pathOrUrl()); + // 2) parse - bool matched = m_session->parse( &m_ast ); - - if ( matched ) + QPair parserResults = m_session->parse(m_ast); + m_ast = parserResults.first; + + if ( parserResults.second ) { kDebug() << m_url; // AstPrinter printer; // printer.visitCode( m_ast ); - { + if ( abortRequested() ) + return abortJob(); + + PythonEditorIntegrator editor; + DeclarationBuilder builder( &editor ); + + editor.setParseSession(m_session); + + m_duContext = builder.build(filename, m_ast); + setDuChain(m_duContext); + + UseBuilder usebuilder( &editor ); + usebuilder.buildUses(m_ast); - if ( abortRequested() ) - return abortJob(); - - PythonEditorIntegrator editor; - DeclarationBuilder builder( &editor ); - - IndexedString filename = KDevelop::IndexedString(m_url.pathOrUrl()); - m_session->setCurrentDocument(filename); - - editor.setParseSession(m_session); - - m_duContext = builder.build(filename, m_ast); - setDuChain(m_duContext); - - UseBuilder usebuilder( &editor ); - usebuilder.buildUses(m_ast); - - kDebug() << "----Parsing Succeded---***"; - -// { -// DUChainReadLocker lock( DUChain::lock() ); -// DumpChain dump; -// dump.dump( m_duContext ); -// } - - { - if ( m_parent && m_parent->codeHighlighting() ) { - kDebug() << m_duContext.data(); -// DUChainReadLocker lock(DUChain::lock()); - KDevelop::ICodeHighlighting* hl = m_parent->codeHighlighting(); - hl->highlightDUChain(m_duContext); - } - } - + { + DUChainWriteLocker lock(DUChain::lock()); +// m_duContext->clearProblems(); + ParsingEnvironmentFilePointer parsingEnvironmentFile = m_duContext->parsingEnvironmentFile(); + parsingEnvironmentFile->clearModificationRevisions(); + parsingEnvironmentFile->setModificationRevision(contents().modification); + DUChain::self()->updateContextEnvironment(m_duContext, parsingEnvironmentFile.data()); + } + + kDebug() << "----Parsing Succeded---***"; + + if ( m_parent && m_parent->codeHighlighting() ) { + kDebug() << "Starting highlighter..."; + DUChainReadLocker lock(DUChain::lock()); + KDevelop::ICodeHighlighting* hl = m_parent->codeHighlighting(); + hl->highlightDUChain(m_duContext); } } else { - kDebug() << "===Failed==="; -// cleanupSmartRevision(); - return; + kWarning() << "===Failed==="; + DUChainWriteLocker lock; + m_duContext = DUChain::self()->chainForDocument(document()); + if ( m_duContext ) { + m_duContext->parsingEnvironmentFile()->clearModificationRevisions(); + m_duContext->clearProblems(); + } + else { + ParsingEnvironmentFile *file = new ParsingEnvironmentFile(document()); + static const IndexedString langString("python"); + file->setLanguage(langString); + m_duContext = new TopDUContext(document(), RangeInRevision(0, 0, INT_MAX, INT_MAX), file); + DUChain::self()->addDocumentChain(m_duContext); + } + + foreach ( ProblemPointer p, m_session->m_problems ) { + kDebug() << "Added problem to context"; + m_duContext->addProblem(p); + } + setDuChain(m_duContext); } -// cleanupSmartRevision(); + +// DUChainWriteLocker lock(DUChain::lock()); +// if ( ! DUChain::self()->chainForDocument(document()) && m_duContext ) { +// DUChain::self()->addDocumentChain(m_duContext); +// } + } ParseSession *ParseJob::parseSession() const diff --git a/pythonparsejob.h b/pythonparsejob.h index dbbe9ce..b9db7af 100644 --- a/pythonparsejob.h +++ b/pythonparsejob.h @@ -33,13 +33,11 @@ #include #include +#include -namespace KDevelop -{ -class TopDUContext; -} +using namespace KDevelop; namespace Python { @@ -66,6 +64,10 @@ class ParseJob : public KDevelop::ParseJob bool wasReadFromDisk() const; const LanguageSupport* m_parent; + static TopDUContext* m_internalFunctions; + const KUrl* internalFunctionsFile; + + void checkInternalFunctionsParsed(); protected: virtual void run(); diff --git a/pythonpythonparser.py b/pythonpythonparser.py new file mode 100755 index 0000000..91a54f7 --- /dev/null +++ b/pythonpythonparser.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python2.6 +# -*- coding: utf-8 -*- + +# +# This file is part of KDevelop +# Copyright 2010 Sven Brauch +# Licensed under the GNU GPL +# + +import ast +from xml.dom.minidom import Document +from lxml import etree +import types +import sys +import re + +class KDevelopNodeVisitor(ast.NodeVisitor): + basenode = etree.Element("pythonast") + currentnode = None + nodecnt = 0 + childNodeMap = {} + + def __init__(self, *arg, **args): + super(KDevelopNodeVisitor, self).__init__(*arg, **args) + self.currentnode = self.basenode + + def generic_visit(self, node): + self.nodecnt += 1 + + #self.childNodeMap[self.nodecnt] = node + self.childNodeMap[node] = self.nodecnt + + node_xmlrepr = etree.Element(node.__class__.__name__ + "Ast") + node_xmlrepr.set('nodecnt', str(self.nodecnt)) + self.currentnode.append(node_xmlrepr) + + save_currentnode = self.currentnode + self.currentnode = node_xmlrepr + + fields = list(node._attributes) + fields.extend(list(node._fields)) + + searching_locally = [] + for field in fields: + value = getattr(node, field) + if type(value) not in [types.IntType, types.StringType, types.FloatType, types.BooleanType]: + continue + try: + node_xmlrepr.set(field.lower(), str(value)) + except: + sys.stderr.write("Warning: Invalid string literal replaced by empty string!\n") + node_xmlrepr.set(field.lower(), "") + + + super(KDevelopNodeVisitor, self).generic_visit(node) + + key = '' + for field in fields: + multiple_keys = [] + value = getattr(node, field) + if type(value) not in [types.IntType, types.StringType, types.FloatType, types.BooleanType]: + if type(value) == types.ListType: + for currentValue in value: + try: + multiple_keys.append(str(self.childNodeMap[currentValue])) + except KeyError: + sys.stderr.write("Warning: missing key on node " + str(node) + "\n") + multiple_keys.append('') + key = ','.join(multiple_keys) + node_xmlrepr.set("NRLST_" + field.lower(), str(key)) + else: + try: + key = self.childNodeMap[value] + except KeyError: + key = '' + node_xmlrepr.set("NR_" + field.lower(), str(key)) + + + self.currentnode = save_currentnode + +f = sys.stdin.read() +v = KDevelopNodeVisitor() +try: + parsetree = ast.parse(f) +except Exception as e: + try: + sys.stderr.write(str(e.lineno) + ':::' + str(e.offset)) + sys.stderr.write(":::" + str(type(e)).replace('', '') + ':::' + str(e.msg) + ": \"" + str(e.text).replace("\n", "") + "\"") + except: + sys.stderr.write('?:::?:::'+str(e)+':::?') +else: + v.visit(parsetree) + sys.stdout.write(etree.tostring(v.basenode, xml_declaration=True, pretty_print=True, encoding='UTF-8')) diff --git a/utilities/classes b/utilities/classes new file mode 100644 index 0000000..5df6415 --- /dev/null +++ b/utilities/classes @@ -0,0 +1,604 @@ +/*************************************************************************** + * This file is part of KDevelop * + * Copyright 2007 Andreas Pakulat * + * * + * This program 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 program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +// The Python 2.6 Language Reference was used as basis for this AST + +#ifndef PYTHON_AST_H +#define PYTHON_AST_H + +#include +#include +#include +#include +#include + +#include "parserexport.h" + +namespace KDevelop +{ + class DUContext; +} + +namespace Python { + class StatementAst; + class FunctionDefinitionAst; + class AssignmentAst; + class PrintAst; + class PassAst; + class ExpressionAst; + class NameAst; + class CallAst; + class AttributeAst; + class ArgumentsAst; + class KeywordAst; + + class ExpressionAst; + class StatementAst; + class Ast; + class ExceptionHandlerAst; + class AliasAst; + class ComprehensionAst; + class SliceAstBase; + class SliceAst; +} + +namespace Python +{ + +class KDEVPYTHONPARSER_EXPORT Identifier { +public: + Identifier(QString value); + QString value; +}; + +// Base class for all other Abstract Syntax Tree classes +class KDEVPYTHONPARSER_EXPORT Ast +{ +public: + enum AstType + { + FunctionDefinitionAstType, + AssignmentAstType, + PrintAstType, + PassAstType, + ExpressionAstType, + NameAstType, + CallAstType, + AttributeAstType, + ArgumentsAstType, + KeywordAstType, + ClassDefinitionAstType, + ReturnAstType, + DeleteAstType, + AugAssignAstType, + ForAstType, + WhileAstType, + IfAstType, + WithAstType, + RaiseAstType, + TryExceptAstType, + TryFinallyAstType, + AssertAstType, + ImportAstType, + ImportFromAstType, + ExecAstType, + GlobalAstType, + ExprAstType, + BreakAstType, + ContinueAstType, + AttributesAstType, + + BooleanOperationAstType, + BinaryOperationAstType, + UnaryOperationAstType, + LambdaAstType, + IfExpressionAstType, // the short one, if a then b else c + DictAstType, + SetAstType, + ListComprehensionAstType, + SetComprehensionAstType, + DictComprehensionAstType, + GeneratorExpressionAstType, + YieldAstType, + CompareAstType, + ReprAstType, + NumberAstType, + StringAstType, + SubscriptAstType, + ListAstType, + TupleAstType, + + SliceAstType, + EllipsisAstType, + IndexAstType, + + ComprehensionAstType, + ExceptionHandlerAstType, + AliasAstType // for imports + }; + + enum BooleanOperationTypes { + BooleanAnd, + BooleanOr + }; + + enum OperatorTypes { + OperatorAdd, + OperatorSub, + OperatorMult, + OperatorDiv, + OperatorMod, + OperatorPow, + OperatorLeftShift, + OperatorRightShift, + OperatorBitwiseOr, + OperatorBitwiseXor, + OperatorBitwiseAnd, + OperatorFloorDivision + }; + + enum UnaryOperatorTypes { + UnaryOperatorInvert, + UnaryOperatorNot, + UnaryOperatorAdd, + UnaryOperatorSub + }; + + enum ComparisonOperatorTypes { + ComparisonOperatorEquals, + ComparisonOperatorNotEquals, + ComparisonOperatorLessThan, + ComparisonOperatorLessThanEqual, + ComparisonOperatorGreaterThan, + ComparisonOperatorGreaterThanEqual, + ComparisonOperatorIs, + ComparisonOperatorIsNot, + ComparisonOperatorIn, + ComparisonOperatorNotIn + }; + + Ast(Ast* parent, AstType type); + virtual ~Ast(); + Ast* parent; + AstType astType; + + qint64 start; + qint64 end; + qint64 startCol; + qint64 startLine; + qint64 endCol; + qint64 endLine; + + KDevelop::DUContext* context; +}; + +// this replaces ModuleAst +class KDEVPYTHONPARSER_EXPORT CodeAst : public Ast { +public: + CodeAst(Ast* parent, AstType type); + QList body; +}; + +/** Statement classes **/ +class KDEVPYTHONPARSER_EXPORT StatementAst : public Ast { +public: + StatementAst(Ast* parent, Ast::AstType type); +}; + +class KDEVPYTHONPARSER_EXPORT FunctionDefinitionAst : public StatementAst { +public: + FunctionDefinitionAst(Ast* parent, Ast::AstType type); + Identifier* name; + ArgumentsAst* arguments; +}; + +class KDEVPYTHONPARSER_EXPORT ClassDefinitionAst : public StatementAst { +public: + ClassDefinitionAst(Ast* parent, AstType type); + Identifier* name; + QList baseClasses; + QList body; + QList decorators; +}; + +class KDEVPYTHONPARSER_EXPORT ReturnAst : public StatementAst { +public: + ReturnAst(Ast* parent, AstType type); + ExpressionAst* value; +}; + +class KDEVPYTHONPARSER_EXPORT DeleteAst : public StatementAst { +public: + DeleteAst(Ast* parent, AstType type); + QList targets; +}; + +class KDEVPYTHONPARSER_EXPORT AssignmentAst : public StatementAst { +public: + AssignmentAst(Ast* parent, Ast::AstType type); + QList targets; + ExpressionAst* value; +}; + +class KDEVPYTHONPARSER_EXPORT AugmentedAssignmentAst : public StatementAst { +public: + AugmentedAssignmentAst(Ast* parent, AstType type); + ExpressionAst* target; + Ast::OperatorTypes op; + ExpressionAst* value; +}; + +class KDEVPYTHONPARSER_EXPORT ForAst : public StatementAst { +public: + ForAst(Ast* parent, AstType type); + ExpressionAst* target; // may be a tupleAst for something like for a, b in j + ExpressionAst* iterator; + QList body; + QList orelse; +}; + +class KDEVPYTHONPARSER_EXPORT WhileAst : public StatementAst { +public: + WhileAst(Ast* parent, AstType type); + ExpressionAst* condition; + QList body; + QList orelse; +}; + +class KDEVPYTHONPARSER_EXPORT IfAst : public StatementAst { +public: + IfAst(Ast* parent, AstType type); + ExpressionAst* condition; + QList body; + QList orelse; +}; + +class KDEVPYTHONPARSER_EXPORT WithAst : public StatementAst { +public: + WithAst(Ast* parent, AstType type); + ExpressionAst* contextExpression; + ExpressionAst* optionalVars; + QList body; +}; + +class KDEVPYTHONPARSER_EXPORT RaiseAst : public StatementAst { +public: + RaiseAst(Ast* parent, AstType type); + ExpressionAst* type; + // TODO check what the other things in the grammar actually are and add them +}; + +class KDEVPYTHONPARSER_EXPORT TryExceptAst : public StatementAst { +public: + TryExceptAst(Ast* parent, AstType type); + QList body; + QList handlers; + QList orelse; +}; + +class KDEVPYTHONPARSER_EXPORT TryFinallyAst : public StatementAst { +public: + TryFinallyAst(Ast* parent, AstType type); + QList body; + QList finalbody; +}; + +class KDEVPYTHONPARSER_EXPORT AssertionAst : public StatementAst { +public: + AssertionAst(Ast* parent, AstType type); + ExpressionAst* condition; + ExpressionAst* message; +}; + +class KDEVPYTHONPARSER_EXPORT ImportAst : public StatementAst { +public: + ImportAst(Ast* parent, AstType type); + QList names; +}; + +class KDEVPYTHONPARSER_EXPORT ImportFromAst : public StatementAst { +public: + ImportFromAst(Ast* parent, AstType type); + Identifier* module; + QList names; + int level; +}; + +class KDEVPYTHONPARSER_EXPORT ExecAst : public StatementAst { +public: + ExecAst(Ast* parent, AstType type); + ExpressionAst* body; + ExpressionAst* globals; + ExpressionAst* locals; +}; + +class KDEVPYTHONPARSER_EXPORT GlobalAst : public StatementAst { +public: + QList name; +}; + +// TODO what's stmt::Expr(expr value) in the grammar and what do we need it for? + +class KDEVPYTHONPARSER_EXPORT BreakAst : public StatementAst { +public: + BreakAst(Ast* parent, AstType type); +}; + +class KDEVPYTHONPARSER_EXPORT ContinueAst : public StatementAst { +public: + ContinueAst(Ast* parent, AstType type); +}; + +class KDEVPYTHONPARSER_EXPORT PrintAst : public StatementAst { +public: + PrintAst(Ast* parent, AstType type); + ExpressionAst* destination; + QList values; + bool newline; +}; + +class KDEVPYTHONPARSER_EXPORT PassAst : public StatementAst { +public: + PassAst(Ast* parent, AstType type); +}; + + +/** Expression classes **/ +class KDEVPYTHONPARSER_EXPORT ExpressionAst : public Ast { +public: + ExpressionAst(Ast* parent, AstType type); + enum Context { + Load, // the object is read + Store, // the object is written + Delete, // the object is deleted + Parameter, // the object is passed as a parameter + AugLoad, AugStore // Augmented assignments, like a += 1 + }; +}; + +class KDEVPYTHONPARSER_EXPORT BooleanOperationAst : public ExpressionAst { +public: + BooleanOperationAst(Ast* parent, AstType type); + Ast::BooleanOperationTypes type; + QList values; +}; + +class KDEVPYTHONPARSER_EXPORT BinaryOperationAst : public ExpressionAst { +public: + BinaryOperationAst(Ast* parent, AstType type); + Ast::OperatorTypes type; + ExpressionAst* lhs; + ExpressionAst* rhs; +}; + +class KDEVPYTHONPARSER_EXPORT UnaryOperationAst : public ExpressionAst { +public: + UnaryOperationAst(Ast* parent, AstType type); + Ast::UnaryOperatorTypes type; + ExpressionAst* operand; +}; + +class KDEVPYTHONPARSER_EXPORT LambdaAst : public ExpressionAst { +public: + LambdaAst(Ast* parent, AstType type); + ArgumentsAst* arguments; + ExpressionAst* body; +}; + +class KDEVPYTHONPARSER_EXPORT IfExpressionAst : public ExpressionAst { +public: + IfExpressionAst(Ast* parent, AstType type); + ExpressionAst* condition; + ExpressionAst* body; + ExpressionAst* orelse; +}; + +class KDEVPYTHONPARSER_EXPORT DictAst : public ExpressionAst { +public: + QList keys; + QList values; +}; + +class KDEVPYTHONPARSER_EXPORT SetAst : public ExpressionAst { +public: + SetAst(Ast* parent, AstType type); + QList elements; +}; + +class KDEVPYTHONPARSER_EXPORT ListComprehensionAst : public ExpressionAst { +public: + ExpressionAst* element; + QList generators; +}; + +class KDEVPYTHONPARSER_EXPORT SetComprehensionAst : public ExpressionAst { +public: + SetComprehensionAst(Ast* parent, AstType type); + ExpressionAst* element; + QList generators; +}; + +class KDEVPYTHONPARSER_EXPORT DictionaryComprehensionAst : public ExpressionAst { +public: + DictionaryComprehensionAst(Ast* parent, AstType type); + ExpressionAst* key; + ExpressionAst* value; + QList generators; +}; + +class KDEVPYTHONPARSER_EXPORT GeneratorExpressionAst : public ExpressionAst { +public: + GeneratorExpressionAst(Ast* parent, AstType type); + ExpressionAst* element; + QList generators; +}; + +class KDEVPYTHONPARSER_EXPORT CompareAst : public ExpressionAst { +public: + CompareAst(Ast* parent, AstType type); + ExpressionAst* leftmostElement; + QList operators; + QList comparands; +}; + +// TODO whats this exactly? +class KDEVPYTHONPARSER_EXPORT ReprAst : public ExpressionAst { +public: + ReprAst(Ast* parent, AstType type); + ExpressionAst* value; +}; + +class KDEVPYTHONPARSER_EXPORT NumberAst : public ExpressionAst { +public: + NumberAst(Ast* parent, AstType type); + QString value; // everything else would be even more strange +}; + +class KDEVPYTHONPARSER_EXPORT StringAst : public ExpressionAst { +public: + StringAst(Ast* parent, AstType type); + QString value; +}; + +class KDEVPYTHONPARSER_EXPORT YieldAst : public ExpressionAst { +public: + YieldAst(Ast* parent, AstType type); + ExpressionAst* value; +}; + +class KDEVPYTHONPARSER_EXPORT NameAst : public ExpressionAst { +public: + NameAst(Ast* parent, AstType type); + Identifier* identifier; + ExpressionAst::Context context; +}; + +class KDEVPYTHONPARSER_EXPORT CallAst : public ExpressionAst { +public: + CallAst(Ast* parent, AstType type); + ExpressionAst* function; + QList arguments; + QList keywords; + ExpressionAst* starArguments; + ExpressionAst* keywordArguments; +}; + +class KDEVPYTHONPARSER_EXPORT AttributeAst : public ExpressionAst { +public: + AttributeAst(Ast* parent, AstType type); + ExpressionAst* value; + Identifier* attribute; + ExpressionAst::Context context; +}; + +class KDEVPYTHONPARSER_EXPORT SubscriptAst : public ExpressionAst { +public: + SubscriptAst(Ast* parent, AstType type); + ExpressionAst* value; + SliceAst* slice; + ExpressionAst::Context context; +}; + +class KDEVPYTHONPARSER_EXPORT ListAst : public ExpressionAst { +public: + ListAst(Ast* parent, AstType type); + QList elements; + ExpressionAst::Context context; +}; + +class KDEVPYTHONPARSER_EXPORT TupleAst : public ExpressionAst { +public: + TupleAst(Ast* parent, AstType type); + QList elements; + ExpressionAst::Context context; +}; + +/** Slice classes **/ +class KDEVPYTHONPARSER_EXPORT SliceAstBase : public Ast { +public: + SliceAstBase(Ast* parent, AstType type); +}; + +class KDEVPYTHONPARSER_EXPORT EllipsisAstType : public SliceAstBase { +public: + EllipsisAstType(Ast* parent, AstType type); +}; + +class KDEVPYTHONPARSER_EXPORT SliceAst : public SliceAstBase { +public: + ExpressionAst* lower; + ExpressionAst* upper; + ExpressionAst* step; +}; + +class KDEVPYTHONPARSER_EXPORT ExtendedSliceAst : public SliceAstBase { +public: + ExtendedSliceAst(Ast* parent, AstType type); + QList dims; +}; + +class KDEVPYTHONPARSER_EXPORT IndexAst : public SliceAstBase { +public: + ExpressionAst* value; +}; + +/** Independent classes **/ +class KDEVPYTHONPARSER_EXPORT ArgumentsAst : public Ast { +public: + ArgumentsAst(Ast* parent, AstType type); + QList arguments; + QList defaultValues; + Identifier* vararg; + Identifier* kwarg; +}; + +class KDEVPYTHONPARSER_EXPORT KeywordAst : public Ast { +public: + KeywordAst(Ast* parent, AstType type); + Identifier* argumentName; + ExpressionAst* value; +}; + +class KDEVPYTHONPARSER_EXPORT ComprehensionAst : public Ast { +public: + ComprehensionAst(Ast* parent, AstType type); + ExpressionAst* target; + ExpressionAst* iterator; + QList conditions; +}; + +class KDEVPYTHONPARSER_EXPORT ExceptionHandlerAst : public Ast { +public: + ExceptionHandlerAst(Ast* parent, AstType type); + ExpressionAst* type; + ExpressionAst* name; + QList body; +}; + +class KDEVPYTHONPARSER_EXPORT AliasAst : public Ast { +public: + AliasAst(Ast* parent, AstType type); + Identifier* name; + Identifier* asName; +}; + +} + +#endif diff --git a/utilities/generate.py b/utilities/generate.py new file mode 100644 index 0000000..3255ee5 --- /dev/null +++ b/utilities/generate.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python2.6 + +import re + +f = open('classes') +contents = f.read() +f.close() + +r = re.findall(r'class KDEVPYTHONPARSER_EXPORT (?P\w+)Ast', contents) +r.sort() +for item in r: + funcname = 'visit' + item + astname = item + 'Ast' + #print 'virtual void ' + funcname + '(' + astname + '* node);' + #print 'virtual void ' + funcname + '(' + astname + '* node) { Q_UNUSED(node); };' + #print 'case Ast::' + astname + 'Type:\t\t\t' + 'AstVisitor::' + funcname + '(dynamic_cast<' + astname + '>(node)); break;' + print 'else if ( name == "' + astname.lower() +'" ) ast = new ' + astname + '();' \ No newline at end of file