В текущей версии макросов при описании синтаксиса можно использовать только строковые литералы (которые превращаются в ключевые слова или токены скобок), и имена параметров макроса (идентификаторы) которые всегда имеют тип PExpr или arrya[PExpr].
Отличительной особенностью новых макросов (которые должны появиться в Nemerle 2.0) будет то, что при описании синтаксиса можно будет использовать правила грамматики объявляемые другими макросами.
Таким образом при описании синтаксиса будет необходимо указывать имена других макросов. Например, в следующем фрагменте:
macro class Xml
{
macro tagOpen(lt, _, id, attrs, gt, _) : XmlAst.TagOpen
syntax: '<' s identifier attr* '>' s
{
TagOpen(lt.StartPos, gt.EndPos, id.Value, attrs.Map(x => x.Value :> Attr))
}
macro attr(id, _, _, value) : XmlAst
syntax: identifier '=' s attrValue;
{
Attr(id.StartPos, value.EndPos, id, value)
}
}
identifier, attr, attrValue и s — это имена других синтаксических макросов (правил).
Для того чтобы к результатам работы этих макросов можно было обратиться в коде конкретного макроса им нужно дать имена. При этом не всегда возможно использовать в качестве имен имена правил, так как:
1. Правила могу встречаться в описании синтаксиса более одно раза.
2. Правила могут использоваться в подправилах (например, "attr*" в примере выше).
3. Иногда необходимо обращаться к литералам (например, чтобы считать их местоположение). А у них имен нет в принципе.
По сему есть две схемы задания имен конкретным подправилам. Первая схема приводилась мной ранее — это схема в которой имена задаются позиционным способом:
macro tagOpen(lt, _, id, attrs, gt, _) : XmlAst.TagOpen
syntax: '<' s identifier attr* '>' s
Для тех кто не понял я привожу имя и чему оно соответствует:
Имя Подправило
lt '<'
_ s
id identifier
attrs attr*
gt '>'
_ s
Вторым вариантом задания имени является задание его прямо внутри грамматики:
macro tagOpen : XmlAst.TagOpen
syntax: '<' as lt s identifier as id attr* as attrs '>' as gt s
Плюс этого подхода заключается в том, что нет нужны задавать имена для не нужных элементов грамматики и в том, что имена идут сразу за подправилами, так что их проще идентифицировать.
Минус состоит в том, что в этом варианте грамматика начинает содержать не относящиеся к делу вещи (имена), что делает ее хуже читаемой.
Собственно вопрос заключается в том какой вариант выбрать?
Если есть свои варианты, то их тоже можно озвучивать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD> macro tagOpen : XmlAst.TagOpen
VD> syntax: '<' as lt s identifier as id attr* as attrs '>' as gt s
Но с поправками varname должно быть опциональным, по умолчанию в макре доступна переменная с соответствующим именем, если имя допустимо спецификацией.
синтаксис задания переменной через as слабочитабелен (непонятно кстати как задать грамматику с as)
Например так:
Выражение '<':lt и последующее использование переменной lt с т.з. существующего синтаксиса языка нелогично.
Ожидается, что после двоеточия идет уточнение типа, а здесь получется наоборот.
S>Выражение '<':lt и последующее использование переменной lt с т.з. существующего синтаксиса языка нелогично. S>Ожидается, что после двоеточия идет уточнение типа, а здесь получется наоборот.
Да, это я ступил. Конечно логичнее наоборот. Понял пока ходил курить.
Здравствуйте, Ziaw, Вы писали:
S>>Выражение '<':lt и последующее использование переменной lt с т.з. существующего синтаксиса языка нелогично. S>>Ожидается, что после двоеточия идет уточнение типа, а здесь получется наоборот.
Z>Да, это я ступил. Конечно логичнее наоборот. Понял пока ходил курить.
Наоборот тоже не очень хорошо, получатся выражение вида: lt:< или gt:< или даже bkt:) Смайлы одним словом ))
Хотя вот если взять строковые литералы в кавычки, то выходит поприличнее:
Здравствуйте, VladD2, Вы писали:
VD>Минус состоит в том, что в этом варианте грамматика начинает содержать не относящиеся к делу вещи (имена), что делает ее хуже читаемой.
Лучше так:
macro tagOpen : XmlAst.TagOpen
syntax: lt : '<' s id : identifier attrs : attr* gt : '>' s
Это более в стиле языка.
Плюс в ИДЕ можно делать подсветку и тогда проблем вообще не будет.
Еще можно сделать два вида синтаксиса:
Упрощенный. В данном случае после всех литералов вставляется правило выжерающее проблелы
Здравствуйте, Ziaw, Вы писали:
S>>Хотя вот если взять строковые литералы в кавычки, то выходит поприличнее: Z>Конечно строковые литералы должны быть в кавычках. Я не представляю как описывать грамматики без этого.
упс, теперь я ступил — показалось, что в твоем варианте строки были без кавычек. сорри )
В таком синтаксисе не нужно обязывать указание имен переменных. Если имя лексемы может являться именем переменной это и будет имя переменной. Если их несколько — это будет список лексем.
Здравствуйте, WolfHound, Вы писали:
WH>Лучше так: WH>
WH> macro tagOpen : XmlAst.TagOpen
WH> syntax: lt : '<' s id : identifier attrs : attr* gt : '>' s
WH>
WH>Это более в стиле языка.
С чего бы это? Это не тип все же. Это именно имя. В паттернах оно через as задается.
В прочем по существу это те же яйца вид в профиль.
WH>Плюс в ИДЕ можно делать подсветку и тогда проблем вообще не будет.
Код переодически приходится читать и там где подсветки нет. Например, в том же гуглькоде.
К тому же остается проблема распухания грамматики. Чем больше букв тем сложнее понять смысл.
Потом с подсветкой проблем не будет и в первом варианте. Можно же при наведении на параметр подсвечивать соответствующее ему подправило.
WH>Еще можно сделать два вида синтаксиса:...
Об этом я уже говорил. Похоже что нечто подобное точно нужно сделать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VD>> macro tagOpen : XmlAst.TagOpen
VD>> syntax: '<' as lt s identifier as id attr* as attrs '>' as gt s
Z>
Z>Но с поправками Z>* varname должно быть опциональным, по умолчанию в макре доступна переменная с соответствующим именем, если имя допустимо спецификацией.
Слишком много "если". Как я уже говорил правило может встретиться боле одного раза. Плюс имя может быть уже занято чем-то (тем же макросом, например).
Z>* синтаксис задания переменной через as слабочитабелен
Вот именно. Плюс сама грамматика при этом становится слабо читаемой.
Z>(непонятно кстати как задать грамматику с as)
Это то как раз понятно. Правило конечно назвать "as" не удастся, но как литерал можно будет задать:
Это был первый вариант который мне пришел в голову, но потом я подумал, что ":" используется для уточнения типов, а "as" как раз для задания имен частям паттерна. Так что как раз в жилу. К тому же будет в синтаксисе синим выделяться.
Синтаксис предложенный Вольфхаундом и то лечше, так как хоть немного ближе к употреблению ":" в Nemerle. Но as мне кажется более подходящим использованием.
В прочем, как я уже ответил Вольфхаунду — это все те же яйца, вид в профиль.
Для начала нужно определиться допустимо ли указывать имена параметров прямо внутри грамматики?
На мой взгляд это не очень хорошо, так как мешает изучению самой грамматики. Полезно это только для того кто пишет макрос, так как ему будет проще "попасть" с именами параметров.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Ziaw, Вы писали:
Z>В таком синтаксисе не нужно обязывать указание имен переменных. Если имя лексемы может являться именем переменной это и будет имя переменной. Если их несколько — это будет список лексем.
Z>Т.е. валиден будет такой синтаксис:
Z>
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Ziaw, Вы писали:
Z>>В таком синтаксисе не нужно обязывать указание имен переменных. Если имя лексемы может являться именем переменной это и будет имя переменной. Если их несколько — это будет список лексем.
Z>>Т.е. валиден будет такой синтаксис:
Z>>
Здравствуйте, hardcase, Вы писали:
H>Первое впечатление (я не вникал в стартовый топик): жутко. Со скобочками и запятыми как-то понятнее и привчнее, чтоли.
С какими запятыми?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, hardcase, Вы писали:
H>>Первое впечатление (я не вникал в стартовый топик): жутко. Со скобочками и запятыми как-то понятнее и привчнее, чтоли.
VD>С какими запятыми?
Который сейчас:
syntax("x", "y", Optional("z"))
Такая нотация для меня читается легче, чем непрерывный набор идентификаторов предлагаемый сейчас.
Здравствуйте, hardcase, Вы писали:
VD>>С какими запятыми?
H>Который сейчас: H>
H>syntax("x", "y", Optional("z"))
H>
H>Такая нотация для меня читается легче, чем непрерывный набор идентификаторов предлагаемый сейчас.
Ясно. Запятые и лишние скобки ровным счетом ничего не дают (разве что шум).
Но основная разница не в них. Основная разница в том, что идентификаторы которые сейчас можно вставлять в макросы обязаны быть одного типа PRxpr. Таким образом задать что-то выходящее за рамки "when(expr) body" физически невозможно. В новой же версии макросы будут позволять полность расширять синтаксис, т.е. вводить новые типы выражений. Кроме того они будут поддерживать рекурсию, циклы и т.п.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Ziaw, Вы писали:
Z>>В таком синтаксисе не нужно обязывать указание имен переменных. Если имя лексемы может являться именем переменной это и будет имя переменной. Если их несколько — это будет список лексем.
Z>>Т.е. валиден будет такой синтаксис:
Z>>
Я не спец в макрах, но встеаки:
0)Задавать игнорируемые парсером паттерны, так будут и коменты работать, и можно будет whitespace не указывать
1)Строки (лиералы) обязательно в двойных кавычках все, даже односимвольные.
2)Мозолят глаза ключевые слова macro ... syntax, выглядят как два разных объявления, хотя это две части одного.
3)Я бы предпочел писать имя для токена после указания его паттерна, так однородность записи не нарушается
4)API для работы внутри макроса
5)Coding style для определений синтаксиса, вплоть до варнингов при компиляции. Например все паттерны — с большой буквы, все алиасы — маленькими.
Что-то вроде
6)вместо macro class написать language, и чтобы в исходниках было using language <Name>, а внутри другого language — import language <Name>
7)Писать макры без вывода + атрибуты на макрах = красивая подсветка.
Надо что-то думать насчет независимости правил от порядка либо явного задания и просмотра порядка. Чем дольше я об этом думаю, тем меньше мне нравится идея при которой отлаженная грамматика может взбеситься от невинного выражения. Вспомнилась диагностика проблемной ситуации, когда в куче ошибок я просто не заметил макровскую NRE.
Re[4]: Вопрос по синтаксису макросов для v2
От:
Аноним
Дата:
22.06.10 17:08
Оценка:
Здравствуйте, Ziaw, Вы писали:
Z>Надо что-то думать насчет независимости правил от порядка либо явного задания и просмотра порядка. Чем дольше я об этом думаю, тем меньше мне нравится идея при которой отлаженная грамматика может взбеситься от невинного выражения. Вспомнилась диагностика проблемной ситуации, когда в куче ошибок я просто не заметил макровскую NRE.
Это скорее решается продуманной обработкой ошибок в макросе.
Здравствуйте, Аноним, Вы писали:
Z>>Надо что-то думать насчет независимости правил от порядка либо явного задания и просмотра порядка. Чем дольше я об этом думаю, тем меньше мне нравится идея при которой отлаженная грамматика может взбеситься от невинного выражения. Вспомнилась диагностика проблемной ситуации, когда в куче ошибок я просто не заметил макровскую NRE.
А>Это скорее решается продуманной обработкой ошибок в макросе.
Надо просто писать программы без багов и все будет хорошо? нюню
Re[6]: Вопрос по синтаксису макросов для v2
От:
Аноним
Дата:
22.06.10 18:04
Оценка:
Здравствуйте, Ziaw, Вы писали:
А>>Это скорее решается продуманной обработкой ошибок в макросе.
Z>Надо просто писать программы без багов и все будет хорошо? нюню
Я имел в виду обработку ошибок макроса компилятором.
Здравствуйте, Аноним, Вы писали:
Z>>Надо просто писать программы без багов и все будет хорошо? нюню
А>Я имел в виду обработку ошибок макроса компилятором.
На сегодня компилятор перехватывает исключения возникшие в макросах и сообщает о них как об ошибках. Так что не заметить их очень не просто.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Ziaw, Вы писали:
Z>В паттернах есть еще разделители кроме пробела, там есть за что зацепиться глазу. В данном случае все сливается в один поток идентификаторов.
В принципе вот такой вариант:
macro tagOpen : XmlAst.TagOpen
syntax: lt:'<' s id:identifier attrs:attr* gt:'>' s
читается относительно не плохо. Только вот несколько противоречит концепциям языка. Хотя меньше чем твой вариант, в котором имя переменной находилось в месте где обычно находится тип.
Z>Кстати насчет типов, хотелось бы без явного объявления синтаксиса делать макры вида: Z>
Не вполне уверен, что понял вопрос верно. Но если речь идет о макросах без синтаксиса (с синтаксисом аналогичным вызову функций), то для них конечно же никаких расширений не требуется. Речь идет только о макрах расширяющих синтаксис.
Z>Надо что-то думать насчет независимости правил от порядка либо явного задания и просмотра порядка.
Опять же не уверен на счет правильного понимания вопроса. Единственная проблема которая мне известна на сегодня — это последовательность выполнения макро-атрибутов. Из-за этого, например, макро-атрибут фабрики классов может не видеть конструкторов сгенерированных макро-атрибутом Record. Тут — да, надо вводить зависимости. Например, дать возможность указывать создателям макросов, что их макрос должен выполняться до или после некоторого другого известного на момент компиляции макро-атрибута.
Z>Чем дольше я об этом думаю, тем меньше мне нравится идея при которой отлаженная грамматика может взбеситься от невинного выражения.
Можно пример?
Z> Вспомнилась диагностика проблемной ситуации, когда в куче ошибок я просто не заметил макровскую NRE.
Дык кучу ошибок просто нельзя допускать. От исключений никто не застрахован. И компилятор честно о них сообщает.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Ну первый вариант однозначно плох: сложное выражение редактировать и воспринимать будет сверхнеудобно, и будет крайне легко допустить ошибку. Плюс нехороший мусор для непоименованных элементов (подчеркивания). Второй — тоже не фонтан, так как ухудшается читаемость собственно грамматики, легко запутаться в этом чередовании 'as', все сливается.
Указание имен в каком-то смысле эквивалентно введению подправил грамматики. Названия правил в описании грамматики могут говорить сами за себя (например, attrs), так что и без расшифровки понятно, что это. Если какой-то макрос упоминается только один раз (а таких случаев будет 99%), его можно явно не именовать.
По поводу пробелов (а точнее, разделителей, в которые входят также комментарии). Выписывать явно разделители, тем более с таким именем как "s" — явно некрасиво. Потому что в 99% случаев он будет нужен и только в 1% — не нужен. Скорее, было бы лучше, если бы макрос s вставлялся по умолчанию, если не вставлен кейворд nospace, например:
'<' nospace identifier nospace '>'.
Еще с разделителями интересная вещь. Если я захочу добавить новый вид комментариев, значит мне как-то надо расширить макрос s? Значит, макросы можно будет определять более одного раза, расширяя их, так что ли?
Здравствуйте, VladD2, Вы писали:
VD>На сегодня компилятор перехватывает исключения возникшие в макросах и сообщает о них как об ошибках. Так что не заметить их очень не просто.
Не заметить их очень просто, если данная ошибка влечет за собой десятки ошибок компиляции.
Дело в том, что интеграция как-то хитро сортирует ошибки. NRE просто потонула в десятках ошибок overload resolution. Почему она оказалась у них в середине — непонятно.
Здравствуйте, VladD2, Вы писали:
VD>читается относительно не плохо. Только вот несколько противоречит концепциям языка. Хотя меньше чем твой вариант, в котором имя переменной находилось в месте где обычно находится тип.
, еще до появления аналогичного варианта от WolfHound. Первый вариант родился из опыта других описаний, ganjustas вон тоже его предложил.
Z>>Кстати насчет типов, хотелось бы без явного объявления синтаксиса делать макры вида: Z>>
Z>>Это реально?
VD>Не вполне уверен, что понял вопрос верно. Но если речь идет о макросах без синтаксиса (с синтаксисом аналогичным вызову функций), то для них конечно же никаких расширений не требуется. Речь идет только о макрах расширяющих синтаксис.
Да, речь идет о макрах без синтаксиса принимающих Аst отличный от PExpr.
Z>>Надо что-то думать насчет независимости правил от порядка либо явного задания и просмотра порядка.
VD>Опять же не уверен на счет правильного понимания вопроса. Единственная проблема которая мне известна на сегодня — это последовательность выполнения макро-атрибутов. Из-за этого, например, макро-атрибут фабрики классов может не видеть конструкторов сгенерированных макро-атрибутом Record. Тут — да, надо вводить зависимости. Например, дать возможность указывать создателям макросов, что их макрос должен выполняться до или после некоторого другого известного на момент компиляции макро-атрибута.
Это да, будет очень полезно. Что нибудь типа depends on SomeMacro.
Z>>Чем дольше я об этом думаю, тем меньше мне нравится идея при которой отлаженная грамматика может взбеситься от невинного выражения.
VD>Можно пример?
.
VD>Дык кучу ошибок просто нельзя допускать. От исключений никто не застрахован. И компилятор честно о них сообщает.
Проблема в том, что неверно работающий макрос может породить сотни, а то и тысячи ошибок. У меня было 50+ на трех простых методах, ломался overload resolution на цепочках вызовов linq.
Здравствуйте, gandjustas, Вы писали:
G>Я не спец в макрах, но встеаки: G>0)Задавать игнорируемые парсером паттерны, так будут и коменты работать, и можно будет whitespace не указывать
+1, а для отключения этого режима можно использовать предложенный rawsyntax
G>1)Строки (лиералы) обязательно в двойных кавычках все, даже односимвольные.
Имхо, это просто опечатка, я не обращаю внимания.
G>2)Мозолят глаза ключевые слова macro ... syntax, выглядят как два разных объявления, хотя это две части одного.
Тут все нормально, это ты с непривычки, тебе же не кажутся class и when двумя разными объявлениями.
G>3)Я бы предпочел писать имя для токена после указания его паттерна, так однородность записи не нарушается
Это только на первый взгляд, в немерле интуитивнее будет указание биндящейся переменной перед двоеточием.
G>4)API для работы внутри макроса
?
G>5)Coding style для определений синтаксиса, вплоть до варнингов при компиляции. Например все паттерны — с большой буквы, все алиасы — маленькими. G>Что-то вроде G>
syntax лучше
G>6)вместо macro class написать language, и чтобы в исходниках было using language <Name>, а внутри другого language — import language <Name>
далеко не все макры определяют свой синтаксис. но идея using language мне нравится.
G>7)Писать макры без вывода + атрибуты на макрах = красивая подсветка.
G>
Здравствуйте, Ziaw, Вы писали:
G>>2)Мозолят глаза ключевые слова macro ... syntax, выглядят как два разных объявления, хотя это две части одного. Z>Тут все нормально, это ты с непривычки, тебе же не кажутся class и when двумя разными объявлениями.
Кажутся. Форматтер студии отбивает на один таб when по-умолчанию, хотя по возможности я в одну строчку пишу.
G>>3)Я бы предпочел писать имя для токена после указания его паттерна, так однородность записи не нарушается Z>Это только на первый взгляд, в немерле интуитивнее будет указание биндящейся переменной перед двоеточием.
Возможно, при наличии строгих coding style для макросов не так заметно отличие будет.
G>>4)API для работы внутри макроса Z>?
Чтобы можно было работать с частями макроса без присвоения им имен.
Здравствуйте, Тролль зеленый и толстый, Вы писали:
ТЗИ>Указание имен в каком-то смысле эквивалентно введению подправил грамматики. Названия правил в описании грамматики могут говорить сами за себя (например, attrs), так что и без расшифровки понятно, что это. Если какой-то макрос упоминается только один раз (а таких случаев будет 99%), его можно явно не именовать.
Более того, можно не именовать явно даже макросы упоминающиеся несколько раз, они просто превратятся в одноименный массив. attr attr* тоже.
ТЗИ>По поводу пробелов (а точнее, разделителей, в которые входят также комментарии). Выписывать явно разделители, тем более с таким именем как "s" — явно некрасиво. Потому что в 99% случаев он будет нужен и только в 1% — не нужен. Скорее, было бы лучше, если бы макрос s вставлялся по умолчанию, если не вставлен кейворд nospace, например:
ТЗИ>
ТЗИ> '<' nospace identifier nospace '>'.
ТЗИ>
Отличная мысль, даже лучше чем rawsyntax.
ТЗИ>Еще с разделителями интересная вещь. Если я захочу добавить новый вид комментариев, значит мне как-то надо расширить макрос s? Значит, макросы можно будет определять более одного раза, расширяя их, так что ли?
Имхо, определения разделителей просто должны помечаться, например атрибутом. Расширение макросов как то уж слишком сложно выглядит.
Здравствуйте, Тролль зеленый и толстый, Вы писали:
ТЗИ>...
Интересные предложения. Беру небольшой таймаут на их осмысление.
ТЗИ>Еще с разделителями интересная вещь. Если я захочу добавить новый вид комментариев, значит мне как-то надо расширить макрос s? Значит, макросы можно будет определять более одного раза, расширяя их, так что ли?
Откровенно говоря расширение списка пробельных символов не стоит на повестке дня. Получится сделать это естественным образом и без потери производительности — хорошо. Не получится, ну и ладно.
Важна другая возможность — возможность учета тех самых пробельных символов (и вообще чего угодно) внутр некоторых макросов. Например, при парсинге ХМЛ-я очень важно учитывать все детали. Ведь даже какзлось бы не значащие пробелы могут быть очень важны.
Кроме того те же комментарии являются весьма важной вещью для компилятора и IDE. В них может содержаться метаинформация и (возможно) управляющие конструкции. Посему полезно иметь возможность разбора их макросом.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Ziaw, Вы писали:
Z>Не заметить их очень просто, если данная ошибка влечет за собой десятки ошибок компиляции.
Ну, дык разбираться нужно в причине ошибки. Если ошибок много — это уже само по себе основание чтобы насторожиться.
Z>Дело в том, что интеграция как-то хитро сортирует ошибки. NRE просто потонула в десятках ошибок overload resolution. Почему она оказалась у них в середине — непонятно.
Она их просто не сортирует. Можно подумать над расширенной поддержкой для исключений. Скажем если при обработке метода есть исключения в макросах, то все остальные ошибки выбрасывать и оставлять только информацию об исключениях.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
, еще до появления аналогичного варианта от WolfHound. Первый вариант родился из опыта других описаний, ganjustas вон тоже его предложил.
Пока что из всего предложенного я склоняюсь к смеси вариантов Троля и Вольфхаунда, т.е. вести некий раздел where для именования подправил и кроме того позволить именовать через двоеточние, но принудительно требовать чтобы двоеточие шло без пробелов, т.е.:
lt:'<'
Z>Да, речь идет о макрах без синтаксиса принимающих Аst отличный от PExpr.
Хм. А нужно ли это? На сегодня макросы без синтаксиса могут принимать только PExpr (т.е. выражения) и это не приводит к каким-то трудностям.
В прочем, думаю что это не будет проблемой.
Z>Это да, будет очень полезно. Что нибудь типа depends on SomeMacro.
Я думал о возвожности задать приоритет таким образом:
macro MyMacro...
priority: SomeMacro > this > OtherMacro // т.е. текущий макрос мене приоритетный нежели SomeMacro но более приоритетный нежели OtherMacro
Не понял связи обсуждения с этим примером.
Z>Проблема в том, что неверно работающий макрос может породить сотни, а то и тысячи ошибок. У меня было 50+ на трех простых методах, ломался overload resolution на цепочках вызовов linq.
Ясно. Тут пока ничего не придумано. Отладка макросов дело по прежнему не простое. Но с опытом проблемы исчезают.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Тролль зеленый и толстый, Вы писали:
ТЗИ>Ну первый вариант однозначно плох: сложное выражение редактировать и воспринимать будет сверхнеудобно, и будет крайне легко допустить ошибку.
На мой взгляд — нормально. Но если будет найден более удобный вариант, я буду не против.
ТЗИ>Плюс нехороший мусор для непоименованных элементов (подчеркивания).
Это стандартное соглашение в языке означающие игнорирование имени.
ТЗИ>Второй — тоже не фонтан, так как ухудшается читаемость собственно грамматики, легко запутаться в этом чередовании 'as', все сливается.
+1
ТЗИ>Указание имен в каком-то смысле эквивалентно введению подправил грамматики.
Если только вырожденных. В грамматиках частенько правила используются по много раз в рамках тела другого правила. Собственно задание имени и нужно чтобы отличить разные вхождения.
Однако если это позволяет ввести имена для правил которые можно будет использовать в теле макросов, то в этом появляется смысл. Идея ввести "where" — мне нравится. В принципе с ее помощью можно решить проблемы именования полностью, но при этом грамматике не допустимо будет использовать литералы. Наше пример при этом выродится в:
Что мне не нравится. Так что все же именование по месту все равно нужно.
ТЗИ> Названия правил в описании грамматики могут говорить сами за себя (например, attrs), так что и без расшифровки понятно, что это. Если какой-то макрос упоминается только один раз (а таких случаев будет 99%), его можно явно не именовать.
Есть еще одна проблема. Это имена макросов! А они сами могут потребоваться (в коде макроса). Ведь макрос сам может использовать макросы. Так в стандартной библиотеке макросов немерла сейчас во всю используются эти же макросы (из прошлой версии библиотеки). Может возникнуть конфликт имен. В прочем его можно решать или в том же "where" или путем добавления префикса '@'.
ТЗИ>Я убрал из примера сохранение позиции в тексте, так как мне кажется, что этот нудный процесс обязательно должен быть как-то автоматизирован.
"Как-то" это несерьезно! Лично я не вижу как это сделать, по сему пока не появится внятного решения придется делать это вручную.
Тут нужно понимать, что копировать местположение руками нужно довольно редко. Все стандартные правила уже будут содержать корректное местоположение.
ТЗИ>Здесь, кстати, есть какая-то аналогия с регэксповыми "матчами". То есть мы как бы создаем поименованное подвыражение.
Скорее с грамматиками. Твое where — это ни что иное как явное задание имен подправилам которые раньше просто их не имели. В принципе можно на каждый чих создавать мелкий макрос. Но это очень неудобно!
ТЗИ>Вариант синтаксиса со скобками, скобки вроде как визуально выделяют поименованный фрагмент:
ТЗИ>
Это мне совсем не нравится. Уж лучше двоеточния, но в синтаксисе жетско оговорить, что между двоеточием и подправилом которое оно именует не должно быть пробелов. Тогда можно будет легко отличать то подправило для которого задается имя.
ТЗИ>Можно смешать варианты:
ТЗИ>
Пожалуй смешанный вариант твоего where и вольфхаудовского именования через двоеточие будет выглядеть лучше:
macro tagOpen : XmlAst.TagOpen
syntax: lt:'<' identifier attrs gt:'>';
where
attrs = attr* // "Равно" здесь более уместно. Как вариант, можно использовать "<-" раз уж мы используем PEG
{
...
}
ТЗИ>По поводу пробелов (а точнее, разделителей, в которые входят также комментарии). Выписывать явно разделители, тем более с таким именем как "s" — явно некрасиво. Потому что в 99% случаев он будет нужен и только в 1% — не нужен. Скорее, было бы лучше, если бы макрос s вставлялся по умолчанию, если не вставлен кейворд nospace, например:
ТЗИ>
ТЗИ> '<' nospace identifier nospace '>'.
ТЗИ>
Хорошая идея! Еще как вариант, можно ввести соглашение по которому "nospace" подразумевается если несколько литералов и правил записаны без отступов. Таким образом приведенный ниже пример будет эквивалентным твоему:
'<'identifier'>'.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Хорошая идея! Еще как вариант, можно ввести соглашение по которому "nospace" подразумевается если несколько литералов и правил записаны без отступов. Таким образом приведенный ниже пример будет эквивалентным твоему: VD>
VD> '<'identifier'>'.
VD>
Согласен со всем написаным кроме этого. Не так уж часто будет использоваться nospace, чтобы вводить такие правила. Тем более учитывая то, что документация не в лучшем состоянии.
Кстати, ты не ничего не сказал про идею помечать макросы атрибутом который добавляет его грамматику к списку пробельных. Комментарии действительно должны считаться пробельными символами по умолчанию.
Здравствуйте, Ziaw, Вы писали:
Z>Кстати, ты не ничего не сказал про идею помечать макросы атрибутом который добавляет его грамматику к списку пробельных. Комментарии действительно должны считаться пробельными символами по умолчанию.
А что тут скжешь? Пока что детали не ясны.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Собственно вопрос заключается в том какой вариант выбрать? VD>Если есть свои варианты, то их тоже можно озвучивать.
Можно на макросах же реализовать полноценный DSL для описания всех свойств макроса. Вроде как:
macro FooMacro
{
dependencies // отдельно описать зависимости
{
BarMacro,
BazMacro
}
parameters // отдельно — параметры и их типы
{
First : BarMacro
Second : BazMacro
Third : PExpr
Fourth : int
}
syntax// отдельно — синтаксис (почему бы его не описывать PEG-грамматикой, если он все равно к ней сводится)
{
FooMacro = ShortMode | LongMode
ShortMode = First *
LongMode = Third *
}
beforeTypedMembers // отдельно — реализацию для каждой фазы компиляции
{
// blah blah
}
}
Упрощенные формы синтаксиса можно так же реализовать другими макросами.
у нас есть грамматика, есть некий ast который она создает. не зря ли мы смешиваем грамматики и ast? может быть грамматика отдельно, AST отдельно?
language Xml
{
macro XmlAttr: Xml.Attr
syntax name '='value
where name : PExpr.Ref, value : PExpr.Expr
{
Xml.Attr(name.Name, value);
}
macro XmlNodeStart: Xml.NodeStart
syntax '<' name attrs:attr* '>'
where name : PExpr.Ref, attr : Xml.Attr
{
Xml.NodeStart(name.Name, attrs)
}
macro XmlNodeEnd: Xml.NodeEnd
syntax '</' name '>'
where name : PExpr.Ref
{
Xml.NodeEnd(name)
}
macro XmlNode: Xml.Node
syntax start nodes:node* end
where start : Xml.NodeStart,
node : Xml.Node,
end : Xml.NodeEnd
{
assert(start.Name == end.Name);
Xml.Node(start.Name, start.Attrs, nodes)
}
}
// programusing language Xml
{
def x = <foo x="1">; // def x = Xml.NodeStart("foo", [Xml.Attr("x", "1")])def str = "1";
def x = <foo x="1">; // def x = Xml.NodeStart("foo", [Xml.Attr("x", str)])def attr = Xml.Attr("x", "1");
def x = <foo <( [attr : Xml.Attr] )> ></foo>; // def x = Xml.Node(foo, [attr], [])
}
Где <()> антицитата, уточнение типа потребуется парсеру. Идея в том, чтобы PExpr'ы генерили код, а остальной Ast генерил обычные конструкторы, что то типа expression tree в C#.
Либо вариант, с возможностью выбора подходящего возвращаемого значения:
language Xml
{
macro XmlAttr: (Xml.Attr | PExpr)
syntax name '='value
where name : PExpr.Ref, value : PExpr.Expr
{
| Xml.Attr => Xml.Attr(name.Name, value);
| PExpr => <[ Xml.Attr( $(name.Name : string), $value); ]>
}
}
Получается, что парсер работает не только с текстом, но и с астом. Алгоритм тот же, рекурсивный спуск. Если ожидается аст мы проверяем все синтаксические макры способные вернуть нужный нам тип.
Здравствуйте, Ziaw, Вы писали:
Z>у нас есть грамматика, есть некий ast который она создает. не зря ли мы смешиваем грамматики и ast? может быть грамматика отдельно, AST отдельно?
Задача макроса — вернуть некоторый АСТ. Так что я вообще не понимаю о чем идет речь.
Z>
Z> macro XmlAttr: Xml.Attr
Z> syntax name '='value
Z> where name : PExpr.Ref, value : PExpr.Expr
Z> {
Z> Xml.Attr(name.Name, value);
Z> }
Z>
Я вижу тут банальное задание типов для элементов грамматики. В этом нет смысла, так как типы и так известны. Ведь каждое имя — это имя макроса у которого и так известен возвращаемый им тип.
Z>Где <()> антицитата, уточнение типа потребуется парсеру. Идея в том, чтобы PExpr'ы генерили код, а остальной Ast генерил обычные конструкторы, что то типа expression tree в C#.
PExpr — это тоже "обычные конструкторы".
Z>Либо вариант, с возможностью выбора подходящего возвращаемого значения:
Зачем это все?
Z>Получается, что парсер работает не только с текстом, но и с астом. Алгоритм тот же, рекурсивный спуск. Если ожидается аст мы проверяем все синтаксические макры способные вернуть нужный нам тип.
Парсер на то парсер, чтобы разбирать текст. В общем ничего не понимаю. Похоже у тебя образовалась каша в понимании того о чем идет речь. Или ты не можешь сформулировать свои идеи так чтобы я смог их понять.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
Z>>у нас есть грамматика, есть некий ast который она создает. не зря ли мы смешиваем грамматики и ast? может быть грамматика отдельно, AST отдельно?
VD>Задача макроса — вернуть некоторый АСТ. Так что я вообще не понимаю о чем идет речь.
Речь идет о том, что вместо конкретного макроса в синтаксисе можно указывать их подмножество, которое можно потом расширять макросами, которые неизвестны на этапе разработки.
Z>>
Z>> macro XmlAttr: Xml.Attr
Z>> syntax name '='value
Z>> where name : PExpr.Ref, value : PExpr.Expr
Z>> {
Z>> Xml.Attr(name.Name, value);
Z>> }
Z>>
VD>Я вижу тут банальное задание типов для элементов грамматики. В этом нет смысла, так как типы и так известны. Ведь каждое имя — это имя макроса у которого и так известен возвращаемый им тип.
Я предлагаю задавать элементы не только именами макросов, но и типами AST. Впрочем синтаксис задания их именами я толком не продумывал, можно во where двоеточие оставить для имени, а is для типа. Это пока неважно, важна идея указывать открытое подмножество макросов.
Z>>Где <()> антицитата, уточнение типа потребуется парсеру. Идея в том, чтобы PExpr'ы генерили код, а остальной Ast генерил обычные конструкторы, что то типа expression tree в C#.
VD>PExpr — это тоже "обычные конструкторы".
Да, но они не генерят в код свой собственный вызов.
Z>>Либо вариант, с возможностью выбора подходящего возвращаемого значения:
VD>Зачем это все?
Способы протащить AST в код, это уже другая идея, слабо связанная с первой. Может быть и не особо востребованная.
Кстати, а нельзя это АСТ генерировать автоматически, используя информацию из грамматики?
Ведь в этом примере тела макросов это просто boilerplate, который мог бы делаться автоматически:
macro class Xml
{
macro tagOpen(lt, _, id, attrs, gt, _) : XmlAst.TagOpen
syntax: '<' s identifier attr* '>' s
{
TagOpen(lt.StartPos, gt.EndPos, id.Value, attrs.Map(x => x.Value :> Attr))
}
macro attr(id, _, _, value) : XmlAst
syntax: identifier '=' s attrValue;
{
Attr(id.StartPos, value.EndPos, id, value)
}
}
Пример мог бы стать таким:
language Xml
{
TagOpen = '<' id:Identifier attrs:Attr* '>';
Attr = id:Identifier '='value:AttrValue;
}
Из этого описания в пространстве имен Xml.Ast будет создан класс TagOpen с полями id и attrs, и класс Attr с полями id и value.
Конечно, бывают случаи, где нужна какие-то преобразования, но для только них и можно сделать какую-то поддержку, чтобы все остальные тривиальные случаи обрабатывались автоматически.
Здравствуйте, Тролль зеленый и толстый, Вы писали:
ТЗИ>Конечно, бывают случаи, где нужна какие-то преобразования,
Бывают. И чаще чем кажется. Многие макры содержат весьма сложную логику.
ТЗИ>но для только них и можно сделать какую-то поддержку, чтобы все остальные тривиальные случаи обрабатывались автоматически.
Автоматика — это хорошо, но это требует серьезного продумывания идеологии и реализации.
ТЗИ>Пример мог бы стать таким:
ТЗИ>
Тут сразу встает несколько вопросов.
1. Для работы интеграции (да и для компилятора) нужны местоположения всех "токенов". В данном примере нужны местоположения для '<', '>' и '=', не говоря уже о именованных полях (id, attrs, id, value). Не ясно в каком виде все это дело представлять.
2. AST обычно генерируется не из классов, а из вхождений вариантных типов. Не ясно как вводить эти вариантные типы (или иными словами как группировать элементы AST?)?
3. В новой версии Nemerle планируется ввести в AST информацию о типах. Она будет заполняться по мере типизации. Сейчас в процессе типизации создается отдельное типизированное AST. Этот подход создает ряд сложностей. Кроме того такое преобразование так же является ручным процессом, что приводит к массе ошибок.
4. Так как AST должно быть расширяемым, нужно подумать о том как сопрягать разные виды AST и как расширять отдельные его виды.
Но в принципе можно подумать о введении чисто декларативных макросов. Но только на ряду с макросами позволяющими производить произвольные вычисления.
Если подумать, то в одном пакете (назовем это так) можно располагать множество декларативных (назовем это так) макросов и ряд рукописных (которые будут осуществлять какие-то действия).
Но для этого нужно как-то решить описанные выше вопросы.
ТЗИ>Из этого описания в пространстве имен Xml.Ast будет создан класс TagOpen с полями id и attrs, и класс Attr с полями id и value.
Как я уже говорил выше, создавать нужно не классы, а variant-ы.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Ziaw, Вы писали:
Z>Речь идет о том, что вместо конкретного макроса в синтаксисе можно указывать их подмножество, которое можно потом расширять макросами, которые неизвестны на этапе разработки.
Для расширения предусмотрены точки расширения. Это почти то о чем ты говоришь. Они описывают места и правила подстановки расширений.
Z>Я предлагаю задавать элементы не только именами макросов, но и типами AST. Впрочем синтаксис задания их именами я толком не продумывал, можно во where двоеточие оставить для имени, а is для типа. Это пока неважно, важна идея указывать открытое подмножество макросов.
Опять ничего не понял. Приведи примеры.
Z>>>Где <()> антицитата, уточнение типа потребуется парсеру. Идея в том, чтобы PExpr'ы генерили код, а остальной Ast генерил обычные конструкторы, что то типа expression tree в C#.
VD>>PExpr — это тоже "обычные конструкторы".
Z>Да, но они не генерят в код свой собственный вызов.
Бррр. Не странно но что мы не можем с тобой ни о чем договориться. Казалось бы в этой теме я должен понимать все, но я опять не понимаю твоих слов.
Z>>>Либо вариант, с возможностью выбора подходящего возвращаемого значения:
VD>>Зачем это все?
Z>Способы протащить AST в код, это уже другая идея, слабо связанная с первой. Может быть и не особо востребованная.
Я не телепат.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
Z>>Речь идет о том, что вместо конкретного макроса в синтаксисе можно указывать их подмножество, которое можно потом расширять макросами, которые неизвестны на этапе разработки.
VD>Для расширения предусмотрены точки расширения. Это почти то о чем ты говоришь. Они описывают места и правила подстановки расширений.
А можно подробнее про точки расширения?
Z>>Я предлагаю задавать элементы не только именами макросов, но и типами AST. Впрочем синтаксис задания их именами я толком не продумывал, можно во where двоеточие оставить для имени, а is для типа. Это пока неважно, важна идея указывать открытое подмножество макросов.
VD>Опять ничего не понял. Приведи примеры.
Допустим кто-то разработал грамматику для Xml
macro XmlElement : Xml.Element
syntax start children:subNode* end // children это алиас для списка вложенных теговwhere start : XmlStartTag, subNode : XmlElement, end : XmlEndTag // уточняем парсеру типы элементов грамматики, XmlStartTag, XmlElement, XmlEndTag
{
Xml.XmlElement(start.Name, start.Attrs, children)
}
Добавить в такой синтаксис элемент вида <node a="1"/> становится не так просто (это просто пример не заложенной изначально в синтаксис конструкции), но если мы сделаем так:
macro XmlElement : Xml.Element
syntax start children:subNode* end
where start : XmlStartTag, subNode is Xml.Element, end : XmlEndTag // теперь subNode может быть любым макросом возвращающим Xml.Element
{
Xml.XmlElement(start.Name, start.Attrs, children)
}
// то сможем расширить синтаксис примерно такmacro XmlEmptyElement : Xml.Element
syntax '<' name attrs:attr* '/>'
where name : XmlName, attr is Xml.Attr
{
Xml.XmlElement(name.Name, attrs, [])
}
VD>Бррр. Не странно но что мы не можем с тобой ни о чем договориться. Казалось бы в этой теме я должен понимать все, но я опять не понимаю твоих слов. VD>Я не телепат.
В этой части я думал над тем, как передавать бесшовно аст из макросов в выполняемый код и обратно. Но что-то сыро получается На примере того же XML — хотелось бы иметь возможность сгенерить пачку атрибутов в рантайме и бесшовно подсунуть в грамматику.
def attrs = [Xml.Attr("a", "1"), Xml.Attr("b", "2")];
def xml = <foo $(attrs : list[Xml.Attr]) />; // сахар для Xml.XmlElement("foo", attrs, []), уточнение типа понадобится парсеру
Вобщем черт его знает, как-то все сыро. Давай пока эту тему отложим.
Здравствуйте, Ziaw, Вы писали:
Z>А можно подробнее про точки расширения?
В двух словах — мы позволяет пометить некоторое правило как расширяемое, а компилятор для такого макроса сгенерит такую грамматку:
rule = extPoint1 / ruleImpl / extPoint2;
Это в дальнейшем позволит позволит создавать другие макросы указывая что они расширяют точки расширения extPoint1 и/или extPoint2. Далее когда компилятор будет читать список открытых пространств имен, если в них встречаются макросы расширяющие точки расширения считанные к данному моменту, то они встраивают эти макросы вместо этих точек. При этом так же должны учитываться зависимости приоритеты. Это даст возможность сформировать желаемый порядок выполнения расширений (попыток разбора грамматик определяемых макросами вставляемых в точки расширения).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.