posobie_CP_skakovskay

advertisement
Оглавление
С.
1 Структура математического обеспечения
ЭВМ ………………………………………………...3
1. 1 Средства программирования…………………………3
1. 2 Средства управления …………………………………4
2 Элементы компиляции. Анализ формальных
языков……………………………………………... 4
2. 1Сведения о регулярных выражениях и грамматиках .4
2. 2 Регулярные выражения……………………………… 6
2. 3 Грамматика…………………………………………… 7
2. 4 Способы получения одних цепочек символов из
других……………………………………………………. 9
2. 5 Формальное определение языка…………………... 10
2. 6 Расширенные грамматики…………………………. 12
2. 7 Задачи анализа.………………………………………13
2. 8 Синтаксические диаграммы……………………….. 16
2. 9 Введение в компиляцию…………………………… 19
2. 10 Структура компилятора …………………………...21
2. 11 Проходы компилятора……………………………. 23
3 Алгоритмический язык SPL …………………24
3. 1 Символы ……………………………………………..25
3. 2 Идентификаторы…………………………………… 25
3. 3 Константы …………………………………………...26
3. 4 Переменные …………………………………………26
3. 5 Выражения …………………………………………..27
3. 6 Служебные слова ……………………………………27
3. 7 Функции ……………………………………………..27
3. 8 Оператор цикла ……………………………………...29
4 Лексический анализ …………………………..31
4. 1 Блок - схема функции void main ()…….………..…37
4. 2 Блок-схема функции void number () ………………..39
4. 3 Блок-схема функции void word ()………………….. 42
4. 4 Блок-схема функции char*add()……………..…….. 45
5 Полный синтаксис языка SPL ………………47
5. 1 Алфавит нетерминальных символов ………………48
5.2 Синтаксические диаграммы и функции
распознавания цепочек для нетерминальных
символов…………………………………………………..54
6 Пояснения к выполнению курс овой
работы……………………………………………. 69
6. 1 Пример выполнения курсовой работы …………….74
6. 2 Варианты заданий для курсовой работы………….. 80
6. 3 Требования к оформлению курсовой работы…….. 88
7 Вопросы к экзамену …………………………...89
8 Список литературы…………………………... 92
2
1 Структура математического
обеспечения ЭВМ
СМО
Системное-программное Прикладное программное
обеспечение
обеспечение
(операционные системы)
Средства
программирования
Средства
Программы
Программы
управления пользователя технического
обслуживания
(тесты)
1. 1 Средства программирования
Существуют такие средства программирования:
Текстовые редакторы (диалоговые программы);
Трансляторы (перевод с одного языка на другой,
чаще из алгоритмического языка на язык
программных кодов), препроцессор, интерпретатор,
компилятор - перекладывают с одного языка высокого
уровня на другой;
Редакторы связей (компоновщики) lіnker;
Утилиты (сервисные программы, копирование,
перемещение и т.д.);
3
1. 2 Средства управления
Средствами управления являются:
−Внутренние команды (при изготовлении ПК);
−Внешние программы (в DOS);
−Драйверы (программы, которые руководят
внешними устройствами).
2 Элементы компиляции. Анализ
формальных языков
2. 1Сведения о регулярных выражениях и
грамматиках
Любой язык имеет свой алфавит.
Алфавит – это конечное множество I элементов,
называемых символами.
Цепочка или слово в алфавите I – это конечная
последовательность элементов (символов) из алфавита
I. Например, если алфавит языка состоит только из
заглавных и строчных букв латинского алфавита, то
любые
последовательности
этих
букв
являются
цепочками (словами), в том числе цепочка может
состоять из одного символа.
4
С цепочками (словами) могут быть проделаны
действия, которые имеют следующие обозначения:

хn. Цепочка символов х повторяется (пишется
без пробелов одна за другой) n раз. Например, abba2 –
это abbaabba.

хk. Цепочка символов х записывается в обратной
последовательности. Например, portR - это trop.

xy. За цепочкой символов x без пробела
помещается цепочка символов y.

х*. Цепочка символов х в цикле может
повторяться нуль и более раз.
Обычно в алгоритмических языках  это
действие реализуется циклом с предварительной
проверкой условия.
Например: intl iden (‘,’ iden)* ‘;’
Это означает, что за символом intl должно
следовать iden. Затем через запятую может еще
повторяться iden нуль и более раз. В конце должна
быть точка с запятой.

х+. Цепочка символов х должна повторяться один и
больше
раз.
В
алгоритмическом
5
языке
Pascal
это
реализуется оператором repeat, а в языке Си – оператором
цикла do while.

х.
Определение
длины
цепочки
символов
х
(количество символов в цепочке).

{} или l, или  - обозначение пустой цепочки
символов.

