Анимация
JavaScript


Главная  Библионтека 

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

каждая прикладная реализация Wednesday может вызвать сообщение об ошибке

IDENTIFIER Wednesday NOT DECLARED

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

1. Его можно приводит к любому типу /виду.

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

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

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

IDENTIFIER blank ALREADY DECLARED IN BLOCK

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

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

Правила, определяющие, где в программе возможно появление значений различных типов, в большинстве случаев не являются контекстно-независимыми. Если идентификатор описан таким образом, что ему могут присваиваться значения в виде целых чисел, то во многих языках попытка присвоить ему литерное значение будет считаться недопустимой. Для языков с явно выраженными типами (например, Паскаль, Алгол 68) такая ошибка обнаруживается во время компиляции с помощью таблицы символов. В Алголе 68 преобразование значении одного вида в другой осуществляется с помощью последовательности приведений (см. разд.10.4.. В тех случаях, когда в программе априорный вид значения не может быть приведен в соответствующий апостериорный вид, компилятор Алгола 68 выдает следующее сообщение:



MODE char CANNOT BE COERCED TO int

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

i:= c

где i есть вид ref int ,а с- вид char . Тогда более понятным становится сообщение

MODE NOT COMPATIBLE IN ASSIGNMENT

Как мы уже видели (в разд. 8.2), описания видов в Алголе 68 определяются частично контекстно-свободными правилами и частично контекстно-зависимыми. Например, следующие описания видов допустимы с точки зрения контекстно-свободных правил, но требованиям контекстно-зависимых правил не удовлетворяют:

Mode x = y Mode y = ref x

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

1) неправильное число индексов массива;

2) неправильное число параметров для вызова процедуры или функции;

3) несовместимость типа (или вида) фактического параметра в вызове с типом формального параметра;

4) невозможность определения знака операции по его операндам.

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

12.6. Ошибки, допускаемые во время прогона

Во время прогона в программах могут возникать ошибки, которые нельзя предусмотреть в процессе компиляции. Конечно, при компиляции можно обнаружить явное деление на нуль, например m/0. Но выражение m/n также может повлечь за собой деление на нуль (если n покажется нулем, когда будет вычислено выражение) и это нельзя (как правило) обнаружить во время компиляции. При прогоне возможны другие ошибки:

1) нахождение индекса массива вне области действия;

2) целочисленное переполнение (вызванное, например, попыткой сложить два наибольших целых чисел, допускаемых реализацией);

3) попытка чтения за пределами файла.

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



Разработчики языков обычно стараются предотвратить возможность возникновения ошибок, которые нельзя обнаружить до прогона [54]. Одно из решений - постараться дать исчерпывающую формулировку задачи, например результат деления чего- либо на нуль определить как нуль, выходящий за пределы области действия, индекс считать эквивалентным какому-нибудь значению в пределах области действия, при попытке чтения за пределами файла выполнять некоторое стандартное действие и т.д. В Коболе программист специфицирует действие, которое следует выполнять при достижении конца файла, и аналогичным образом он мог бы определять необходимые действия в случае других ошибок, встречающихся во время прогона.

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

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

12.7. ОШИБКИ, СВЯЗАННЫЕ С НАРУШЕНИЕМ ОГРАНИЧЕНИЙ

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

1) размер программ, которые можно скомпилировать;

2) число элементов в таблице символов или идентификаторов;

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