Здравствуйте, konsoletyper, Вы писали:
K>А тайпбилдера не предусмотрено. Это же макроатрибут уровня сборки.
Я тебе говорю о возможностях "вообще". Ты же не спросил "как сделать то-то, там-то?"?.
K>Это плохо. А если я хочу как-то поработать с классом из другого места?
Какого? В принципе проблем работы с классами нет. Проблемы есть с работой вообще из кода. По уму код макросов уровня выражения не должен вообще создавать побочных эффектов (если конечно ты не далешь их осознанно и понимаешь последствия). Создание или изменение типа (тайпбилдера) — это побочный эффект.
Вот из макро-атриб побочные эффекты можно делать смело.
K>Как мы уже выяснили, макрос имеет синтаксис, несколько отличный от Nemerle, так что без лексемных макросов не обойтись.
Наверно, так.
K> Но лексемные макросы предусмотрены только на уровне выражений.
Да, но выражения предусмотрены не только в телах методов .
Выражения передаются и макро-атрибуту. Незнаю гут ли там быть применены лексические макросы. Скорее всего — да. Если это так, то никаких проблем нет.
K> А я хочу юзать их на уровне макроатрибута, используя обходной путь, описанный ниже. Вот и спрашивается, прежде чем передать PExpr макроатрибуту, будет ли компилятор обрабатывать макросы внутри него?
Несомненно будет. Но думаю, что если код содержит ссылку на лексический макрос, то он будет тупо помещен в специальную конструкцию и доп. обработке не подвергнется. Лексические макросы вообще раскрываются только во время типизации. Так что дума, все будет ОК, но надо пробовать.
K>OK. Пусть имеется что-то вроде:
K>
K>А потом макрос BNF возьмёт это выражение, проанализирует его и сформирует поток лексем. Далее поток лексем направляется парсеру, формируется AST, по AST генерится спосок регэкспов и т.д.
Слушай, я вот смотрю на то как ты теоретизируешь и не пойму, а почему бы просто не попробовать? Темболее, что сейчас с интеграцией это стало так просто!
В общем, вместо того чтобы рассуждать вместе с тобой, я просто попробовал сам...
И вот что у меня получилось.
Я создал два проекта: MacroLibrary2 и тесовый TestConsoleApplication в котором примаеняются макросы из MacroLibrary2. Поместил их в один солюшен.
Создал два макроса:
1. Bnf — макроатрибут.
2. lexer — лексический макрос. Он нужен, чтобы получить от компилятора набор токенов.
Вот код макро-проекта:
using MacroLibrary2;
using Nemerle;
using Nemerle.Compiler;
using Nemerle.Collections;
using Nemerle.Utility;
using System.Diagnostics.Trace;
namespace MacroLibrary2
{
[MacroUsage (MacroPhase.WithTypedMembers, MacroTargets.Assembly, Inherited = true)]
macro Bnf (expr)
{
WriteLine(expr); // это просто чтобы поставить точку останова в отладчике.
}
macro lexer (group : Token)
syntax ("lexer", group)
{
def str = group.ToString(); // это тоже чистый fake.
<[ $(str : string) ]>
}
}
В тестовом проекте написал:
using System;
using System.Console;
using Nemerle.Utility;
using MacroLibrary2;
[assembly: Bnf(
lexer
{
aaaa ::= bbb* | ccc+;
bbb ::= xxx*;
})]
module Program
{
Main() : void
{
_ = ReadLine();
}
}
Все отлично скомпилировалось.
Далее я сделал активным (в IDE) проект MacroLibrary2 и в его свойствах, на закладке Debug задал в качестве отлаживаемого модуля ncc.exe.
Более конкретно:
Command Arguments =>
Main.n -m:C:\MyProjects\Tests\MacroTest1\MacroLibrary2\bin\Debug\MacroLibrary2.dll > C:\1.TXT
Start Program => C:\Program Files\Nemerle\NCC.exe
Working Directory => C:\MyProjects\Tests\MacroTest1\MacroLibrary2\..\TestConsoleApplication
Далее я поставил точку останова в макросе Bnf, на строке:
WriteLine(expr);
и нажал F5.
Далее оставалось поглядеть в окне Locals отладчике чему равно значение параметра expr.
В нем находился вызов PExpr.MacroCall в последнем параметре которого было значение SyntaxElement.RawToken, которое в свою очередь, содержало список токенов конструкции "lexer". Вот что я увидел в отладчике:
K>Но такие вот макросы обязаны быть внутри метода и возвращать PExpr. Можно расширять имеющиеся классы при помощи макросов-атрибутов. Так же, насколько я знаю, можно исхитриться и из таких макросов создать новые классы. Но как заюзать такую возможноть в BnfMacro?
K>Сам я не вижу способа исхитриться и как-то решить проблему обходным путём. Есть ли у кого соображения по этому поводу?
Ну разве что assembly-level macro. Вот как-то так:
Здравствуйте, konsoletyper, Вы писали:
VD>>О том и речь. Макрос lexer получается просто способом сказать компилятору "не порси, плиз, вот этот код, а заверни мне его в SyntaxElement.RawToken".
K>Уже сделал
Маладэс!
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
). Наткнулся на одну концептуальную проблему: макросистема Nemerle попросту для этого не предназначена. В идеале хотелось бы сделать что-то вроде:
namespace Some.Name.Space
{
bnf
{
тра-ля-ля
}
}
Но такие вот макросы обязаны быть внутри метода и возвращать PExpr. Можно расширять имеющиеся классы при помощи макросов-атрибутов. Так же, насколько я знаю, можно исхитриться и из таких макросов создать новые классы. Но как заюзать такую возможноть в BnfMacro?
Сам я не вижу способа исхитриться и как-то решить проблему обходным путём. Есть ли у кого соображения по этому поводу?
Здесь есть недостаток: "тря-ля-ля" имеет синтаксис, несколько отличный от Nemerle. В случае с обычными макросами можно делать расширения синтаксиса (причём даже на уровне лексем). А вот так, я понимаю, синтаксических расширений не положено.
Здравствуйте, konsoletyper, Вы писали:
K>Здесь есть недостаток: "тря-ля-ля" имеет синтаксис, несколько отличный от Nemerle. В случае с обычными макросами можно делать расширения синтаксиса (причём даже на уровне лексем). А вот так, я понимаю, синтаксических расширений не положено.
С синтаксически свободными макросами не работал, сказать поэтому ничего не могу. Но ничто не мешает просто сделать атрибут, в нем ссылку на файл .bnf и отдельно его парсить, ибо он может быть довольно большой.
Здравствуйте, konsoletyper, Вы писали:
K>Здесь есть недостаток: "тря-ля-ля" имеет синтаксис, несколько отличный от Nemerle. В случае с обычными макросами можно делать расширения синтаксиса (причём даже на уровне лексем). А вот так, я понимаю, синтаксических расширений не положено.
Дык:
1. Внутри блока кода ты можешь использовать вплоть до лексерных макросов которые вообще накладывают ограничения только на две вещи: 1) парность скобок, 2) то что все токены должны быть совместимы с Немерловым лексером.
2. Парсер не особо разбирается в том, что он там разбирает. Класс выражений пропускаемый парсером значительно шире языка Nemerle. А там ты уже можешь разобрать все что нужно с помощью match-а и преобразовать в реальный код.
Опиши те выражения которые ты бы хотил видеть в этом ДСЛ-е и попробуем что-нить придумать.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Дык: VD>1. Внутри блока кода ты можешь использовать вплоть до лексерных макросов которые вообще накладывают ограничения только на две вещи: 1) парность скобок, 2) то что все токены должны быть совместимы с Немерловым лексером.
Впрочем, это не сильно критично, но что-то не очень приятно. Хотя можно попробовать сделать по макросу (parser, lexer, ast, subst) для каждого раздела
VD>2. Парсер не особо разбирается в том, что он там разбирает. Класс выражений пропускаемый парсером значительно шире языка Nemerle. А там ты уже можешь разобрать все что нужно с помощью match-а и преобразовать в реальный код.
VD>Опиши те выражения которые ты бы хотил видеть в этом ДСЛ-е и попробуем что-нить придумать.
Собственно, синтаксис очень похож на Nemerle. Но есть критичные вещи. Например, "*" — операция не инфиксная, а постфиксная. У операций несколько иные приоритеты. Будет ли парсер гарантировать, например, что у операции "::=" окажется самый низкий приоритет? А как парсер Nemerle среагирует на последовательно идущие друг за другом идентификаторы и строки (конкатенация в терминах BNF).
А зачем нужны эти "bnf {"?
K>Впрочем, это не сильно критично, но что-то не очень приятно. Хотя можно попробовать сделать по макросу (parser, lexer, ast, subst) для каждого раздела
Так и надо сделать. А для колорера вообще все это не нужно. Нужны только список ключевых слов и плавила для остальных констуркций.
K>Собственно, синтаксис очень похож на Nemerle. Но есть критичные вещи. Например, "*" — операция не инфиксная, а постфиксная.
Вот это фиг его знает. Тут нужно пробоавать. На крайням можно воспоьзоваться лексемными макросами. Там парсить ты будешь сам. Тебе только список лексем предоставят.
K> У операций несколько иные приоритеты. Будет ли парсер гарантировать, например, что у операции "::=" окажется самый низкий приоритет? А как парсер Nemerle среагирует на последовательно идущие друг за другом идентификаторы и строки (конкатенация в терминах BNF).
Приоритеты новых операций можно задавать самому. Так что это не проблема.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
K>>Собственно, синтаксис очень похож на Nemerle. Но есть критичные вещи. Например, "*" — операция не инфиксная, а постфиксная.
VD>Вот это фиг его знает. Тут нужно пробоавать. На крайням можно воспоьзоваться лексемными макросами. Там парсить ты будешь сам. Тебе только список лексем предоставят.
Лексерный макрос делается элементарно. Объявляешь макрос следующего вида:
Кстати, забыл про очень важный момент. Как всё это дело заставить дружить с интеграцией? Ведь для генерации более-менее сложного ДКА может потребоваться много времени (~0.5 с). Но это нужно только на этапе компиляции. Для Интеграции нужно просто сказать, какие классы будут созданы и какие методы/свойства будут внутри этих классов, не генеря ДКА. Как такое можно сделать?
Здравствуйте, VladD2, Вы писали:
VD>Макрос "rule" будет преобразовывать твои правила в нужный тебе вид. При этом мкрос Bnf уже будет работать с этим преобразованным видом.
Лучше сделать по-моему, т.е. макрос lexer, который будет описывать сразу группу правил. А то зачем перед каждым правилом лепить rule?
Здравствуйте, VladD2, Вы писали:
VD>Так и надо сделать. А для колорера вообще все это не нужно. Нужны только список ключевых слов и плавила для остальных констуркций.
Списка ключевых слов не предусмотрено. Ключевые слова описываются обычными правилами. Уже на этапе генерации, чтобы оптимизировать лексер и ускорить саму генерацию, делается автоопределение ключевых слов.
Здравствуйте, konsoletyper, Вы писали:
K>Лучше сделать по-моему, т.е. макрос lexer, который будет описывать сразу группу правил. А то зачем перед каждым правилом лепить rule?
Можно попробовать сделать макро-атрибут получающий токены. Если это прокатит, то никакие префиксы не потребуются.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, konsoletyper, Вы писали:
K>Списка ключевых слов не предусмотрено. Ключевые слова описываются обычными правилами.
Как ты и сам наверно понимаешь для колорера совершенно излишне давать ключевым словам какие-то имена. Это должен быть простой спиоск. Так что надо что-то придумать.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, konsoletyper, Вы писали:
K>Кстати, забыл про очень важный момент. Как всё это дело заставить дружить с интеграцией? Ведь для генерации более-менее сложного ДКА может потребоваться много времени (~0.5 с).
Вообще-то 0.5 ске. это конечно многовато.
K>Но это нужно только на этапе компиляции. Для Интеграции нужно просто сказать, какие классы будут созданы и какие методы/свойства будут внутри этих классов, не генеря ДКА. Как такое можно сделать?
Можно проверять не находится ли Menager в резиме IntelliSense (свойство IsIntelliSenseMode).
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Как ты и сам наверно понимаешь для колорера совершенно излишне давать ключевым словам какие-то имена. Это должен быть простой спиоск. Так что надо что-то придумать.
Здравствуйте, VladD2, Вы писали:
K>>Кстати, забыл про очень важный момент. Как всё это дело заставить дружить с интеграцией? Ведь для генерации более-менее сложного ДКА может потребоваться много времени (~0.5 с).
VD>Вообще-то 0.5 ске. это конечно многовато.
Жизнь такая. Самая медленная фаза — преобразование НКА в ДКА. Причём если вместо интервалов юникода тупо писать что-то вроде "a".."z" | "A".."Z", то скорость генерации резко возрастает. Больше всего времени приходится на работу метода CharRange.SplitToDisjoint(), который используется в алгоритме преобразования НКА -> ДКА. Но это не из-за тормознутости метода, а из-за объективных причин — методу передаётся совокупность из большого числа множеств символов, каждое из множеств имеет довольно сложную структуру. Я модифицировал метод SplitToDisjoint(), так что теперь его асимтотическая сложность уменьшилась. Но даже после этого генерация ДКА происходит 0.5 с.
Кстати, реально генерация ДКА происходит 2 раза, так что лексер для C# будет генериться чуть больше секунды. Это нужно для автоопределения ключевых слов. Если же не делать автоопределения и тем самым включать их в ДКА, то не только сам ДКА увеличивается в 10 раз, но время его генерации возрастает до 12 секунд. Так что лучше уж делать два быстрых прохода, чем один медленный.
Минимизации ДКА сейчас не производится, т.к. эта операция дороже, чем преобразование НКА в ДКА, так что неизвестно, сколько секунд после включения этой фичи будет работать генератор. К тому же, минимизация ДКА позволяет сократить количество состояний процентов на 10, что не так уж много.
K>>Но это нужно только на этапе компиляции. Для Интеграции нужно просто сказать, какие классы будут созданы и какие методы/свойства будут внутри этих классов, не генеря ДКА. Как такое можно сделать?
VD>Можно проверять не находится ли Menager в резиме IntelliSense (свойство IsIntelliSenseMode).
Так, пойдет.
K>Правда, придётся чуть-чуть расширить автоопределитель ключевых слов, чтобы оптимизировать и такие ситуации. Но это делается за 10 минут.
Так в чем вопрос?
K>А то очень мне уж хочется сделать всё с единых позиций, чтобы не перегружать синтаксис.
Дык, надо еще думать о пользователе (том, что будет писать эти грамматики). Если пользователь будет вынужден лепить море не нужного ему кода и при этом не сможет одим взглядом охватить список ключевых слов, то он будет не очень доволет (мягко говоря).
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Lazy Cjow Rhrr, Вы писали:
LCR>А yield?
А yield/get/set и прочее в колорере, думаю, предусмотрено не будет. Ведь для этого нужно делать синтаксический анализ. А чтобы делать синтаксический анализ, нужно знать, какой из нетерминалов стартовый. Кроме того, многие товарищи приводят не совсем корректный код (например, многоточие на месте пропуска вставят). Хотя можно подумать наж этим и записать "облегчённую" грамматику для каждого из языков. Так или иначе, для начала нужно придумать хорошее решение для клексики, а потом будем думать.
Здравствуйте, konsoletyper, Вы писали:
K>Здравствуйте, Lazy Cjow Rhrr, Вы писали:
LCR>>А yield?
K>А yield/get/set и прочее в колорере, думаю, предусмотрено не будет. Ведь для этого нужно делать синтаксический анализ. А чтобы делать синтаксический анализ, нужно знать, какой из нетерминалов стартовый. Кроме того, многие товарищи приводят не совсем корректный код (например, многоточие на месте пропуска вставят). Хотя можно подумать наж этим и записать "облегчённую" грамматику для каждого из языков. Так или иначе, для начала нужно придумать хорошее решение для клексики, а потом будем думать.
На самом деле фигня все это. yield вполне себе ключевое слово. А get/set хорошо бы вычислять по эвристике Если перед ним токен "{" и после него "{" или ";", то считаем ключевым словом. Хотя это конечно тоже для негерируемого лексера проблема. А вот для ручного пустяки.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>На самом деле фигня все это. yield вполне себе ключевое слово. А get/set хорошо бы вычислять по эвристике Если перед ним токен "{" и после него "{" или ";", то считаем ключевым словом. Хотя это конечно тоже для негерируемого лексера проблема. А вот для ручного пустяки.
Можно сделать сгенерированный парсер. При этом это не будет вообще парсер C#, это будет парсер некого языка, в котором просто yield, get и set придаётся особое значение в некоторых ситуациях.
konsoletyper,
VD>>На самом деле фигня все это. yield вполне себе ключевое слово. А get/set хорошо бы вычислять по эвристике Если перед ним токен "{" и после него "{" или ";", то считаем ключевым словом. Хотя это конечно тоже для негерируемого лексера проблема. А вот для ручного пустяки.
K>Можно сделать сгенерированный парсер. При этом это не будет вообще парсер C#, это будет парсер некого языка, в котором просто yield, get и set придаётся особое значение в некоторых ситуациях.
Возможно ли сделать быстрый парсер, в котором будет правило: "если yield перед return, то считать его ключевым словом"? (+ правила для get/set)
Здравствуйте, konsoletyper, Вы писали:
K>Здравствуйте, Lazy Cjow Rhrr, Вы писали:
LCR>>А yield?
K>А yield/get/set и прочее в колорере, думаю, предусмотрено не будет. Ведь для этого нужно делать синтаксический анализ. А чтобы делать синтаксический анализ, нужно знать, какой из нетерминалов стартовый. Кроме того, многие товарищи приводят не совсем корректный код (например, многоточие на месте пропуска вставят). Хотя можно подумать наж этим и записать "облегчённую" грамматику для каждого из языков. Так или иначе, для начала нужно придумать хорошее решение для клексики, а потом будем думать.
Думается что yield/get/set и прочие должны считаться ключевыми без сколь либо слождного анализа.
Потому что в общем случае по коду узнать что они означают нельзя, их значение легко может меняться макросами.
Здравствуйте, Иванков Дмитрий, Вы писали:
ИД>Думается что yield/get/set и прочие должны считаться ключевыми без сколь либо слождного анализа. ИД>Потому что в общем случае по коду узнать что они означают нельзя, их значение легко может меняться макросами.
Здравствуйте, konsoletyper, Вы писали:
K>Можно сделать сгенерированный парсер. При этом это не будет вообще парсер C#, это будет парсер некого языка, в котором просто yield, get и set придаётся особое значение в некоторых ситуациях.
И как это будет работать? Два прохода?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
K>>Но такие вот макросы обязаны быть внутри метода и возвращать PExpr.
VD>Макро-атрибуты не обязаны. Погляди как сделано здесь: [Nemerle] Семантический контроль над размерностями
Поглядел 0.05. Ничего хорошего не увидел. Макрос, видимо, написан под устаревший компилятор. Например, контруктору GlobalEnv передаётся один параметр (string), тогда как я обнаружил единственный публичный конструктор GlobalEnv с двумя параметрами (string, ManagerClass). Не совсем понятно, что должно быть в string. Так же не ясно, откуда брать ManagerClass. Создавать?
Вообще, очень интересует, как из макроса создать класс. Судя по примеру Oyster'а, это делается через GlobalEnv. Но не является ли это хаком? Как на это среагирует Интеграция? И если всё нормально, то каким образом можно корректно получить GlovalEnv из макро-атрибута уровня сборки?
Кстати, сейчас пытаюсь сделать так. Делаю два макроса: макро-атрибут BNF, и макрос lexer. Во-первых, будет ли макрос lexer разворачиваться внутри атрибута? Во-вторых, сейчас lexer просто разворачивается в списко кортежей, каждый из которых просто описывает лексему bnf. Планируется, что макрос BNF будет разбирать этот список, преобразовывать в поток лексем и т.д. Но, ИМХО, это криво. Неужели нет возможности всё это проделать непосредственно?
Здравствуйте, konsoletyper, Вы писали:
K>Поглядел 0.05. Ничего хорошего не увидел.
Очень плохо. Значит плохо разбирался.
K> Макрос, видимо, написан под устаревший компилятор.
Насколько я знаю, макрос этот вместе с тестом включен в недельные тесты. Можно пошукать по СВН-у.
K>Например, контруктору GlobalEnv передаётся один параметр (string), тогда как я обнаружил единственный публичный конструктор GlobalEnv с двумя параметрами (string, ManagerClass). Не совсем понятно, что должно быть в string. Так же не ясно, откуда брать ManagerClass. Создавать?
Экземляры GlobalEnv вообще нет смысла создавать сомостоятельно. Они хранят контекст (список открытых пространств имен, ключевых слов и операторов). Ссылки на них есть в любом имени и во многих других конструкциях.
ManagerClass или простро Manager — это "главный центр управления", что ли. В интеграции он переопределяется на Engine. Manager доступен почти везде. Просто поищи "Manager" по коду компилятора или "Engine" по коду интеграции.
K>Вообще, очень интересует, как из макроса создать класс.
Только, как я уже говорил, нужно использовать доп. проверки совйств InErrorMode и IsIntelliSenseMode.
K> Судя по примеру Oyster'а, это делается через GlobalEnv. Но не является ли это хаком?
Нет. Но можно и через тайпбилдер.
K> Как на это среагирует Интеграция? И если всё нормально, то каким образом можно корректно получить GlovalEnv из макро-атрибута уровня сборки?
С интеграцией есть только одна особенность. Она парсит код методов по требованию. То есть если ты попыташся добавить класс внутри метода, то:
1. Это не произойдет до тех пор пока программист не откроет файл содержащий этот метод.
2. Этот код будет вызваться неопределенное количество раз, так как любое изменение, интелисенс и т.п. могут привести к повторному парсингу тел методов.
K>Кстати, сейчас пытаюсь сделать так. Делаю два макроса: макро-атрибут BNF, и макрос lexer. Во-первых, будет ли макрос lexer разворачиваться внутри атрибута?
Э... не совсем понял вопрос.
K> Во-вторых, сейчас lexer просто разворачивается в списко кортежей, каждый из которых просто описывает лексему bnf. Планируется, что макрос BNF будет разбирать этот список, преобразовывать в поток лексем и т.д. Но, ИМХО, это криво. Неужели нет возможности всё это проделать непосредственно?
Тоже не понял.
Попытайся описать то что ты хочешь более детально.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
K>Вообще, очень интересует, как из макроса создать класс. Судя по примеру Oyster'а, это делается через GlobalEnv. Но не является ли это хаком? Как на это среагирует Интеграция? И если всё нормально, то каким образом можно корректно получить GlovalEnv из макро-атрибута уровня сборки?
Перечитал ещё раз документацию по Nemerle и нашёл там один "документированный" способ создать класс: вызвать у GlobalEnv методы Define и Compile. Сам GlobalEnv берётся вот где: ImplicitCTX().Env.
Возникает несколько вопросов. Во-первых, почему Oytser делал по-другому? Во-вторых, меня смутило определение макроса ImplicitCTX в исходниках — совершенно непонятно, что на его место подставляется. В третьих, смущает название метода Compile. В документации про него сказано только:
Another gotcha is the builder.Compile() call. If you forget it, then the compiler will throw ICE when the macro is used.
Здравствуйте, VladD2, Вы писали:
K>> Судя по примеру Oyster'а, это делается через GlobalEnv. Но не является ли это хаком?
VD>Нет. Но можно и через тайпбилдер.
А тайпбилдера не предусмотрено. Это же макроатрибут уровня сборки.
K>> Как на это среагирует Интеграция? И если всё нормально, то каким образом можно корректно получить GlovalEnv из макро-атрибута уровня сборки?
VD>С интеграцией есть только одна особенность. Она парсит код методов по требованию. То есть если ты попыташся добавить класс внутри метода, то: VD>1. Это не произойдет до тех пор пока программист не откроет файл содержащий этот метод.
Это плохо. А если я хочу как-то поработать с классом из другого места?
K>>Кстати, сейчас пытаюсь сделать так. Делаю два макроса: макро-атрибут BNF, и макрос lexer. Во-первых, будет ли макрос lexer разворачиваться внутри атрибута?
VD>Э... не совсем понял вопрос.
Как мы уже выяснили, макрос имеет синтаксис, несколько отличный от Nemerle, так что без лексемных макросов не обойтись. Но лексемные макросы предусмотрены только на уровне выражений. А я хочу юзать их на уровне макроатрибута, используя обходной путь, описанный ниже. Вот и спрашивается, прежде чем передать PExpr макроатрибуту, будет ли компилятор обрабатывать макросы внутри него?
K>> Во-вторых, сейчас lexer просто разворачивается в списко кортежей, каждый из которых просто описывает лексему bnf. Планируется, что макрос BNF будет разбирать этот список, преобразовывать в поток лексем и т.д. Но, ИМХО, это криво. Неужели нет возможности всё это проделать непосредственно?
VD>Тоже не понял.
VD>Попытайся описать то что ты хочешь более детально.
OK. Пусть имеется что-то вроде:
[assembly:BNF(
lexer
{
Identifier ::= Letter (Letter | Digit)*;
subst Letter ::= "a".."z" | "A".."Z";
...
}
)]
Макрос lexer развернётся (я этого ожидаю) во что-то вроде:
А потом макрос BNF возьмёт это выражение, проанализирует его и сформирует поток лексем. Далее поток лексем направляется парсеру, формируется AST, по AST генерится спосок регэкспов и т.д.
Здравствуйте, konsoletyper, Вы писали:
K>Перечитал ещё раз документацию по Nemerle и нашёл там один "документированный" способ создать класс: вызвать у GlobalEnv методы Define и Compile. Сам GlobalEnv берётся вот где: ImplicitCTX().Env.
K>Возникает несколько вопросов. Во-первых, почему Oytser делал по-другому? Во-вторых, меня смутило определение макроса ImplicitCTX в исходниках — совершенно непонятно, что на его место подставляется. В третьих, смущает название метода Compile. В документации про него сказано только:
K>
K>Another gotcha is the builder.Compile() call. If you forget it, then the compiler will throw ICE when the macro is used.
K>Ну и как это понимать?
Что понимать то?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Слушай, я вот смотрю на то как ты теоретизируешь и не пойму, а почему бы просто не попробовать? Темболее, что сейчас с интеграцией это стало так просто!
А я не совсем теоретизирую. Я уже сделал макрос lexer, который выполняет указанное в прошлом посте преобразование. И вроде студия показывает, во что разворачивается макрос — именно в то, что я изначально запланировал. Только пока не разборался с макросами уровня сборки, потому протестировать не было возможности.
VD>В общем, вместо того чтобы рассуждать вместе с тобой, я просто попробовал сам... VD>И вот что у меня получилось. VD>
[skip] VD>Далее оставалось поглядеть в окне Locals отладчике чему равно значение параметра expr. VD>В нем находился вызов PExpr.MacroCall в последнем параметре которого было значение SyntaxElement.RawToken, которое в свою очередь, содержало список токенов конструкции "lexer". Вот что я увидел в отладчике: VD>
VD>Так что все что тебе нужно легко достижимо.
VD>Тебе только остается разобрать содержимое переданных в макрос Dnf выражений, распарсить грамматику и создать нуный код.
Вот только как такое разобрать? Я вот вместо такого ужаса генерю другой ужас — нагромождение кортежей. ИМХО, это всё кривовато получается. Во-первых, мы добавляем лишней работы парсеру (что в случае с Интеграцией м.б. критично). Во-вторых, никто не запрещает поместить lexer вне атрибута BNF, что не есть хорошо.
Здравствуйте, konsoletyper, Вы писали:
K>Вот только как такое разобрать?
Непонимаю, что за проблема разобрать список токенов?
K> Я вот вместо такого ужаса генерю другой ужас — нагромождение кортежей.
Какой ужас? Какое награмождение? Ты вообще о чем? Может просто попробуешь? Там делать не фига. Ровно 5 минут нужно чтобы со всем разобраться.
K> ИМХО, это всё кривовато получается.
А по-моему, это необоснованное суждение. Ты вмето того чтобы разобраться и что-то сделать просто ищещь проблемы там где их нет.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
K>> Я вот вместо такого ужаса генерю другой ужас — нагромождение кортежей.
VD>Какой ужас? Какое награмождение? Ты вообще о чем? Может просто попробуешь? Там делать не фига. Ровно 5 минут нужно чтобы со всем разобраться.
Посыпаю голову пеплом Невнимательно посмотрел предыдущее сообщение, и не заметил одной вещи: макрос lexer не разворачивается. Следовательно, и впрямь можно непосредственно плоучить список лексем...
Здравствуйте, konsoletyper, Вы писали:
K>Посыпаю голову пеплом Невнимательно посмотрел предыдущее сообщение, и не заметил одной вещи: макрос lexer не разворачивается. Следовательно, и впрямь можно непосредственно плоучить список лексем...
О том и речь. Макрос lexer получается просто способом сказать компилятору "не порси, плиз, вот этот код, а заверни мне его в SyntaxElement.RawToken".
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>О том и речь. Макрос lexer получается просто способом сказать компилятору "не порси, плиз, вот этот код, а заверни мне его в SyntaxElement.RawToken".