Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ШПОРЫ_ТЯП.docx
Скачиваний:
1
Добавлен:
01.03.2025
Размер:
1.63 Mб
Скачать
  1. Семантический анализ. Не-контекстно-свободные характеристики языков.

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

При этом первая программа компилируется и выполняется, а вторая приводит к появлению сообщений об ошибках.

Первая программа корректна в С, а вторая — нет. Другими словами, появление в программе переменных подразумевает, что в каком-то месте программы должны быть их объявления. Следовательно, существуют ограничения на способ производства порождения. Контекстно-свободные грамматики не имеют механизма определения таких ограничений, следовательно, не могут применяться для точного определения, что составляет программу на С. В то же время контекстно-свободные грамматики можно использовать для определения расширенного множества всех программ на С, расширенного множества всех корректных программ, а также расширенного множества всех программ, некорректных вследствие не­контекстно-свободных сбоев, подобных отсутствию объявления переменных.

Проиллюстрируем другую категорию не-контекстно-свободных дефектов на следующем примере.

main() {

int first;

int second[5] = {3,4,6,1,8};

first = second;

printf(“%d”, first);

}

Очевидно, что проблема этой программы заключается в несовместимости типов по обе стороны оператора присваивания:

first = second;

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

int p = 4.3;

real x = 2;

int x = ‘a’;

int x = NULL;

В таких зыках как Ada и ALGOL 68, типы трактуются строже, поэтому неявные преобразования либо вообще отсутствуют, либо их очень мало. Это связано с тем, что при таком подходе в процессе компиляции будет обнаружено большинство программных ошибок. Такие языки называются языками со строгим контролем типов. Другие языки имеют не статические, а динамические типы, когда тип величины неизвестен в процессе компиляции и должен определяться уже во время выполнения программы. Это означает, что во время выполнения программы также должны осуществляться преобразования типов, для чего следует сгенерировать код еще в процессе компиляции.

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

int bigger(int no1, int no2) {

if(nol > no2) return nol;

else return no2;

}

main() {

int first, second;

first = 4;

second = 5;

second = bigger (first);

printf ("%d", second);

}

Функция bigger определена с двумя параметрами, но из main она вызывается только с одним параметром, что приводит к выдаче следующей ошибки в процессе компиляции.

Error. Too few parameters in call to ‘bigger’ in function main

Типы параметров в вызове функции также должны соответствовать типам в объявлении. В языке С, разумеется, изменение типов между int, float и char осуществляется автоматически. В то же время несоответствие между параметром типа int в описании функции и массивом чисел типа int, используемым в качестве данного параметра функции, будет ошибочным.

Подобного рода ошибки могут возникнуть в индексах массивов, как это показано на следующем примере.

main() {

int number;

int matrix [3][2] = {{4,5},{8,9}, {11,12}};

number = matrix [1,1,1];

printf (“%d”, number);

}

Здесь к массиву matrix, определенному как двумерный, обращаются как к трехмерному массиву.

Несколько иная разновидность контекстно-свободных дефектов возникает в связи с определенными в языке правилам области видимости. Проиллюстрируем это на следующем примере.

int p = 7;

void fun1() {

int p = 4;

printf (“локальное р = %d\n”,p); {

int p = 11;

printf(“более локальное р = %d\n”, p);

}

}

void fun2() {

printf(“глобальное р = %d\n”, p)

}

main() {

fun1();

fun2();

}

Ошибки времени компиляции, связанные с рассмотренными выше примерами, не могут выявляться программой синтаксического анализа, основанной исключительно на контекстно-свободной грамматике. Иначе говоря, синтаксический анализатор, построенный с помощью YACC, не сможет обнаружить ни одной из подобных ошибок, обратившись к “пустой” позиции (соответствующей синтаксической ошибке) в созданной YАСС таблице LALR(1)-анализа. Таким образом, для обнаружения этих ошибок потребуются дополнительные проверки. Тот факт, что данные типы ошибок не приводят к появлению ошибочной записи в таблице синтаксического анализа, значительно упрощает восстановление после них, поскольку для продолжения анализа не нужно делать каких-то предположений об ошибках программиста. Операция, выявляющая ошибки, может просто сообщать о проблеме и продолжить синтаксический анализ. Обычно программисту проще произвести подробную диагностику на предмет наличия не-контекстно-свободных ошибок, чем найти контекстно-свободные ошибки.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]