Комментарии 16
yacc/bison с друзьми рыдают в сторонке.
+3
Про друзей знаем, статья для тех, кому интересно, как всё работает.
+1
Простите, но статья никак не поможет разработчикам парсеров языков программирования. Как и «разобраться как сделать разбор КС-грамматики».
От простмотра метода parse() в
gitlab.com/2che/markedit/blob/master/parser.cpp
у меня, если честно, холодок по коже.
Ладно вы один раз это написали, а поддерживать как?
А если я вам скажу, что там в одной из строк ошибка, и вызывается не тот метод, вы ее быстро найдете?)
Такой код может и должен автогенерироваться.
А еще, зачем такое количество статик кастов? там прямо напрашивается использование виртуальных методов.
Я писал интерпретатор паскаля сперва ручками, потом с помощью CoCo генератора, потом перешел на bison, если интересно, какой мой опыт.
От простмотра метода parse() в
gitlab.com/2che/markedit/blob/master/parser.cpp
у меня, если честно, холодок по коже.
Ладно вы один раз это написали, а поддерживать как?
А если я вам скажу, что там в одной из строк ошибка, и вызывается не тот метод, вы ее быстро найдете?)
Такой код может и должен автогенерироваться.
А еще, зачем такое количество статик кастов? там прямо напрашивается использование виртуальных методов.
Я писал интерпретатор паскаля сперва ручками, потом с помощью CoCo генератора, потом перешел на bison, если интересно, какой мой опыт.
+1
> зачем такое количество статик кастов?
Виртуальные методы, напомню, требуют дополнительную память и ресурсы процессора. Мы знаем по перечисляемому типу Node_type, что объект принадлежит к классу Quote, и подкастовываем его, чтобы вызвать метод add_bold(), который, мы точно знаем, в нём содержится.
Виртуальные методы, напомню, требуют дополнительную память и ресурсы процессора. Мы знаем по перечисляемому типу Node_type, что объект принадлежит к классу Quote, и подкастовываем его, чтобы вызвать метод add_bold(), который, мы точно знаем, в нём содержится.
0
Виртуальные методы, напомню, требуют дополнительную память и ресурсы процессора
Напомню, чтобы утверждать про ресурсы процессора, нужно сделать бенчмарк со сравнением.
У меня нет уверенности что
if (some_cond) obj->direct_call();
сильно быстрее условного obj->indirect_call() (по указателю), т.к. процессору переходы гораздо легче предсказывать без ветвлений (он уже видит куда дальше будет переход).
И по поводу памяти — если честно, смешно. Оверхед идет только на класс, таблица виртуальных методов одна на все объекты.
Да, у объекта появляется один указатель. Но у вас и так есть память под node_type; на 32-битной архитектуре разницы нет вообще. Да, на 64 битах наличие vtable вместо enum даст +4 байта, но вы реально в это упираетесь? Зато насколько будет чище код…
+1
Нашёл, быстро, можете проверить :)
0
А что если я вам скажу, что там все равно осталась строчка с логической ошибкой?) вы мне поверите?)
0
Там до сих пор как-то много подозрительной копипасты типа:
Прямо глаз цепляется.
else if (type == Node::QUOTE)
open_node = static_cast<Title*>(open_node)->open_italic();
Прямо глаз цепляется.
+1
Ну что вы, зато мы экономим память на виртуальных методах! Вы ничего не понимаете!
0
Ладно, вы правы, лучше использовать виртуальные методы. Но замечу, что совсем без геморроя не обойтись: придётся прописывать выбросы исключений в реализации методов у классов, которые с этими методами роднятся меньше, чем никак. Классу Root, например, совершенно не идёт метод add_bold, поскольку его объекты могут содержать указатели лишь на разделы (Section) и линии (Underline). И прописывать придётся у десятков методов, в десятках классов, так что неопытный погромист вроде меня может опять же наделать ошибок.
0
Можно использовать std::variant и std::visit. Очень давно, когда последний раз делал что-то такое, то использовал их (только из boost).
+1
Можно сразу прописать выбросы исключений абсолютно во всех виртуальных методах некоего корневого класса, от которого далее будут наследоваться остальные (собственно, по умолчанию все, что они будут делать — это выбрасывать исключение), а в наследниках переопределять только те из этих методов, которые реально должны что-то делать именно в этом конкретном типе наследника.
0
yacc/bisonВыглядят как г-но мамонта. Серьёзно? Глобальные переменные, чтение из stdin/FILE* (а если у меня в компиляторе исходники читаются из zip-потока, мне их в файл пересохранять?). Каждый найденный токен копируется в отдельную область памяти, чтобы сделать из него asciiz-строку (ладно, string_view тогда не было, но хотя бы кортеж [char*,size_t]). Перемешанный си-код с грамматикой (удачи подебажить). Если тупо хочу построить AST, а не сразу выдавать что-то наружу, всё равно надо писать кучу бойлерплейта.
От всего этого в соседстве с C++14 кодом просто кровь из глаз.
Да и в реальных проектах, которые планируется поддерживать годами, не похоже, что кто-то использует. Тупо невозможно выдать юзер-френдли сообщения об ошибках, типа — «не найден END к открытому BEGIN». Или «ключевое слово 'function' обнаружено внутри функции — вложенные функции не поддерживаются». Неудобно вывести позицию (строка/столбец), где реально находится ошибка (например, незакрытый BEGIN), а не текущая позиция чтения.
lex/yacc это очень ограниченный инструмент.
0
Я написал «и друзья». yacc/bison самые известные. Как минимум postgresql использует bison и поддерживается больше 20 лет. Несмотря на ужасы наследства С кода… я буду рад увидеть yacc/bison (или любой другой кодогенератор) в проекте, куда приду работать, чем тонну недокументированного кода как бывает чаще всего… если у вас все красиво — я рад, вы попадаете в исключения.
+1
буду рад увидеть yacc/bison (или любой другой кодогенератор) в проекте, куда приду работать, чем тонну недокументированного кодаТак там и будет тонна недокументированного кода, в файле с грамматикой. Только его ещё и не подебажишь — на выходе куски юзер-кода, перемешанные с кодом парсера.
использует bison и поддерживается больше 20 летМне интересно, что говорят в коммерческой разработке, когда появляется хотелка, не предусмотренная генератором (как пример выше с вложенными ф-циями). Говорят: «извините, это невозможно, у нас Бизон», или начинают корёжить грамматику, описывая некорректные конструкции как корректные, но транслирующиеся в код выдачи ошибки. Вот уж где граблей и костылей можно насобирать.
-1
Лично мне понравилась идея интеллектуальных ссылок.
===========================
=================
===========================
// main.cpp
#include «fgl_glut_t.h»
//=============================
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
auto my_gl = std::make_unique<MyGL::GlEngine>();
}
=================
StaticFun::StaticFun()
{
auto *bispiral = std::make_unique;
if (!bispiral)
std::exit(0);
}
-2
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Искусство парсинга 2 или транслитерация собственной разметки