Здравствуйте, vdimas, Вы писали:
V>А вот это уже тело ф-ий удобней разбирать восходящими грамматиками — особенно что касается вложенных вызовов или математических выражений или совместной работе одного и другого. Что и происходит в реальности. Там тот же самый *LR. Потому что нисходящий разбор страдает склонностями к зацикливаниям на неоднозначных участках кода by design или к тяжеловесным откатам — и это тоже на всех углах обсосано, не буду повторяться.
Точно так же отлично парсятся рекурсивным спуском. Еще есть такие вещи как приоритетный парсинг он и леворекурсивным бывает. Мы его как раз используем. И никаких ЛР-ов там нет, так как ЛР без автоматов парсить не просто. А руками автоматы писать — мазохизм.
F>>Предполагаю что c# рослин тоже.
V>C# рассматривать нелепо:
V>- грамматика сильно однозначная;
Все грамматики современных языков одназначны за мелкими исплючениями которые легко обходятся. Для С++ нужно всего лишь использовать таблицу имен в качестве контекста (проще прямо в лексере).
V>- объем перемалываемых исходников смешон; для сравнения, в современной С++ программе из каждого CPP-фала может быть подключено до половины Буста (утрирую, но оно близко к такому положению дел).
Это просто бред. Объем исходников определяется разменами проектов. Например, у какого нибудь решарпера это гигабайты кода.
Тормоза при компиляции плюсов определяются возней с инклюдами (которые кэшируются) и раскрытием шаблонов.
V>Наоборот, если речь о неоднозначных участках кода.
Речь об ошибках в коде и восстановлении после них.
V>При рекурсивном спуске проще генерить ошибку только о простых ситуациях, типа, тут идёт объявление класса, а после имени класса написано, скажем, const — и генерим ошибку "это не ожидается здесь". ))
Очередной бред. Леворекурсивные парсеры падают в месте ошибки. Это их главная особенность. По этому их и восстанавливать проще и ошибки для них проще выдавать. Хотя это вопрос решаемый. Ошибки можно по лесу вычислять. Мы в итоге так и стали делать, так как это дает качественные сообщения не только в месте первого падения, но и далее.
V>LR-разбор в состоянии подсказать множество верных ожидаемых символов. В реальности там выбор из 1-2-х вариантов максимум, потому что в С++ неоднозначность происходит в выборе м/у идентификатором типа или переменой, вот и вся неоднозначность, которой "болеет" С++.
ЛР падает на свертках. И эти почти наверняка не места реальных ошибок. В тоже время ЛЛ падет на входе в правило и это почти наверняка реальное место ошибки.
Ты почитай какими извращениями занимаются те кто восстановление для GLR-ов пишут. Это жесть!
V>Т.е. типа такого:
V>V>id1 id2(id3);
V>
V>В этом месте id1 — всегда идентификатор типа, а вот чем является id2 зависит от id3.
Пипец ты жжешь! Какие типы в ЛР-парсере? Вот в ЛЛ можно тупо таблицу имен протащить и действительно во время парсинга резолвнуть имя узнав является ли оно типом (если речь о С++). А как это сделать в ЛР, который обломался на свертке некого правила?
V>Если id3 — это значение (переменная/константа/перечисление), то id2 — это идентификатор переменной, а всё выражение означает объявление переменной типа id1 и вызов соотв. конструктора этого типа. А если id3 — это имя типа, то всё выражение означает объявление сигнатуры некоей ф-ии id2.
Какая чушь?! Никто не типизирует выражения С++ во время парсинга. Только лукапят имена по таблице имен. И тип выражения определяется по тому является ли id1 типом. Хотя здесь выражение и так объявление переменной. Вот в случае id1* id2(id3) действительно было бы не ясно.
V>С другой стороны, подобную неоднозначность НЕ обязательно сваливать на парсер и её не сваливают — для такой конструкции заводится отдельный "недоопределённый" нетерминал и его ресолвят до однозначного через таблицу идентификаторов ручками. Т.е. на скорости работы именно парсера по алгоритму *LR это не сказывается (если убрать ресолвинг). Это конкретные заморочки конкретно С++, которые верны для ЛЮБОГО алгоритма парсинга.
Причем тут скорость? А... не я пас. Это такая жесть, что просто нет слов.