- •Вопрос 4. Деревья синтаксического анализа.
- •Генерация дерева синтаксического анализа
- •Вопрос 5. Приоритет операторов. Ассоциативность операторов.
- •Вопрос 6. Расширенная форма Бэкуса-Наура и синтаксические графы.
- •Вопрос 10. Концепция связывания, способы связи переменных с памятью
- •Вопрос 11. Арифметические выражения, порядок вычисления операторов.
- •Вопрос 12. Перегруженные операторы
- •Вопрос 13. Преобразование типов.
- •Вопрос 14. Отношения и булевские выражения. Сокращенные вычисления.
- •Вопрос 15. Операторы присваивания.
- •Вопрос 16.Структура операторов управления. Составной оператор и оператор ветвления (2 варианта).
- •Вложенные операторы ветвления и замыкание операторов ветвления.
- •I. Вложенные операторы ветвления.
- •Многовариантные операторы ветвления.
- •Вопрос 19. Операторы цикла. Циклы со счетчиком. Оператор do в Fortran. Оператор цикла в Pascal, algol 60.
- •Вопрос 21. Логически управляемые циклы.
- •Вопрос 22. Циклы с механизмом управления, размещаемым пользователем.
Сферы применения языков программирования. Критерии оценки и характеристики. Читабельность с точки зрения характеристик.
В настоящее время языки программирования применяются в самых различных областях человеческой деятельности, таких как:
научные вычисления (языки C++, FORTRAN, Java);
системное программирование (языки C++, Java);
обработка информации (языки C++, COBOL, Java);
искусственный интеллект (LISP, Prolog);
издательская деятельность (Postscript, TeX);
удаленная обработка информации (Perl, PHP, Java, C++);
описание документов (HTML, XML).
С течением времени одни языки развивались, приобретали новые черты и остались востребованы, другие утратили свою актуальность и сегодня представляют в лучшем случае чисто теоретический интерес. В значительной степени это связано с такими факторами, как:
наличие среды программирования, поддерживающей разработку приложений на конкретном языке программирования;
удобство сопровождения и тестирования программ;
стоимость разработки с применением конкретного языка программирования;
четкость и ортогональность конструкций языка;
применение объектно-ориентированного подхода.
Критерии оценки языков программирования:
Читабельность (легкость чтения и понимания программ) и ортогональность (легкость и удобство языка для создания программ) - это свойство, обеспечивающее легкость восприятия программ человеком. Это свойство языка программирования зависит от целого ряда факторов, начиная с выбора ключевых слов и заканчивая возможностью построения модульных программ.
Надежность – выполнение программой предназначенных ей действий при любых условиях. Понимается степень автоматического обнаружения ошибок, которое может быть выполнено транслятором или операционной средой, в которой выполняется программа. Надежный язык позволяет выявлять большинство ошибок во время трансляции программы, а не во время ее выполнения.
Легкость создания программ характеризует удобство языка для создания программ в выбранной области.
Критерии:
-
Характеристика языка:
Читабельность
Легкость создания
Надежность
Простота/ортогональность
+
+
+
Управляющие структуры
+
+
+
Типы и структуры данных
+
+
+
Синтаксическая структура языка
+
+
+
Поддержка абстракции
-
+
+
Выразительность
-
+
+
Проверка типов
-
-
+
Обработка искл. ситуаций
-
-
+
Читабельность:
Простота – обеспечивает легкость понимания семантики языковых конструкций и запоминания их синтаксиса. Простой язык предоставляет ясный, простой и единообразный набор понятий, которые могут быть использованы в качестве базовых элементов при разработке алгоритма.
Количество элементарных конструкций
Множественность свойств
Перегрузка операторов
Ортогональность – наличие относительно небольшого количества элементарных конструкций, с помощью которых выражаются управляющие операторы и структуры данных
Управляющие операторы и оператор goto
Адекватные средства определения типов и структур данных
Синтаксис
Правила образования идентификаторов
Написание и семантика ключевых слов
Легкость создания программ и характеристика языков программирования. Надежность и характеристики.
Легкость создания программ характеризует удобство языка для создания программ в выбранной области. Легкость создания программ в большинстве случаев определяется теми же характеристиками, что и читабельность. Это объясняется тем, что в процессе создания программ автор вынужден перечитывать уже написанные части программы.
Легкость создания:
Простота и ортогональность (Если язык содержит много разнообразных конструкций, то программисты могут не знать некоторые из них. Это может привести неправильному использованию одних возможностей и игнорированию других. В результате получаются менее изящные и менее эффективные программы. Следовательно, использовать имеющееся небольшое количество элементарных конструкций и согласованные между собой правила их комбинирования (т.е. ортогональность) лучше, чем применять большое количество примитивов. С другой стороны, слишком ортогональная структура языка усложняет его использование. Если можно использовать любую комбинацию элементарных конструкций, то ошибки, допущенные при создании программы, могут остаться незамеченными. Это приведет к логическим противоречиям в программе, которые не сможет выявить компилятор.)
Поддержка абстракции (Поддержка абстракции – это возможность определять и использовать сложные структуры или операции, игнорируя при этом многие детали. Абстракция – ключевая концепция разработки для современных языков программирования. Высокая степень абстракции, допускаемая языком программирования, значительно облегчает его применение. Языки программирования поддерживают две категории абстракции: абстракцию процессов и абстракцию данных. Примером абстракции процесса может служить реализующая алгоритм сортировки подпрограмма, которая несколько раз вызывается в различных местах главной программы. Если бы этой подпрограммы не было, то код сортировки пришлось бы внедрять везде, где это нужно. Это усложняет и делает более громоздким программирование. Кроме того, в этом случае детали алгоритма сортировки загромоздили бы основную программу, делая неясным ее общий смысл. В качестве примера абстракции данных можно рассмотреть двоичное дерево, хранящее в своих узлах целочисленные данные. На языке FORTRAN77 такое дерево обычно реализуется в виде трех параллельных целочисленных массивов, в которых два целых числа используются в качестве индексов для идентификации положения данных в дереве. На языках C++ и Java эти деревья можно реализовать, описав узел дерева в форме простого класса, содержащего два указателя и целое число. Такое описание является более естественным, поэтому написать программу, использующую двоичное дерево, на языке, поддерживающем такую абстракцию, легче, чем на языкеFORTRAN77. Это означает, что следует выбирать язык, абстракции которого наиболее близки к рассматриваемой предметной области.)
Выразительность (Выразительность языка может оцениваться различными показателями. В языке APL она означает наличие мощных операторов, позволяющих производить большое количество вычислений с помощью маленькой программы. В более широком смысле выразительность позволяет производить вычисления более удобными и менее громоздкими способами. Например, в языке Cзапись count++ удобнее и короче записи count = count + 1. Аналогично, булевские операторы and и then в языке Ada помогают описать сокращенное вычисление булевских выражений. В языке Pascal циклы со счетчиком проще создавать с помощью оператора for, чем с помощью оператора while. Очевидно, что все перечисленные возможности облегчают использование языка.)
Программа называется надежной, если она соответствует своему предназначению в любых условиях. На надежность также влияет читабельность программы и на стадии создания, и на стадии ее эксплуатации. Программу, которую сложно прочесть, одинаково трудно будет писать, и редактировать, а значит, будет больше вероятность появления ошибок.
Надежность:
Проверка совместимости типов при компиляции (Под этим критерием подразумевается проверка совместимости типов в программе, осуществляемая на этапе компиляции либо выполнения. Проверка типов является важным фактором надежности языка. В целях экономии ресурсов рекомендуется использовать проверку в процессе компиляции. Кроме того, чем раньше в программе будет обнаружена ошибка, тем дешевле обойдется ее устранение. Структура языка Ada требует проверки типов практически всех переменных и выражений в процессе компиляции, если только пользователь не дал явного указания заблокировать проверку. Такой подход фактически устраняет ошибки типов при выполнении программы, написанной на языке Ada. Примером того, как отказ от проверки типов может привести к ошибкам в программе, служит использование параметров подпрограмм в ранней версии языка C. В этом языке совпадение типов фактического и формального параметров при вызове функции не проверялось. В частности, переменная типа int могла использоваться в качестве параметра функции, ожидавшей формального параметра типа float. Ни компилятор, ни система поддержки выполнения программ не выявляли такое несоответствие. Это приводило к проблемам, источник которых было трудно определить. В языке Pascal диапазон изменения индексов массива является частью типа переменной, представляющей собой массив. Следовательно, проверка диапазона изменения индексов является частью проверки типов, хотя она должна производиться во время выполнения программы. Поскольку в языке Pascal контролируется большинство типов, то в нем выполняется также проверка диапазона изменения индексов. Такая проверка весьма важна для обеспечения надежности программы, поскольку индексы, выходящие за пределы допустимого диапазона, часто создают проблемы, проявляющиеся намного позже того момента, когда действительно произошло нарушение. Аналогичная проверка диапазона индексов проводится также в языках Ada, Java, C#.)
Средства обработки исключительных ситуаций (Возможность обработки исключительных ситуаций позволяет перехватывать ошибки и другие необычные ситуации во время выполнения программы, принимать соответствующие меры, а затем продолжать работу. Это значительно повышает надежность программ. Языки Ada, C++, Java, C# позволяют обрабатывать исключительные ситуации, однако в ряде широко используемых языков, таких как Cили FORTRAN, эти возможности практически отсутствуют.)
Совмещение имен (Совмещение имен – это наличие нескольких разных имен у одной и той же ячейки памяти. Существует мнение, что совмещение имен – опасная возможность языка программирования. Большинство языков программирования допускают некоторые виды совмещения имен. Например, в языке C как указатели, так и члены объединения, могут ссылаться на одну и ту же переменную. Некоторые разновидности совмещения имен могут запрещаться структурой языка.)
Легкость чтения и использования (На надежность программы влияет как легкость ее чтения, так и легкость создания. В программе, написанной на языке, не поддерживающем естественные способы выражения алгоритмов, будут использоваться неестественные методы, а такие методы не всегда корректно работают во всех возможных ситуациях. Чем легче написать программу, тем выше вероятность того, что она будет правильно работать. Читабельность влияет на надежность программы на этапе создания и в процессе ее сопровождения. Программу, которую сложно прочесть, одинаково трудно будет писать и редактировать.)
Синтаксис и семантика. Формальные методы описания синтаксиса.
Синтаксис языка программирования — набор правил, описывающий комбинации символов алфавита, считающиеся правильно структурированной программой (документом) или её фрагментом. Синтаксису языка противопоставляется его семантика. Синтаксис языка описывает «чистый» язык, в то же время семантика приписывает значения (действия) различным синтаксическим конструкциям.
Каждый язык программирования имеет синтаксическое описание. Синтаксис языка можно описать, например, с помощью правил Бэкуса — Наура.
Синтаксис проверяется на ранних стадиях трансляции. В интерпретируемых языках программирования проверка синтаксиса производится или в процессе интерпретации (выполнения), или в процессе предварительной компиляции в промежуточный код. Кроме того, синтаксис может проверяться непосредственно при редактировании исходных текстов программ при использовании IDE.
Синтаксис записи функции — жёсткое правило, которому должна удовлетворять запись кода функции; форма записи функции. Если синтаксис функции будет неверен, компилятор вернет ошибку и программа не будет собрана, пока ошибка не будет исправлена.
К синтаксическим ошибкам записи функции относятся (неправильная сигнатура):
неверное написание названия функции при её вызове (неверный регистр символов для регистрострогих языков, неверное пространство имен);
неверное количество аргументов;
неверный тип переданных аргументов (например, нужно передать строковое значение, а передано числовое);
неверный тип возвращаемого значения (в частности, неуказанный тип).
Формальные методы описания синтаксиса.
Для описания синтаксических конструкций языка программирования в настоящее время наиболее распространены два формальных метода. Первый использует форму записи, предложенную Джоном Бэкусом и Питером Нау-ром, когда они описывали синтаксис языка Алгол-60. С тех пор эта форма называется Backus Naur Form, или сокращенно BNF.
Другой формальный метод, наглядно представляющий синтаксические конструкции языка в графическом виде, использует синтаксические диаграммы. Популяризировал синтаксические диаграммы создатель языка Pascal H. Вирт, и поэтому их часто называют синтаксическими диаграммами Вирта. На синтаксических диаграммах используются два вида четырехугольников – с прямыми и скругленными углами (иногда их заменяют кружками или овалами). В прямоугольники заключаются элементы языка, значение которых должно быть определено (так называемые нетерминальные символы). В четырехугольниках со скругленными углами (или кружках, овалах) размещаются так называемые терминальные (базовые) символы, или иероглифы языка, значение которых в определении не нуждается. Направление движения по диаграмме при раскрытии структуры понятия, записанного при входе в диаграмму, указывают стрелки.
Чтобы получить правильные грамматические конструкции языка, используя синтаксические диаграммы, нужно идти по путям, указанным стрелками, от одного четырехугольника к другому до тех пор, пока не встретится выход. Там, где предусмотрено более одного направления движения, можно выбирать любое. Если по пути встречается ссылка к другой синтаксической диаграмме, то следует войти в эту новую диаграмму, пройти по ней, выйти из нее и возвратиться на старое место в первоначальной диаграмме. Если по пути движения встречается точка, то это означает, что данный путь характерен только для Turbo Pascal и является расширением стандарта языка. Варианты представления синтаксических конструкций языка программирования методом BNF или методом синтаксических диаграмм являются тождественными.
Семантикой называется интерпретация (т.е. смысловое значение) абстрактного синтаксиса (т.е. множества допустимых конструкций языка), выраженное в терминах той или иной математически строгой модели. Основные подходы к семантике:
1) ориентированные на компиляцию (семантика – множество преобразований над синтаксической моделью);
2) ориентированные на интерпретацию (семантика – множество описанных на метаязыке преобразований синтаксически правильных языковых конструкций).
Виды семантик, ориентированные на интерпретацию:
1) Операционные (смысл конструкций языка в терминах переходов абстрактной машины из одного состояния в другое), например, SECD-машина П. Лендина;
2) Пропозиционные (смысл конструкций языка в терминах множества формул, описывающих состояния объектов программы), например, подходы Хоара и Флойда;
3) Денотационные (смысл конструкций языка в терминах абстракции функций на состояниях программы), например, теория семантических доменов Д. Скотта.
Вопрос 4. Деревья синтаксического анализа.
Дерево синтаксического анализа арифметического выражения - это бинарное дерево, листьями которого служат операнды, а остальными вершинами - операции, причем уровень вершины соответствует приоритету выполнения операции: чем ближе к листьям, тем приоритет выше.
Например, на рисунке изображено дерево синтаксического анализа для выражения
((a / (b + c)) + (x * (y — z))).
Деревья синтаксического разбора строятся компиляторами во время синтаксического анализа программ. Помимо арифметических выражений, которые являются простейшим случаем, аналогичные, но более сложные деревья строятся для всех грамматических конструкций компилируемой программы.
Генерация дерева синтаксического анализа
Одно и то же арифметическое выражение может быть записано тремя способами:
Инфиксный способ записи (знак операции находится между операндами):
((a / (b + c)) + (x * (y - z)))
Все арифметические операции, привычные нам со школьных лет, записываются именно таким образом.
Префиксный способ записи (знак операции находится перед операндами):
+( /(a, +(b,c)), *(x, -(y,z)))
Из знакомых всем нам функций префиксный способ записи используется, например, для sin(x), tg(x), f(x,y,z) и т.п.
Постфиксный способ записи (знак операции находится после операндов):
((a,(b,c)+ )/ ,(x,(y,z)- )* )+
Вопрос 5. Приоритет операторов. Ассоциативность операторов.
Приоритет, ранг или старшинство операции или оператора — формальное свойство оператора/операции, влияющее на очередность его выполнения в выражении с несколькими различными операторами при отсутствии явного (с помощью скобок) указания на порядок их вычисления.
Операции могут иметь одинаковый приоритет, тогда они вычисляются по правилу ассоциативности, установленному для этих операций.
Ассоциативность — свойство операций, позволяющее восстановить последовательность их выполнения при отсутствии явных указаний на очерёдность при равном приоритете; при этом различается левая ассоциативность, при которой вычисление выражения происходит слева направо, и правая ассоциативность — справа налево. Соответствующие операторы называют левоассоциативными и правоассоциативными.
Вопрос 6. Расширенная форма Бэкуса-Наура и синтаксические графы.
Форма Бэкуса-Наура (БНФ) была впервые применена при описании Алгола-60. БНФ совпадает по сути с нотацией КС-грамматик, отличаясь лишь обозначениями. Предусмотрены следующие метасимволы:
<> — служат для выделения нетерминалов — понятий языка. | — «или». Разделяет альтернативные правые части правил. — «есть по определению». Заменяет стрелку, используемую при записи продукций КС-грамматик.
Терминальные символы записываются как есть, никаких специальных способов их выделения не предусмотрено. Вот пример определений на БНФ, взятый из спецификации Алгола-60 — «Модифицированного сообщения»:
<простое арифметическое выражение> ::=
<терм> 1 Окак операции типа сложения> <терм> |
<простое арифметическое выражение>
<знак операции типа сложения> <терм>
<знак операции типа сложения> ::= + | -
Как уже говорилось, отсутствие в нотации формальных грамматик (и БНФ) средств явного задания повторений создает ряд трудностей. Во-первых, определения оказываются сложными для понимания, недостаточно наглядными из-за обилия рекурсий. Во-вторых, возникают проблемы с тем, что грамматики, дающие подходя- щие семантические деревья, оказываются леворекурсивными. При описании Модулы-2 и Оберона Н. Вирт использовал расширенную Бэкуса-Наура форму (РБНФ). Главные модификации касаются введения скобок { и} для повторений, а [ и ] — для обозначения необязательного вхождения цепочек терминалов и нетерминалов в правые части правил. Соглашения относительно обозначений терминалов и нетерминалов также изменены, что не столь принципиально. В дальнейшем мы будем пользоваться именно РБНФ. Вот как она определяется в спецификации Оберона-2: Варианты разделяются знаком |. Квадратные скобки [ и ] означают необязательность записанного внутри них выражения, а фигурные скобки { и } означают его повторение (возможно, 0 раз). Нетерминальные символы начинаются с заглавной буквы (например, Оператор). Терминальные символы или начинаются малой буквой (например, идент), или записываются целиком заглавными буквами (например, begin), или заключаются в кавычки (например, ":="). К этому следует добавить, что в роли знака «есть по определению» в РБНФ используется «=», а каждое правило заканчивается точкой. Вот так может быть определен синтаксис идентификатора (имени) с помощью РБНФ:
Имя = Буква { Буква | Цифра }.Являясь метаязыком, РБНФ должна быть пригодна для описания языков, имеющих практический интерес. В том числе с помощью РБНФ может быть определен и синтаксис самой РБНФ:
Синтаксис = { Правило }.
Правило = Имя "=" Выражение
Выражение = Вариант { "I" Вариант }.
Вариант - Элемент { Элемент }.
Элемент = Имя | Цепочка | "{" Выражение "}" |
"[" Выражение "]" | "{" Выражение "}".
Цепочка = "'" { символ ) "'" | '"'{ символ } '"'.
Синтаксическая диаграмма — это направленный граф с одним входным ребром и одним выходным ребром и помеченными вершинами. Синтаксическая диаграмма задаёт Формальный язык. Цепочка пометок при вершинах на любом пути от входного ребра к выходному — это цепочка языка, задаваемого синтаксической диаграммой. Поэтому можно считать, что синтаксическая диаграмма - это одна из форм порождающей грамматики автоматных языков. Синтаксические диаграммы и конечные автоматы имеют тесную связь: любой автоматный язык задаётся синтаксической диаграммой и обратно, по любой синтаксической диаграмме можно построить конечный автомат(в общем случае недетерминированный), распознающий тот же язык, который задаёт диаграмма.
Построив по синтаксической диаграмме соответствующий распознающий конечный автомат, можно затем реализовать этот автомат либо аппаратно, либо программно.
Таким образом, синтаксические диаграммы могут служить не только для порождения, но и для распознавания автоматных языков. Диаграммы стали популярны после выхода книги К. Йенсен и Н. Вирта «Паскаль». Они использованы в первой ее части — «Руководстве» — компактном учебнике языка.
Вопрос 7. Статистическая и динамическая семантика. Динамическая семантика языка – это математическое описание, которое любой строке-фразе языка сопоставляет поведение. Программам с разным поведением будут соответствовать разные математические объекты. Объектные коды могут быть разные, а семантики должны быть одинаковыми. Хотя бы по этим соображениям компилятор не может являться описателем семантики.
Статическая семантика, определяемая только для синтаксически корректных программ, задает правила вычисления свойств программы, не требующих для своего вычисления ее выполнения. К таким свойствам относятся, в частности, типы переменных и выражений. Помимо правил вычисления задаются также правила проверки статической корректности программы, или контекстные условия, которые накладывают ограничения на возможные комбинации значений статических свойств программы.
Вопрос 8. Имена и специальные слова. Имена в программах служат той же цели, что и имена людей, - чтобы обращаться к программным объектам и различать их, то есть идентифицировать. Поэтому имена также называют идентификаторами ( в информатике специальное наименование, имя элементарных данных, массивов данных, программ или других объектов, которые запрашиваются, обрабатываются и выдаются на выход ЭВМ).
Имена дает программист, при этом следует соблюдать некоторые правила:
Имя должно начинаться с буквы;
Имя должно содержать только буквы, знак подчеркивания и цифры;
Прописные и строчные буквы не различаются;
Длина имени практически не ограничена (63 символа ).
Имена даются элементам программы, к которым требуется обращаться: переменным, константам, процедурам, функциям и т. д.).
Ключевые слова – это идентификаторы, имеющие специальное значение для компилятора . Их можно использовать только в том смысле, в котором они определены. Например, для описания переменных определено ключевое слово var . Имена, создаваемые программистом не должны совпадать с ключевыми словами.
Вопрос 9. Переменные и области видимости. Динамический обзор данных. Классы содержат методы и поля – переменные класса. Методы имеют параметры, а внутри методов возможно определение локальных переменных. У каждой переменной есть область видимости, т.е. та область программы, в которой возможна работа с этой переменной. Таким образом у переменных с одинаковыми именами не должны пересекаться области видимости. объявление локальных переменных возможно в любом месте метода. При этом областью видимости переменной будет тот блок, в котором она определена. У параметров область видимости – весь метод. У полей область видимости – весь класс, однако возможно создавать локальные переменные с именем, совпадающим с именем поля. В этом случае после определения данное имя будет связано именно с переменной, а не с полем класса. Это крайне не рекомендуется делать, т.к. такое определение будет создавать некоторую двусмысленность восприятия кода и может способствовать появлению ошибок.