[х].
Так
обозначается
необязательная
цепочка
символов. Например, такая запись нужна для того, чтобы
обозначить, что перед числом знак может быть, а может и
отсутствовать.
Кроме алфавита и цепочки символов (слов),
важным понятием является язык.
Язык
в
алфавите
I
–
это
произвольное
множество цепочек (слов).
2. 2 Регулярные выражения
Это цепочки символов, в которые входят не
только символы из некоторого алфавита I, но и другие
символы, которые часто носят служебный характер.
Например, это могут быть запятая для разделения
других символов, а также символы для обозначения
6
каких-либо действий над цепочками. Пусть множество
{,* } из перечисленных в фигурных скобках символов
не входит в алфавит I. Тогда цепочка символов из
объединения
}
IU{,*
называется
регулярным
выражением. Эти выражения обычно используются
для
описания
синтаксиса
какого-либо
алгоритмического языка.
2. 3 Грамматика
Грамматика G=(T,N,P,S),
где T - алфавит т.н. терминальных символов.
Это
символы,
которые
заведомо
определены.
Например, это символы, используемые в каком-либо
алгоритмическом языке. Какие это символы и какое их
качество – все это заведомо определено. Только эти
символы в дальнейшем используются при написании
программ на этом алгоритмическом языке.
N – алфавит т.н. нетерминальных символов. Это
символы,
которые
обычно
используются
для
определения каких-либо понятий. Такими понятиями в
алгоритмическом
языке,
7
например,
являются
идентификатор, переменная, константа, выражение,
оператор и, в конце концов, программа. Обычно эти
понятия перечисляются и обозначаются символами.
Эти символы определяются через терминальные
символы
или,
кроме
того,
через
другие
нетерминальные символы более низкого уровня.
Например, при определении нетерминального символа
“программа” используются терминальные символы
алгоритмического языка, а также нетерминальные
символы “оператор”, “выражение”, “слагаемое” и др.
Множества T и N не пересекаются. Обычно
терминальные
символы
обозначаются
строчными
буквами, а нетерминальные – заглавными. Р –
множество
правил
символов.
Например,
переменных,
операторов
вывода
это
констант,
и
т.д.
для
нетерминальных
правила
написания
–
S
стартовый
описания
выражений,
(главный)
нетерминальный символ. Для алгоритмических языков
это обычно нетерминальный символ “программа”.
8
2. 4 Способы получения одних цепочек
символов из других
1 Это можно сделать непосредственно, т.е. за
одну операцию. Условное обозначение .
Например,  означает, что цепочку символов
 можно получить из цепочки  за одну операцию.
Пример. Есть цепочки  и . Здесь,
,,
-
терминальные
символы
(обозначены
строчными буквами), а А – нетерминальный символ.
Пусть среди множества правил Р есть такое: ЄР.
То есть нетерминальный символ АЄN можно заменить
на терминальный . Таким образом, за одну операцию
из  можно получить цепочку .
2 Одну цепочку из другой можно получить не за
одну операцию. Условное обозначение *.
Например, 0 *n.
Это имеет место или если 0 = n , или
существует
последовательность
непосредственно
получаемых цепочек, таких, что 0  1  2  …
 n-1  n.
9
2. 5 Формальное определение языка
Способы
получения
цепочек
символов
необходимы, чтобы формально дать определение
языка L(G), описываемого заданной грамматикой G.
Язык L(G) – это множество цепочек w терминальных
(и только терминальных!) символов, выводимых из
начального (стартового, главного) нетерминального
символа S. L(G)={w|S *w, где w – терминальные
цепочки}.
Вертикальная черта после w в выражении
означает, что w получены при условии, что они
выводятся из S. А звездочка означает, что этот вывод
может происходить
за произвольное
количество
операций. При этом используется множество P
правил, описанное в грамматике G.
Ниже
приводятся
примеры
грамматики
и
описываемые ими языки.
Пример 1 G=(T,N,P,S).
T={x,y,w,z} – алфавит терминальных символов.
Только эти символы можно использовать.
10
N={S,A,B}
–
множество
нетерминальных
символов, из которых S – главный (стартовый)
нетерминальный символ. Именно с него должен
начинаться вывод цепочек (слов) в соответствии с
множеством правил вывода P.
Р={SAB, Ax, Ay, Bw, Bz}.
Эти правила означают, что S можно заменить на
последовательность нетерминальных символов AB.
Ax, A y означает, что А можно заменить на x или
на y и т.д. Осуществляя эти замены, получаем цепочки
w
из
терминальных
символов,
составляющих
формальный язык L(G).
L(G)={xw, yw, xz, yz}.
Только эти четыре цепочки (слова) и составляют
язык, описываемый заданной грамматикой G.
11
2. 6 Расширенные грамматики
Существуют т.н. расширенные грамматики. Они
порождают те же языки, что и рассмотренные выше
грамматики, но являются более наглядными.
Расширенные грамматики задаются парами
Ai  ri,
где Ai ЄN – i-й нетерминальный символ.
ri – i-е регулярное выражение в алфавите NUT.
Нетерминальный символ первой пары является
ГЛАВНЫМ (стартовым)для вывода цепочек.
Например,
расширенная
грамматика,
эквивалентная описанной в примере выше, имеет вид:
SAB ,
Ax|y,
Bw|z.
Напомним, что вертикальная черта читается как
“или”. То есть А может быть заменена на x или y, а В –
на w или z.
12
2. 7 Задачи анализа
Даны цепочка символов (слов) и грамматика.
Нужно
узнать,
формальному
принадлежит
языку,
ли
это
слово
определяемому
этой
грамматикой. Для решения этой задачи обычно
используются алгоритмы анализа слева направо,
которые просматривают в этом направлении символы
цепочки.
Например, цепочка [[x]] принадлежит языку
L(G2), описанному грамматикой G2.
G2=(T2, N2, P2, A), где
T2 = {х, +, [, ]},
N2 ={А,В},
P2 = {Ах, А[В], ВА, ВВ+А}.
Действительно,
заданную
цепочку
можно
получить, применив последовательно по одному из
правил вывода, заданному в Р2.
А [В]  [А]  [[В]] [[А]]  [[х]].
Следовательно, цепочка [[х]]
формальному языку L(G2).
13
принадлежит
Различают две стратегии: стратегия анализа
сверху вниз и снизу вверх. При анализе сверху вниз
для построения вывода заданной цепочки начинают с
ГЛАВНОГО нетерминального символа и выбирают
правила из заданного множества так, чтобы прийти к
заданной цепочке w.
В примере [[х]] в грамматике G2 главных
нетерминальных
символов
А
среди
заданного
множества правил вывода для А есть два: А  х и
А[В]. Первое из них не дает возможности вывести
[[х]]. Берем А  [В], то есть А заменяем на [В].
Теперь нужно выбирать из двух правил вывода для В.
В  А и В  В+А.
Выбираем В  А, заменяем в [В] В на А и
получаем [А]. Вновь из двух правил вывода для А
выбираем А  [В]. Заменяем в [А] А на [В]. Получаем
[[В]]. Вновь из двух правил для вывода В выбираем
В А и заменяем в [[В]] В на А. Получим [[А]]. И,
наконец, из двух правил вывода для А выбираем Ах.
Заменяем в [[А]] А на х и получаем требуемую
цепочку [[х]]. Таким образом, доказано, что заданная
14
цепочка
действительно
принадлежит
языку,
определяемому грамматикой G2.
При анализе сверху вниз основная проблема в
определении необходимого правила V  i для
построения следующего шага вывода цепочки w, когда
в найденной части А * V известны левый
нетерминальный символ V и n правил вывода V  1 ,
V 2, …V  n с левой частью V. Решение этой
проблемы,
в
частности,
возможно
с
помощью
алгоритма лексического <А(1) – анализа, который
определяет
необходимое
правило
V

i
в
зависимости от первого, еще не распознанного
символа х в цепочке w.
Для
многих
грамматик
LA(1)
–
анализ.
Осуществляется LA(1) – анализ путем создания для
нетерминалов процедур анализа. Эти процедуры, как
правило, оказываются взаимно рекурсивными и часто
неэффективными.
Для многих грамматик LA(1) – анализ в таком
виде не используется. Тогда приходят к расширенным
15
грамматикам или к синтаксическим диаграммам
(графикам).
2. 8 Синтаксические диаграммы
Это
ориентированный
граф
с
двумя
фиксированными вершинами: входной, из которой
дуги только выходят, и выходной, в которую дуги
только входят.
Дуги
этого
графа
могут
быть
помечены
терминальными и нетерминальными символами. В
расширенной
диаграмме
с
терминальным
Т
и
нетерминальным N алфавитами каждому нетерминалу
А и его правилу вывода А  , где  - регулярное
выражение
в
алфавите
16
TUN,
отвечает
одна
синтаксическая диаграмма. При обозначении дуг
помеченными терминальными символами условились
эти символы писать строчными буквами и заключать в
окружность. Нетерминальные символы условились
писать
заглавными
прямоугольнике.
буквами
Регулярное
и
размещать
выражение
в
будет
обозначаться греческими буквами внутри ромба.
На рисунке 2 показаны примеры изображения
дуг,
помеченных
терминальным
символом
а,
нетерминальным – А и регулярным выражением – .
А
а

Рисунок 2
Алгоритм лексического анализа может быть
применен
диаграммах
при
условиях,
нет
что
в
непомеченных
синтаксических
дуг
от входной
вершины до выходной и в каждом разветвлении ветки
начинаются
с
неодинаковых
17
последующих
терминальных
символов.
По
синтаксической
диаграмме строится функция анализа выводимых из
нетерминала цепочек символов
w . Прохождению
дуги, помеченной терминальным символом а, отвечает
распознавание этого символа (т.е. а) в предъявленном
для анализа слове и сдвиг на следующий символ в
этом слове (чтение очередного символа из слова).
Прохождению
дуги,
помеченной
нетерминальным символом А, отвечает вызов функции
распознавания цепочек, которые выводятся из А.
Перед вызовом этой функции предварительно должен
быть прочитан первый терминальный символ х,
*
который выводят из А, т.е. A  x . Функция успешно
анализирует цепочку w , выводимую из нетерминала
А, если в соответствии с синтаксической диаграммой,
прочитав посимвольно всю цепочку
попасть из входной вершины в выходную.
18
w , удается
2. 9 Введение в компиляцию
Все
изложенные
выше
сведения
о
грамматических и формальных языках необходимы
для создания трансляторов.
Транслятором
называется
компьютерная
программа, которая осуществляет переход программы
на входном языке (например, алгоритмическом) на
эквивалентную
ей
объектную
программу.
Если
входной язык высокого уровня (алгоритмические
языки Паскаль, Си и др.), а выходной – машинные
коды
или
ориентированный
АССЕМБЛЕР
язык),
то
(т.е.
машинно-
такой
транслятор
называется компилятором.
Интерпретатор - разновидность транслятора,
который переводит программу на язык простых
промежуточных команд и выполняет их.
Препроцессор – транслятор, осуществляющий
перевод с одного языка высокого уровня на другой
язык тоже высокого уровня.
19
Итак, транслятор – компьютерная программа,
которая читает последовательно символ за символом
текст некоторой другой компьютерной программы на
каком-то алгоритмическом языке и осуществляет
перевод этой программы на другой (выходной) язык. В
случае,
если
транслятором
является
программа-
компилятор, после перевода с алгоритмического языка
будет получена компьютерная программа или на
ассемблере или в виде последовательности машинных
команд в кодах (объектный модуль). Один или
нескольких
объектных
модулей
обрабатываются
программой - компоновщиком. В результате создается
готовая
к
исполнению
программа.
Компилятор
осуществляет перевод программы на алгоритмическом
языке в программу, состоящую из последовательности
некоторых промежуточных команд. Сразу же после
окончания перевода интерпретатор в отличие от
компилятора выполняет полученную программу.
20
2. 10 Структура компилятора
Обработка ошибок
Программа
на входном
языке
Объектный
код
ЛА
СА
ГПК
ГК
ОК
Управление таблицами
Здесь:
ЛА – лексический анализ;
СА – синтаксический анализ;
ГПК – генерация промежуточного кода;
ОК – оптимизация кода;
ГК – генерация кода.
На
всех
этапах
осуществляется
обработка
ошибок и происходит заполнение различных таблиц.
Рассмотрим
более
подробно
21
некоторые
этапы
обработки исходной программы на алгоритмическом
языке.
ЛА – лексический анализ. Лексема – это один
или
несколько
символов,
имеющих
некоторый
самостоятельный смысл. В процессе лексического
анализа считываемые последовательно из программы
на
выходном (алгоритмическом) языке символы
объединяются в лексемы. Различаются следующие
лексемы:

служебные слова;

идентификаторы;

знаки для обозначения операций, а также
другие специальные символы;

числа;

признак конца считываемого файла.
Служебные (зарезервированные) слова нельзя
использовать в программе на алгоритмическом языке в
качестве
идентификаторов
для
обозначения
переменных и функций. Например, для языка Си это
if, do, while, for, return, goto, char, int, float и др.
Поэтому
программа-транслятор
22
должна
“уметь”
отличить эти зарезервированные служебные слова. На
следующем этапе происходит построение из лексем
синтаксических
структур,
составляющими
других.
осуществляются
транслируемой
таблиц.
которые
На
обнаружение
программы,
Конкретно
а
могут
всех
ошибок
быть
этапах
в
тексте
также
заполнение
заполняются
таблица
идентификаторов переменных и функций, таблица
объектов,
глобальные
которые
и
обрабатываются
локальные
(константы,
переменные),
таблица
функций (главная функция main и др.), таблица
команд.
2. 11 Проходы компилятора
При
реализации
компилятора
одна
или
несколько фаз (этапов, а может и часть фазы)
объединяются в программные модули. Их называют
проходами. За каждый проход считываются файл с
программой на алгоритмическом языке, а также
результат предыдущего прохода. Во время каждого
прохода происходит преобразование, заданное фазами,
23
и
записывается
результат.
Количество
проходов
определяется особенностями алгоритмического языка
и ПЭВМ. Есть одно-, двух- и многопроходные
компиляторы. Многопроходной компилятор занимает
меньше
оперативной
однопроходной,
необходимости
но
памяти
работает
многоразового
ПЭВМ,
чем
медленно
из-за
чтения
и
записи
файлов. В однопроходном компиляторе ведущей
фазой является фаза синтаксического анализа. Она
каждый раз, когда ей нужна лексема, запрашивает
фазу лексического анализа, управляет генерацией
промежуточного кода, заполняет таблицы и т.д.
3 Алгоритмический язык SPL
Используем основные методы компиляции для
реализации однопроходного интерпретатора. Этот
интерпретатор, написанный на языке Си, должен
прочитать, перевести на язык промежуточных команд
и
выполнить
программу,
написанную
на
алгоритмическом языке SPL. SPL (Simple Programming
24
Language) – простой язык программирования. Это
учебный язык высокого уровня, который напоминает
Паскаль и Си. Ему присущи все особенности
алгоритмических
языков
высокого
уровня,
за
исключением того, что работает он только с одним
типом данных – с целыми числами.
Язык SPL, как и любой алгоритмический язык,
являет собой набор символов и правил для ввода
интерпретации в ЭВМ.
3. 1 Символы

Буквы:
AZ ,
az
(только
латинского
алфавита).

Цифры: 0  9 .

Специальные символы: +, -, *, /, %, ( , ) ,  ;, =,
\n, \t – всего 13 символов.
3. 2 Идентификаторы
Это имена переменных, констант и функций.
Идентификатор – это последовательность букв или
25
букв и цифр, но первой должна быть буква.
Количество символов от 1 до 40, символы пробела,
перехода на новую строку (\n) и табуляции (\t) в
идентификаторе недопустимы.
3. 3 Константы
Это только целые числа. Описание констант
предваряется
служебным
словом
const.
Одна
константа от другой отделяется запятой. В конце
описания должна быть точка с запятой.
Пример: сonst a=12, b=4, d=0;
3. 4 Переменные
Переменные могут быть только целого типа.
Имеются
глобальные
и
локальные
переменные.
Глобальные переменные могут использоваться всеми
функциями, а локальные – только теми функциями, в
которых описаны. Описание переменных имеет вид:
int имя_переменной_1, имя_переменной_2.
26
Пример: int x, y, z, teta.
3. 5 Выражения
В выражениях применяются знаки +, -, *
(умножить), / (деление), % (остаток от деления).
3. 6 Служебные слова
Напомним, что это зарезервированные слова,
которые
нельзя
использовать
в
качестве
идентификаторов. Это следующие 11 слов: begin, end,
read, print, return, if, then, while, do, int, const.
3. 7 Функции
Как и в языке Си, программа на SPL состоит из
одной или нескольких функций. Количество функций
и их имена определяет программист, но одна функция
должна обязательно называться main, т.к. именно с нее
будет начинаться выполнение программы.
Описание программы имеет вид:
27
Имя_функции (формальные_параметры)
begin
описание локальных констант и переменных;
операторы
end
В отличие от Паскаля после end в конце
программы точка не ставится.
Операторы заканчиваются точкой с запятой.
Однако перед end точку с запятой ставить нельзя.
Оператор условной передачи управления if имеет вид:
if условие then последовательность операторов end
В качестве условия может быть имя переменной
или выражение. Если значение переменной или
выражения
больше
нуля,
тогда
выполняется
последовательность операторов между then и end.
Иначе переход на следующий за if оператор.
28
3. 8 Оператор цикла
while условие do
последовательность операторов
end
Как и для if, “условие” может представлять
собой переменную или выражение. Если переменная
или
результат
вычисления
больше
нуля,
тогда
вычисляется последовательность операторов между
словами do и end. После этого вновь проверяется
“условие”.
Если
результатом
проверки
условия
оказалось число  0, то происходит выход из цикла.
При использовании функции в языке SPL
результаты их работы можно возвращать или с
помощью оператора return или через глобальные
переменные,
которые
описываются
в
начале
программы вне области действия какой-либо из
функций. Как и для других языков высокого уровня, в
SPL допускаются рекурсии, когда функция может
вызывать саму себя.
29
Ниже приводится пример программы на SPL, в
которой, кроме главной функции main( ), также
используется функция возведения в степень аb.
Назовем ее exp ( ).
exp (а, b)
end;
begin
return z
int z;
end
z=1;
main ( )
while b do
begin
if b %2 then
int x, y;
z=z*a
read x;
end;
read y;
a=a*a;
print exp (x, y)
b=b/2
end
В главной функции описаны и введены с
клавиатуры две локальные переменные x, y. Затем в
строке print exp (x, y) происходит вызов функции exp
(), в которую вместо формальных параметров a и b
подаются соответствующие фактические параметры x
30
и y. Функция exp ( ) с помощью оператора return z
возвращает в main ( ) результат, который выводится на
экран.
Алгоритм
возведения
ab
предлагается
рассмотреть самостоятельно, например, вычисляя 27.
4 Лексический анализ
Сначала составим программу на языке Си,
которая
предназначена
только
лишь
для
распознавания лексем в тексте программы на языке
SPL, а также для размещения идентификаторов в
специальной таблице TNM. Эта таблица представляет
собой глобальный одномерный массив char TNM[400].
В процессе лексического анализа распознаются
лексемы:
 служебные слова;
 знаки операций и разделители;
 целые числа;
 идентификаторы;
 признак конца файла.
31
В программе каждой лексеме ставится в
соответствие
целое
число.
Знаки
операций
и
разделители закодированы литеральными константами
‘+’, ‘-‘, ‘*’, ‘/’, ‘%’, ‘=’, ‘(‘, ‘)’, ‘,’, ‘;’. Каждой из них
соответствует целое число из таблицы кодов ASCII
(М. Уэйт, С. Прата, Д. Мартин. Язык Си.- М.: Изд-во
“Мир”, 1988.- 512 с.). В таблице кодов ASCII также
закодированы целыми числами символы букв и цифр.
Обычно в этой таблице приводятся
коды 256
символов.
Для служебных слов имена лексем выбирает
программист. Предлагается эти лексемы обозначать
соответствующими заглавными буквами с буквой L в
конце. Например: служебными словами if и while
имена лексем соответственно IFL, WHILEL и т.д. А
связь с целыми числами определяется с помощью
перечисляемых констант целого типа.
enum {BEGINL=257, ENDL, READL, PRITL,
RETRL, IFL, THENL, WHILEL, DOL, INTL, CONSTL,
NUMB, IDEN },
32
где NUMB – лексема для целого числа;
IDEN – лексема для идентификатора.
Благодаря
применению
enum
лексема
{},
BEGINL получила числовое значение 257, ENDL – 258
и т.д. Таким образом, при написании программы нет
необходимости
помнить,
что
лексема
BEGINL
закодирована числом 257, а ENDL – 258. Везде, где
нужно сослаться на эти лексемы, удобнее писать
BEGINL,
ENDL
и
т.д.,
а
вместо
них
будут
представляться числа 257, 258 и т.д.
Кроме
перечисленных
лексем,
также
используется признак конца файла EOF. В файле
stdio.h есть директива препроцессора #define EOF – 1 .
Из нее видно, что EOF закодирован – 1.
Текст программы на Си для лексического
анализа
программы
на
SPL
приведен
ниже.
Целесообразно привести некоторые пояснения к этой
программе.
В
переменные
и
ней
используются
приведенные
выше
глобальные
в
перечисленные константы:
int lex; //распознанная лексема (целое число);
33
enum
{}
int lval; // значение лексемы. Для константы – это
числовое значение, а для идентификатора – адрес в
таблице идентификаторов TNM;
int nst=0; // номер считываемой строки в программе на
языке SPL;
char nch=’\n’; // считываемый символ из программы на
SPL;
char TNM [400]; // таблица идентификаторов;
char * ptn=TNM; // указатель на первый свободный
элемент в таблице идентификаторов. В самом начале
он стоит на TNM [0], т.к. имя массива TNM в языке Си
является адресом его самого первого (т.е. нулевого)
элемента;
FILE * PF; // указатель на файл с текстом программы
на SPL;
FILE * padres // указатель на файл, в который будут
заноситься адреса идентификаторов в таблице TNM.
В программе лексического анализа при вызове
функции main () используются параметры. Заголовок
функции main ( ) имеет вид:
void main (int av, char *
av [ ]). Здесь ac – количество параметров (символьных
34
строк); char* av [ ] – массив показателей. Конкретно,
в av [0] автоматически заносятся имя файла с готовой
для исполнения программой (например, part1.exe).
В av [1] следует занести самостоятельно имя файла с
текстом программы на языке SPL. Например, var2.s
Для этого необходимо, находясь в среде Borland C++ ,
вызвать
в
главном
меню
Run/arguments
и
в
появившееся диалоговое окно ввести требуемое имя
файла на SPL. В данном примере – это var2.s.
Предполагается, что var2.s находится в текущей
директории. Чтобы обеспечить обращение к этому
файлу при запуске Borland C++ из другой директории,
надо
ввести
полный
путь,
например,
C:\\USER\\SPL\\var2.s part1.s .
Для лучшего понимания работы лексического
анализа part1.c предлагается рассмотреть блок-схемы
отдельных его функций.
Вначале проверяется количество параметров ac.
Должно быть не меньше двух, например, part1.exe в
av[0] и var2.s в av [1]. Если параметров меньше двух,
происходит остановка работы программы. Иначе
35
открывается файл для чтения с текстом на SPL. В
примере это – var2.s и его имя находится в av [1]. Если
файл открылся, то происходит вызов функции get ( ),
которая является “поставщиком” лексем.
36
4. 1 Блок-схема функции void main (int ac,
char * av [ ])
1
Начало
5
2
Нет
ас<2
PF=fopen (av [1], “2”)
Да
3
6
Нет
PF=0
„Нет исходного
файла”
Да
7
9
get ()
„Файл SPL
не открылся”
4
exit
10
8
exit
37
Конец
В функции get () блок №1 в интерпретаторе должен
быть if(nch==EOF)
{ lex=EOF;
.-.-.-.-.-.-.-.return;
}
Однако программа part1.c предназначена только
лишь для того, чтобы прочитать текст программы на
SPL, распознать лексемы, а идентификаторы занести в
специальную таблицу идентификаторов char TNM
[400]. Поэтому в данной программе в функции get ( )
блок №1 являет собой цикл
while (nch!=EOF). Он
позволяет прочитать весь текст программы на SPL до
конца файла. Далее идет цикл
позволяющий
пропускать
while (isspace(nch)),
символы
пробела,
табуляции, перехода на новую строку и на новую
страницу.
Если символ nch, прочитанный из файла с
помощью функции get (), не является одним из
перечисленных
выше,
то
38
происходит
его
распознавание с вызовом функции
number( ) или
word( ).
Если nch является одним из перечисленных
специальных символов, например, ‘(’, ‘)’, ‘+’ и т.д., то
лексема
получает
соответствующее
значение,
считывается новый символ и происходит возврат в
цикл (блок №1). В противном случае выдается
сообщение о недопустимом символе и происходит
выход из программы.
4. 2 Блок-схема функции void number ()
Вход
1
lval = 0; пока nch цифра;
nch=getc(PF)
2
lval=10*lval+nch-‘0’
lex - NUMB
Вход
Блок №1 представляет собой цикл for ( ). В нем
значение лексемы lval вначале определяется. Затем
39
проверяется, что nch – цифра. Если это так, то
происходит вычисление нового значения lval в блоке
№2. Старое значение lval умножается на 10. К нему
прибавляется
результат
вычитания
из
кода
прочитанной цифры (nch) кода нуля. В таблице кодов
ASCII код нуля в “10” системе равен 48, код “1” – 49,
код “2” – 50 и т.д. Соответственно в
“16”-ричной
системе – (30)16, (31)16, (32)16 и т.д.
Таким
образом,
разница
кодов
позволяет
получить прочитанную цифру. Предположим, из
программы на SPL считывается константа 541. По
первой цифре “5” будет вызвана функция number ().
lval=0. nch содержит код цифры “5”, т.е. (35)16. Итак,
условие – является ли содержимое nch кодом цифры,
дает положительный ответ. В результате происходит
переход
к
блоку
№2.
В
нем
вычисляем
lval=10*0+(35)16 – (30)16 =5. После этого происходит
возврат в циклы, считывается из файла новое значение
nch. Это код (34)16 цифры “4”.
Теперь в блоке №2 lval=10*5+(34)16 – (30)16 =54.
40
После
считывания
кода
цифры
“1”
lval=10*54+(31)16 – (30)16=541.
После того как очередной прочитанный символ
окажется не цифрой, происходит выход из цикла.
Лексема получает значение NUMB, и происходит
выход из функции.
41
4. 3 Блок-схема функции void word ()
П
Вход
1
Описание массивов: char tx [40];
static char * serv [ ] = {“begin”, “end”…};
static int cdl [ ] = { BEGINL, ENDL,…};
указатель char * p;
2
p=tx; пока nch буква или цифра; nch=getc(PF)
3
* (p++) = nch;
4
*p = ‘\0’
5
i= 0; i<11; i++
8
нет
lex=IDEN
6
Строки tx и
serv[i] совпадают
9
да
lval=(int)add (tx)
7
lex=cdl[i]
10
Печать tx, lval
Выход
42
10
Блок №2 реализуется в виде цикла for. В
указатель p заносится адрес tx[0]. Затем проверяется
условие, что nch – буква или цифра. Если это так, то
реализуется блок №3. По адресу в указателе p, т.е. в
tx[0], заносится символ nch. После этого значение p
увеличивается на 1, то есть в р находится адрес tx [1].
Происходит возвращение в цикл. Считывается новое
значение nch. Если он буква или цифра, вновь
выполняется блок №2. теперь уже символ nch
заносится в tx [1], после чего в р заносится адрес tx [2]
и т.д. Если прочитанный символ nch – не буква и не
цифра, происходит выход из цикла. В очередной
элемент массива tx заносится ‘\0’ – признак конца
строки
символов.
В
tx
сформирована
некая
последовательность букв или букв и цифр. Теперь
предстоит проверить, что собой она представляет. Это
может быть одно из служебных (зарезервированных)
слов, перечисленных в массиве char*serv [] .
Проверка выполняется в цикле (блоки №5 и
№6). В случае совпадения строки символов tx и одного
43
из
служебных
слов
получает
lex
значение
соответствующего элемента из массива int cdl [ ].
Например, если в tx находится строка символов
end, то она совпадает с элементом end из seev [1] и
лексема
т.е.
lex=ENDL,
lex=258
(см.
enum={BEGINL=257, ENDL…};).
Если содержимое tx не совпало ни с одним
служебным словом, значит, - это идентификатор
переменной или функции. В этом случае lex=IDEN,
т.е. 269, и его необходимо занести в таблицу
идентификаторов
вызывается
указатель
char
функция
char*.
TNM
add
[400].
(tx).
Поэтому,
Для
этого
Она
возвращает
чтобы
присвоить
возвращаемый адрес переменной целого типа int lval,
применяется явное преобразование типа
lval=(int) add (tx).
44
4. 4 Блок-схема функции char*add(char*nm)
Вход
4
Нет
TNM
переполнен
а
1
Да
Описание
указателя char * P
5
„Переполнена
таблица TNM”
2
p=TNM; p<ptn;
p+=strlen(p)+1
exit
Нет
3
6
Списки с адресом
p и адресом mn
совпадают
return (strcpy(p,nm))
Да
return p
При вызове функции add (char*nm) вместо
формального параметра nm передается фактический
параметр
tx
(последовательность
символов
-
идентификатор). В блоках №2 и №3 в цикле
осуществляется проверка – не был ли ранее занесен в
TNM этот идентификатор. Для этого используется
45
указатель char*p. В блоке №2 вначале в p заносится
TNM, т.е. адрес TNM [0]. Затем проверяется условие
p<ptn. Следует вспомнить, что char*ptn был описан
выше в качестве глобального указателя на первый
свободный элемент в таблице идентификаторов TNM.
Если
p<ptn,
то
выполняется
прочитанных
из
TNM.
происходит
возвращение
В
сравнение
случае
адреса,
строк,
совпадения
где
этот
идентификатор был ранее записан. При несовпадении
происходит изменение адреса на длину строки только
что сравниваемого из TNM идентификатора плюс
один символ на ‘\0’.
p+=strlen (p) +1.
После этого вновь проверяется условие p<ptn и
т.д. Если условие p<ptn не выполнилось, это означает,
что проверены все ранее
записанные в TNM
идентификаторы. Тогда нужно изменить значение
указателя ptn на количество символов заносимого в
таблицу идентификатора плюс один.
ptn+=strlen(nm)+1.
46
Кроме того, нужно проверить, не выйдет ли
новое значение ptn за пределы таблицы TNM. Все это
оформлено в виде оператора
if ((ptn+=strlen(nm)+1)>TNM+400)
{
puts(“Переполнение таблицы TNM”);
exit (1);
}
return (strcpy(p, nm));
Если переполнения нет, то вызывается функция
копирования строк strcpy( ), которая строку nm
скопирует в р и адрес вернет в функцию word( ).
5 Полный синтаксис языка SPL
Из структурной схемы компилятора видно, что
после
лексического
анализа
следует
фаза
синтаксического анализа. То же самое имеет место и
для идентификатора. Для ее реализации нужно знать
основные конструкции языка SPL. Эти конструкции
обозначаются
нетерминальными
47
символами,
для
каждого
из
которых
имеется
правило
вывода.
Терминальными символами являются лексемы.
Как уже говорилось выше, язык SPL, как и
любой
другой
формальный
язык,
описывается
грамматикой. Конкретно, его полный синтаксис ниже
представлен
расширенной
последовательности
грамматикой
регулярных
в
выражений.
виде
Для
каждого нетерминального символа имеется отдельное
регулярное выражение. Условимся нетерминальные
символы
в
регулярных
выражениях
обозначать
заглавными буквами, а терминальные (лексемы) –
строчными. Помимо символов ‘*’,
‘,’,
‘\’ в
регулярных выражениях также будут использованы
квадратные
скобки
‘[‘,
‘]’
для
выделения
необязательных цепочек символов.
5. 1 Алфавит нетерминальных символов
1) PROG – программа;
2) DCONST – описание констант;
3) CONS – константа;
48
4) DVARB – описание переменных;
5) DFUNC – описание функций;
6) PARAM – параметры функции;
7) BODY – тело функции;
8) STML – последовательность операторов;
9) STAT – оператор;
10) EXPR – выражение;
11) TERM – слагаемое;
12) FACT – множимое;
13) FCTL – последовательность выражений.
Ниже приводятся 13 регулярных выражений,
представляющих полный синтаксис языка SPL. Первое
из них – для главного (стартового) нетерминального
символа PROG.
1) PROG  (DCONST | DFUNC | DVARB)* eof.
Здесь показано, что программа – это описание
констант или описание функции, или описание
переменных. Причем все эти описания заключены в
круглые скобки, после которых стоит звездочка.
Напомним, это означает, что эти описания могут
повторяться нуль и больше раз. То есть может
49
случиться, что ни одного из них нет. А за этими
описаниями обязательно должна быть лексема eof –
признак конца файла. Таким образом, в принципе
программа может состоять только из признака конца
файла.
2) DCONST  constl CONS (‘,’ CONS)* ‘;’.
Описание констант должно начинаться со
служебного слова const, которому соответствует
лексема constl в регулярном выражении. За лексемой
должен быть нетерминальный символ CONS. Затем
через запятую могут быть еще нуль и больше
констант. В конце описания констант должна быть ‘;’.
3) CONS  iden ‘=’ [‘+’ | ‘-‘] numb.
Константа являет собой идентификатор, за
которым
следует
‘=’,
а
за
ним
–
число
с
необязательным знаком.
Для лучшего понимания 2-го и 3-го регулярных
выражений вспомним, как описываются константы в
программе на языке SPL.
Например,const k=4, m=-5, q=125;
4) DVARB → intl iden (‘,’ iden) * ‘;’.
50
Перед описанием переменных должно быть
служебное слово int. Ему соответствует в регулярном
выражении лексема intl. Далее должен следовать
идентификатор.
Через запятые могут быть еще
идентификаторы. В конце описания ставится ‘;’.
5) DFUNC → iden PARAN BODY.
Вначале следует имя функции, а за ним –
описание параметров и тело функции.
6) PARAM → ‘(‘ [iden (‘,’ iden) *] ‘)’.
При описании параметров обязательно должны
быть круглые скобки. В них необязательно может
быть
идентификатор
или
последовательность
идентификаторов через запятую.
7) BODY → beginl (DVARB | DCONST)*STML endl.
Тело функции начинается служебным словом
begin, которому соответствует лексема beginl. Далее
могут следовать нуль и еще (много раз) описание
переменных или констант. Затем – последовательность
операторов и служебное слово end (лексема endl).
8)
STML → STAT (‘,’ STAT)*.
51
Последовательность операторов может состоять
из одного или их последовательности через ‘,’.
9)
STAT → iden ‘=’ EXPR |
readl iden |
printl EXPR |
retrl EXPR |
ifl EXPR thenl STML endl |
whilel EXPR dol STML endl.
Операторы в языке SPL следующие:
1 Оператор присвоения, когда переменной
присваивается результат вычисления выражения.
2 Чтение переменной.
3 Вывод на печать результата вычисления.
4 Возврат из функции результата вычисления.
5 Оператор условной передачи управления.
Проверяется результат вычисления выражения. Если
он больше нуля, то вычисляется последовательность
операторов,
расположенная
словами then и end.
52
между
ключевыми
6
Оператор
операторов,
цикла.
расположенная
Последовательность
между
служебными
(ключевыми) словами do и end, выполняется в цикле
до тех пор, пока результат вычисления выражения
после while больше нуля.
10) EXPR → [‘,’ | ‘-‘] TERM ((‘+’ | ‘-‘) TERM)*.
Выражение
представляет
собой
слагаемое,
перед которым необязательно может стоять знак.
Через знаки „+” или „-” могут также быть и другие
слагаемые.
11) TERM → FACT ((‘*’ | ‘/’ | ‘%’) FACT)*.
Слагаемое может состоять из одного множителя
или через знаки „*” или „/”, или „%” следовать другие
сомножители.
12) FACT → ‘(‘ EXPR ‘)’ | numb | iden [‘(‘[FCTL]’)’].
Множитель – это или выражение в круглых
скобках, или число, или идентификатор. Последний
может быть идентификатором переменной или же
функции. Тогда за идентификатором следуют круглые
скобки.
Внутри
них
последовательность выражений.
53
–
необязательная
13) FCTL → EXPR (‘,’ EXPR)*.
Это одно или несколько выражений через
запятую.
5. 2 Синтаксические диаграммы и
функции распознавания цепочек для
нетерминальных символов
Приведенные выше регулярные выражения, как
уже говорилось, описывают полный синтаксис языка
SPL. Теперь необходимо написать такую программу
на языке Си, которая читала бы символ за символом
слева направо такой программы на языке SPL и
осуществляла
синтаксический
анализ
текста
программы. Рассматривая весь текст программы на
SPL
как
одну
синтаксического
цепочку
анализа
символов,
программа
должна
решить,
принадлежит ли эта цепочка (программа на SPL)
языку, описываемому грамматикой, представленной
тринадцатью регулярными выражениями. В случае
наличия синтаксической ошибки программа должна
выдать сообщение о том, в какой строке программ на
54
SPL имеется ошибка, какая лексема и с какой не
совпадает. После чего анализ прекращается. В случае
отсутствия ошибок должно появиться сообщение, что
ошибок нет.
Перед тем как изучить последующий материал,
следует
повторить
материал
по
синтаксическим
диаграммам: что это такое и зачем, как обозначаются
дуги
и
правила
прохождения
дуг,
помеченных
терминальными и нетерминальными символами. Суть
в том, что по каждому регулярному выражению
изображается
соответствующая
синтаксическая
диаграмма, а по ней пишется текст функции на Си
для
распознавания
цепочек,
выводимых
для
рассматриваемого нетерминального символа. В этих
функциях часто вызывается функция get ( ), которая
возвращает лексему lex с ожидаемой lx. Для этого
служит функция exam ( ). В случае совпадения lex и lx
считывается новая лексема (вызывается get ( )). При
несовпадении выдается сообщение и прекращается
работа программы. Такая функция exam ( ) приводится
ниже.
55
void exam (int lx)
{
if (lex!=lx)
{
printf (“Не совпадают лексемы lex=%i и lx=%i в
строке nst=%i \n”,
lex, lx, nst);
exit (1);
}
get ( );
return;
}
Текст программы part2.c на Си, которая
осуществляет лексический и синтаксический анализы,
приведен в главе 6. В эту программу полностью
входит рассмотренная ранее программа лексического
анализа
part1.c,
однако
в
ней
имеются
одно
дополнение и одно изменение. В функции main( )
после вызова get( ) следует также вызвать функцию
вывода для главного нетерминального символа prog( ).
Изменение внесено в самом начале функции
get( ). Вместо
56
while (nch!=EOF)
{…
…
}
следует
if (nch = = EOF)
{
lex = EOF;
return;
}
………
Таким образом, функцией
get( ) при каждом
обращении к ней выдается только одна
очередная
лексема.
Кроме того, part2.c отличается от part1.c
наличием
функций,
соответствующих
каждому
нетерминальному символу. Как уже сказано выше, для
их
написания
используются
синтаксические
диаграммы, полученные в строгом соответствии с
регулярными выражениями. Ниже рассмотрим, как это
делается конкретно. Первым должен рассматриваться
главный нетерминальный символ PROG.
57
1 PROG → (DCONST |DVARB |DFUNK) * eof
DFUNK
DVARB
DCONST
PROG
eof
void prog( )
{
while (lex!=EOF)
{
switch (lex)
{
case IDEN: dfunc( ); break;
case INTL: dvarb( ); break;
case CONSTL: dconst( ); break;
default: printf(“Ошибка синтаксиса в строке
nst=%i. Лексема lex=%i \n”, nst,
lex);
}
}
return;
}
По диаграмме видно, что если lex!=EOF, то
реализуется разветвление по нескольким ветвям в
зависимости от lex. Лексема lex может быть IDEN,
если описывается функция, INTL при описании
переменных и CONSTL – констант. Естественно
58
производится вызов одной из функций dfunc( ),
dvarb( ), dconst( ).
2 DCONST → constl CONS (‘,’ CONS)* ‘;’
DCONST
const
CONS
CONS
‘,’
void dconst( )
{
// Нет неиспользованной “свежей” лексемы
// Ее нужно получить, вызвав get( );
do
{
get( );
cons( );
} while (lex = = ‘,’);
exam (‘;’);
return;
}
Вначале
проходная
дуга,
помеченная
терминальным символом constl. Проверка того, что
лексема lex была равна constl, осуществляется в
функции prog( ). Именно там по switch (lex) в случае
case CONSL была вызвана функция dconst( ). Перед
прохождением
следующей
59
дуги,
обозначенной
нетерминальным символом CONS, согласно правилу
прохождения дуг синтаксической диаграммы должен
быть прочитан очередной терминальный символ
(лексема).
Напомним,
что
лексема
constl
уже
использована. “Свежей” лексемы нет. Поэтому в
функции dconstl перво-наперво считывается новая
лексема ( вызов get( )), а затем уже вызывается
функция cons( ), соответствующая нетерминальному
символу CONS. Цикл осуществляется пока lex = =’,’. В
случае выхода из цикла проверяется условие, что
lex==’;’. Для этого вызывается exam (‘;’). Если это
условие выполняется, то функция exam( ) в конце
вызывает get( ) и, таким образом, поставляет “свежую”
лексему,
необходимую
для
дальнейшего
синтаксического анализа.
3 CONS → iden ’=’ [‘+’|’-’] numb
‘+’
CONS
iden
‘=’
numb
‘-’
60
//“Свежая” лексема есть
void cons( )
{
exam (IDEN);
exam (‘=’);
if (lex = = ‘+’ || lex = = ‘-‘)
get( );
exam (NUMB);
return;
}
При
прохождении
дуги,
помеченной
терминальным символом, проверяется совпадение
имеющейся лексемы lex с той, которая должна быть.
Это делает функция exam( ). При совпадении
происходит чтение следующей лексемы и т.д. Здесь
проверяется, чтобы была лексема IDEN, затем ‘=’.
Далее необязательный знак ‘+’ или ‘-‘ и в конце –
NUNB.
4 DVARB → intl iden (‘,’ iden) * ‘;’
DVARB
intl
‘;’
iden
iden
61
‘;’
// “Свежей” лексемы нет. Лексема INTL была
использована в функции prog( ) в
// switch (lex). По
ней была вызвана функция dvarb( ).
void dvarb( )
{
do
{
get( );
exam(IDEN);
} while(lex = = ‘,’);
exam(‘;’);
return;
}
В связи с отсутствием “свежей” лексемы
необходимо
вызвать
get(
).
Затем
проверяется,
является ли полученная лексема IDEN. В случае
совпадения в конце функции exam( ) следует вызов
get( ). Если будет прочитана лексема ”запятая”, то
вновь в цикле do while повторяются get( ) и
exam(IDEN). После того как очередная лексема не
будет равной ‘,’ , происходит выход из цикла и
проверяется наличие ‘;’.
62
5 DFUNK → iden PARAM BODY
DFUNK
iden
PARAM
BODY
// Лексема IDEN была использована в функции
prog( ) в switch(lex). Нужно
//вызвать get( ) для
получения новой лексемы.
void dfunc( )
{
get( ); // получение новой лексемы
param( );
body( );
return;
}
6 PARAM → ‘(’ [iden (‘,’ iden)*] ‘)’
PARAM
‘(’
‘)’
‘,’
iden
iden
63
Перед вызовом функции param( ) из dfunc( ) был
вызов get( ). Следовательно, “свежая” лексема
имеется.
void param( )
{
exam (‘(‘);
if (lex!=’)’ )
{
exan (IDEN);
while (lex = =’,’)
{
get( );
exam (IDEN);
}
}
exam (‘)’);
return;
}
В соответствии с синтаксической диаграммой
вначале проверяется, является ли прочитанная лексема
левой скобкой ‘(‘. При совпадении в функции (‘)’).
При совпадении в функции exam( ), как уже не раз
подчеркивалось, вызывается get( ) и поставляет новую
лексему. И если это не ’)’, то должна быть IDEN. И
вновь-таки, если после проверки лексемы IDEN была
прочитана лексема ‘,’, то в цикле идет exam (IDEN).
64
После выхода из цикла лексема должна быть ‘)’, и
никакая другая.
7 BODY → beginl (DCONST |DVARB) * STML endl
BODY
beginl
STML
endl
DVARB
DCONST
Функция bоdy( ) вызывается из dfunc( ) после
param( ). Функция param( ) заканчивается вызовом
exam(‘)’). Следовательно, в случае успешной проверки
будет вызвана get( ) и появится новая лексема. Она
должна быть BEGINL.
void body( )
{
exam (BEGINL);
while(lex = = INTL || lex = = CONSTL)
if (lex = = INTL) dvarb( );
else
dconst( );
stml( );
exam(ENDL);
return;
}
65
8 STML → STAT (‘;’ STAT)*
STML
STAT
STAT
‘;’
Перед вызовом stml( ) выполнялось dconst( ). В
начале этой функции есть exam(‘;’). При успешной
проверке вызывается get( ), и она возвращает новую
лексему.
void stml( )
{
stat( );
while (lex = = ‘;’)
{
get( );
stat( );
}
return;
}
9 STAT → iden ‘=’ EXPR |readl iden |pritl EXPR
|retrl EXPR |ifl EXPR thenl STML endl |whilel EXPR
dol STML endl
66
STAT
iden
‘=’
readl
iden
pritl
EXPR
retrl
EXPR
ifl
EXPR
thenl
STML
endl
while
EXPR
dol
STML
endl
EXPR
Перед вызовом stat( ) “свежая” лексема есть. В
зависимости от ее значения идет разветвление по witch
(lex).
void stat( )
{
switch (lex)
{
case IDEN: get( ); exam (‘=’); expr( ); break;
case READL: get( ); exam (IDEN); break;
case PRITL: get( ); expr( ); break;
case RETRL: get( ); expr( ); break;
case IFL: get( ); expr( ); exam(THENL);
stml( ); exam(ENDL); break;
case WHILEL: get( ); expr( ); exam(DOL);
stml( ); exam(ENDL); break;
default: printf(“stat nst=%i \n”, nst);
}
return;
}
67
10 EXPR → [‘+’|’-’] TERM ((‘+’|’-’)TERM)*
‘+’
EXPR
TERM
‘+’
‘-’
TERM
‘-’
//“Свежая” лексема есть
void expr( )
{
if (lex = = ‘+’ || lex = = ‘-‘)
get( ); term( );
while (lex = = ‘+’ || lex = = ‘-‘)
{
get( );
term( );
}
return;
}
68
6 Пояснения к выполнению курсовой
работы
Постановка задачи. Составить программу на Си для
лексического
анализа
программы
на
модифицированном языке SPL. Вывести в файл
"getrez.dan" и на экран таблицу идентификаторов и их
адресов.
Программа на модифицированном языке SPL
(вычисляет xy)
exp(a,b)
begin int z;
z=1;
while b do
if b%2 then z=z*a end;
a=a*a;b=b/2
end;
return z
end
main()
begin int x,y;
read x;
read y;
print exp(x,y)
end
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<conio.h>
#include<alloc.h>
#include<string.h>
69
/*Коды лексем языка SPL*/
enum{BEGINL=257,ENDL,IFL,THENL,WHILEL,DOL,
RETRL,READL,PRITL,INTL,CONSTL,IDEN,NUMB};
int nst=0;
int lval,lex;
static char nch='\n';
FILE*PF,*padres;
void get(void);
void number(void);
void word(void);
char*add(char*nm);
void main(int ac,char*av[])
{
clrscr();
if(!ac)
puts("Hет исходного файла");
PF=fopen(av[1],"r");
padres=fopen("getrez.dan","w");
if(!PF)
puts("Файл не открывается");
else
get();
}
void get()
{
while(nch!=EOF)
{
while(isspace(nch))
{
70
if(nch=='\n')
nst++;
nch=getc(PF);
}
if(isdigit(nch))
number();
else
if(isalpha(nch))
word();
else
if(nch=='('||nch==')'||nch==','||nch==';'||nch=='='||nch=='+'||
nch=='-'||nch=='*'||nch=='/'||nch=='%')
{
lex=nch;
nch=getc(PF);
}
}
if(nch==EOF)
lex=EOF;
else
puts("Hедопустимый символ");
return;
}
void number()
{
for(lval=0;isdigit(nch);nch=getc(PF))
lval=lval*10+nch-'0';
lex=NUMB;
return;
}
void word()
{
71
static int
cdl[]={BEGINL,ENDL,IFL,THENL,WHILEL,DOL,RET
RL,READL,
PRITL,INTL,CONSTL,IDEN,NUMB};
static
char*serv[]={"begin","end","if","then","while","do","retur
n","read",
"print","int","const"};
int i;
char tx[40];
char*p,*add();
for(p=tx;isdigit(nch)||isalpha(nch);nch=getc(PF))
*(p++)=nch;
*p='\0';
for(i=0;i<11;i++)
if(strcmp(tx,serv[i])==0)
{
lex=cdl[i];
return;
}
lex=IDEN;
lval=(int)add(tx);
printf("Адрес для %s =%p\n",tx,lval);
fprintf(padres,"Адрес для %s =%p\n",tx,lval);
return;
}
char TNM[400];
char*ptn=TNM;
char*add(char*nm)
{
72
char*p,*strcpy();
for(p=TNM;p<ptn;p+=strlen(p)+1)
if(strcmp(p,nm)==0)
return p;
if((ptn+=strlen(nm)+1)>TNM+400)
{
puts("Переполнение таблицы");
exit(0);
}
return(strcpy(p,nm));
}
Образец результата работы программы
Адрес для exp =0586
Адрес для a =058B
Адрес для b =058D
Адрес для z =058F
Адрес для z =058F
Адрес для b =058D
и т. д.
73
6. 1 Пример выполнения курсовой работы
Вариант задания курсовой работы
Составить программу на Си для лексического анализа
программы на модифицированном языке SPL,в
котором все ключевые слова переведены на русский
язык и вместо
begin и end используются
соответственно левая и правая фигурные скобки.
Вывести в файл и на экран таблицу идентификаторов
и их адресов.
Вариант программы на модифицированном языке SPL
(вычисляет xy), где по условию задачи все ключевые
слова заменены на соответствующие на русском
языке, а вместо begin
и end используются
соответственно { и }
возврат z
}
main()
{
целый x,y;
читать x;
читать y;
печатать exp(x,y)
exp(a,b)
{
целый z;
z=1;
пока b делать
если b%2 тогда z=z*a };
a=a*a;b=b/2
};
}
Замечание: жирным шрифтом выделены изменения,
которые необходимо внести в предыдущую
программу, согласно варианту задания.
74
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<conio.h>
#include<alloc.h>
#include<string.h>
/*Коды лексем языка SPL*/
enum{BEGINL=257,ENDL,IFL,THENL,WHILEL,DOL,
RETRL,READL,PRITL,INTL,CONSTL,IDEN,NUMB};
int nst=0;
int lval,lex;
static char nch='\n';
FILE*PF,*padres;
void get(void);
void number(void);
void word(void);
char*add(char*nm);
int isalpharus(char c);
int isspace1(char c);
int isdigit1(char c);
void main(int ac,char*av[])
{
clrscr();
if(!ac)
puts("Hет исходного файла");
PF=fopen(av[1],"r");
padres=fopen("getrez.dan","w");
if(!PF)
75
puts("Файл не открывается");
else
get();
}
void get()
{
while(nch!=EOF)
{
while(isspace1(nch))
{
if(nch=='\n')
nst++;
nch=getc(PF);
}
if(isdigit1(nch))
number();
else
if(isalpharus(nch))
word();
else
if(nch=='('||nch==')'||nch==','||nch==';'||nch=='='||nch=='+'||
nch=='-'||nch=='*'||nch=='/'||nch=='%'||nch=='{'||nch=='}')
{
lex=nch;
nch=getc(PF);
}
}
if(nch==EOF)
lex=EOF;
else
puts("Hедопустимый символ");
return;
76
}
void number()
{
for(lval=0;isdigit1(nch);nch=getc(PF))
lval=lval*10+nch-'0';
lex=NUMB;
return;
}
void word()
{
static int
cdl[]={BEGINL,ENDL,IFL,THENL,WHILEL,DOL,RET
RL,READL,
PRITL,INTL,CONSTL,IDEN,NUMB};
static
char*serv[]={"{","}","если","тогда","пока","делать
","возврат","читать","печатать","целый","const"};
int i;
char tx[40];
char*p,*add();
for(p=tx;isdigit1(nch)||isalpharus(nch);nch=getc(PF))
*(p++)=nch;
*p='\0';
for(i=0;i<11;i++)
if(strcmp(tx,serv[i])==0)
{
lex=cdl[i];
return;
}
lex=IDEN;
lval=(int)add(tx);
printf("Адрес для %s =%p\n",tx,lval);
77
fprintf(padres,"Адрес для %s =%p\n",tx,lval);
return;
}
char TNM[400];
char*ptn=TNM;
char*add(char*nm)
{
char*p,*strcpy();
for(p=TNM;p<ptn;p+=strlen(p)+1)
if(strcmp(p,nm)==0)
return p;
if((ptn+=strlen(nm)+1)>TNM+400)
{
puts("Переполнение таблицы");
exit(0);
}
return(strcpy(p,nm));
}
/* Добавляем к исходной программе функции
пользователя для распознавания букв, специальных
знаков и цифр */
int isalpharus(char c)
{
int v;
if(isalpha(c)||c=='а'||c=='б'||c=='в'||c=='г'||c=='д'||c=='е'
||c=='ж'||c=='з'||c=='д'||c=='е'||c=='ж'||c=='з'||c=='и'||
c=='й'||c=='к'||c=='л'||c=='м'||c=='н'||c=='о'||c=='п'||
c=='р'||c=='с'||c=='т'||c=='у'||c=='ф'||c=='х'||c=='ц'||c==
78
'ч'||c=='ш'||c=='щ'||c=='ы'||c=='э'||c=='ю'||c=='я'||c==
'ь')
v=8;
else
v=0;
return v;
}
int isspace1(char c)
{
int v;
if(c=='\ '||c=='\n'||c=='\t'||c=='\f')
v=1;
else
v=0;
return v;
}
int isdigit1(char c)
{
int v;
if(c=='0'||c=='1'||c=='2'||c=='3'||c=='4'||c=='5'||c=='6'||c=
='7'||c=='8'||c=='9')
v=2;
else
v=0;
return v;
}
79
6. 2 Варианты заданий для курсовой
работы
Вариант 1
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL,в котором все
ключевые слова переведены на русский язык и вместо
begin и end используются соответственно левая и
правая фигурные скобки. Вывести в файл и на экран
таблицу идентификаторов и их адресов.
Вариант 2
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL,в котором все
ключевые слова переведены на украинский язык и
вместо begin и end используются соответственно
левая и правая фигурные скобки. Вывести в файл и на
экран таблицу идентификаторов и их адресов.
Вариант 3
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором
все
ключевые слова переведены на русский язык и вместо
begin и end используются слова start и finish. Вывести
в файл и на экран таблицу идентификаторов и их
адресов.
80
Вариант 4
Составить программу на СИ для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова переведены на украинский язык и вместо begin и
end bиспользуются слова start и finish. Вывести в файл
и на экран таблицу идентификаторов и их адресов.
Вариант 5
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором все
ключевые слова переведены на русский язык, а вместо
Int используется Var. Вывести в файл и на экран
таблицу идентификаторов и их адресов.
Вариант 6
Составить программу на СИ для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова переведены на украинский язык и вместо слова
NUMB используется DIGIT. Вывести в файл и на
экран таблицу идентификаторов и их адресов.
Вариант 7
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова BEGIN, END, READ, PRINT заменены
соответствующими словами на русском языке, а
вместо int используется var. Вывести в файл и на экран
таблицу идентификаторов и их адресов.
81
Вариант 8
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL,в котором ключевые
слова WHILE, DO, END, IF, THEN переведены на
русский язык, а точка с запятой заменена двоеточием.
Вывести в файл и на экран таблицу идентификаторов
и их адресов.
Вариант 9
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором все
ключевые слова переведены на русский и вместо
точки с запятой используется символ @. Вывести в
файл и на экран таблицу идентификаторов и их
адресов.
Вариант 10
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором все
ключевые слова переведены на украинский язык, а
вместо Int используется Var. Вывести в файл и на
экран таблицу идентификаторов и их адресов.
Вариант 11
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова BEGIN, END, READ, PRINT переведены на
82
русский язык, а вместо точки с запятой используется
символ @. Вывести в файл и на экран таблицу
идентификаторов и их адресов.
Вариант 12
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова RETURN, IF, THEN, WHILE, DO переведены на
украинский язык, а вместо END используется символ
@. Вывести в файл и на экран таблицу
идентификаторов и их адресов.
Вариант 13
Составить программу на СИ для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова WHILE, DO, END, IF, THEN переведены на
украинский язык, а вместо int используется слово var.
Вывести в файл и на экран таблицу идентификаторов
и их адресов.
Вариант 14
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором все
ключевые слова переведены на украинский язык, а
вместо точки с запятой используется символ ^.
Вывести в файл и на экран таблицу идентификаторов
и их адресов.
83
Вариант 15
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором BEGIN,
END заменены на { и }, а все остальные ключевые
слова переведены на украинский язык. Вывести в файл
и на экран таблицу идентификаторов и их адресов.
Вариант 16
Составить программу на СИ для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова переведены на русский язык, а вместо точки с
запятой используется символ #. Вывести в файл и на
экран таблицу идентификаторов и их адресов.
Вариант 17
Составить программу на СИ для лексического и
синтаксического
анализов
программы
на
модифицированном языке, в котором BEGIN и END
заменены соответственно на {и }, а READ, PRINT,
RETURN переведены на русский язык. Вывести в
файл и на экран таблицу идентификаторов и их
адресов.
Вариант 18
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова read,print,return переведены на украинский язык,
а вместо end служит символ @. Вывести в файл и на
экран таблицу идентификаторов и их адресов.
84
Вариант 19
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором BEGIN,
END заменены соответственно на { и }, а все
остальные ключевые слова переведены на русский
язык. Вывести в файл и на экран таблицу
идентификаторов и их адресов.
Вариант 20
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL,в котором ключевые
слова переведены на украинский язык, а вместо точки
с запятой используется символ #. Вывести в файл и на
экран таблицу идентификаторов и их адресов.
Вариант 21
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором все
ключевые слова переведены на русский язык, а вместо
Int используется Var. Вывести в файл и на экран
таблицу идентификаторов и их адресов.
Вариант 22
Составить программу на СИ для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова переведены на украинский язык и вместо слова
85
NUMB используется DIGIT. Вывести в файл и на
экран таблицу идентификаторов и их адресов.
Вариант 23
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова BEGIN, END, READ, PRINT заменены
соответствующими словами на русском языке, а
вместо int используется var. Вывести в файл и на экран
таблицу идентификаторов и их адресов.
Вариант 24
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова WHILE, DO, END, IF, THEN переведены на
русский язык, а точка с запятой заменена двоеточием.
Вывести в файл и на экран таблицу идентификаторов
и их адресов.
Вариант 25
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором переведены
на русский язык и вместо точки с запятой
используется символ @. Вывести в файл и на экран
таблицу идентификаторов и их адресов.
Вариант 26
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
86
модифицированном языке SPL, в котором все
ключевые слова переведены на украинский язык, а
вместо Int используется Var. Вывести в файл и на
экран таблицу идентификаторов и их адресов.
Вариант 27
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова BEGIN, END, READ, PRINT переведены на
русский язык, а вместо точки с запятой используется
символ @. Вывести в файл и на экран таблицу
идентификаторов и их адресов.
Вариант 28
Составить программу на Си для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова RETURN, IF, THEN, WHILE, DO переведены на
украинский язык, а вместо END используется символ
@.
Вариант 29
Составить программу на СИ для лексического и
синтаксического
анализов
программы
на
модифицированном языке SPL, в котором ключевые
слова WHILE, DO, END, IF, THEN переведены на
украинский язык, а вместо int используется слово var.
87
Вариант 30
Составить программу на Си для лексического анализа
программы на модифицированном языке SPL, в
котором все ключевые слова переведены на
украинский язык, а вместо точки с запятой
используется символ ^.
6. 3 Требования к оформлению курсовой
работы
Курсовая работа должна содержать:
1) титульный лист (с указанием номера варианта);
2) постановку задачи;
3) пояснительную записку:
o задача лексического анализа;
o задача синтаксического анализа;
o понятие лексемы;
o понятие „регулярное выражение”;
o кратко описать назначение функций
лексического анализа: (get(); number(); word();
add(char*nm));
o записать регулярные выражения для своего
варианта;
o инструкция пользователю (как запустить
программу на выполнение);
4) текст программы на SPL;
5) текст программы на Cи;
6) таблицу идентификаторов и их адресов (файл
getres.dan);
88
7) выводы
(чему
научились,
применить);
8) список литература.
где
можно
7 Вопросы к экзамену
Экзаменационный билет включает два вопроса:
первый - вопрос по теории, второй - составить
программу языком системного программирования Си.
Задача на программирование подобна тем, которые
имели место в контрольной работе по дисциплине
„Алгоритмические языки”.
1 Структура программного обеспечения ЭВМ.
Назначение системных программ.
2 Формальные языки: алфавит, операции над
символами, слово и определение формального
языка.
3 Регулярные выражения: определение, примеры.
Грамматика, правила вывода цепочек.
4 Расширенные грамматики. Пример определения
формального языка, который описывается заданной
грамматикой.
5 Задача анализа цепочки символов. Стратегии
анализа. Пример.
6 Синтаксические диаграммы: определение, условия
для LA(1)-анализа. Правила прохождения дуг
синтаксической диаграммы.
7 Трансляторы: назначение, типы трансляторов.
Структура и проходы компилятора.
89
8 Алгоритмический язык SPL: символы, переменные,
константы, служебные слова, функции.
9 Лексический анализ: определение лексемы, виды
лексем, которые различаются при анализе текста на
алгоритмическом языке.
10 Структура главной функции интерпретатора для
языка SPL.
11 Структура функции get() интерпретатора для языка
SPL.
12 Структура функции number() интерпретатора для
языка SPL.
13 Структура функции word() интерпретатора для
языка SPL.
14 Структура функции add() интерпретатора для языка
SPL.
15 Полный синтаксис языка SPL.
16 Написать регулярное выражение, синтаксическую
диаграмму и соответствующую функцию для
нетерминального символа PROG.
17 Написать регулярное выражение, синтаксическую
диаграмму и соответствующую функцию для
нетерминального символа DCONST.
18 Написать регулярное выражение, синтаксическую
диаграмму и соответствующую функцию для
нетерминального символа CONS.
19 Написать регулярное выражение, синтаксическую
диаграмму и соответствующую функцию для
нетерминального символа DVARB.
20 Написать регулярное выражение, синтаксическую
диаграмму и соответствующую функцию для
нетерминального символа DFUNC.
90
21 Написать регулярное выражение, синтаксическую
диаграмму и соответствующую функцию для
нетерминального символа PARAM.
22 Написать регулярное выражение, синтаксическую
диаграмму и соответствующую функцию для
нетерминального символа BODY.
23 Написать регулярное выражение, синтаксическую
диаграмму и соответствующую функцию для
нетерминального символа STML.
24 Написать регулярное выражение, синтаксическую
диаграмму и соответствующую функцию для
нетерминального символа STAT.
25 Написать регулярное выражение, синтаксическую
диаграмму и соответствующую функцию для
нетерминального символа EXPR.
26 Написать регулярное выражение, синтаксическую
диаграмму и соответствующую функцию для
нетерминального символа TERM.
27 Написать регулярное выражение, синтаксическую
диаграмму и соответствующую функцию для
нетерминального символа FACT.
28 Написать регулярное выражение, синтаксическую
диаграмму и соответствующую функцию для
нетерминального символа FCTL.
91
Список литературы
1. Проценко В.С., Чаленко П.Й., Ставровський А.Б.
Техніка програмування мовою Сі.- Київ: «Либідь»,
1993.
2. Керниган Б., Ритчи Д. Язык программирования Си.- М.:
Финансы и статистика, 1992.
92
Учебное
пособие
по
дисциплине
«Системное
программирование и операционные системы» для
студентов всех специальностей заочной формы обучения /
Составители: А.Н. Скаковская, В.В. Авраменко – Сумы:
изд-во СумГУ, 2006. – 94 с.
Кафедра информатики
93
Download