Информация об изменениях

Сообщение [Nitra] Roadmap - Milestone 2 (Символы и связывание) от 28.10.2014 23:00

Изменено 28.10.2014 23:16 VladD2

Здравствуйте, VladD2, Вы писали:

VD>Как обещал ранее, обновил Nitra Roadmap


Пару слов по поводу символов, деклараций и биндинга, раз уж оно интересно и не понятно.

Все метаданные в Нитра выражаются через символы. Символ — это эдакий объект описывающий ту или иную сущность языка. У символов есть типы (что-то вроде классов).

В общем-то задача процесса типизации пройтись по исходникам преобразовать информацию из них в граф символов. Примером сиволов можно считать рефлексию дотнета. Только объекты рефлексии описывают финсированный набор символов, а Найтра будет предоставлять возможность вводить собственные типы символов.

Любая ссылка внутри кода может быть только на символы. Скажем, если у вас в языке есть запись:
a + b

то a и b — это ссылки на некоторые символы. Задача процесса связывания найти символы на которые ссылаются эти a и b, а общая задача процесса типизации вычислить полный набор символов и связать с ними все имена. Конечно это не весь круг задач типизации, но важная его часть.

Разные языки организуют символы по разному. В некоторых языках вроде Немерла, Шарпа и Найтры есть глобальное дерево символов организованное в пространства имен. Найтра будет предоставлять подобные пресеты для часто используемых случаев и позволять создавать свои.

Если язык подразумевает наличие внешних бинарных модулей или библиотек, то символы можно будет сериализовать и загружать в эти внешние модули (кое какой функционал для этого уже имеется). Мы будет обеспечивать автоматическую сериализацию и десериализацию символв.

На практике не всегда просто отобразить Дерево разбора (детальный АСТ порождаемый Найтрой) на символы. Во-первых, разные синтаксические конструкции могут отображаться на одинаковые типы символов, во-вторых, отображение может быть не прямолинейным, в-третьих, при отображении есть некоторые проблемы с производительностью.

Проект может быть большим и довольно наивно пытаться производить полное отображение ДР (дерева разбора) при каждом изменении. По этому мы вводим промежуточную структур — декларацию. Декларация — это что-то типа АСТ файла (еденицы компиляции) только содержит оно исключительно конструкции верхнего уровня. Скажем для Шарпа в декларации будут попадать пространства имен (объявленные в файле плюс глобальное пространство файла), типы и их члены. Код членов (методов, свойств и т.п.) в них попадать не будет.

Для каждого файла создается дерево деклараций. Оно автоматически сериализуется и при открытии проекта нет нужды парсить все его файлы снова. При изменении файла декларации для его автоматически пересоздаются.

Декларации создаются на основе так называемых отображений. Это эдакая декларативная запись говорящая что брать из ДР и куда помещать в декларации. Пример деклараций и отображения можно поглядеть в проекте нового форматера РСДН-а (правда там они используются как AST, в целях тестирования).

Имея описание деклараций и отображение мы можем автоматически (при первой загрузке проекта или при изменении исходника) получать дерево деклараций. В дальнейшем они сериализуются и подгружаются при открытии проекта без парсинга.

Точно так же описываются символы и отображение с деклараций на символы.

При изменении сходника перестраивается дерево деклараций для измененного файла, старая версия деклараций "вычитается" из старого набора символов а новая, наоборот, накатывается. В результате этого процесса мы актуализируем символы и они начинают отражать актуальную информацию о проекте/солюшене.

Таким образом набор символов автоматически обновляется при изменении кода проекта или при изменении внешних библиотек (символы могут быть автоматически сериализованны и загружены из внешних модулей).

Кроме того, автор языка должен задать описание областей видимости для своего языка. Если области видимости идут слева направо и сверху вниз, то области видимости задавать не надо, так как это дефолтное поведение. Если же области видмости отличаются от указанного подхода, то придется описать отличия явно. Пока что язык для этого не разработан, так что не буду приводить примеры. Лучше приведу примеры того где это актуально. Опять возьмем в качестве примера Шарп. При объявлении обобщенного метода в шарпе параметр типов может декларироваться правее его первого использования (в возвращаемом значении):
T Foo<T>() {}
▲     ▲
│     └─Декларация
└─Использование


Вот здесь в грамматике (точнее в расширяющем ее DSL-е) придется описать, что параметры типов видны не только за правее и ниже по коду, но и левее, где описывается возвращаемый тип.

Такие случаи в языках встречаются редко, так что аннотаций будет не много.

Имея определенные области видимости, символы, декларации и отображение с ДР на декларации и с деклараций на символы мы можем связывать имена внутри программы.

