В некоторых генераторах нисходящего(LL) синтаксического анализа с увеличением объема текста исходного кода время парсинга увеличивается в более чем арифметической прогрессии.
Часть проблем связанных с производительностью ANTLR описана в статье Вредные советы при работе с ANTLR.
Идея состоит в том, чтобы ввести в самом парсере понятие препарсинга — разбиение текста исходной программы на блоки для отдельного парсинга.
Тексты программ имеют блочную структуру.
к примеру
1 уровень — namespace
2 уровень — классы
3 уровень — методы и свойства
и т.д.
Для Си-подобных языков блоки это просто {}
В языке же Clipper, например,
1 уровень — процедуры/функции/методы, классы, директивы
Правило начала процедуры/функции:
Окончанием процедуры/функции может быть
но RETURN для процедур может и отсутствовать, тогда окончанием будет EOF или начало блока первого уровня.
В генераторе парсеров ANTLR для грамматики можно устанавливать опции.
Т.е. мы можем определить:
и после этого для правил указывать атрибуты, например:
Также некоторые конструкции такие как лямбды/анонимные функции могут быть тоже выделены для отдельного парсинга. Для них можно установить PreParseLevel = 0.
Целью синтаксического анализа является построение дерева синтаксического разбора(abstract syntax tree (AST)). В процессе парсинга мы будем получать для каждого блока свое дерево, которое потом можно прицепить к основному дереву.
Конечно, кто-то использует парсер-комбинаторы и для него это все понятно и так.
Хотелось бы увидеть комментарии о данной концепции.
Разумно ли все это?
Применялось ли где?
Применяли ли вы в своей работе?
Стоит ли выносить это на обсуждение в англоязычном сообществе?
With best regards
Часть проблем связанных с производительностью ANTLR описана в статье Вредные советы при работе с ANTLR.
Идея состоит в том, чтобы ввести в самом парсере понятие препарсинга — разбиение текста исходной программы на блоки для отдельного парсинга.
Тексты программ имеют блочную структуру.
к примеру
1 уровень — namespace
2 уровень — классы
3 уровень — методы и свойства
и т.д.
Для Си-подобных языков блоки это просто {}
В языке же Clipper, например,
1 уровень — процедуры/функции/методы, классы, директивы
Правило начала процедуры/функции:
funcProc
: funScope? (FUNCTION | PROCEDURE) identName ('(' params? ')')? crlfStmnt
;
funScope
: STATIC
| INIT
| EXIT
;
Окончанием процедуры/функции может быть
RETURN ( expression )? crlfStmnt
но RETURN для процедур может и отсутствовать, тогда окончанием будет EOF или начало блока первого уровня.
В генераторе парсеров ANTLR для грамматики можно устанавливать опции.
Т.е. мы можем определить:
options { PreParse=ON; }
и после этого для правил указывать атрибуты, например:
[PreParseLevel = 1]
funcProcSection
: funcProcHead
statements
;
Также некоторые конструкции такие как лямбды/анонимные функции могут быть тоже выделены для отдельного парсинга. Для них можно установить PreParseLevel = 0.
Целью синтаксического анализа является построение дерева синтаксического разбора(abstract syntax tree (AST)). В процессе парсинга мы будем получать для каждого блока свое дерево, которое потом можно прицепить к основному дереву.
Конечно, кто-то использует парсер-комбинаторы и для него это все понятно и так.
Хотелось бы увидеть комментарии о данной концепции.
Разумно ли все это?
Применялось ли где?
Применяли ли вы в своей работе?
Стоит ли выносить это на обсуждение в англоязычном сообществе?
With best regards
SerafimArts
1) Предлагаю это всё на тостер перенести.
// а если по теме, то
2) Так лексер и так и так проходит от начала до конца. В чём смысл вместо линейного N делать N*X, где X — количество этих самых "блоков"? Ради последующего многопоточного построения AST?
3) Такое делают (по крайней мере делал я) в случае инлайнинга одной грамматики внутрь другой: JS/CSS внутри HTML, всякие шаблонизаторы (twig/jinja/etc) или императивный код в редьюсерах PEG грамматик. В antrl для этого есть каналы, в phplrt стейты лексера и композиция парсеров.
3.1) Ну, и, возможно, для реализации толерантного разбора.
anonymous Автор
Лексер работает вместе с парсером и обрабатываются куча правил грамматики. Допустим средствами регулярных выражений мы можем получить меньшие по размеру блоки, которые обработаются быстрее, чем линейно весь текст. А идея с многопоточностью хорошая.
Можно, конечно, самому изменить сгенерированный парсер, но я хотел предложить общее решение.
SerafimArts
Зависит от реализации. И даже в LL вполне применим подход с итерацией по уже заранее разобранному потоку лексем (т.е. одно единственное регулярное выражение на все токены), что, естественно, потребляет больше оперативы, но ускоряет разбор за счёт однозначности токенов. Ну и вместо re2c или pcre можно взять уже какой-нибудь hyperscan, т.к. лексер полностью изолирован от парсера и его состояния, и его задача лишь возвращать следующий однозначный токен.
P.S. Но это всё такое, вилами по воде.