Каковы аргументы против разбора пути Ктулху?


24

Передо мной была поставлена ​​задача внедрения предметно-ориентированного языка для инструмента, который может стать весьма важным для компании. Язык простой, но не тривиальный, он уже допускает вложенные циклы, конкатенацию строк и т. Д., И практически уверен, что другие конструкции будут добавлены по мере продвижения проекта.

По своему опыту я знаю, что написание лексера / парсера вручную - если грамматика не тривиальна - это трудоемкий и подверженный ошибкам процесс. Таким образом, у меня остались два варианта: генератор синтаксического анализа а-ля yacc или библиотека комбинаторов, например Parsec. Первое тоже было хорошо, но я выбрал второе по разным причинам и реализовал решение на функциональном языке.

Результат довольно впечатляющий на мой взгляд, код очень лаконичен, элегантен и удобен для чтения. Я допускаю, что это может выглядеть немного странно, если вы никогда не программируете ни на что, кроме java / c #, но тогда это будет верно для всего, что не написано в java / c #.

Однако в какой-то момент я был буквально атакован сотрудником. После быстрого взгляда на мой экран он объявил, что код непонятен и что я не должен заново изобретать синтаксический анализ, а просто использовать стек и String.Split, как это делают все. Он издал много шума, и я не смог его убедить, частично потому, что меня застали врасплох, и у меня не было четких объяснений, частично потому, что его мнение было неизменным (без каламбура). Я даже предложил объяснить ему язык, но безрезультатно.

Я уверен, что дискуссия вновь появится перед руководством, поэтому я готовлю некоторые веские аргументы.

