1. Мови програмування (процедурні, візуальні, специфікацій). Концепція інструмен­тального середовища розробки програм. Синтаксис І семантика, набори мовних конструктивів: описи, оператори, лексеми, поняття, атрибути, ділянки дії, блоки. Принцип модульності Алгоритмічна мова

Вид материалаДокументы
Синтаксический анализатор
Подобный материал:
1   2   3
Тема 2. Мовні процесори: компілятори, інтерпретатори, макрогенератори, метакомпілятори. Синтаксичне управління мовного процесора. Задачі семантичного (контекстного) аналізу та генерації коду. Схеми компіляції з визначенням проходів і етапів: лексичний аналізатор (сканер), синтак­сичний аналізатор, контекстний аналіз, генератор коду. Стеки операторів і операндів, підпрограми виводу і діагностика помилок. Компонувальник скомпільованих програм, завантаження і виконання програм. Debuger і візуальна оболонка інструментального середовища

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

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

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

Интерпретатор осуществляет пооператорную трансляцию и выполнение исходной программы без порождения в итоге программы на машинном языке. Распознав команду исходного языка, он тут же выполняет ее. В компиляторах и интерпретаторах используются одинаковые методы анализа исходного текста программы. Но интерпретатор позволяет начать обработку данных после написания даже одной команды; поэтому процесс разработки и отладки программ более гибкий. Кроме того, отсутствие выходного машинного кода позволяет не "захламлять" компьютер дополнительными файлами, а сам интерпретатор можно достаточно легко адаптировать к любой архитектуре, разработав его один раз на распространенном языке программирования. Поэтому интерпретируемые языки типа Java Script, VB Script получили широкое распространение. Недостатком интерпретаторов является низкая скорость выполнения программ. Обычно интерпретируемые программы выполняются в 50-100 раз медленнее программ в машинных кодах.

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

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

Часто эмулятор используется для выполнения старых программ на новых компьютерах, обладающих большим быстродействием и качественным оборудованием. Старые программы эффективнее эмулируются в сравнении с их выполнением на старых компьютерах. До сих пор находятся любители поиграть на эмуляторе в устаревшие игры, все еще не утратившие былой привлекательности. Эмулятор также используется как более дешевый аналог современных компьютерных систем, обеспечивая при этом приемлемую производительность, эквивалентную младшим моделям семейства платформ. В качестве примера можно привести эмуляторы IBM PC-совместимых компьютеров, реализованные на более мощных компьютерах фирмы Apple.

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

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

Макропроцессоры часто используются как надстройки над языками, увеличивая функциональность систем программирования. Любой ассемблер содержит макропроцессор, чем повышается эффективность разработки машинных программ. Макропроцессоры увеличивают функциональные возможности таких языков, как ПЛ/1, Cи, C++. Особенно упростилось написание C++-программ благодаря библиотеке классов, например, MFC. Причем макропроцессоры повышают эффективность программирования без изменения синтаксиса и семантики языка.

Метакомпилятор (иногда называется компилятором компиляторов) обеспечивает построение компилятора по заданию входного языка этого компилятора. Разумеется, язык нового компилятора принадлежит весьма узкому классу языков, преимущественно спецификаций, описанных набором образцов. Чаще всего метакомпилятор строится как параметризуемый (generic) модуль, способный оценить правильность (полноту и непротиворечивость) заданной системы правил нового языка, отобрать и настроить необходимую совокупность образцов, превратив их макрорасширения. Затем компонуется монитор как главная программа нового компилятора, которая поддерживает выполнение макрорасширений через подключение конкретных макросов, реализующих отобранную совокупность образцов.

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

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

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

Семантичний або контекстний аналізатор як компонент мовного процесора перевіряє семантичну узгод­женість вживання у програмі мовних конструктів. Це найбільш залежна від мови програмування частина транслятора, проте для кожної мови реалізуються наступні функції:
  • виконати перевірку контекстних умов в програмі;
  • провести розподіл пам’яті;
  • провести синтез програмного коду у єдиний модуль.

