Pull to refresh

Comments 16

Обнимаю, коллега. SQL — отвратительный язык (у меня парсер DDL и выражений, это какой-то кошмар)

Мне очень нравится работать с данными с использованием SQL, но вот парсить его было той еще задачкой.
Некоторое время занимаюсь разработкой своего генератора парсеров на C#, по открытым грамматикам PL/Sql и C# получаются парсеры на 35к и 6.8к строк соответственно. Visual Studio с Resharper'ом вообще не в восторге от сгенерированного парсера PL/Sql
Всем сердцем люблю Resharper и многие функции, что он дает, но какое-то время назад был вынужден его отключить именно из-за получающихся тормозов.
Прикладываю иногда кусочек воли к переходу на rider. Нравится еще больше, но перейти не могу. За что себя и ругаю.
А, было бы интересно почитать, как пришли к такой разработке?
Однажды в моём пет-проекте понадобился парсер математических выражений и вместо того, чтобы написать простейший рекурсивный парсер, мне подумалось написать маленький велосипедик, который парсер сгенерирует. Тогда я ещё не осознавал, что это займет чуть больше времени, чем пару недель работы. Когда через год генератор уже работал, но, как оказалось, очень медленно, я решил всё-таки почитать книгу дракона. Потом уже жалко было бросать. Провёл работу по оптимизации, придумал свой язык описания грамматики, дописал генератор модели синтаксического дерева, добавил предикаты/действия в парсере/лексере, реализовал левую рекурсию, вызов правил внешних грамматик. На данный момент нет восстановления после ошибок и рекурсии в лексере. А так можно взять грамматику ANTLR и немного подправив синтаксис скормить генератору и получить парсер.

Согласен — у PL/SQL очень большая грамматика, и лично у меня из-за этого очень медленно работает рефакторинг в Rider :) Но мы (Positive Technologies) работаем над уменьшением размера этой грамматики. Дело в том в PL/SQL очень много контекстных ключевых слов, таких, которые имеются смысл только в определенных правилах, а не везде. Но ANTLR пока что не поддерживает такое, поэтому приходится эмулировать их с помощью настоящих токенов и использовать следующий костыль:


AUTONOMOUS_TRANSACTION:       'AUTONOMOUS_TRANSACTION';

id
    : ID
    | ...
    | AUTONOMOUS_TRANSACTION
    ... ;

Количество токенов влияет на размер генерируемого парсера, а сейчас их очень много. Даже issue на GitHub завели. Пока что не дошли руки закончить эту задачу, но размер сгенерированных файлов уже удалось уменьшить в несколько раз.

А зачем обязательно ANTLR использовать, если его надо много дорабатывать? Парсер по грамматике можно самому написать обычным рекурсивным спуском, без всяких конечных автоматов. У него конечно не будет такого математического бэкграунда для валидации, но вы же не библиотеку делаете, а в своем проекте используете.
Несколько раз, перед необходимостью очередного допила мой, или чей-то еще воспаленный под вечер мозг предлгал эту идею. На утро, к счастью, мы никогда не приступали к такому решению.
Меня дико впечетляло как ANTLR разруливал различные сложные граничные кейсы, которые реально встречались в наших грамматиках. Вырастить свой генератор парсеров для обработки их всех потребовало бы несравнимо больше усилий. Хотя, будь я проклят, думаю у меня бы аж глаза искрили вдохновением от такой задачи первые несколько недель!
Так я не генератор парсеров имею в виду, а сам парсер. Генератор нужен если используются конечные автоматы, потому что получается много однотипного кода. Рекурсивным спуском с откатами может будет немного медленнее работать, зато это проще.

А можете какой-нибудь сложный кейс описать с конкретными примерами?
UFO just landed and posted this here
Вроде бы с автоматами и контекстно-свободной грамматикой можно сделать состояния типа «A или B или C» и по мере чтения новых символов переходить в более конкретные. И таким образом распрасить все за один проход без откатов. Но в математических аспектах я не специалист.
UFO just landed and posted this here

Например когда одно правило равно начальной части другого, как в ANTLR метки для выражений. Название правила имеет вид [A-Za-z]+, выражение с меткой имеет вид [A-Za-z]+ '=' '(' ... ')'. После [A-Za-z]+ надо прочитать следующий символ, и если это =, то продолжить разбор одного варианта, если нет, то вернуться на один символ обратно (или на исходную позицию, если начало не вынесено в отдельное правило), вернуть успешный результат разбора другого варианта, и начать парсинг следующего правила.

UFO just landed and posted this here

Если грамматика большая, то намного приятней и быстрее менять ее, чем вручную написанный код, который еще и по размеру намного больше.

Sign up to leave a comment.

Articles

Change theme settings