В примере выше мы должны указать, что объявление параметра типов порождает символ типа "Параметр типов". Далее мы можем связать имя T использованное в типе возвращаемого значения с этим символом. Когда мы будем обрабатывать код на следующих этапах, то у всех идентификаторов можно будет узнать на какой символ они ссылаются.

Процесс сопоставления имен (ссылок) с символами называется связывание. Для его работы нужно иметь символы, области видимости и алгоритм связывания.

К сожалению, формализовать алгоритм связывания довольно не просто. Мы пока не имеем готового решения на этот счет. Так что любые предложения приветствуются. Если не получится придумать внятного декларативного описания принципов связывания имен, придется использовать для этого императивный код вынеся его в стратегию. В принципе такой код уже используется в самой Найтре. Но он довольно суров и тяжел в поддержке.

Еще раз повторюсь, если есть идеи по этому поводу, просьба ими делиться.

Связывание имен будет производиться в ленивом режиме, чтобы улучшить отклик в режиме IDE.

После связывания имен можно будет найти все ссылки на каждый из символов (будь то локальная переменная или синтаксический модуль).

Параллельно с этой работой мы будем работать над автоматической поддержкой символов в IDE. Символы и декларации должны будут автоматически обновляться при изменении исходников, при этом должны сбрасываться все затронутые связывания имен. При записи проекта на диск декларации сериализуются и пишутся в кэши. При загрузке проекта декларации загружаются из кэша. Так же из внешних библиотек загружаются хранящиеся в них символы (если язык поддерживает бинарные модули).

В итоге сразу после открытия проекта мы будем иметь граф символов поддерживаемый в актуальном состоянии при изменении сходников или библиотек.

Все это позволит создать создать такие сервисы IDE как:
1. Подсветка символов.
2. Навигация по символом (поиск деклараций, поиск ссылок).
3. Рефакторинг переименования.
4. Визуализация графа/дерева/списка символов (граф наследования, граф вызовов, ...).
5. Всплывающие подсказки с информацией о том на какой символ ссылается имя.
6. Комбы со списками деклараций из файла.

К сожалению, работы тут непочатый край. И код будет критичен к производительности. Так что этот этап займет не менее полугода (по моим оценкам).

29.10.14 02:03: Ветка выделена из темы [Nitra] Roadmap
Автор: VladD2
Дата: 27.10.14
— VladD2
[Nitra] Roadmap - Milestone 2 (Символы и связывание)
Здравствуйте, VladD2, Вы писали:

VD>Как обещал ранее, обновил Nitra Roadmap


Пару слов по поводу символов, деклараций и биндинга, раз уж оно интересно и не понятно.

Все метаданные в Нитра выражаются через символы. Символ — это эдакий объект описывающий ту или иную сущность языка. У символов есть типы (что-то вроде классов).

В общем-то задача процесса типизации пройтись по исходникам преобразовать информацию из них в граф символов. Примером сиволов можно считать рефлексию дотнета. Только объекты рефлексии описывают финсированный набор символов, а Найтра будет предоставлять возможность вводить собственные типы символов.

Любая ссылка внутри кода может быть только на символы. Скажем, если у вас в языке есть запись:
a + b

то a и b — это ссылки на некоторые символы. Задача процесса связывания найти символы на которые ссылаются эти a и b, а общая задача процесса типизации вычислить полный набор символов и связать с ними все имена. Конечно это не весь круг задач типизации, но важная его часть.

Разные языки организуют символы по разному. В некоторых языках вроде Немерла, Шарпа и Найтры есть глобальное дерево символов организованное в пространства имен. Найтра будет предоставлять подобные пресеты для часто используемых случаев и позволять создавать свои.

Если язык подразумевает наличие внешних бинарных модулей или библиотек, то символы можно будет сериализовать и загружать в эти внешние модули (кое какой функционал для этого уже имеется). Мы будет обеспечивать автоматическую сериализацию и десериализацию символв.

На практике не всегда просто отобразить Дерево разбора (детальный АСТ порождаемый Найтрой) на символы. Во-первых, разные синтаксические конструкции могут отображаться на одинаковые типы символов, во-вторых, отображение может быть не прямолинейным, в-третьих, при отображении есть некоторые проблемы с производительностью.

Проект может быть большим и довольно наивно пытаться производить полное отображение ДР (дерева разбора) при каждом изменении. По этому мы вводим промежуточную структур — декларацию. Декларация — это что-то типа АСТ файла (еденицы компиляции) только содержит оно исключительно конструкции верхнего уровня. Скажем для Шарпа в декларации будут попадать пространства имен (объявленные в файле плюс глобальное пространство файла), типы и их члены. Код членов (методов, свойств и т.п.) в них попадать не будет.

