ЧВЮ>Авторы: ЧВЮ> Чистяков Владислав Юрьевич
ЧВЮ>Аннотация: ЧВЮ>В данной статье рассказывается о новом проекте языкового фрэймворка – N2
вроде в ней нет ничего нового...
Re[2]: N2 – языковый фрeймворк
От:
Аноним
Дата:
15.12.12 08:13
Оценка:
Здравствуйте, Аноним, Вы писали:
ЧВЮ>>Аннотация: ЧВЮ>>В данной статье рассказывается о новом проекте языкового фрэймворка – N2
А>вроде в ней нет ничего нового...
Вымученая статья, материал подается в том виде, который интерсен и понятен только авторам N2 и сочувствующим. Немерлисты хронически забывают про задачи и цели разработчиков, которые потенциально могут использовать N2.
Здравствуйте, Чистяков Владислав Юрьевич, Вы писали:
1. очепятка:
...недостатков Nemerle. Не будут вдаваться в подробности,...
2.
В грамматике правила можно заметить подправило – Body.
для того, чтобы понять где именно "можно заметить" пришлось воспользоваться поиском
это как то не очень юзабельно. быть может пред этими словами (в начале раздела) стоит повторить это правило
3. В предисловии есть несколько слов о том зачем N2 вообще нужен людям (тем, кто не знает про Nemerle)
но мне кажется этого недостаточно.
было бы интересно прочитать про конкретныт (будущий) пример применения, сравнение с существующими недо-аналогами(по сравнению с N2)
-рукопашный парсинг
-lex+yacc
-ANTLR
-Nemerle+Peg=C#
и т.п.
это то, ради чего непосвящённый в Nemerle люд, будет читать эту статью дальше, мне кажется.
Здравствуйте, Аноним, Вы писали:
А>Вымученая статья, материал подается в том виде, который интерсен и понятен только авторам N2 и сочувствующим. Немерлисты хронически забывают про задачи и цели разработчиков, которые потенциально могут использовать N2.
Приятно получать критику, но только при условии, что из нее можно хоть что-то извлечь. Из такой критики ничего не извлечешь. Так что просьба быть более конкретны.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
А>>Вымученая статья, материал подается в том виде, который интерсен и понятен только авторам N2 и сочувствующим. Немерлисты хронически забывают про задачи и цели разработчиков, которые потенциально могут использовать N2.
VD>Приятно получать критику, но только при условии, что из нее можно хоть что-то извлечь. Из такой критики ничего не извлечешь. Так что просьба быть более конкретны.
Перевожу — материал стоит подавать с учетом целей и задач реальных потребителей фремворка. У тебя ничего этого нет, только перечисление возможностей. Людям, которые не пасутся на этом форуме, перечисление возможностей абсолютно ничего не дает и примеры коды в статье как мертвому припарки.
Цель потенциального потребителя это создание конкретного продукта. Задачи — реализация дсл для использования внутри этого продукта и тд. Соответсвенно, как вариант, нужно показать несколько примеров проектов с примерами внутренних дсл и показать недостатки этого подхода, желательно с выкладками которые понятны потребителю, а не общие фразы "натягивать модель на язык общего назначения". Ну нужны многословные примеры, можно даже тот же linq использовать в качестве одного из примеров.
После того, как читатель поймет саму задачу, нужно показать несколько решений — решения в лоб и решение с учетом фремворка. Не нужны детали реализации if, foreach и тд. Нужно показать сам язык, желательно достаточно актуальный, ну хотя бы автомат какой или "какбэ" linq.
И уже в самом конце на пальцах объяснить, что же нужно сделать потребителю что бы достичь такого же результата, опять же, без деталей реализации.
Если внимательно посмотреть, то можно заметить, что таким подходам пользуется микрософт и по этой причине их решения очень быстро становятся популярными, например linq, rx, wcf, mvc.
Вобщем пока что ты изобретаешь свой собственный подход к популяризации и почему то не хочешь перенимать вещи, которые уже работают.
Здравствуйте, Чистяков Владислав Юрьевич, Вы писали:
N2 не манипулирует объектами, специфичными для конкретных систем типов. Вместо этого он манипулирует абстрактной иерархией метаинформации. Для N2 существует лишь однородная иерархия символов.
Я понимаю, что предметная область сложна и абстрактна, но это слишком уж напоминает какой-то автосгенерированный текст
Здравствуйте, Чистяков Владислав Юрьевич, Вы писали:
На практике парсеры обычно имеют много входов (для expression-ов, типов, statement-ов и т.п.). Это, к примеру, нужно при реализации "Watch expression" в дебаггере или Conditional breakpoint-а, где Condition задается кодом.
Есть ли в N2 механизм раздельного описания и использования лексера, парсера, вывода типов и трансформаций ? Иной раз надо только лексер, другой — только парсинг или разрезолвить имена и типы в AST-е.
Здравствуйте, _Obelisk_, Вы писали:
_O_>На практике парсеры обычно имеют много входов (для expression-ов, типов, statement-ов и т.п.). Это, к примеру, нужно при реализации "Watch expression" в дебаггере или Conditional breakpoint-а, где Condition задается кодом.
В Н2 можно будет парсить начиная с любого правила (за исключением правил расширяющих точки расширения).
_O_>Есть ли в N2 механизм раздельного описания и использования лексера, парсера, вывода типов и трансформаций ? Иной раз надо только лексер, другой — только парсинг или разрезолвить имена и типы в AST-е.
Отдельного лексера, как и в PEG, не будет, так как парсер безлексерный. Все остальное можно делать с любого правила. Будут так называемые дескрипторы плавил. Их можно будет добыть из сборки запустить с них парсинг. Кроме того можно будет собирать из нескольких грамматик одну композитную грамматику и начинать парсинг с любого правила входящего в композитную грамматику.
Что касается резолва типов, то тут все сложнее. На первом этапе полноценной типизаци не будет. Но будет аналог атрибутных грамматик. Их так же можно будет "запускать" с любой ветки AST.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, catbert, Вы писали:
C>Я понимаю, что предметная область сложна и абстрактна, но это слишком уж напоминает какой-то автосгенерированный текст
Скорее всего, где-то после нового года будет первый релиз. К этому времени постараемся сделать описание того что получается. Оно будет более конкретно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Я думаю, что необходимо разбить текст на несколько статей. Первая должна быть посвящена целям N2, поверхностному описанию предоставляемых им средств, а также принципам работы. В ней же надо зафиксировать термины и дать формальные определения для каждого из них. Я полагаю, что существует необходимость введения новых терминов. Например, слова "модуль" и "расширение" используется слишком часто во множестве разных значений, что сильно усложняет чтение.
У меня после прочтения статьи (я читаю ее уже не в первый раз), не образуется понимания принципов N2 в целом. Есть описание некоторых конкретных элементов, но связь между ними не ясна. Одной из причин является отсутствие полноценного описания информации, которой манипулирует N2, а также этапов ее преобразования. Надо перечислить этапы, указав, какая информация доступная, какие действия возможны, а также провести параллели с традиционными средствами создания языков программирования (лексический анализ, разбор в АСТ, процесс типизации, оптимизирующие преобразования АСТ, трансляция).
Далее сумбурные комментарии, как по тексту статьи так и по ее содержанию. Это не категоричные заявления, а мое личное мнение.
>Н2 – это фреймворк или тулкит, позволяющий:
Слово "тулкит" здесь и далее по тексту статьи излишне.
>Расширять расширяемое правило, объявленное в другом модуле (при этом расширяемый модуль должен быть импортирован в расширяющий модуль). Расширяющий модуль знает о том модуле, который он расширяет. Напротив, модуль, содержащий расширение, не должен ничего знать о своих расширениях.
Я полагаю, что здесь требуется иная терминология, поскольку используемая сейчас приводит к путанице. Кроме того, модуль используется еще в таком контексте: >Модуль интеграции с IDE (такой, как Microsoft Visual Studio (VS), Eclipse и т.п.).
Я пытался провести классификацию значения слова "модуль" в тексте статьи, но запутался. Необходима помощь в этом вопросе.
>При работе над Nemerle нами (группой RSDN) было выявлено несколько архитектурных и множество проектных недостатков Nemerle. Не будут вдаваться в подробности, но именно желание их устранить привело нас к проекту N2.
Критика Nemerle не уместна в данной статье, поскольку отвлекает от темы и формирует у далекого от Nemerle читателя неправильное представление о текущем положении дел c Nemerle. К тому здесь присутствует неверный посыл, что N2 — это следующая версия Nemerle. Предлагаю здесь сделать акцент на том, что Nemerle хоть и обладает средствами метапрограммирования, позволяющими создавать edsl и вводить определенные группы синтаксических конструкций, является сформировавшимся языком программирования общего назначения с устоявшейся семантикой и системой типов. N2 — плод обобщения и развития идей метапрограммирования Nemerle. Основная цель N2 — упростить создание не только встроенных, но и внешних языков программирования, предоставив средства описания всех трех компонентов: синтаксиса, семантики и системы типов.
>ПРЕДУПРЕЖДЕНИЕ >Правило «s», пропускающее 0 или
Больше красное предупреждение в разделе "Синтаксический модуль" является лишним, поскольку не является существенным.
>Правило задает: >Семантику. Семантика определяется типами, которые вычисляются в процессе типизации.
Непонятно. Нет описания действующих процессов (разбор, преобразование АСТ, типизация), нет описания порядка их работы, нет описания данных, которыми они манипулируют. Потому нельзя понять, как и какую (операционную, денотационную) семантику задает правило.
>Для того чтобы в IDE моно
очепятка
>Это сделано неспроста, так как в итоге они превращаются в перечисления, значениями которых помечаются токены.
Убрать примечание, сделать просто следующим предложением.
>Если настройка задана в импортируемом синтаксическом модуле и в текущем, то текущие настройки переопределяют импортированные. >Если опция определена более чем в одном импортированном синтаксическом модуле, нужно переопределить ее в текущем модуле, или будет выдано сообщение о неоднозначности.
В первом случае тоже необходимо сообщение о неоднозначности и специальное ключевое слово для переопределения вроде override.
>Повторное использование конструкции «brackets» не переопределяет список скобочных литералов, а дополняет его. >Таким образом, можно добавлять описание скобок в дочерних синтаксических модулях.
Что имеется в виду под дочерним синтаксическим модулем? Возможно, будут необходимы какие-то средства регулирования описанного поведения brackets.
>в правиле может быть опущен тип подправила
Необходимо четкое определение "тип правила", его отношение к типу проектируемого языка.
>«transform to» – содержит код, производящий генерацию кода для данного правила. >«symbols» – описывает операции манипуляции таблицей символов.
К вопросу об этапах работы и компонентах N2. У меня складывается впечатление, что все планируется свалить в кучу. Где описания разбора, там и типизация и трансляция. Я вижу здесь подход аналогичный тому, что используется в макросах Nemerle, но не уверен, что он является удачным для N2.
>Если при используется отношение подтипа
очепятка
>N2 будет предоставлять языки промежуточного уровня. Генерация низкоуровневого кода (MSIL, ассемблер, байт-код Java, коды LLVM и т.п.) будет производиться
Недописанное предложение. Вообще, мне сильно не нравятся эти красные предупреждения, особенно с замечанием о том, что что-то тут может изменить и используется для иллюстрации. Необходимо один раз в начале статьи указать, что N2 развивается, многие компоненты не зафиксированы и тд, и больше к этому не возвращаться. Что касается трансляции и промежуточных языков, то тема не раскрыта совершенно. Я уверен, что у многих возникнут вопросы по этим языкам, по возможным оптимизациям и так далее.
Итог
Нужны следующие статьи:
— Описание N2 (Что?, Что позволяет?, Рантайм N2, Как использовать?, Что получается в результате?, История, Почему фреймворк? Реализация языка)
— Синтаксические возможности N2 (Синтаксический модуль)
— Типизация в N2 (Типизация)
— Трансляция в N2 (Трансформация, Бэкэнды)
Разные статьи необходимы, как минимум потому, что рассчитаны на разную целевую аудиторию. Желающие в общих чертах понять, что такое N2 не обязаны видеть примеры кода или вчитываться в принципы организации областей видимости.
Проблемные термины:
— модуль
— расширение
— язык (внешний, встроенный, промежуточный, языки описания в N2)
— правило (лексическое, синтаксическое, правило типизации)
Ага, значит современный аналог Cocktail-я получился. Это хорошо, а то последний в современные реалии плохо вписывается. Осталось дождаться версии под Eclipse/Java.
ЧВЮ>Авторы: ЧВЮ> Чистяков Владислав Юрьевич
ЧВЮ>Аннотация: ЧВЮ>В данной статье рассказывается о новом проекте языкового фрэймворка – N2
>Разрешение неоднозначностей при парсинге обеспечивается выбором более длинной последовательности в сочетании с выбором более длинных лексем. Этот алгоритм обеспечивает разбор, соответствующий правилам современных «лексерных» языков программирования.
Всё-таки в результате парсинга получается одно синтаксическое дерево и все неоднозначности решаются этой эвристикой? Или же, как было написано где-то в сообщениях на форуме, парсинг может быть неоднозначным и конфликты разрешаются на стадии типизации. Происходит ли типизация строго после парсинга или же она происходит в процессе? В C++ типизация, включающая в себя инстанциирование шаблонов, должна происходить в процессе синтаксического анализа. В противном случае в результате парсинга получится, нечто малополезное, что потребует нового, но уже ручного разбора на стадии типизации. Грубо говоря получится только разметить (определить местоположение и имя) классы и функции. Но именно такая задача решается довольно простой грамматикой и это очень малая часть синтаксического анализа.
>Реальная информация о метаданных будет вычислена только в момент обращения к этим самым метаданным. Когда это произойдет, зависит от языка программирования. Так, для C++ это может случиться еще при парсинге (при разрешении неоднозначности), что не проблематично для C++, так как в этом языке все типы должны быть объявлены (или предекларированны) выше (заранее).
Проблематично. В методах внутри классов можно использовать типы объявленные далее по коду программы.
В процессе разбора тела методов пропускаются (в простейшем случае можно просто считать скобки) и их полный разбор происходит на следующих стадиях.
В исходниках компиляторов clang и gcc это называется tentative parsing. Кроме отложенного разбора методов классов, может понадобиться предварительный парсинг частей выражения для most vexing parse.
Здравствуйте, Aleх, Вы писали:
A>Всё-таки в результате парсинга получается одно синтаксическое дерево и все неоднозначности решаются этой эвристикой?
Нет. Эта эвристика устраняет неоднозначности присущие безлексерным парсерам (а-ля PEG) и general-парсерам (незнаю уж как по-русски назвать) вроде Earley, GLR, GLL.
A>Или же, как было написано где-то в сообщениях на форуме, парсинг может быть неоднозначным и конфликты разрешаются на стадии типизации.
Наш парсер допускает неоднозначности в которых неоднозначные конструкции имеют одинаковое количество одинаковых терминалов (токенов). В случае, если в процессе парсинга появилась два варианта разбора с разным числом терминалов или с терминалами разной длинны, то включается эта эвристика и устраняет неоднозначность в пользу более длинных терминалов или в пользу вариантов с большим числам терминалов. Это аналогично поведению лексерных LL(k)-парсеров.
A>Происходит ли типизация строго после парсинга или же она происходит в процессе?
Строго после окончания парсинга.
A>В C++ типизация, включающая в себя инстанциирование шаблонов, должна происходить в процессе синтаксического анализа.
Это не так.
A>В противном случае в результате парсинга получится, нечто малополезное, что потребует нового, но уже ручного разбора на стадии типизации. Грубо говоря получится только разметить (определить местоположение и имя) классы и функции. Но именно такая задача решается довольно простой грамматикой и это очень малая часть синтаксического анализа.
В результате получится вполне себе нормальное дерево разбора, возможно с неоднозначностями. Именно их можно разрешить отдельным проходом выполняемым более примитивными парсерами во время синтаксического анализа.
С точки зрения теории нет разницы осуществлять ли устранение неоднозначностей во время парсинга или же во время отдельного прохода.
>>Реальная информация о метаданных будет вычислена только в момент обращения к этим самым метаданным. Когда это произойдет, зависит от языка программирования. Так, для C++ это может случиться еще при парсинге (при разрешении неоднозначности), что не проблематично для C++, так как в этом языке все типы должны быть объявлены (или предекларированны) выше (заранее).
A>Проблематично. В методах внутри классов можно использовать типы объявленные далее по коду программы. A>В процессе разбора тела методов пропускаются (в простейшем случае можно просто считать скобки) и их полный разбор происходит на следующих стадиях.
Это не проблема. Более того наш парсер при этом будет даже удобнее, так как всеми этими приседаниями просто не нужно будет заниматься.
Небольшое отступление.... Твой пример немного упрощенный по этому не совсем показывает проблему. В нем просто нет неоднозначности. Чтобы она появилась объявление inner нужно сделать указателем. Тогда будет неоднозначность между объявлением указателя и умножением. Ну, да это не так важно.
После парсинга у нас получится просто неоднозначное дерево разбора в котором будут неоднозначные конструкции будут встречаться два раза. Предположим, что это было объявление указатели, тогда в дереве разбора у нас будет стейтмент с объявлением переменной и еще один с операцией умножения.
Сразу после парсинга мы запускаем процедуру предварительной типизации на которой заполняются таблицы символов и каждому из идентификаторов добавляется информация является ли он именем типа и если да, то каким.
Следующим проходом мы производим устранение неоднозначностей с использованием таблицы символов построенной на предыдущем шаге. Просто связываем имена и если это имя типа, то отбрасываем ветвь с умножением, а если нет, ветвь с объявлением переменной.
A>В исходниках компиляторов clang и gcc это называется tentative parsing.
Вот он нам и не нужен, так как мы используем более мощный парсер.
A>Кроме отложенного разбора методов классов, может понадобиться предварительный парсинг частей выражения для most vexing parse.
Аналогично и тут. Это всего лишь неоднозначность и в стандарте С++ четко описано как ее устранять. Собственно так и поступает Clang++. О чем свидетельствует его выхлоп "parentheses were disambiguated as a function declaration".
ЗЫ
Вообще, парсинг языков вроде С++ должен радикально упроститься с использованием генерализованных парсеров (так что ли их назвать?) именно потому что не нужно никаких танцев с бубнами. Можно получить неоднозначное дерево разбора соответствующее контекстно-свободной грамматике, а потом разрешить неоднозначности использую уже полное дерево разбора.
У С++ проблемы будут совсем с дргуим. Там сложен сам процесс типизации. Причем основная сложность — вычислительная. То что нет метаданных, а есть инклюды сильно усложняет жизнь.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Наш парсер допускает неоднозначности в которых неоднозначные конструкции имеют одинаковое количество одинаковых терминалов (токенов). В случае, если в процессе парсинга появилась два варианта разбора с разным числом терминалов или с терминалами разной длинны, то включается эта эвристика и устраняет неоднозначность в пользу более длинных терминалов или в пользу вариантов с большим числам терминалов. Это аналогично поведению лексерных LL(k)-парсеров.
То есть если количество символов текста одинаковое у двух разных разборов, эвристика не применяется?
Кстати, если в языке описываются или импортируются новые грамматические расширения, то эти места должны наверное быть однозначными? То есть конструкция using GrammarExtension; должна быть однозначной. Если же эту конструкцию невозможно сразу однозначно разобрать, то как подключить расширение для разбора последующего кода? Или же производить несколько ветвей разбора — с подключенным расширением и без?
VD>В результате получится вполне себе нормальное дерево разбора, возможно с неоднозначностями.
В таком дереве разбора будет довольно много ветвей. Даже если хранить его сжато в виде графа, всё равно в худшем случае может получиться как минимум линейное увеличение количество узлов, пропорциональное количеству неоднозначных правил. Интересно, сколько это в цифрах? Но тут проблема в том, что полезной информации в таком дереве разбора мало:
1. Имена конструкций верхнего уровня (классов, функций) и их границы
2. Границы statement внутри функций.
В большинстве случаев то, что лежит внутри statement (между точками с запятой) может быть всем чем угодно — декларация, вызов функции, вызов оператора () и тд. Наверное эффективнее запомнить границы неоднозначности и распарсить её снова, когда будет доступен контекст. VD>Именно их можно разрешить отдельным проходом выполняемым более примитивными парсерами во время синтаксического анализа.
Грамматики для этих парсеров нужно описывать вручную или парсеры могут быть сгенерированы автоматически?
VD>С точки зрения теории нет разницы осуществлять ли устранение неоднозначностей во время парсинга или же во время отдельного прохода.
С точки зрения результата. А а в процессе выполнения может происходить много лишней работы.
VD>Это не проблема. Более того наш парсер при этом будет даже удобнее, так как всеми этими приседаниями просто не нужно будет заниматься.
Если строить парсеры для предварительного разбора (tentative parsing) автоматически на основе исходной грамматики, то приседаний не будет.
VD>Небольшое отступление.... Твой пример немного упрощенный по этому не совсем показывает проблему. В нем просто нет неоднозначности. Чтобы она появилась объявление inner нужно сделать указателем. Тогда будет неоднозначность между объявлением указателя и умножением. Ну, да это не так важно.
Это да.
VD>После парсинга у нас получится просто неоднозначное дерево разбора в котором будут неоднозначные конструкции будут встречаться два раза. Предположим, что это было объявление указатели, тогда в дереве разбора у нас будет стейтмент с объявлением переменной и еще один с операцией умножения.
VD>Сразу после парсинга мы запускаем процедуру предварительной типизации на которой заполняются таблицы символов и каждому из идентификаторов добавляется информация является ли он именем типа и если да, то каким.
VD>Следующим проходом мы производим устранение неоднозначностей с использованием таблицы символов построенной на предыдущем шаге. Просто связываем имена и если это имя типа, то отбрасываем ветвь с умножением, а если нет, ветвь с объявлением переменной.
В процессе типизации нужно будет ещё производить инстанциирование шаблонов, чтобы парсить конструкции типа MyTemplate<param>::typeOrVariable. Как предлагается описывать схему типизации, включающей в себя работу с шаблонами? Кроме этого в зависимости от того, как описана система типов, могут возникать неоднозначности типизации. Будет ли в этом случае рантайм ошибка или же предлагается как-то заранее проверить систему типов на однозначность?
A>>В исходниках компиляторов clang и gcc это называется tentative parsing.
VD>Вот он нам и не нужен, так как мы используем более мощный парсер.
A>>Кроме отложенного разбора методов классов, может понадобиться предварительный парсинг частей выражения для most vexing parse.
VD>Аналогично и тут. Это всего лишь неоднозначность и в стандарте С++ четко описано как ее устранять. Собственно так и поступает Clang++. О чем свидетельствует его выхлоп "parentheses were disambiguated as a function declaration".
VD>ЗЫ
VD>Вообще, парсинг языков вроде С++ должен радикально упроститься с использованием генерализованных парсеров (так что ли их назвать?) именно потому что не нужно никаких танцев с бубнами. Можно получить неоднозначное дерево разбора соответствующее контекстно-свободной грамматике, а потом разрешить неоднозначности использую уже полное дерево разбора.
VD>У С++ проблемы будут совсем с дргуим. Там сложен сам процесс типизации. Причем основная сложность — вычислительная. То что нет метаданных, а есть инклюды сильно усложняет жизнь.
Это только на скорость компиляции влияет. Может в С++17 появятся модули и проблема исчезнет.
Здравствуйте, Aleх, Вы писали:
A>То есть если количество символов текста одинаковое у двух разных разборов, эвристика не применяется?
В общем, да, но дело не в общем количестве токенов.
Точное определение такое. Если два пути разбора дают одинаковый набор токенов (оп длине, тип не важен), то это считается чистой неоднозначностью и никаких действий по ее разрешению не предпринимается. Если набор токенов различается, то победителем является тот у которого первым встретится более длинный токен или тот у которого больше токенов.
A>Кстати, если в языке описываются или импортируются новые грамматические расширения, то эти места должны наверное быть однозначными? A>То есть конструкция using GrammarExtension; должна быть однозначной. Если же эту конструкцию невозможно сразу однозначно разобрать, то как подключить расширение для разбора последующего кода? Или же производить несколько ветвей разбора — с подключенным расширением и без?
Нитра не закладывается на то как будет расширяться зык. Но, да, расширяемый язык должен иметь внятное и детерминированную политику расширения. Если расширение вводится синтаксической конструкцией вроде using-а, она должна быть однозначной, ведь расширение будет загружаться на не последующей стадии, а прямо во время парсинга.
VD>>В результате получится вполне себе нормальное дерево разбора, возможно с неоднозначностями. A>В таком дереве разбора будет довольно много ветвей.
Строго говоря это не дерево, а граф. В нем ничего не дублируется. Так что представление довольно компактное. Но если обходить его как дерево, то возможен экспоненциальный взрыв.
A>Даже если хранить его сжато в виде графа, всё равно в худшем случае может получиться как минимум линейное увеличение количество узлов, пропорциональное количеству неоднозначных правил.
Ну, если язык дико неоднозначен, то — да. Но на практике таких языков никто не создает. В используемых на практике языках очень ограниченный набор неоднозначностей который хранится довольно компактно.
В современных же языках грамматики вообще полностью однозначны. Например, в грамматике C# описаны все случае неоднозначностей и приведены четкие алгоритмы их устранения еще на стадии парсинга. Наш парсер C# реализует их все на базе предикатов.
Исключением является восстановление после ошибок. Тут возможны любые варианты. Именно этим и была вызвана задержка в работе над восстановлением.
A>Интересно, сколько это в цифрах?
В худшем случае это экспонента, т.е. полное фиаско. Но, как я уже говорил выше, это сугуб теоретический случай.
A>Но тут проблема в том, что полезной информации в таком дереве разбора мало: A>1. Имена конструкций верхнего уровня (классов, функций) и их границы A>2. Границы statement внутри функций.
Если неоднозначности по делу, то их будет не много и будет очевидный способ их устранения.
A>В большинстве случаев то, что лежит внутри statement (между точками с запятой) может быть всем чем угодно — декларация, вызов функции, вызов оператора () и тд. Наверное эффективнее запомнить границы неоднозначности и распарсить её снова, когда будет доступен контекст.
Мы имеем дело с конкретным текстом. Чем угодно он быть не может. Скажем если язык С++, а пользователь написал:
A* B;
то в statment мы получим неоднозначность состоящую из двух вариантов (точно грамматику плюсов не знаю, так что пишу примерно):
1. Statment.Expression.
2. Statment.VarDecl.
Где Statment.Expression состоит из одного подправила типа Expression которое разберется по варианту Expression.Mul(Expression.Identifier, "*", Expression.Identifier).
Чтобы выбрать один из них нужно сначала построить таблицы символов, а потом по ним разрешить имя. Если имя будет типом, то на втором проходе мы откинет Statment.Expression(Expression.Mul(Expression.Identifier, "*", Expression.Identifier)), так как первое подвыражение будет типом. Ну, и т.п.
VD>>Именно их можно разрешить отдельным проходом выполняемым более примитивными парсерами во время синтаксического анализа. A>Грамматики для этих парсеров нужно описывать вручную или парсеры могут быть сгенерированы автоматически?
Грамматику нужно писать вручную. По ней будет парсер будет сгенерирован автоматически.
Вот пример грамматики шарпа. Пишешь подобную для нужного языка, помещаешь в проект, компилируешь и получаешь готовый парсер.
A>С точки зрения результата. А а в процессе выполнения может происходить много лишней работы.
Это уже проблемы алгоритмов парсинга, чтобы объем этой работы оставался в приемлемом уровне. В Нитре применяется куча оптимизаций, которые дают неплохой результат.
VD>>Это не проблема. Более того наш парсер при этом будет даже удобнее, так как всеми этими приседаниями просто не нужно будет заниматься. A>Если строить парсеры для предварительного разбора (tentative parsing) автоматически на основе исходной грамматики, то приседаний не будет.
Само создание таких парсеров (я уже не говорю о сопряжении) и есть не хилые приседания. Человек создающий поддержку для языка не дожен тратить свое время на не относящиеся к делу вещи.
Собственно плюсы нами не рассматриваются в качестве целевого языка. Уж больно криво они спроектированы. Мы создаем средство для построения, в первую очередь, расширяемых языков. Понятно, что они должны быть по возможности однозначны и вообще просты. Все же навороты Нитры скорее нужны для того чтобы создавая свои языки и расширения к ним не приходилось обходить ограничения Нитры, а можно было бы сосредоточиться на творчестве в области языкостроения.
A>В процессе типизации нужно будет ещё производить инстанциирование шаблонов, чтобы парсить конструкции типа MyTemplate<param>::typeOrVariable.
Ну, шаблоны можно типизировать исключительно в их конкретных воплощениях (не люблю я этот новояз — "инстанциирование"). По сути шаблоны это АСТ-шные макросы. По сему тут ничего другого и не придумаешь.
A>Как предлагается описывать схему типизации, включающей в себя работу с шаблонами?
Как я уже говорил выше — никак. С++ на наш целевой язык. Но возможностей для его обработки должно хватить. Я не сомневаюсь, что на Нитре можно будет написать типизатор для плюсов. Вопрос только в том насколько он будет приемлем по скорости работы, и насколько он окажется сложен.
A>Кроме этого в зависимости от того, как описана система типов, могут возникать неоднозначности типизации.
Задачи типизации найти однозначное описание для всех имен в программе. Если это не удалось сделать — это ошибка (компиляции/типизации). Так что неоднозначностей тут быть не должно.
A>Будет ли в этом случае рантайм ошибка или же предлагается как-то заранее проверить систему типов на однозначность?
Расширяемость не дает возможность проверять даже однозначность грамматики (да и вычислительно это вряд ли возможно). В рантайме (на конкретном дереве разбора) это делается без проблем.
VD>>Вообще, парсинг языков вроде С++ должен радикально упроститься с использованием генерализованных парсеров (так что ли их назвать?) именно потому что не нужно никаких танцев с бубнами. Можно получить неоднозначное дерево разбора соответствующее контекстно-свободной грамматике, а потом разрешить неоднозначности использую уже полное дерево разбора.
VD>>У С++ проблемы будут совсем с дргуим. Там сложен сам процесс типизации. Причем основная сложность — вычислительная. То что нет метаданных, а есть инклюды сильно усложняет жизнь.
A>Это только на скорость компиляции влияет.
Дык только это и является проблемой.
Вообще, генерализованные парсеры были бы лучшим решением, если бы не скорость их работы. Мы как раз и сделали умерено генерализованное решение которое покрывает потребности реально используемых языков и при этом дает приемлемую для практического использования производительность.
A>Может в С++17 появятся модули и проблема исчезнет.
О модульности в С++ я слышу уже 15 лет, как минимум. А воз и ныне там (ц).
Им придется сломать совместимость, чтобы сделать модульность. Да и шаблоны этому не способствуют. В прочем, если комитету удастся это сделать и это не вызовет проблем, я буду только рад. Пускай дерзают.
Я думаю, что с совместимостью плюсам давно нужно заканчивать. Обеспечить совместимость на уровне линковки и на уровне компиляции старых версиях в едином процессе компиляции и всех это устроит. Один фиг плюсы давно не совместимы с тем же Си на 100%. И никого это не напрягает. А вот то, что даже разные Майкрософты не могут ослить последних стандартов С++ очень сильно печалит.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Нитра не закладывается на то как будет расширяться зык. Но, да, расширяемый язык должен иметь внятное и детерминированную политику расширения. Если расширение вводится синтаксической конструкцией вроде using-а, она должна быть однозначной, ведь расширение будет загружаться на не последующей стадии, а прямо во время парсинга.
А как это сейчас реализовано? Как задать то, что конструкция using MyLanguage; вводит новый язык. В документации к нитре я этого не смог найти. В Nemerle, насколько я помню, это происходит в pre parser — там захордкожена конструкция using ...; Или как в нитре задать новую конструкцию для расширения? Например import langauge MyLanguage;
Возможно ли задать грамматику и подключить её в том же файле?
syntax my_sql_extension (statement_list)
// my_sql_extension - имя расширения
// statement_list - правило, которое будет стартовым. Оно из родительской грамматики родительской грамматики.
// записанные правила добавляются к родительской грамматике
{
closed_statement = 'identifier' '=' sql_expression;
sql_expression = "SELECT" actual_parameter actual_parameter_list "FROM" 'identifier'
sql_where_caluse sql_group_by_caluse sql_order_caluse;
...;
}
void foo1()
{
// здесь расширение не подключено
}
void bar()
{
using my_sql_extension;
sqlResult = SELECT a, sin(x) FROM myTable WHERE x = foo(a, b, c, d) GROUP BY t;
}
void foo2()
{
// здесь расширение не подключено
}
Здесь и описание грамматики, и её подключение должно быть однозначным?
VD>Грамматику нужно писать вручную. По ней будет парсер будет сгенерирован автоматически. VD>Вот пример грамматики шарпа. Пишешь подобную для нужного языка, помещаешь в проект, компилируешь и получаешь готовый парсер.
A>>Если строить парсеры для предварительного разбора (tentative parsing) автоматически на основе исходной грамматики, то приседаний не будет.
VD>Само создание таких парсеров (я уже не говорю о сопряжении) и есть не хилые приседания. Человек создающий поддержку для языка не дожен тратить свое время на не относящиеся к делу вещи.
Если такие парсеры будут генерироваться атоматически на основе исходной полной грамматики языка, пользователь не будет тратить время. Это будут детали реализации парсера. Для каких частей грамматики генерировать такие парсеры можно понять по зависимостям атрибутов, если грамматика атрибутная.
A>>В процессе типизации нужно будет ещё производить инстанциирование шаблонов, чтобы парсить конструкции типа MyTemplate<param>::typeOrVariable.
VD>Ну, шаблоны можно типизировать исключительно в их конкретных воплощениях (не люблю я этот новояз — "инстанциирование"). По сути шаблоны это АСТ-шные макросы. По сему тут ничего другого и не придумаешь.
Имелось ввиду то, что для типизации MyTemplate<param>::type variable; нужно раскрыть шаблон. Для того, чтобы раскрыть шаблон нужно не только подставить типы, но ещё найти тело шаблона с учетом вывода типов, частичных специализаций и SFINAE.
A>>Будет ли в этом случае рантайм ошибка или же предлагается как-то заранее проверить систему типов на однозначность?
VD>Расширяемость не дает возможность проверять даже однозначность грамматики (да и вычислительно это вряд ли возможно). В рантайме (на конкретном дереве разбора) это делается без проблем.
VD>>>Вообще, парсинг языков вроде С++ должен радикально упроститься с использованием генерализованных парсеров (так что ли их назвать?) именно потому что не нужно никаких танцев с бубнами. Можно получить неоднозначное дерево разбора соответствующее контекстно-свободной грамматике, а потом разрешить неоднозначности использую уже полное дерево разбора.
Так задача разрешения неоднозначности переносится со стадии парсинга на типизацию, но никуда не исчезает. С другой стороны можно описывать атрибутную грамматику с предикатами, которая будет однозначна.
VD>А вот то, что даже разные Майкрософты не могут ослить последних стандартов С++ очень сильно печалит.
У них много старого кода, который они не хотят переписать с нуля. А для интеллисенс вообще используют сторонний парсер.