Я как-то кидал лексический анализатор.
Теперь мысли о парсере.
- Синтаксис грамматики должен быть описан явно в максимально читаемом виде, максимально близко к erbnf. Самый простой путь: задавать сырые строки и описывать там. Но это сомнительно в связи с пунктом 3.
- Использование парсера как при чтении конфигов, текстовых данных (типа VRML, XML), при построении языков программирования, в особенности небольших встроенных, но и крупные проекты для реализации некоторых мыслей о расширении С++.
- Максимальное количество проверок на этапе компиляции.
- Возможность создавать библиотеки парсинга. Подключение библиотек, быстрое их использование. Вероятно, оптимизация, если позволяет концепция. Оптимизация подразумевает использование для обработки гагабайт данных в текстовой форме.
- Создание максимально эффективного парсера, который позволяет в том числе работать и как сканер (лексический анализатор) без потери эффективности.
- Возможно экспорт библиотек парсера в JS. Пока это кажется набором слов, потом поясню.
Размышляя об этом я рассматривал несколько стратегий построения парсера:
- Описание строками. Такое описание само надо разбирать, вероятно как альтернативный метод он войдет и в другие.
- Шаблонное построение. Пример парсера на шаблонах я кидал при демонстрации сканера на регэкспах. Этот вариант привлекал меня для максимального анализа согласованности данных на этапе компиляции. Например: данные в Абстрактом Синтаксическом Дереве AST должны соответствовать по числу и типам функций-обработчиков задаваемых пользователем.
- Написать объекты, которые синтаксически соответствуют конструкциям С++, но при этом выглядят как конструкции erbnf. Этот метод меня смущал тем, что мне казалось в нем отсекается возможность статической проверки согласованности на этапе компиляции. Но потом я вспомнил, что когда-то написал библиотеку tuple-сов, в которых вызывались функции, и тип результата этих функций порождался в зависимости от аргумента! То есть был такой эффект:
- Код: Выделить всё
tuple<int,float> t0;
tuple<char,std::string> t1;
результат применения merge объединяет эти типы:
tuple<int,float,char,std::string> tres = t0.merge(t1); // шаблонная функция merge порождает разные типы!
Таким образом я остановился на этом варианте, как наиболее перспективном.
Spoiler: показать
Что потом будет происходить с этим графом? Я буду компилировать его некий язык, интерпретация которого соответствует разбору. Причем язык этот будет максимально типизированный, то есть это типизированный ERBNF со встроенной возможностью подключения функций-обработчиков. У меня даже есть уже готовая реализация, но не до конца оттестированная и она мне сейчас не нравится. Там есть свой ассемблер, дизасемблер. Но идея хороша, я возьму его как базис. Под катом дизассемблированный фрагмент парсера арифметических выражений. Кто сталкивался с P-кодом, Java-кодом и прочими подобными системами, узнает о чем речь, хоть и без подробностей.
Spoiler: показать