Основным назначением синтаксиса языка программирования является обеспечение системы обозначений для обмена информацией между программистом и процессором языка программирования.
Язык строится из синтаксических структур (конструкций).
Выбор конкретных синтаксических структур, тем не менее, только в небольшой степени определяется необходимостью передачи конкретной информации.
Например, тот факт, что значение некоторой переменной принадлежит к типу вещественных чисел, можно выразить достаточно многими способами: в языке С - это явным описанием типа, в FORTRAN - неявным соглашением о начальном символе переменной, в BASIC - вообще допускается универсальный тип данных Variant.
При разработке деталей синтаксиса по большей части исходят из второстепенных соображений, которые не связаны напрямую с основной задачей передачи информации процессору языка.
Выделим четыре группы второстепенных факторов: легкость чтения, написания и трансляции, а также однозначность
Легкость чтения. Программа легка для чтения, если ее структура ее алгоритма и представленные в ней данные становятся ОЧЕВИДНЫМИ при просмотре ее текста. Это как шаг к самодокументируемой программе. Факторы, способствующие легкости чтения – это естественные форматы операторов, структурированные операторы, свободное использование ключевых и необязательных слов, возможность встраивания в текст программы комментариев, неограниченность длины идентификаторов, мнемонические символы операций, запись программы в свободном формате и полный набор объявлений используемых данных. Примером плохой удобочитаемости текста программы – на языке APL, наиболее яркое стремление к удобочитаемости – в языке COBOL, но за счет удобства записи и трансляции.
Улучшает чтение программы такой синтаксис языка, когда синтаксические различия отражают лежащие в его основе семантические особенности. Отсюда следует, что программные конструкции, имеющие схожее назначение, должны выглядеть одинаково, а те конструкции, которые выполняют различные действия, должны и выглядеть по разному. Например, в APL и SNOBOL предусмотрен только один формаи оператора. В связи с этим различия между оператором присваивания, вызовом подпрограммы, оператором goto, выходом из подпрограммы, многовариантным условным ветвлением и многими другими распространенными программными структурами синтаксически отражены только различиями в одном или нескольких символах операторов внутри сложного выражения. Более того, такая незначительная ошибка (типа неправильное написание одного символа в операторе) может вообще исказить смысл, хотя сам оператор может быть синтаксически правильным.
В целом, чем больше разнообразие синтаксических конструкций, тем проще отобразить в структуре программы различные семантические структуры, соответствующие этим конструкциям. Нельзя считать нормальным, когда неправильное написание только одного символа в операторе может совершенно исказить смысл оператора ( LISP – несоответствие числа открывающих и закрывающих скобок приводит к разным результатам).
Легкость написания. Легкость написания обеспечивается краткостью и однородностью синтаксических конструкций (СК) – хотя это входит в противоречие с удобочитаемостью, где именно разнообразие СК позволяет выделять семантические структуры. Неявные синтаксические соглашения, которые позволяют не определять операции и типы переменных, упрощают написание программы и укорачивают запись, но усложняют чтение. Но есть и такие средства, которые способствуют достижению обеих целей: использование структурированных операторов, простых естественных форматов операторов, мнемонических символов операций и неограниченных по длине идентификаторов.
Неявные синтаксические соглашения, которые позволяют не определять операции и типы переменных, упрощают написание программ и укорачивают запись, но значительно усложняют чтение программ.
Легкость верификации. Контроль правильности и концепции корректности. С легкостью чтения и написания программ связана концепция корректности программ, также называемая верификацией программ. Многолетний опыт программирования позволяет утверждать, что разобраться в любом отдельно взятом операторе относительно просто, но сам процесс написания правильной программы чрезвычайно сложен. Следовательно, нужны методики, позволяющие математически строго доказывать корректность написанных программ.
Легкость трансляции. Третья цель противоречит первым двум (чтения, написания), так как первые цели для программиста, а третья – для транслятора. Ключевым моментом для упрощения трансляции является РЕГУЛЯРНОСТЬ структуры программы. LISP является примером языка, программы которого не являются легкими для чтения и записи, но при этом исключительно просты для трансляции. Благодаря регулярности синтаксиса вся синтаксическая структура любой LISP – программы может быть описана несколькими простыми правилами. Сложность трансляции возрастает по мере увеличения количества специальных синтаксических конструкций.
Например, очень сложно транслировать программы, написанные на языке COBOL, из-за большого количества допустимых форм операторов и объявлений, хотя этот язык нельзя считать семантически сложным.
Отсутствие неоднозначности. Неоднозначность является центральной проблемой любого языка. Определение языка должно в идеале обеспечить однозначность любой синтаксической конструкции, которую программист может написать. Неоднозначная конструкция допускает два и более толкований. Неоднозначность возникает обычно не в структуре отдельно взятого элемента программы, а при взаимодействии различных структур.
Например, и Pascal, и ALGOL допускают две различные формы условных операторов:
if булево_выражение then оператор1 else оператор2
if булево_выражение then оператор1
При интерпретации каждого из этих операторов смысл их ясно определен. Но если две эти формы объединяются вместе и в качестве первого оператора берется вторая форма условного оператора, то получается следующая конструкция, называемая условным оператором с повисшим else:
if булево_выражение1 then if булево_выражение2 then оператор1 else оператор2.
Эта структура является неоднозначной, так как неясно, какая из представленных на рис. 3.1.а, б последовательности вычислений имеется в виду.
Рис.3.1.а. Две интерпретации условного оператора
Рис.3.1.б. Две интерпретации условного оператора
Другой пример из FORTRAN: ссылка на элемент массива A(I,J) и в тоже время вызов функции имеет такой же синтаксис.
Неоднозначность с условными операторами была снята изменением синтаксиса – добавлена пара разделителей begin … end, отделяющая вложенный условный оператор от внешних конструкций:
if булево_выражение1 then begin if булево_выражение2 then оператор1 end else оператор2
if булево_выражение1 then begin if булево_выражение2 then оператор1 else оператор2 end
Для разрешения неоднозначности типа ссылки A(I,J) – было принято следующее решение: считать ссылку обращением к функции, если к этому моменту массив А не был объявлен. Поскольку каждый массив должен быть описан до того, как он будет использован в программе, то транслятор имеет возможность проверить, был ли объявлен массив, на который имеется ссылка. Если объявления массива не обнаружено, то транслятор принимает решение, что имеется ввиду обращение к функции A. Правильность этого предположения можно окончательно проверить во время загрузки, когда все внешние функции будут собраны в исполняемую программу. Если загрузчик не обнаружит функцию A, то он объявит о наличие ошибки. В Pascal разрешение неоднозначности выполняется синтаксической конструкцией: квадратные скобки A[I,J] используются для ссылки на элемент массива, а круглые скобки A(I,J) – для обращения к функции.
Похожие статьи: