Формальное описание синтаксиса и однозначность разбора несомненно важны для автоматической обработки текста программы. Однако, не менее важно, чтобы текст программы воспринимался человеком[6]. А человеку свойственно ошибаться. Мы будем говорить, что синтаксис языка устойчив к ошибкам, если опечатки, слабо изменяющие текст программы, приводят к синтаксическим ошибкам. Иными словами:
1. Случайные ошибки и опечатки должны обнаруживаться;
2. Разные конструкции должны визуально различаться.
Поскольку данное определение неформально и весьма расплывчато, продемонстрируем понятие устойчивости на нескольких примерах. Рассмотрим следующий оператор цикла на языке C:
for (i = 0; i<N-1; i++);
A[i] = A[i+1];
Очевидно, что здесь имелось ввиду, что оператор присваивания A[i]=A[i+1]; выполнится N-1 раз. Однако, «случайная» точка с запятой в конце первой строки кардинально меняет структуру, и оператор присваивания оказывается вне цикла и, соответственно, цикл выполнит N-1 «холостую» итерацию, после чего пересылка будет выполнена лишь один раз. Заметим, что если бы оператор цикла имел завершитель, то такой ситуации не возникло, как, например, в языке Алгол 68:
|
|
.for i.from 0.to N-2.do
A[i]:= A[i+1]
.od
Ещё один пример на языке C - синтаксически корректный фрагмент:
float x;
for (float x=0.0; x<=1,2; f=+0.1)
s = + f(x);
Здесь первая проблема заключается в «случайной» запятой в условии x<=1,2, хотя, очевидно, подразумевалось x<=1.2. Синтаксис последовательного выражения языка C делает это условие эквивалентным x<=2, что приведёт к тому, что цикл будет выполнен лишних 8 раз. Вторая ошибка связана с устаревшей формой присваивания =+, означающего «увеличить на». «Случайный» пробел после равенства делает равенство обычной пересылой, а + - унарной операцией. В результате оператор стал эквивалентным s=f(x). Новая форма совмещённого присваивания += не имеет этого недостатка.
Рассмотрим пример на языке C, где присваивается переменная y, а источник присваивания достаточно длинный, чтобы разумно было записать его на двух строчках:
y = a[0]/2 + a[1]//3 + a[2]/5 + a[3]/7
+ a[4]/11 + a[5]/13 + a[6]/17 + a[7]/19;
«Случайно» удвоенный символ деления / превращается в начало комментария, заканчивающегося концом строки, но оператор остаётся синтаксически правильным и эквивалентным
y = a[0]/2 + a[1]
+ a[4]/11 + a[5]/13 + a[6]/17 + a[7]/19;
Пример на языке Фортран, где цикл DO имеет вид
DO переменная-цикла = начальное-значение [, шаг ], конечное значение
В следующем операторе «случайная» точка вместо запятой в заголовке цикла
10 DO I = 1.2,N
S = S * I
CONTINUE
приводит к тому, что шаг цикла становится равным не 2, а умолчательному, равному 1.
Последний пример – на языке Алгол 68:
|
|
for i from 10.to N.do
print(“ “)
od;
Здесь сыграли роль три решения, заложенных в синтаксисе языка. Во-первых, ключевые слова отличаются от идентификаторов точкой в начале. Во-вторых, внутри идентификаторов для лучшей читаемости можно использовать пробелы. В-третьих, в цикле.for можно опускать начальное значение, по умолчанию равное 1. В результате «случайно» забытая точка перед ключевым словом.from делает цикл эквивалентным
.for ifrom10.from 1.to N.do
print(“ “)
.od;
Таким образом, устойчивость синтаксиса языка также служит для раннего обнаружения ошибок: гораздо дешевле и безопаснее выявить ошибку на этапе синтаксического анализа, чем откладывать это на неопределённый срок с непредсказуемыми последствиями.