python + libclang; итерация вперед и назад: привязка комментариев поля к полю

Некоторое время я пытался найти достойный способ привязать поле в структуре C++ к его комментариям, используя libclang 3.9.1 и python 3.5.2.

Пока что я настроил и запустил эту программу: при условии, что у меня есть файл Foo.h:

typedef int arbType;

struct Foo {
    //First bar comment
    //Second bar comment
    int Bar; //Third bar comment - after bar

    /* First line baz comment - before baz
       Second line baz comment - before baz
    */
    arbType Baz; //Third line baz comment - after baz
};

Мой код на Python извлекает только встроенные комментарии:

#bind_comments.py
import clang.cindex

def get_cur_comments(cursor):
    comment = ''
    print ('\nGetting comment for:', cursor.spelling.decode())
    parent_cur = cursor.lexical_parent
    token_iter = parent_cur.get_tokens()
    for token in token_iter:
        if token.cursor == cursor:            
            while token.kind.name != 'PUNCTUATION':
                token = next(token_iter)
            token = next(token_iter)
            if token.kind.name == 'COMMENT':
                comment = token.spelling.decode().strip('/')
    return comment

def main():
    index = clang.cindex.Index.create()
    tu = index.parse(b'Foo.h', [b'-x', b'c++'])
    tu_iter = tu.cursor.get_children()
    next(tu_iter)
    root_cursor = next(tu_iter)

    for cur in root_cursor.type.get_fields():
        print(get_cur_comments(cur))

if __name__ == '__main__':
    main()

И вывод:

C:\>bind_comments.py

Getting comment for: Bar
'Third bar comment - after bar'

Getting comment for: Baz
'Third line baz comment - after baz'

Теперь для моих задач, упорядоченных по важности, на нисходящем уровне:

  1. Как я могу связать комментарии перед полями? Я посмотрел на многие "заглядывающие" решения в python, чтобы во время итерации токенов выяснить, является ли следующий курсором (полем), который меня интересует, но не нашел ничего, что я мог бы правильно реализовать в моем случае, Просто чтобы показать вам, насколько я серьезен, вот несколько решений, на которые я посмотрел:

  2. Концептуальный недостаток: я пока не знаю, как определить разницу между:

    struct Foo {
       int Bar; // This comment belong to bar
                // As well as this one
    
       // While this comment belong to baz already
       int Baz;
     };
    
  3. Проблемы с производительностью: обратите внимание, что для каждого поля я перебираю весь список токенов этой структуры. Если он большой, и у меня много токенов - думаю, это будет стоить мне. Я хотел бы найти несколько ярлыков.. Я думал о сохранении токенов в глобальном списке, но что тогда, если поле является объявлением другой структуры / класса? Добавить токены своих родителей в список? Это начинает становиться грязным...

Просто помощники для тех, кто еще не знает libclang:

>>> print(root_cursor.spelling.decode())
Foo
>>> root_cursor.type.get_fields()
<list_iterator object at 0x0177B770>
>>> list(root_cursor.type.get_fields())
[<clang.cindex.Cursor object at 0x0173B940>, <clang.cindex.Cursor object at 0x017443A0>]
>>> for cur in root_cursor.type.get_fields():
...   print (cur.spelling.decode())
...
Bar
Baz
>>> root_cursor.get_tokens()
<generator object TokenGroup.get_tokens at 0x01771180>

1 ответ

libclang обеспечивает прямую поддержку для извлечения комментариев в стиле javadoc с использованием Cursor свойства brief_comment а также raw_comment

С небольшой настройкой вашего входного кода:

s = '''
typedef int arbType;

struct Foo {
    /// Brief comment about bar
    ///
    /// Extra Text about bar
    int Bar; 

    /** Brief comment about baz
     *
     * Extra Text about baz
     */
    arbType Baz; 

    /// Brief only comment
    int blah;
};
'''

import clang.cindex
from clang.cindex import CursorKind

idx = clang.cindex.Index.create()
tu = idx.parse('tmp.cpp', args=['-std=c++11'],  unsaved_files=[('tmp.cpp', s)],  options=0)
for c in tu.cursor.walk_preorder():
    if c.kind == CursorKind.FIELD_DECL:
        print c.brief_comment
        print c.raw_comment
        print 

Производит:

Brief comment about bar
/// Brief comment about bar
    ///
    /// Extra Text about bar

Brief comment about baz
/** Brief comment about baz
     *
     * Extra Text about baz
     */

Brief only comment
/// Brief only comment
Другие вопросы по тегам