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

Сообщение Nemerle 2.x & Cx# от 12.01.2017 0:07

Изменено 07.06.2018 7:46 Serginio1

Nemerle 2.x & Cx#
English version

Что такое Cx#.

Для работы над данными проектами всё готово. Поэтому в данной теме я изложу свое видение этого проекта и объясню, почему я объединяю эти проекты воедино.

Почему два языка в одном проекте?


Начну с того, почему я объединяю эти проекты. Дело в том, что у близких по идеологии языков очень много общего. Несмотря на то, что их синтаксис может сильно различаться, внутренняя реализация у этих языков будет совпадать процентов на 80%.

Дело в том, что синтаксис для нас больше не проблема. Без ложной скромности скажу, что парсер у нас получился очень мощным (в этом огромная заслуга WolfHound-а).

Основной сложностью при разработке языка (на чём угодно, в том числе и на Nitra) является типизация (или семантический анализ, если по-научному). В этой области Nitra тоже предоставляет очень мощные инструменты, но всё же правильная типизация языка со сложным выводом типов и сложными областями видимости (к которым относится и Nemerle, и C#) является интеллектуальным вызовом. Nitra лишь повышает уровень абстракции и позволяет держать сложность в приемлемых рамках за счёт уровня абстракции и отличных возможностей декомпозиции.

Типизация в Nitra осуществляется на AST (не путать с Parse Tree). AST может быть как уникальным для языка, так и общим для ряда языков. В жизни чаще встречается общий AST. Различия в семантике могут быть абстрагированы за счёт фабрик алгоритмов и параметров.

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

При этом, предлагаю в большинстве случаев отказываться от семантических особенности Nemerle1 в угоду большей совместимости Nemerle2 и Cx#. Естественно, делать будем это не в ущерб выразительности языка. Просто у Nemerle1 есть множество решений, которые были сделаны отличными от C# не потому, что в C# решения были плохими, а потому, что так получилось при реализации или потому, что авторы языка просто плохо знали спецификацию C#.

Так же предлагаю в Cx# сразу использовать совместимые с Nemerle2 семантические решения, если они являются надмножеством C#. Например, на мой взгляд, допустимо использовать в Cx# более мощный вывод типов, а вывод типов Nemerle2 сделать более похожим на C# там, где это целесообразно.

О расширенных системах типов и прочих мега-фичах


Мега-фичи — это очень интересно. Но надо учитывать следующие соображения:
1. Всего сразу реализовать нельзя. Увлёкшись крутой, но не очень важной фичей, можно потратить много времени, но так и не получить желаемого результата.
2. Мысль о разработке и расширении языков программирования (ЯП): большинство из нас мыслит о них как о монолитных решениях, которые надо сразу проектировать как всемогуторы, продумывая интеграцию всех фич сразу. Это неверный подход. Nitra спроектирована так, что позволяет интегрировать как синтаксические расширения, так и расширения системы типов. Конечно, совсем не учитывать совместимость фич нельзя, но всё же у нас нет проблем, существующих у авторов монолитных языков:
1. Боязни испортить язык.
2. Боязни поломать совместимость.

Кроме того, нам нужно как можно быстрее получить работающий компилятор (и движок IDE) для подмножества Nemerle1, используемого в Nitra. Это нужно по следующим причинам (перечисляются в порядке важности):
1. Для ускорения компиляции. Это очень важная проблема! Скорость компиляции Nemerle1 в 10 и более раз ниже, чем у C#-компиляторов. А объём генерируемого Nitra кода довольно велик. Компиляция самой Nitra занимает до 10 минут. Компиляция даже не очень большого языка может занимать около минуты. Это резко снижает интерактивность разработки, что снижает скорость разработки и мотивацию программиста. Уверен, что нам удастся довести скорость компиляции Nemerle2 до сравнимой с современным компилятором C#. Даже если он будет отставать в два раза, это будет приемлемый результат, так как это обеспечит практически интерактивную разработку.
2. Для того, чтобы заработал IntelliSense во встроенных в Nitra блоках кода Nemerle. Сейчас они выглядят как "чёрные" блоки текста. Нет подсветки. Нет IntelliSens-а. Нет навигации по коду.
3. Для того, чтобы произвести полный бутстрапинг Nitra. Сейчас Nitra зависит от старого компилятора Nemerle 1.2.x. Это не позволит перенести Nitra на другую платформу.
4. Это улучшит поддержку Nemerle в IDE, что опять же ускорит разработку и сделает её более комфортной.

Посему, предлагаю отложить реализацию мега-фич до окончания реализации в Nemerle2 базовых возможностей Nemerle1, используемых в Nitra. Я, конечно, не могу никому приказывать, но сам точно не буду заниматься увлекательными, но второстепенными фичами до того момента, когда мы сможем полностью забутить Nitra на связке самой Nitra и Nemerle2.

Подсистемы и стадии компиляции


Компиляция (в том числе и в режиме IDE) таких сложных языков как C# и Nemerle идёт в несколько стадий. Эти языки принципиально нельзя скомпилировать в одну стадию — они являются так называемыми многопроходными языками.
Кроме того, эти языки нельзя создать без считывания и записи символов (объект, описывающий именованную сущность языка — например, класс, см. определение в статях по Nitra) из внешних файлов (сборок .Net, например).
Общий список стадий компиляции таков:

  1. [LoadExternals] Загрузка внешних символов из референс-сборок. Это можно делать один раз (к сожалению пока это не так, надо исправлять) для сборки (её версии) и использовать для всех проектов в солюшене (кстати, в режиме IDE работа идёт с солюшеном, а не с проектом). Загружаться символы могут путем десериализации (если это типы были записаны в сборку средствами Nitra) или вручную, если они должны считываться из стандартных форматов вроде метаданных .Net. За загрузку (а также за сериализацию) сборок отвечает бэкэнд. Это позволяет относительно легко переносить языки между платформами (на первых порах будет доступна платформа .Net, далее Java и LLVM, т.е. Native).

  2. [InitTree] Формирование дерева символов (пространства имён, типы, ...) для каждого проекта. Эти деревья уникальны для каждого проекта. Дерево является изменяемой структурой. На данном этапе в него загружаются внешние символы.

  3. [Parsing] Стадия парсинга исходников в Parse Tree и его последущее отображения на AST. После этой стадии выдаются синтаксические ошибки, код представляется в виде AST (на котором ещё не произведены вычисления).

  4. [Declarations] Стадия построения начальных областей видимости (Scopes) и сбора глобальных деклараций. Такими декларациями являются:
    * Пространства имён.
    * Типы (class, struct, interface, variant, ).
    * Глобальные функции.
    * Глобальные атрибуты.
    * Макросы.
    * Члены типов (methods, properties, constructors, fields, ...).
    Каждая декларация порождает символ. Этот механизм автоматизирован в Nitra, но может быть реализован и вручную.

  5. [Inheritance] Стадия вычисления списков базовых классов, окончательного построения областей видимости для глобальных деклараций и сбора вложенных деклараций (вложенных типов). Данная стадия повторяется по одному разу для каждого уровня вложенности типов, так как на каждом из них надо вычислять области видимости тел типов.

  6. [Typing] Стадия связывания (binding) имён на глобальном уровне. В конце этой стадии выполняются проверки, выявляющие семантические ошибки на уровне вплоть до тел членов типов (но не включая их).

  7. [Typing] Стадия постобработки глобальных структур. Здесь можно включать всевозможные макросы и семантические анализаторы, которые будут обходить дерево проекта. Анализаторы могут работать в фоновом режиме. Модифицирующие макросы — в синхронном.
    После этой стадии изменение дерева символов запрещено, но макросы смогут создавать своё, скрытое от других, дерево символов, которое будет сливаться с основным деревом после завершения процесса компиляции. В режиме IDE эти скрытые деревья использоваться не будут. Они нужны для технической реализации разных фич вроде замыканий или для генерации кода на поздних стадиях компиляции.

  8. [Typing] Стадия связывания имён внутри тел членов типов и глобальных функций, т.е. кода. Здесь выполняются: вывод типов, проверки, выявляющие семантические ошибки в коде. В режиме IDE данная стадия может проходить после каждого редактирования тела метода и в ленивом режиме (пока не реализовано).

  9. [Typing] Стадия постобработки тел членов типов и глобальных функций. Здесь, опять таки, могут отрабатывать плагины, которые позволят как просто производить анализ (в фоновом режиме), так и заниматься генерацией кода.

  10. [Typing] Стадия финальной, глобальной постобработки. Отличается от п. 8 тем, что обработчики могут читать весь код проекта, а не только отдельных тел его членов.

  11. [Backend] Стадия трансформации кода и его подготовки к генерации. При этом могут создаваться дополнительные (рабочие) типы, преобразовываться код (в стиле макросов). Данная (и последующие) стадия не выполняется в режиме IDE. Ошибки на этой стадии выдаются пользователю с пометкой "ICE" (Internal Compiler Error), так как являются ошибками реализации бэкэнда (за исключением IO-ошибок).

  12. [Backend] Запись метаданных. Для языков, имеющих свои форматы метаданных, бэкэнд осуществляет сериализацию в этот формат в ручном режиме. В случае отсутствия спецификации для метаданных, производится автоматическая сериализация средствами Nitra (уже реализовано и используется самой Nitra).

  13. [Backend] Стадия генерации низкоуровневого кода (в зависимости от бэкэнда: MSIL, Java-байткод, код LLVM).

  14. [Backend] Стадия финализации. Нужна, например, для .Net. Но в принципе, её наличие – это внутреннее дело бэкэнда.

  15. [Backend] Запись содержимого на диск. Опять же, внутреннее дело бэкэнда. Может и не производиться или производиться, например, в память.

Что из этого реализовано в Nitra:
1. Есть зачатки бэкэнда для .Net. Он реализован на базе Microsoft CCI Metadata. Умеет читать типы и их члены из .Net-сборок. Надо допилить напильникам, чтобы оно умело читать типы, специфичные для Nemerle (варинты, функциональные типы). На выходе получаются символы из проекта DotNetLang.
2. В проекте DotNetLang описаны декларации (а значит и символы) для большинства типов .Net. Это описание неполное: 1) нет многих проверок; 2) кое-что не сделано; 3) кое-что требует доработки.
3. Сделаны стадии, помеченные тегами [LoadExternals], [InitTree], [Parsing], [Declarations], [Inheritance]. В них сделано не всё, так что там есть над чем поработать. Парсинг и маппинг сделаны только для C#.
4. Сделано связывание имён для членов типов (без тел), т.е. пункт 6.
5. Реализована автоматическая сериализация символов в собственный формат.