Для каждого файла создается дерево деклараций. Оно автоматически сериализуется и при открытии проекта нет нужды парсить все его файлы снова. При изменении файла декларации для его автоматически пересоздаются.

Декларации создаются на основе так называемых отображений. Это эдакая декларативная запись говорящая что брать из ДР и куда помещать в декларации. Пример деклараций и отображения можно поглядеть в проекте нового форматера РСДН-а (правда там они используются как AST, в целях тестирования).

Имея описание деклараций и отображение мы можем автоматически (при первой загрузке проекта или при изменении исходника) получать дерево деклараций. В дальнейшем они сериализуются и подгружаются при открытии проекта без парсинга.

Точно так же описываются символы и отображение с деклараций на символы.

При изменении сходника перестраивается дерево деклараций для измененного файла, старая версия деклараций "вычитается" из старого набора символов а новая, наоборот, накатывается. В результате этого процесса мы актуализируем символы и они начинают отражать актуальную информацию о проекте/солюшене.

Таким образом набор символов автоматически обновляется при изменении кода проекта или при изменении внешних библиотек (символы могут быть автоматически сериализованны и загружены из внешних модулей).

Кроме того, автор языка должен задать описание областей видимости для своего языка. Если области видимости идут слева направо и сверху вниз, то области видимости задавать не надо, так как это дефолтное поведение. Если же области видмости отличаются от указанного подхода, то придется описать отличия явно. Пока что язык для этого не разработан, так что не буду приводить примеры. Лучше приведу примеры того где это актуально. Опять возьмем в качестве примера Шарп. При объявлении обобщенного метода в шарпе параметр типов может декларироваться правее его первого использования (в возвращаемом значении):
T Foo<T>() {}
▲     ▲
│     └─Декларация
└─Использование


Вот здесь в грамматике (точнее в расширяющем ее DSL-е) придется описать, что параметры типов видны не только правее и ниже по коду, но и левее, где описывается тип возвращаемого значения метода.

Такие случаи в языках встречаются редко, так что аннотаций будет не много.

Имея определенные области видимости, символы, декларации и отображение с ДР на декларации и с деклараций на символы мы можем связывать имена внутри программы.

В примере выше мы должны указать, что объявление параметра типов порождает символ типа "Параметр типов". Далее мы можем связать имя T использованное в типе возвращаемого значения с этим символом. Когда мы будем обрабатывать код на следующих этапах, то у всех идентификаторов можно будет узнать на какой символ они ссылаются.

Процесс сопоставления имен (ссылок) с символами называется связывание. Для его работы нужно иметь символы, области видимости и алгоритм связывания.

К сожалению, формализовать алгоритм связывания довольно не просто. Мы пока не имеем готового решения на этот счет. Так что любые предложения приветствуются. Если не получится придумать внятного декларативного описания принципов связывания имен, придется использовать для этого императивный код вынеся его в стратегию. В принципе такой код уже используется в самой Найтре. Но он довольно суров и тяжел в поддержке.

Еще раз повторюсь, если есть идеи по этому поводу, просьба ими делиться.

Связывание имен будет производиться в ленивом режиме, чтобы улучшить отклик в режиме IDE.

После связывания имен можно будет найти все ссылки на каждый из символов (будь то локальная переменная или синтаксический модуль).

Параллельно с этой работой мы будем работать над автоматической поддержкой символов в IDE. Символы и декларации должны будут автоматически обновляться при изменении исходников, при этом должны сбрасываться все затронутые связывания имен. При записи проекта на диск декларации сериализуются и пишутся в кэши. При загрузке проекта декларации загружаются из кэша. Так же из внешних библиотек загружаются хранящиеся в них символы (если язык поддерживает бинарные модули).

В итоге сразу после открытия проекта мы будем иметь граф символов поддерживаемый в актуальном состоянии при изменении сходников или библиотек.

Все это позволит создать создать такие сервисы IDE как:
1. Подсветка символов.
2. Навигация по символом (поиск деклараций, поиск ссылок).
3. Рефакторинг переименования.
4. Визуализация графа/дерева/списка символов (граф наследования, граф вызовов, ...).
5. Всплывающие подсказки с информацией о том на какой символ ссылается имя.
6. Комбы со списками деклараций из файла.

К сожалению, работы тут непочатый край. И код будет критичен к производительности. Так что этот этап займет не менее полугода (по моим оценкам).

29.10.14 02:03: Ветка выделена из темы [Nitra] Roadmap
Автор: VladD2
Дата: 27.10.14
— VladD2