Первые радости

Начнем с синтаксиса (самого, казалось бы, простого аспекта, однако борьбой сним завершаются попытки очень многих). Несколько первых впечатлений. Во-первых,язык просто очень большой. Это означает, что синтаксические таблицы?—?составленные вручную или построенные каким-нибудь генератором распознавателей,вроде YACC, будут довольно велики, что, естественно, замедлит скоростьсинтаксического разбора.

Однако при внимательном анализе оказывается, что в языке имеется сравнительнобольшое число «микро»-регулярностей?—?часто повторяющихся устойчивыхпоследовательностей лексем. Например, пары пустых скобки: (), [],пустой список параметров (void), завершитель списка параметров ...)встречаются очень часто. После служебных слов if, switch, whileвсегда должна стоять левая круглая скобка, после break и continue?—?точка с запятой, а после слова goto располагаются идентификатор и точка сзапятой. Таких регулярностей набирается несколько десятков, так что еслирассматривать их как отдельные лексемы, объем синтаксиса заметно сокращается.Введение каждой такой "суперлексемы" экономит по крайней мере одно обращениесинтаксического анализатора к таблице разбора. Усложнение распознавателя лексем(сканера), вынужденного составлять суперлексемы из пар или троек обычныхлексем, при этом получается весьма незначительное; более того, если сканер вовремя одного вызова распознает, например, не только служебное слово switch,но и левую круглую скобку, идущую за ним, получится экономия и на числеобращений к сканеру!

Во-вторых, в синтаксисе есть неоднозначности. Это надо оценить: в Стандарте (!)языка программирования прямо написано, что некоторые конструкции можнотрактовать двояко?—?либо как объявление, либо как оператор! В несколькоупрощенном виде формулировка из стандарта выглядит так: "выражение, содержащеев качестве своего самого левого подвыражения явное преобразование типа, котороезаписано в функциональном стиле, может быть неотличимо от объявления, в которомпервый декларатор начинается с левой круглой скобки". Классический пример: чтотакое T(a); если T?—?некоторый тип? С одной стороны, это как быобъявление переменной с именем a, тип которой задан как T. С другой?—?конструкцию можно трактовать как преобразование типа уже объявленной где-торанее переменной a к типу T. Все дело в том, что в Си++ статусоператоров и объявлений полностью уравнен; последние даже и называютсяdeclaration-statements?—?операторы-объявления, то есть традиционныеоператоры и объявления могут записываться вперемежку. Все же радости с круглымискобками перекочевали в Си++ прямо из Си, в котором типы конструируются подобновыражениям, и тривиальное объявление можно задать либо как "int a;", либокак "int(a);". Все это понятно, но от этого не легче. И такой язык любятмиллионы программистов?! Мир сошел с ума. Яду мне, яду!..

Смысл правил разрешения неоднозначностей сводится, по существу, к поразительнойфразе, простодушно выведенной в "Зеленой книге": "если конструкция выглядит какобъявление, то это и есть объявление. В противном случае это оператор". Инымисловами, чтобы разрешить неоднозначность, следует рассмотреть всю конструкциюцеликом; фрагмент "T(a)" для анализа недостаточен?—?за ним сразу можетследовать либо точка с запятой, тогда выбор делается в пользу объявления, либо"что-то еще". Например, вся конструкция может выглядеть как "T(a)>m = 7;"или "T(a)++;"?—?это, конечно, операторы (точнее,операторы-выражения, в терминах стандарта). Ну а как понимать следующее:"T(e)[5];" или "T(c)=7;"? А это, будьте уверены, еще не самыеразительные примеры?—?загляните в разд. 6.8 Стандарта.

Человеку хорошо, он ко всему привыкает, рано или поздно он разберется, но какзаставить анализатор понимать эту чехарду? Пока он не доберется до точки сзапятой, он, в общем случае, ничего не сможет сказать о конструкции. Друзья, непишите объявления, которые невозможно отличить от операторов! Пожалейтекомпилятор, ему же тяжело! Кроме того, можно запросто ошибиться и самому…

Несколько дней прошли в бесплодных попытках выразить неоднозначности на входномязыке YACC. Выход был похоже, только в организации просмотра вперед, причем назаранее не известное количество лексем. Алгоритм разбора,заложенный в YACC, этого делать не умеет. В принципе известны и доступнысистемы, в которых заявлена подобная возможность, однако мы были ограниченытребованием: синтаксический анализатор писать на YACCе, более того, на еговерсии, сделанной в одном европейском университете… Пришлось пойти наухищрения и "сломать" классическую схему разбора: делать предварительный анализеще на уровне разбора лексем и, встретив левую скобку после имени типа (а ещепойди распознай, что идентификатор?—?имя типа, а не какой-то другой сущности!),"отменять" автоматический анализ и организовывать "ручной" перебор последующихлексем, складывая их про запас в буфер.

Спасибо, в "Зеленой книге" подсказали схему такого анализа. Не знаем, как иблагодарить, сами бы ни за что не придумали…









 


Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Верх