Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Хорошие идеи взгляд из Зазеркалья.doc
Скачиваний:
7
Добавлен:
04.11.2018
Размер:
151.55 Кб
Скачать
          1. Расширяемые языки

Фантазии компьютерных ученых в 1960-е гг. не знали границ. Вдохновленные успехом в направлениях автоматического синтаксического анализа и генерации анализаторов, некоторые исследователи предложили идею гибких или, по крайней мере, расширяемых языков, представляя, что программе будут предшествовать синтаксические правила, которыми будет руководствоваться общий синтаксический анализатор при ее анализе. Более того, синтаксические правила можно было рассыпать повсюду в тексте. Например, можно было бы использовать какую-нибудь причудливую форму оператора for, специфицируя даже разные варианты одного и того же понятия в разных частях одной программы.

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

          1. Древовидные таблицы символов

Компиляторы конструируют таблицы символов. Компилятор наращивает таблицы при обработке объявлений и производит в них поиск при обработке операторов. Для языков, допускающих вложенные области видимости, каждая область видимости представляется собственной таблицей.

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

В программах, написанных 30-40 лет назад, большинство переменных объявлялось на глобальном уровне. Поскольку их было много, было оправданным и применение древовидной структуры. Однако, тем временем, возрастал скептицизм по отношению к практике использования глобальных переменных. В современных программах не используется много глобальных переменных, и в этом случае древовидные таблицы вряд ли являются подходящими.

Современные программные системы состоят из многих модулей, каждый из которых, вероятно, содержит несколько глобальных переменных, но не сотни. В ранних программах многие глобальные переменные распределялись по многочисленным модулям, и доступ к ним производился не по одиночному идентификатору, а по комбинации имен M.x, определяющей исходный путь поиска.

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

          1. Использование неподходящих инструментальных средств

Хотя очевидно, что использование неподходящих инструментов является плохой идеей, часто мы обнаруживаем неадекватность инструментального средства только после затраты существенных усилий на его построение и понимание. К сожалению, затрачивание этих усилий придает инструменту воспринимаемую ценность (perceived value) независимо от его функциональной ценности. Это произошло с моей группой, когда мы реализовывали в 1969 г. первый компилятор языка Pascal.

Инструментальными средствами, доступными для написания программ, являлись ассемблер и компиляторы языков Fortran и Algol. Компилятор Algol был настолько плохо реализован, что мы опасались на него полагаться, а использовать для работы язык ассемблера нам было стыдно. Оставался только Fortran.

Наш наивный план состоял в том, чтобы использовать Fortran для конструирования компилятора основного подмножества языка Pascal, который после завершении работы был бы оттранслирован на Pascal. Затем мы собирались использовать классическую технику раскрутки (bootstrapping) для завершения и совершенствования компилятора.

Перед лицом действительности наш план потерпел крах. Когда мы завершили первый шаг - затратив около одного человеко-года труда, - оказалось, что трансляция Fortran-кода на Pascal является невозможной. Эта программа настолько определялась возможностями языка Fortran (вернее, отсутствием каких-либо возможностей), что единственным доступным для нас выходом было написание компилятора заново. Поскольку в языке Fortran не поддерживались указатели и записи, нам пришлось втискивать таблицы символов в неестественную форму массивов. В языке Fortran не было и рекурсивных подпрограмм. Поэтому нам пришлось использовать сложный таблично-управляемый, восходящий метод синтаксического анализа с использованием синтаксиса, представленного массивами и матрицами. Коротко говоря, для использования преимуществ языка Pascal требовалось полностью переписать и реструктурировать компилятор.

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

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

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

После этого эксперимента у нас появилось смутное понимание причин, по которым сообщество инженерии программного обеспечения не признает преимуществ применения высокоуровневого, типизированного языка вместо языка C.