Мы начали с Владом переписку по почте с обсуждением различных вопросов, но Влад предложил вынести обсуждение на форум, чтобы если будут интересные ответы, которые могли бы быть полезны другим. Начали обсуждение с вопроса о Peg, парсере для текстового языка диаграмм состояний, а также проблем генерации исходников в макросах. Сейчас продолжаю задавать вопросы Владу, ребятам создававшим Nemerle.Peg и CSharpParser, и всему сообществу. На обсуждение вопрос о генерации исходника из дерева.
Есть встроенный генератор для отладочных нужд, типа TypeBuilder.DefineWithSource, но он не поддерживает вложенные типы, верхние объявления, которые должны добавляться в TypeBuilder методами DefineNested, но исходника никак не сделать для отладки, за отсутствием метода с приставкой WithSource. Код методов худо бедно генерится PrettyPrint ом.
Поэтому было бы неплохо доделать полноценную генерацию для верхних типов TopDeclaration, вложенных и всего остального, чтобы можно было делать качественные отладочные исходники. А также приятный плюс впридачу к парсингу C# и использованию в проектах Nemerle, конвертить C# в исходник Nemerle, благо большая часть уже готова, то есть PrettyPrint.
За все это я решил взяться, и уже начал работать, потому что потребность назрела, и возник ряд насущных вопросов в связи с этим.
1) При использовании конвертера CSharpToNemerle который получает в результате Ast Nemerle, ему на вход в конструкторе требуется ManagerClass, откуда его взять? Если мы в обычной программе, а не в макросе, Создать самому? Он инициализирует некоторые структуры только при вызове Run, как его инициализировать до этого, чтобы полноценно распарсить C# и получить Ast Nemerle для последующей обработки?
2) Куда все это положить, я начал делать у себя в своей папке, думаю сделать метод PrintBody в TopDeclaration как в методе ClassMember, сам TopDeclaration сделать partial и вынести код печати в отдельный исходник, которому как в ClassMember передается LocatableTextWriter, и он сам себя должен распечатать в него. То есть объединить генерацию исходника Nemerle из C# с генерацией обычного отладочного кода Nemerle, чтобы не делать двойную работу. Может кто придумает вариант с местоположением лучше.
3) Исходник в parsetree ncc будет наверное называться TreePrint.n, или PrintTree.n есть у кого варианты лучше?
4) Ничего если я сделаю открытыми типы LocatableTextWriter и LocatingTextWriter в Nemerle.Compiler чтобы работать из внешнего кода с ними, из своей библиотеки, чтобы тестить код без перекомпиляции всего Nemerle?
5) Также для этого нужен открытым метод ClassMember.PrintBody можно ли его открыть, или это даст брешь в архитектуре компилятора?
6) Встает проблема с генерацией namespace, парсер CSharpToNemerle в результате дает ParseResult в котором есть список TopDeclaration, понятно что каждый из них находиться в своем namespace может в одном а может и в разных. Тут надо как то придумать как создавать пространства имен, как в исходном тексте C#, если есть Location можно как то отсортировать по ним сами пространства, а потом вложить их друг в друга как они должны быть и в них раскрывать TopDeclaration, может кто предложит вариант лучше?
Прошу простить если кого смутит большое навороченное сообщение, с разными частными деталями, я задавал вопрос Владу по почте, но он настоял чтобы вынести на форум, что получилось то получилось. Сам же Влад да не презрит сие начертание.
Re: Генератор исходника Nemerle из дерева и из C#, а также P
Здравствуйте, CodingUnit, Вы писали:
CU>1) При использовании конвертера CSharpToNemerle который получает в результате Ast Nemerle, ему на вход в конструкторе требуется ManagerClass, откуда его взять?
Он есть у многих объектов. Например, у тайпера:
def typer = Macros.ImplicitCTX();
typer.Manager
CU>Если мы в обычной программе, а не в макросе, Создать самому?
В обычной программе его взять не откуда, так как это часть компилятора. Только зачем он в обычной программе? Он нужен в интеграции с IDE. Там такой объект есть. Для каждого проекта заводится так называемый Engin. Engin является наследником
ManagerClass.
Если хочется из обычного приложения его получить, то пидется вручную создавать ManagerClass и инициализировать его. Это не очень просто, но возможно.
В прочем, на мой взгляд подобный конвертер должен быть частью интеграции с IDE.
CU>Он инициализирует некоторые структуры только при вызове Run, как его инициализировать до этого, чтобы полноценно распарсить C# и получить Ast Nemerle для последующей обработки?
. В нем я парсил выражения с помощью компилятора немерла.
CU>2) Куда все это положить, я начал делать у себя в своей папке, думаю сделать метод PrintBody в TopDeclaration как в методе ClassMember, сам TopDeclaration сделать partial и вынести код печати в отдельный исходник, которому как в ClassMember передается LocatableTextWriter, и он сам себя должен распечатать в него. То есть объединить генерацию исходника Nemerle из C# с генерацией обычного отладочного кода Nemerle, чтобы не делать двойную работу. Может кто придумает вариант с местоположением лучше.
Мне кажется лучше сделать отдельный файл и модуль (на подобии PrettyPrint.n). Создать рядом еще один файл с именем вроде TopDeclarationsPrettyPrint.n и разместить в нем модуль содержаций по одному методу на каждый тип АСТ-веток.
CU>3) Исходник в parsetree ncc будет наверное называться TreePrint.n, или PrintTree.n есть у кого варианты лучше?
Все АСТ — это дерево. Оно как-то не внятно. Лучше TopDeclarationsPrettyPrint.n.
CU>4) Ничего если я сделаю открытыми типы LocatableTextWriter и LocatingTextWriter в Nemerle.Compiler чтобы работать из внешнего кода с ними, из своей библиотеки, чтобы тестить код без перекомпиляции всего Nemerle?
LocatableTextWriter — можно сделать публичным. Но LocatingTextWriter точно не надо. Это всего лишь подкласс класса LocatableTextWriter с переопределенными методами. Знание о его наличии не даст никаких дивидендов. Нужно грамотно капсулирование его использование и все.
CU>5) Также для этого нужен открытым метод ClassMember.PrintBody можно ли его открыть, или это даст брешь в архитектуре компилятора?
Вот это не нужно. Лучше пусть весь код PrettyPrint-а лежит в отдельном модуле и получает доступ к АСТ через публичный интерфейс.
И дело тут не в бреши (их и так поляки наделали не мало). Дело в группировке кода. Будет лучше, если код выполняющий одну задачу будет лежать в одном месте.
CU>6) Встает проблема с генерацией namespace, парсер CSharpToNemerle в результате дает ParseResult в котором есть список TopDeclaration, понятно что каждый из них находиться в своем namespace может в одном а может и в разных. Тут надо как то придумать как создавать пространства имен, как в исходном тексте C#, если есть Location можно как то отсортировать по ним сами пространства, а потом вложить их друг в друга как они должны быть и в них раскрывать TopDeclaration, может кто предложит вариант лучше?
Это косяк заложенный в компилятор Н еще на самых ранних стадиях его разрботки. Поляки увлеклись оптимизацией и опустили некоторые нужные структуры данных.
Насколько я знаю парсер C#-а выдает АСТ в том виде в котором он был спарсен из файла. При этом там есть и информация об пространствах имен. Наверно имеет смысл вынимать эту информацию из необработанного (C#-ного) АСТ. Конкретнее, из типа NamespaceNode.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Генератор исходника Nemerle из дерева и из C#, а такж
Здравствуйте, CodingUnit, Вы писали:
CU>1) При использовании конвертера CSharpToNemerle который получает в результате Ast Nemerle, ему на вход в конструкторе требуется ManagerClass, откуда его взять? Если мы в обычной программе, а не в макросе, Создать самому? Он инициализирует некоторые структуры только при вызове Run, как его инициализировать до этого, чтобы полноценно распарсить C# и получить Ast Nemerle для последующей обработки?
Вам, надеюсь, будут полезны класс-наследник ManagerClass, в котором есть метод LoadSources, который инициализирует компилятор для конкретного проекта, а также класс, который обходит все синтактическое дерево Немерле.
CU>2) Куда все это положить, я начал делать у себя в своей папке, думаю сделать метод PrintBody в TopDeclaration как в методе ClassMember, сам TopDeclaration сделать partial и вынести код печати в отдельный исходник, которому как в ClassMember передается LocatableTextWriter, и он сам себя должен распечатать в него. То есть объединить генерацию исходника Nemerle из C# с генерацией обычного отладочного кода Nemerle, чтобы не делать двойную работу. Может кто придумает вариант с местоположением лучше.
Если вы имеете в виду сделать методы Print*, которые будут генерировать код на одном из языков из расширяемого списка, это хорошая идея. Но может быть стоит рассмотреть вариант отдельной от компилятора сборки.
CU>4) Ничего если я сделаю открытыми типы LocatableTextWriter и LocatingTextWriter в Nemerle.Compiler чтобы работать из внешнего кода с ними, из своей библиотеки, чтобы тестить код без перекомпиляции всего Nemerle?
CU>5) Также для этого нужен открытым метод ClassMember.PrintBody можно ли его открыть, или это даст брешь в архитектуре компилятора?
Да она и так дырявая, эта архитектура. Компилятор не рассчитан на использование в виде библиотеки. Так что открывайте, имхо.
CU>6) Встает проблема с генерацией namespace, парсер CSharpToNemerle в результате дает ParseResult в котором есть список TopDeclaration, понятно что каждый из них находиться в своем namespace может в одном а может и в разных. Тут надо как то придумать как создавать пространства имен, как в исходном тексте C#, если есть Location можно как то отсортировать по ним сами пространства, а потом вложить их друг в друга как они должны быть и в них раскрывать TopDeclaration, может кто предложит вариант лучше?
Вариант хардкейса — каждый TopDeclaration в своем файле со своим неймспейсом и юзингами — мне нравится больше всего.
CU>Прошу простить если кого смутит большое навороченное сообщение, с разными частными деталями, я задавал вопрос Владу по почте, но он настоял чтобы вынести на форум, что получилось то получилось. Сам же Влад да не презрит сие начертание.
Вам спасибо за работу.
Re[3]: Генератор исходника Nemerle из дерева и из C#, а такж
Здравствуйте, hardcase, Вы писали:
H>А я вот думаю что список TopDeclaration это хорошая отличный кандидат на то, чтобы каждый его элемент превратить в файл.
Делегаты и энумы тоже? Не все с этим согласятся.
Как опция — это может и было бы хорошо. Но не по умолчанию.
Лучше уж сделать рефакторинг разнесения типов по файлам. Он бы был очень кстати при рефакторинге компилятора.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Генератор исходника Nemerle из дерева и из C#, а такж
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, hardcase, Вы писали:
H>>А я вот думаю что список TopDeclaration это хорошая отличный кандидат на то, чтобы каждый его элемент превратить в файл.
VD>Делегаты и энумы тоже? Не все с этим согласятся.
VD>Как опция — это может и было бы хорошо. Но не по умолчанию.
VD>Лучше уж сделать рефакторинг разнесения типов по файлам. Он бы был очень кстати при рефакторинге компилятора.
Да я с Владом согласен, TopDeclaration там и интерфейсы, альясы, делегаты, помимо больших классов, поэтому их каждый в свой файл как то не красиво. Вариант с обработкой NamespaceNode из CSharpParser мне кажется более похож на реальность. Хотя это годится для конвертера из C#, для обычной печати надо придумывать другой способ, наверное надо обрабатывать NamespaceNode в которых находятся TopDeclaration и сортировать их по позициям, тогда должны быть точно известны позиции Location, они известны при парсинге, но если добавляется член в макросе, то его надо грамотно подключить к текущему пространству. Давайте думу думать
Мне в голову приходит такой алгоритм:
1) Если мы имеем родной Ast C# из парсера то объявления должны быть точно соответствовать файлу С#, чтобы пользователь потом не плакал, говоря, я положил тут класс, оформил красиво два рядом namespace, а они куда то уехали.
2) Если производится печать по готовому дереву из парсера N из некоего источника и известны Location, то стараемся максимально приблизиться к оригиналу, при этом:
1. Можно печатать некий список TopDeclaration, тогда мы можем их отсортировать в памяти так:
а) Берем всю структуру и создаем типа Hashtable[namespace,list[TopDeclaration]], в одном пространстве может быть группа
б) Для типов лежащих в одном пространстве принимается решение класть их в отдельный файл или в разные. На основе Location, если известно то определяется по нему, если его нет значит на основе некоего критерия. Я предлагаю следующее, делегаты, интерфейсы, альясы объединяются в одну группу и лежат в одном файле, классы могут разделяться на разные файлы, по одному для класса. partial всегда в разные
в) Можно также некиими аттрибутами декларативно указать, если нет реального источника в коде в какой файл должен сгенериться после печати, что и куда
д) Потом все сортируется по Location если есть, или по порядку их в исходном списке и выдается в файлы. Разные пространства всегда лучше писать в разные файлы
2) Если печатаем только один TopDeclaration например методом DefineWithSource в GlobalEnv, то он пишется в своем пространстве, файл можно указать перегрузкой методов, один генерирует имена сам, другой можно задать самому, если находятся декларативные аттрибуты в коде (например можно пометить в цитате [PrintTo("testclass.n")] класс) то используются они.
Просьба писать комментарии, пожелания, кто что думает по этому поводу, проект открытый и будет использоваться в самом Nemerle поэтому просьба сообществу координировать его для верного его развития.
Re[2]: Генератор исходника Nemerle из дерева и из C#, а такж
Здравствуйте, catbert, Вы писали:
C>Здравствуйте, CodingUnit, Вы писали:
CU>>1) При использовании конвертера CSharpToNemerle который получает в результате Ast Nemerle, ему на вход в конструкторе требуется ManagerClass, откуда его взять? Если мы в обычной программе, а не в макросе, Создать самому? Он инициализирует некоторые структуры только при вызове Run, как его инициализировать до этого, чтобы полноценно распарсить C# и получить Ast Nemerle для последующей обработки?
C>Это почти законченный проект раскраски кода Nemerle и навигации по нему, который работает, используя компилятор Nemerle: C>https://bitbucket.org/catbert/projects/src/33d9a8113403/CodeColorizer/
C>Вам, надеюсь, будут полезны класс-наследник ManagerClass, в котором есть метод LoadSources, который инициализирует компилятор для конкретного проекта, а также класс, который обходит все синтактическое дерево Немерле.
Спасибо за ссылочку, особо хочу сказать спасибо за игру в точки на N, очень полезная штука, в детстве особенно любили в нее играть
CU>>5) Также для этого нужен открытым метод ClassMember.PrintBody можно ли его открыть, или это даст брешь в архитектуре компилятора?
C>Да она и так дырявая, эта архитектура. Компилятор не рассчитан на использование в виде библиотеки. Так что открывайте, имхо.
Еще проблема в том что сам ClassMember.PrintBody придется модифицировать потому что он не поддерживает сейчас всего что нужно, сейчас помоему только методы и свойства, надо еще вложенные типы, поля, события и остальное, все это доделать в нем же и вызывать при печати TopDeclaration для каждого ClassMember PrintBody или вынести вообще все во вне и сделать заново, мб скопировав код из PrintBody?
Re[5]: Генератор исходника Nemerle из дерева и из C#, а такж
Здравствуйте, CodingUnit, Вы писали:
CU>Да я с Владом согласен, TopDeclaration там и интерфейсы, альясы, делегаты, помимо больших классов, поэтому их каждый в свой файл как то не красиво. Вариант с обработкой NamespaceNode из CSharpParser мне кажется более похож на реальность. Хотя это годится для конвертера из C#, для обычной печати надо придумывать другой способ, наверное надо обрабатывать NamespaceNode в которых находятся TopDeclaration и сортировать их по позициям, тогда должны быть точно известны позиции Location, они известны при парсинге, но если добавляется член в макросе, то его надо грамотно подключить к текущему пространству.
"Обычная" печать одна для чего нужна? Я так понимаю только для отладки. А для отладки совершенно по фигу где лижит тип. Можно каждый тип сувать в отдельный файл. А алиасы печатать для отладки просто нет смысла.
CU>Давайте думу думать CU>Мне в голову приходит такой алгоритм:
CU>1) Если мы имеем родной Ast C# из парсера то объявления должны быть точно соответствовать файлу С#, чтобы пользователь потом не плакал, говоря, я положил тут класс, оформил красиво два рядом namespace, а они куда то уехали.
+1 Если только, конечно, пользователь сам не хочет разложить все поп полочкам. А это можно некой настройкой сделать.
CU>2) Если производится печать по готовому дереву из парсера N из некоего источника и известны Location, то стараемся максимально приблизиться к оригиналу, при этом:
А зачем печатать то для чего есть реальные Location-ы? Если они есть, значит исходник тоже есть. А вот если код генерирован макросом, то может они и есть, но это будут черт знает что за локейшоны и сортировать по ним просто бесполезно.
Так что при дебажной печати раскладываем типы по отдельным файлам и все.
CU> ...в) Можно также некиими аттрибутами декларативно указать,
Это все слишком сложно и никому не нужно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.