Вот первые несколько причин, которые приходят мне в голову, чтобы избежать решения на основе String.Split:

  • вам нужно много ifs для обработки особых случаев и вещей, которые быстро выходят из-под контроля
  • много жестко закодированных индексов массивов делает обслуживание болезненным
  • чрезвычайно трудно обрабатывать такие вещи, как вызов функции в качестве аргумента метода (например, add ((add a, b), c)
  • очень сложно предоставить значимые сообщения об ошибках в случае синтаксических ошибок (очень вероятно, что это произойдет)
  • Я все за простоту, ясность и избегая ненужных умных и загадочных вещей, но я также считаю, что ошибкой было бы заглушить каждую часть кодовой базы, чтобы ее мог понять даже флиппер бургера. Это тот же аргумент, который я слышу за то, что не использовал интерфейсы, не принимал разделение задач, не копировал и не вставлял код и т. Д. Для работы над программным проектом в конце концов требуется минимум технических знаний и желания учиться. (Я не буду использовать этот аргумент, поскольку он, вероятно, будет звучать оскорбительно, и начало войны никому не поможет)

Какие твои любимые аргументы против анализа Ктулху ? *

* конечно, если ты сможешь убедить меня, что он прав, я тоже буду совершенно счастлив


9
Похоже, ваш коллега добровольно готов сделать для вас проект DSL!
GrandmasterB

23
«Я не должен заново изобретать синтаксический анализ, а просто использовать стек и String.Split, как все», - черт побери, этот парень должен быть рад, что невежество не повредит ...
Майкл Боргвардт

4
Посоветуйте своему коллеге не возвращаться к этому обсуждению, если он не прочитает всю Книгу Дракона и не сдаст тест. В противном случае он не имеет права обсуждать что-либо, связанное с анализом.
SK-logic

4
извините, кто заново изобретал разбор?
Rwong

2
Я думаю, что моя голова буквально взорвется в следующий раз, когда я увижу кого-то, использующего слово «буквально» в переносном смысле.

Ответы:


33

Критическое различие между этими двумя подходами состоит в том, что тот, который он считает единственным правильным, является обязательным, а ваш - декларативным.

  • Ваш подход явно объявляет правила, то есть правила грамматики (почти) непосредственно кодируются в вашем коде, а библиотека синтаксического анализатора автоматически преобразует необработанный ввод в анализируемый вывод, одновременно заботясь о состоянии и других вещах, с которыми трудно справиться. Ваш код написан в пределах одного уровня абстракции, который совпадает с проблемной областью: анализ. Разумно предположить правильность parsec, что означает, что единственное место для ошибки здесь, это то, что ваше грамматическое определение неверно. Но опять же у вас есть полностью квалифицированные объекты правил, и они легко тестируются изолированно. Также стоит отметить, что зрелые библиотеки синтаксического анализатора поставляются с одной важной функцией: отчет об ошибках. Достойное исправление ошибок при разборе не было тривиальным. В доказательство я использую PHP parse error, unexpected T_PAAMAYIM_NEKUDOTAYIM: D

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

Ирония заключается в том, что правильность парсера, написанного с вашим подходом, относительно легко доказать. В его случае это практически невозможно.

Существует два способа конструирования программного обеспечения: один из них заключается в том, чтобы сделать его настолько простым, чтобы в нем явно не было недостатков, а другой способ - сделать его настолько сложным, чтобы в нем не было явных недостатков. Первый способ гораздо сложнее.

CAR Hoare

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

В конце концов, однако, вопрос: кто будет поддерживать его? Если это ты, то это твой звонок, независимо от того, что кто-нибудь говорит. Если это будет он, то есть только две возможности: найти способ заставить его понять библиотеку синтаксического анализатора или написать для него императивный анализатор. Я предлагаю вам сгенерировать его из структуры вашего парсера: D


Отличное объяснение разницы между двумя подходами.
smarmy53

6
Вы очевидно связали с TVTropes для Программистов. Прощай, день ...
Изката

10

Грамматика синтаксического анализа выражения (например, подход синтаксического анализатора Packrat) или комбинатор синтаксического анализа не переизобретают синтаксический анализ. Это хорошо зарекомендовавшие себя методы в мире функционального программирования, и в правильных руках он может быть более читабельным, чем альтернативы. Несколько лет назад я видел довольно убедительную демонстрацию PEG в C #, которая фактически сделала бы его моим первым средством для относительно простых грамматик.

Если у вас есть элегантное решение с использованием комбинаторов синтаксического анализатора или PEG, это должно быть относительно легко продать: его достаточно расширяемо, обычно относительно легко читать, когда вы преодолеваете страх функционального программирования, и иногда легче читать, чем обычный генератор синтаксического анализатора Инструменты предлагают, хотя это очень сильно зависит от грамматики и уровня опыта, который вы имеете с любым набором инструментов. Также довольно легко писать тесты для. Конечно, есть некоторые грамматические неоднозначности, которые могут привести к довольно ужасной производительности синтаксического анализа в наихудших сценариях (или большому потреблению памяти с помощью Packrat), но средний случай довольно приличный, и на самом деле некоторые грамматические неоднозначности лучше обрабатываются с помощью PEG, чем LALR, так как Я перезвоню.

Использование Split и стека работает с некоторыми более простыми грамматиками, чем PEG, или может поддерживать, но весьма вероятно, что со временем вы либо будете плохо изобретать рекурсивный спуск, либо у вас будет сложный набор поведений, которые вы помощь в представлении за счет крайне неструктурированного кода. Если у вас есть только простые правила токенизации, это, вероятно, не так уж и плохо, но по мере того, как вы добавляете сложность, это, вероятно, будет наименее поддерживаемым решением. Вместо этого я бы взял генератор парсера.

Лично мое первое желание при создании DSL было бы использовать что-то вроде Boo (.Net) или Groovy (JVM), поскольку я получаю всю мощь существующего языка программирования и невероятную настраиваемость, создавая макросы и простые настройки. в конвейер компилятора, без необходимости реализовывать утомительные вещи, которые я бы закончил делать, если бы начал с нуля (циклы, переменные, объектная модель и т. д.). Если бы я был в магазине, занимающемся разработкой для Ruby или Lisp, я бы просто использовал идиомы, которые имеют смысл (метапрограммирование и т. Д.)

Но я подозреваю, что ваша настоящая проблема связана с культурой или эго. Вы уверены, что ваш коллега в равной степени не испугался бы, если бы вы использовали Antlr или Flex / Bison? Я подозреваю, что «спорить» о вашем решении может быть проигрышной битвой; вам, возможно, придется тратить больше времени на более мягкий подход, в котором используются методы достижения консенсуса, а не на обращение в местные органы управления. Парное программирование и демонстрация того, как быстро вы можете вносить коррективы в грамматику, не жертвуя ремонтопригодностью, и делая «коричневую сумку» для объяснения методики, ее истории и т. Д., Могут пойти дальше 10 пунктов и «грубых вопросов и ответов» в некоторых случаях. конфронтационная встреча.


9

Я не очень хорошо разбираюсь в алгоритмах разбора и тому подобное, но я думаю, что доказательство пудинга в еде. Поэтому, если ничего не помогает, вы можете предложить ему реализовать синтаксический анализатор по-своему. затем

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

Чтобы тестирование было действительно справедливым, вы можете захотеть, чтобы оба решения реализовали один и тот же API и использовали общий тестовый стенд (или среду модульного тестирования, известную вам обоим). Вы оба можете написать любое количество и вид функциональных тестовых примеров и убедиться, что его собственное решение проходит все из них. И, конечно, в идеале ни один из вас не должен иметь доступ к реализации другого до истечения срока. Решающим тестом будет перекрестное тестирование обоих решений с использованием набора тестов, разработанного другим разработчиком.


Эта великолепная идея! Было бы также легко использовать инфраструктуру модульного тестирования.
smarmy53

1
+1 за то, что коллега выполнил сплит-версию ... Оператору было поручено создать его, поэтому он, скорее всего, должен будет его поддержать, а не коллега. Просто предложить ему это на вершине его другой работы может быть достаточно, чтобы снять его со спины.
Изката

7

Вы задали этот вопрос так, как будто у вас есть технический вопрос, но, как вы, вероятно, уже знали, здесь нет технического вопроса. Ваш подход намного лучше взлома чего-либо на уровне персонажа.

Настоящая проблема заключается в том, что ваш (предположительно более опытный) коллега неуверен и чувствует угрозу от ваших знаний. Вы не будете убеждать его техническими аргументами ; это только сделает его более оборонительным. Вместо этого вам придется найти какой-то способ облегчить его страхи. Я не могу предложить много предложений, но вы можете попытаться показать его уважение к унаследованному коду.

Наконец, если ваш менеджер согласен с его конкретными техническими аргументами и откажется от вашего решения, то я думаю, что вам придется искать другую должность. Очевидно, что вы были бы более ценным и более ценным в более сложной организации.


Вы правы, я уже знал, что мой подход превосходен, однако я не смог дать хорошее, убедительное объяснение - это техническая информация, которую я ищу. Согласованная «человеческая взаимодействие» сторона проблемы так же важна, как и техническая (если не больше).
smarmy53

4

Я буду краток

Разобрать путь Ктулху сложно. Это самый простой и самый убедительный аргумент против этого.

Это может сделать трюк для простых языков; скажем, обычные языки. Это, вероятно, не будет проще, чем регулярное выражение.

Это также может помочь немного более сложным языкам.

Тем не менее, я хотел бы увидеть синтаксический анализатор Cthulhu для любого языка с вложенностью или просто с «существенным состоянием» - математическими выражениями или вашим примером (вызовы вложенных функций).

Представьте, что произойдет, если кто-то попытается использовать синтаксический анализатор для такого (нетривиального контекстно-свободного) языка. Если он достаточно умен, чтобы написать правильный синтаксический анализатор, я бы поспорил, что во время кодирования он «обнаружит» сначала токенизацию, а затем рекурсивный разбор спуска - в некоторой форме.

После этого все просто: «Эй, слушай, ты написал что-то, что называется парсером рекурсивного спуска! Знаете ли вы, что оно может быть сгенерировано автоматически из простого описания грамматики, как регулярные выражения?


Короче говоря:
единственное, что может помешать кому-либо использовать цивилизованный подход, - это его незнание.


1

Возможно, работа над хорошей семантикой DSL также важна (синтаксис имеет значение, но также и семантика). Если вы не знакомы с этими проблемами, я бы посоветовал вам прочитать некоторые книги, такие как Pragmatics (by Scott) Programming Languages (M. Scott) и Christian Queinnec. Лисп в маленьких кусочках . Издательство Кембриджского университета, 1996.

Чтение последних работ на конференциях DSL, например, DSL2011, также должно помочь.

Разработка и реализация языка, специфичного для предметной области, сложна (и большая часть трудностей заключается не в разборе!).

Я не совсем понимаю, что вы имеете в виду, анализируя путь Ктулху ; Я думаю, вы просто хотите разобраться каким-то странным образом.


Хорошие ссылки. Что касается Ктулху, извините, я забыл ссылку. Это ссылка на классическую статью codinghorror : codinghorror.com/blog/2009/11/parsing-html-the-cthulhu-way.html . Я обновил оригинальный пост.
smarmy53
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.