Чего нет:
1. Нет стадий постобработки.
2. Нет маппинга тел членов типов (стэйтментов и выражений) для C#.
3. Нет парсинга и маппинга для Nemerle.
4. Не реализованы все пункты, начиная с 7 и до конца (за исключением сериализации символов, о которой говорилось выше).

Проекты


Предполагаются следующие проекты:
1. DotNet.BackEnd.CCI – проект бэкэнда на основе Microsoft CCI Metadata. Скорее всего, лучше заменить его на код из Roslyn. Там есть клон CCI Metadata, изрядно подправленный и слитый с кодом для C# и VB. Но учитывая его лицензию (Apache License Version 2.0) и наличие общего проекта Core\Portable\CodeAnalysis.csproj, нужно попробовать сделать бэкэнд на основе Roslyn.
2. DotNetLang – содержит AST и код типизации (неполный) для языков дотнета. Это общий проект для всех ЯП, сходных с Nemerle и C#.
3. CSharp.Grammar (нужно переименовать в Nitra.CSharp) – проект, содержащий код парсера, маппинга (в символы описанные в проекте DotNet.BackEnd.CCI) и прочий специфичный для C# код.
4. (проекта пока нет) Nitra.Nemerle – проект, который должен содержать код парсера, маппинга и прочий специфичный для Nemerle код.
5. (проекта пока нет) DotNet.Generator.BackEnd.CCI – проект должен содержать код, производящий запись метаданных, генерацию MSIL и запись результирующей сборки на диск.
6. (проекта пока нет) Nitra.Compiler.exe – универсальный компилятор. На вход будет принимать все поддерживаемые расширения. На выходе генерировать .Net-сборку.
7. (проекта пока нет) Nitra.MSBuildTask – MSBuildTask для сборки проектов под VS.
8. (надо переименовать в Nitra.DotNetLanguages.VisualStudio.Plugin) CSharp.VisualStudio.Plugin – проект, автоматизирующий генерацию IDE-плагина для VS.