Контекстні умови мов програмування відбивають дві їхні властивості: (1) категоризація (типізація) даних та (2) реалізація модульного принципу через пізнє зв’язування та ділянки дії (адресний простір чи область видимості) імен. У Фортрані використовується тільки пізнє зв’язування через лінкування за іменами об’єктів головної програми та підпрограм. Структурні мови (Сі, С++), крім пізнього зв’язування, використовують механізм вкладених блоків.

Виділяють чотири групи контекстних умов:
  • одиничність іменування таких об’єктів, як прості змінні, масиви, процедури, мітки, формальні параметри процедур;
  • відповідність між визначальним і вживальним входженням ідентифікаторів. Визначальним є входження ідентифікатора у конструкт, що описує цей ідентифікатор. Скажімо, ідентифікатор міченого оператора –­ це визначальне входження, а цей же ідентифікатор у операторі переходу – вживальне входження. Кожне вживальне входження треба зіставити з визначальним. Пошук визначальних входжень виконують алгоритми ідентифікації. Для програм з блоками алгоритми ідентифікації спрацьовують у областях видимості­. Для багатьох мов (Фортран) за відсутності визначальних входжень спрацьовують правила за промовчуванням. Ця контекстна умова виграє визначальну роль у контролі типів;
  • відповідність видів даних і синтаксичних структур. Так, у операторі безумовного переходу можуть вживатися лише мітки, а у логічному виразі логічні змінні чи булеві константи;
  • кількісні обмеження конкретного подання (реалізації) мови: макс. Глибина вкладеності блоків чи циклів, максимальне число вимірів масивів, загальна кількість ідентифікаторів тощо.

Складність груп контекстних умов визначена способами розчленування тріади опис –оголошення – означування (specification-declara­ti­on-definition).

Генератор коду – компонент компілятора, який збирає скомпільований код у єдиний модуль. При цьому обов’язково проводиться оптимізація.

Потім цей модуль компонується (лінкується) з іншими, чим підтримується модульний принцип програмування. Проте компонувальним складає частину не транслятора, а інструментального середовища розробки програм. Сюди входить і Debuger, за допомогою якого модуль тестується і налагоджується, тобто в ньому шукаються і виправляються помилки. Головна функція Debuger’а – емуляція злінкованого модуля для отримання траси його виконання і перевірки проміжних і підсумкових результатів виконання.

Розглянемо схему компілятора, в якому:
  1. исходный текст программы;
  2. таблица терминальных символов языка (ключевые слова if, then и т.д.);
  3. лексическая свертка программы (промежуточная программа, удобная для работы парсера);
  4. правила грамматики (правила и аксиомы грамматики);
  5. дерево вывода (дерево грамматического разбора программы);
  6. абстрактная программа (атрибутное дерево программы) Отличия атрибутного дерева от дерева вывода: узлы снабжаются атрибутами (со ссылками на таблицу имен или к другим узлам атрибутного дерева, конкретными вычисленными значениями);
  7. некоторая промежуточная программа после машинно-независимой оптимизации. Может быть несколько этапов такой оптимизации и несколько форм представления машинного кода;
  8. таблица содержит кодовые продукции или заготовки, шаблоны по которым строится следующая версия промежуточной формы 9;
  9. к


    Рис.2.1. Схема компілятора

    од ассемблера или команды, на­поми­нающие ассемблер. Воз­можно, для кода 9 еще не было распределения регистров.




Рис.2.2. Схема интерпретатора




Рис. 2.3. Обобщенная схема синтаксического анализатора



Машинный код


Рис.2.4. Многопроходное взаимодействие блоков компилятора



Рис.2.5. Однопроходная схема компилятора под управлением ЛА




Рис. 2.6. Однопроходная схема интерпретатора



Рис.2.7. Двухпроходная схема с полной генерацией промежуточного кода



Рис. 2.8. Эмуляция скомпилированного объектного кода

1 Фактично розглядалося багато інших подань (наприклад, різноманітні циклічні зсуви, кругова симетрія).

2 Мови четвертого покоління складають частину майбутньої POSIX-стандартизації