Анимация
JavaScript
|
Главная Библионтека Г л а в а 12. ИСПРАВЛЕНИЕ И ДИАГНОСТИКА ОШИБОК В этой главе мы покажем, правда, с некоторым запозданием, как компилятор должен обращаться с программами, в которых имеются ошибки программирования. Разработчик компилятора обязан с самого начала предусмотреть такую возможность, поскольку многие программы (если не подавляющее большинство), представляемые компилятору, содержат по меньшей мере одну ошибку. До сих пор мы старались не касаться вопросов, связанных с обнаружением и исправлением ошибок, но сейчас хотим подчеркнуть, как важно для компилятора правильно реагировать на ошибки, чтобы не снизилось его практическая ценность. Конечно, мы рассмотрим здесь только те ошибки, которые могут быть обнаружены компилятором или привести к ошибке, во время прогона. Мы не будем обсуждать формальные методы доказательства правильности программ, и ошибки программирования, которые не влияют на действенность программы (такие, как написание « - » вместо « + »), останутся необнаруженными во время компиляции или во время прогона. Если программа, представленная компилятору, строго говоря, написана не на исходном языке, «недружелюбный» компилятор может просто проинформировать пользователя об этом, не указав, где могла произойти ошибка. Большинство пользователей не удовлетворит (и вполне оправданно) такой подход, поскольку они ожидают от компилятора 1) точного указания, где находится (первая) ошибка программирования; 2) продолжения компиляции (или, по крайней мере, анализа) программы после обнаружения первой ошибки с целью обнаружения остальных. Как мы увидим позднее (разд. 12.4.), требование (а) не всегда можно выполнить, поскольку результат ошибки программирования может сказаться не обязательно в том месте, где ошибка была допущена. Что касается требования (б), это тоже непростая задача. Если ход выполнения программы стал недействительным, то неясно, какое действие должен предпринять компилятор, чтобы продолжить ее анализ. Существует средства включения, исключения или замены символов в ходе выполнения программы; различные методы исправления ошибок обсуждаются в разд. 12.4. 12.1. ТИПЫ ОШИБОК Ошибки могут возникать в программе по ряду причин: 1. Программист не совсем понимает тот язык, на котором он пишет, и использует неправильную конструкцию программы. 2. Программист недостаточно осторожен в применении конструкции языка и забывает описать идентификатор или согласовать открывающую скобку с закрывающей и т.д. 3. Программист неправильно пишет слово языка или какого-либо другого символа в программе. Ошибки, обусловленные этими тремя факторами, по-разному обнаруживаются компилятором. Ошибки первого типа, очевидно, вылавливаются синтаксическим анализатором, и генерируется сообщение с указанием того символа, на котором поток программы стал недействительным. Ошибки второго типа распадаются на две категории. Те, которые относятся к контекстным средствам языка, например, отсутствие описания идентификатора, обнаруживаются одной из процедур выборки таблицы символов, вызываемой анализатором во время синтаксического анализа. Такие ошибки, как недостающие скобки, могут быть обнаружены самим анализатором либо (в случае согласования скобок) во время специальной фазы или перехода, предназначенного для согласования скобок (см. разд. 7.1.). Ошибки третьего типа обычно выявляются в процессе лексического анализа, хотя в случае неправильного написания идентификатора вновь потребуется выполнить одно из действий с таблицей символов. Существуют ошибки еще одного типа, когда программа пытается выполнить деление на нуль, считывание за пределами файла или что-либо подобное. Они называются ошибками времени прогона, и обычно их нельзя обнаружить в процессе компиляции. Далее в следующей главе мы рассмотрим, как обнаруживаются различные ошибки - лексические, ошибки согласования, синтаксические, контекстные и ошибки прогона - и как о них сообщается программисту в понятой для него форме. Мы также обсудим методы исправления этих ошибок. 12.2. ЛЕКСИЧЕСКИЕ ОШИБКИ Задача лексического анализатора - сгруппировать последовательности литер в символы исходного языка. При этом он работает исключительно с локальной информацией. В его распоряжении имеется очень небольшой объем памяти, и он не осуществляет никакого предварительного просмотра. Следовательно, те случаи, когда лексический анализатор окажется не в состоянии сгруппировать какие-либо последовательности литер в символы, будут свидетельствовать об ошибке. Лексические ошибки могут быть разными: 1. Одна из литер оказывается недействительной, т.е. она не может быть включена ни в один из символов. В таком случае лексический анализатор либо игнорирует эту литеру, либо заменяет ее какой-либо другой. 2. При попытке собрать слово языка, например выделение слово в Алголе 60, выясняется, что последовательность букв не соответствует ни одному из этих слов. Тогда можно воспользоваться алгоритмом подбора слова, чтобы идентифицировать слово, имеющее несколько иное написание. Если алгоритм не сработает, то, допуская, что слова языка выделяются кавычками, как в BEGIN, стоит проверить, не начинается ли последовательность букв в какого-либо слова языка. Если это имеет место, то подстановка одной или двух кавычек сделает нашу последовательность действительной. Например, в Алголе 68 у лексического анализатора возникает затруднение с а) begin integer или б) realab Однако подстановкой двух кавычек в (а) и одной кавычки в (б): а) begin integer б) real ab мы решаем проблему. В Алголе 68 существует бесконечное число возможных выделенных слов, и «неправильно написанное» выделенное слово будет принято лексическим анализатором. 3. Собирая число, лексический анализатор может испытывать затруднения в общении с такой последовательностью, как 42.34.41. Возможное решение здесь -допустить, какая бы ошибка ни была, что предполагалось одно число, и предупредить программиста, что вместо этого числа принято конкретное значение по умолчанию. 4. Отсутствие в программе какой-либо литеры часто приводит к тому, что лексический анализатор не может отделить один символ от другого. В некоторых случаях лексический анализатор принимает два символа за один. Например, если в А + В опущен знак «+», то лексический анализатор просто пропустит идентификатор АИ, не оповещая об ошибке на этой стадии. Однако отсутствие знака «+» в 1 + А вызовет ошибку, хотя лексический анализатор не будет знать, к какой группе он должен отнести 1 А - к недопустимым идентификаторам, недопустимым числам или еще к чему-либо. Иногда лучше разбить недопустимые последовательности на более короткие допустимые. Так, 1А станет числом 1, за которым следует идентификатор А.Тогда синтаксический анализатор обнаружит ошибку. 5. Обычно проблему для лексического анализатора создают недостающие кавычки строки, как, например, в string food : = "BREAD Следовательно, в остальной части программы открывающие и закрывающие кавычки могут быть перепутаны, т. е. то, что заключено в кавычки, окажется вне кавычки и наоборот. В результате почти наверняка на нас лавиной обрушится сообщения об ошибках. «Смышленый» анализатор смог бы обнаружить неправдоподобную последовательность литер внутри кавычек (например, end) и исправить ошибку, поставив в нужном месте кавычки. Подобная проблема возникает в связи с комментариями в Алголе 60 и Алголе 68 и в связи с форматами в Алголе 68. Эти проблемы могли бы быть не такими серьезными, если бы разработчики языков использовали разные символы для обозначения начала и конца комментариев. Существуют компиляторы, которые завершают свою работу тем проходом, где обнаружена ошибка. Однако если задаться целью обнаружить максимальное число ошибок, то желательно, чтобы компилятор продвинулся в своей работе как можно дальше, если это вообще возможно. Поэтому лексический анализатор должен передать следующему проходу (или фазе) последовательность действительных символов (а необязательно действительную последовательность символов). Для правильных в лексическом смысле программ это не представляет трудностей. При лексически неправильных программах придется или игнорировать последовательность литер, или включить их, может понадобиться изменить написание символов, а строки разбить на действительные символы. Игнорирование последовательностей литер, пожалуй, самое простое средство, но почти наверняка оно приведет позднее к возникновению синтаксических ошибок. Методы исправления лексическим анализатором недопустимых входов зависит от обстоятельств, и на практике их выбор почти всегда определяется компилируемым языком и тем, как программисты предполагают им воспользоваться. Имеет смысл разработать такой вариант, чтобы лексический анализатор, когда он не в состоянии распознать какой-либо действительный символ или символы из строки литер, выдавал специальный символ «не знаю», который интерпретировался бы синтаксическим анализатором, имеющим обычно список действительных символов продолжения, по своему разумению. 12.3. ОШИБКИ В УПОТРЕБЛЕНИИ СКОБОК Ошибки, связанные с употреблением скобок, обнаружить относительно легко. Некоторые компиляторы имеют фазу, предшествующую полному синтаксическому анализу, на который проводится согласование скобок (или их идентификация). Если применяются скобки только одного типа, например «(« и «)», то проверку можно 0 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 48 49 50 51 52 53 [ 54 ] 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |