11.4.  Синтаксические ошибки

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

Ошибка в виде пропуска или неправильного употребления, допущенная на более раннем этапе, может проявиться совсем в другом месте программы. Это можно проиллюстрировать следующим примером.

while x>y ; begin something end.

Никакого сообщения об ошибке при встрече «;» на данной стадии синтаксический анализатор не выдаст. Последствия ее могут появиться на более поздней фазе анализа.

Сообщив о синтаксической ошибке, анализатор в большинстве случаев постарается продолжить разбор. Для этого ему понадобится исключить какие-либо символы, включить какие-либо символы или изменить их. Существует ряд стратегий исправления ошибок. Практически все они хорошо срабатывают в одних случаях и плохо – в других. «Хорошая» стратегия заключается в том, чтобы обнаружить как можно больше синтаксических ошибок и генерировать как можно меньше сообщений в связи с каждой синтаксической ошибкой. Обычно наилучшими методами являются методы, зависимые от языка, т. е. от знания исходного языка и от того, как он употребляется.

11.5.  Методы исправления синтаксических ошибок

Режим переполоха

НЕ нашли? Не то? Что вы ищете?

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

Исключение символов

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

c := d+3; end,

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

Включение символов

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

end begin

никогда не будет допустимой. Однако включение «;» между end begin позволит анализатору продолжить работу.

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

Правила для ошибок

Одним из способов исправления некоторых типов синтаксических ошибок заключается в расширении синтаксиса языка за счет включения в него программ, содержащих типичные ошибки. Это не значит, что ошибки пройдут незамеченными, так как в грамматику могут быть включены сообщения о них. Но анализатор не будет считать такой вход недопустимым и не потребует никаких исправлений. Так можно обращаться, например, с ошибками типа «;» перед end или пропуск «;». Дополнительные правила, включенные в грамматику, обычно называются правилами для ошибок. Они неизбежно приводят к увеличению грамматики, и поэтому включать их следует только для наиболее часто встречающихся ошибок программирования. При этом надо следить за тем, чтобы при включении этих правил грамматика не стала неоднозначной.

11.6.  Предупреждения

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

; do

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

11.7.  Сообщения о синтаксических ошибках

Всякий раз при обнаружении анализатором синтаксической ошибки должно печататься соответствующее сообщение. Например

SYNTAX ERROR IN LINE 22.

Или местоположение ошибки может описываться полнее

LINE 22 SYMBOL 4.

В любом случае пользователь может быть недоволен тем, что сообщение не вполне ясное, так как не указывается, в чем заключается ошибка программиста. На практике фактическая ошибка программирования могла произойти гораздо раньше, анализатор же сообщает об ошибке только тогда, когда ему встречается недопустимый символ. Если программист представляет анализатору программу, имеющую синтаксическую ошибку, компилятор, естественно, не сможет решить, какую программу программист должен был написать. Единственное, что компилятор смог бы сделать, это принять решение о «ремонте» на минимальном расстоянии, т. е. о ремонте, требующем минимальное число включений символов в текст программы и исключений из него, дающем синтаксически правильную программу. Цель ремонта – обеспечить анализатору условия для продолжения анализа программы.

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

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

11.8.  Контекстно-зависимые ошибки

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

IDENTIFIER xyz NOT DECLARED

TYPE NOT COMPATIBLE ASSIGNMENT

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

1)  его можно приводит к любому типу/виду;

2)  если значение типа sptype оказывается операндом, знак операции идентифицируется с помощью другого операнда, причем любая неоднозначность разрешается произвольно;

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

Не исключена и другая ошибка: в одном и том же блоке идентификатор описан дважды. Если возникает такая ситуация, то анализатор обычно генерирует сообщение

IDENTIFIER blank ALREDY DECLARED IN BLOCK

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

11.9.  Ошибки, связанные с употреблением типов

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

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47