Долго обдумывал нужно ли выносить проекты языков программирования (Nemerle 2.x и Cx#) в отдельный репозиторий (из репозитория Nitra). Пришел к выводу, что лучше этого не делать, так как Nitra сама является .Net-языком и зависит от ряда выносимых проектов (DotNet.BackEnd.CCI, DotNetLang, Nitra.Nemerle). При разработке Nitra используется бутстрапинг. Вынос части проектов в отдельный репозиторий приведет к сложным зависимостями между ним и репозиторием Nitra. По сему я принял решение вести разработку в рамках репозитория Nitra. Проекты связанные с языками будут как в составе основного солюшена Nitra.sln, так и в специализированном солюшене Nitra.DotNet.Languages.sln.

Что нужно для участия в проекте?


Нужны знания:
1 .Net Framework 4.5.
2. Nemerle.
3. Nitra. Особенно нужны знания по работе с декларациями, символами, областями видимости, связыванием имён, разрешением имён, зависимыми свойствами. Знания бэкэнда и парсинга.
4. Работа с git. Оптимально — GithubExtensions.

На машине должна быть установлена VS 2015 (можно Community edition), VS 2015 SDK (входит в инсталлятор, но надо отдельно ставить «галочки») и отсутствие новых версии VS (2017+). Лучше не устанавливать никаких других версий. Для экспериментов используйте виртуальные машины. Установите Git и GithubExtensions или нечто его заменяющее. Также на машине должен быть собран из исходников (предпочительнее) или установлена последняя версия Nemerle.

PS

Просьба, перевести на английский.
c# nemerle ide nitra nemerle2 Cx# Nemerle 2 Nemerle 2.0 Nemerle 2.x language workbenches programming language development programming language IDE development IDE plugin
Nemerle 2.x & Cx#
Влад посмотри почту.
c# nemerle ide nitra nemerle2 Cx# Nemerle 2 Nemerle 2.0 Nemerle 2.x language workbenches programming language development programming language IDE development IDE plugin