Условные операторы и описание простых типов языка Си. Предиктивный анализатор

 

АННОТАЦИЯ


Целью курсовой работы является:

-овладение и закрепление навыков построения лексического анализатора;

-овладение и закрепление навыков построения синтаксического анализатора;

исследование принципов работы компилятора на каждом этапе обработки входной последовательности;

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

овладение и закрепление навыков трансляции в форму внутреннего представления: обратную польскую запись.

Перечень терминов:

ЛА - Лексический анализ

СА - Синтаксический анализ

ОПД - Обратная польская запись

ЯВУ - Язык высокого уровня

МП-автомат - автомат с магазинной памятью


ВВЕДЕНИЕ


Данный документ содержит описание и характеристику программы, написанной на основе задания на курсовую работу по теме «Условные операторы описание простых типов языка Си. Предиктивный анализатор».

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

В приложении 1 приведено техническое задание на курсовую работу.

В приложении 2 содержатся блок-схемы алгоритмов программы.

Приложение 3 представляет собой руководство пользователя.

Приложение 4 содержит граф конечного автомата

Приложение 5 содержит листинг программы.

Данный программный продукт является программной реализацией компонентов транслятора (таких как лексический и синтаксический анализ) для некоторых конструкций языка программирования Си.

Использование этого программного средства направлено на:

изучение принципов работы лексического анализатора,

изучение принципов работы синтаксического анализатора,

- анализ принадлежности входных строк искомой грамматике,

- трансляцию заданной инструкции в форму внутреннего представления,

оценку и исправление возможных ошибок,

в рамках учебного курса по дисциплине «Теория языков программирования и методов трансляции».


1. Общие сведения


Программный продукт предназначен для анализа входной последовательности символов на соответствие грамматике, указанной в техническом задании

).. Программа позволяет:

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

? на основе построенной грамматики провести синтаксический анализ, результатом которого являются цепочка вывода и дерево вывода;

? транслировать инструкции в одну из форм внутреннего представления: обратную польскую запись.

Для работы программы необходима операционная система Windows 9x / Me / NT /XR. Программа также предусматривает работу с нестандартным компонентом dxOrgChart (производится вывод деревьев в удобной для просмотра форме), для чего потребуется установить в Delphi палитру компонентов ExpressOrgChart.


1.1 Функциональное назначение


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

·Перевод исходной программы на внутренний язык компилятора, т. е. лексическая обработка;

·Синтаксический анализ входной последовательности на соответствие грамматике;

·Построение дерева разбора, строки вывода синтаксического разбора;

·Построение обратной польской записи как формы внутреннего представления программы.


1.2 Описание логической структуры


.2.1 Общая схема работы компилятора

Компиляторы составляют существенную часть программного обеспечения ЭВМ. Это связано с тем, что ЯВУ стали основными средствами разработки программ.

Общая схема работы компилятора показана на рис.1, из неё видно, что процесс компиляции состоит из двух основных этапов - анализа и синтеза.



Рис.1 Общая схема работы компилятора

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

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

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

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

Фазы компиляции.

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

a) Лексический анализ:

Это основная часть компилятора, которая читает литеры программы на исходном языке и строит из них слова (лексемы) исходного языка. На вход лексического анализатора поступает текст исходной программы, а выходная информация передаётся для дальнейшей обработки компилятором на этапе синтаксического разбора.

б) Синтаксический разбор

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

в) Семантический анализ

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

г) Подготовка к генерации кода

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

д) Генерация кода

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

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

1.2.2 Описание предметной области

Предметной областью являются конструкции условных операторов if-else и ?: и простые типы языка Си.

Данные операторы имеют следующий вид:

) if условие оператор_1;

) if условие оператор_1;

else оператор_2;

) условие ? оператор_1;

) условие ? оператор_1 : оператор_2;

Простые типы в языке Си объявляются так:

тип переменная_1, переменная_2, … переменная_n;

Рассмотрим элементы этих конструкций. Можно выделить следующие зарезервированные слова: if, else, ?, :.. После if находится выражение условие. Это выражение должно быть логического типа, т.е однозначно определяться как «истина» или «ложь». Если логическое выражение принимает значение «истина», то выполняется оператор оператор_1. Иначе, если есть зарезервированное слово else, то выполняется оператор оператор_2. В конструкции с оператором ?: перед зарезервированным словом ? находится выражение условие, которое также должно быть логического типа. Если это выражение принимает значение «истина», то выполняется оператор оператор_1. Иначе, если есть зарезервированное слово :, то выполняется оператор оператор_2. В описании переменных переменная_1, переменная_2, … переменная_n - список переменных в которых имена переменных разделены запятыми, тип - задает тип переменных из данного списка и является идентификатором типа.


1.2.3 Фаза компиляции «Лексический анализатор»

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

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

Этапы работы лексического анализатора:

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

. После этого проводится идентификация лексемы. Она выполняется путём сравнения лексемы с эталонным значением. Сначала проверяется, не относится ли лексема к классу операторов. Если нет, то идёт проверка на то, не является ли лексема разделителем. Далее последовательно проверяется принадлежность лексемы к классам идентификаторы и ключевые слова.

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

В рамках задания на курсовую работу представлены следующие классы лексем (Таблицы 1-4):


Табл. 1 Класс лексем «Операторы»

ЛексемаКод замены>><<==++--**//==r!=a

Табл. 2 Класс лексем «Разделители»

ЛексемаКод замены;;,,(()){{}}

Табл. 3 Класс лексем «Идентификаторы»

ЛексемаКод заменыidd typetnumbern

Табл. 4 Класс лексем «Ключевые слова»:

ЛексемаКод заменыifielsee?f:l

Рассмотрим работу лексического анализатора (ЛА) на примере:

На вход ЛА подаются следующие входные данные:


boolean k;a;

{k==0 a=1; else a=5;

}


ЛА читает текст исходной программы и выделяет в её тексте лексемы входного языка.

Таким образом, на выходе лексического анализатора мы имеем следующую цепочку:


td;td;{idrnd=n;ed=n;}


Эта цепочка подается на вход синтаксического анализатора (СА).


1.2.4 Фаза компиляции «Синтаксический анализатор»

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

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

Выходом лексического анализатора является таблица лексем (или цепочка лексем). Эта таблица подается на вход синтаксического анализатора, который исследует только один компонент каждой лексемы - её тип.

Синтаксический анализ (или разбор) - это процесс, в котором исследуется таблица лексем и устанавливается, удовлетворяет ли она структурным условиям, явно сформулированным в определении синтаксиса языка.

Согласно индивидуальному заданию рассмотрим LL-метод (предиктивный анализатор).

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

Существует класс грамматик, основанный именно на этом принципе - выборе одной альтернативы из множества возможных на основе нескольких очередных символов в цепочке. Это так называемые LL(k)-грамматики. Грамматика обладает свойством LL(k), k > 0, если на каждом шаге вывода для однозначного выбора очередной альтернативы МП-автомату достаточно знать символ на верхушке стека и рассмотреть первые k символов от текущего положения считывающей головки во входной цепочке символов. Грамматика называется LL(k)-грамматикой, если она обладает свойством LL(k) для некоторого k > 0. Название «LL(k) » несет определенный смысл. Первая литера «L» происходит от слова «left» и означает, что входная цепочка символов читается в направлении слева направо. Вторая литера «L» также происходит от слова «left» и означает, что при работе распознавателя используется левосторонний вывод. Вместо «k» в названии класса грамматики стоит некоторое число, которое показывает, сколько символов надо рассмотреть, чтобы однозначно выбрать одну из множества альтернатив.

В совокупности все LL(k)-грамматики для всех k>0 образуют класс LL-грамматик.

Алгоритм разбора входных цепочек для LL(k)-грамматик носит название «k-предсказывающего алгоритма».

Требование k > 0, безусловно, является разумным - для принятия решения о выборе той или иной альтернативы МП-автомату надо рассмотреть хотя бы один символ входной цепочки. Если представить себе LL-грамматику с k = 0, то в такой грамматике вывод совсем не будет зависеть от входной цепочки. В принципе такая грамматика возможна, но в ней будет всего одна единственная цепочка вывода. Поэтому практическое применение языка, заданного такого рода грамматикой, представляется весьма сомнительным.

Для LL(k)-грамматик известны следующие полезные свойства:

·всякая LL(k)-грамматика для любого k>0 является однозначной;

·существует алгоритм, позволяющий проверить, является ли заданная грамматика LL(k)-грамматикой для строго определенного числа k.

Есть, однако, неразрешимые проблемы для произвольных КС-грамматик:

·не существует алгоритма, который бы мог проверить, является ли заданная КС-грамматика LL(k)-грамматикой для некоторого произвольного числа k;

·не существует алгоритма, который бы мог преобразовать произвольную КС-грамматику к виду LL(k)- грамматики для некоторого k (или доказать, что преобразование невозможно).

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

Класс LL-грамматик широк, но все же он недостаточен для того, чтобы покрыть все возможные синтаксические конструкции в языках программирования (к ним относим все детерминированные КС-языки). Известно, что существуют детерминированные КС-языки, которые не могут быть заданы LL(к)-грамматикой ни для каких k. Однако LL-грамматики удобны для использования, поскольку позволяют построить распознаватели с линейными характеристиками (линейной зависимостью требуемых для работы алгоритма распознавания вычислительных ресурсов от длины входной цепочки символов).

Для построения LL(1)-анализатора понадобятся специальные множества FIRST и FOLLOW .

Множество FIRST находится по правилам:

1)Если X - терминал, то FIRST(X) = {X};

2)Если имеется продукция вида Х ? ?, добавляем ? к FIRST(X);

3)Если Х - нетерминал и имеется продукция Х ? Y1Y2…Yk, то поместим а в FIRST(Х), если для некоторого i а FIRST(Yi) и ? входит во все множества FIRST(Y1)… FIRST(Yi-1), т.е. Y1 --- Yi-1 ? ?. Если ? имеется во всех FIRST(Yi), i=1...k, то добавляем ? к FIRST(Х).

Множество FOLLOW находится по правилам:

1)Поместим $ в FOLLOW(S), где S - стартовый символ, а $ - правый ограничитель входного потока.

2)Если имеется продукция А??В?, то все элементы множества FIRST(?), кроме e, помещаются во множество FOLLOW(В).

)Если имеется продукция А??В или А??В?, где FIRST(?) содержит e (т.е. ?Þe), то все элементы из множества FOLLOW(A) помещаются во множество FOLLOW(В).

Согласно правилам грамматики и построенным множествам FIRST и FOLLOW строим таблицу предиктивного анализа по существующему алгоритму:

1)Для каждой продукции грамматики А?a выполняем шаги 2. и 3.

2)Для каждого терминала а из FIRST(a) добавляем А?a в ячейку М[А,а].

)Если в FIRST(a) входит e, для каждого терминала b из FOLLOW(A) добавим А?a в ячейку М[A,b]. Если e входит в FIRST(a), а $ - в FOLLOW(A), добавим А?a в ячейку M[A,$].

)Сделаем каждую неопределенную ячейку таблицы М указывающей на ошибку.

Проделаем данные построения для заданной грамматики.

Исходная грамматика:


G=(TV,NV,S,P)

TV=( d, t, n, i, e, f, l, r, a, ;, ,, {, }, (, ), <, >, =, +, -, *, /)

NV=(S, B, E, C, H, A, D, F, K, J, I, W, Z, M, N)={>E{B}>$|A|iM>$|tC;E>dH>$|,C>tC;E|{B}|d=J;B|N>$|eB>$|lB>J|n|d>KI>$|WK>+|-|*|/>>|<|r|a>(KZK)BD|KZKBD->(KZK)fBF|KZKfBF

}


Запись правил грамматики G в форме Бэкуса-Наура:


<Старт>-><описание типа>{<тело программы>}

<тело программы>->$|<тело программы2>|i<операнды после if>

<описание типа>->$|t<переменные>;<описание типа>

< переменные >->d<список переменных>

< список переменных >->$|,< переменные >

< тело программы2>->t< переменные >;<описание типа>|{< тело программы >}|d=<выражение>;< тело программы >|<оператор ?:>

< операция else >->$|e< тело программы >

< операция :>->$|l< тело программы >

<идентификатор>-><выражение>|n|d

<выражение>-><идентификатор ><операция>

<операция>->$|< арифметические операции >< идентификатор >

<арифметические операции>->+|-|*|/

<операции отношения>->>|<|r|a

<операнды после if>->(<идентификатор><операции отношения><идентификатор >)< тело программы ><операция else>|< идентификатор >< операции отношения >< идентификатор >< тело программы >< операция else >

< оператор ?:>->(< идентификатор >< операции отношения >< идентификатор >)f< тело программы ><операция :>|< идентификатор >< операции отношения >< идентификатор >f< тело программы >< операция :>


Запись правил грамматики в графическом виде:

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

·Точка входа (не обозначается на диаграмме, из нее начинается входная дуга графа)

·Нетерминальный символ (обозначается прямоугольником, в который вписано обозначение символа)

·Цепочка терминальных символов (обозначается овалом, кругом или прямоугольником с закругленными краями, внутрь которого вписана цепочка)

·Узловая точка (обозначается закрашенным кружком)

·Точка выхода (не обозначается на диаграмме, в нее входит выходная дуга графа)

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

Синтаксические диаграммы для нетерминальных символов грамматики G

:

:

:

:


H:

:


D:

:

K:

:

:

:

Z:

:

:

Рис. 2-18. Синтаксические диаграммы грамматики G


Множества FIRST и FOLLOW строятся программно (рис. 19).


Рис 19. Множества First и Follow.


Рис. 20. Таблица предиктивного анализа

Т.к. в таблице нет ячеек, где бы было более одной записи, значит эта грамматика из класса LL-грамматик.

Для построения программного анализатора, воспользуемся алгоритмом разбора[1] строки по таблице предиктивного анализа (рис. 20).

Алгоритм разбора:

Таблично-управляемый предсказывающий анализатор имеет входной буфер, таблицу анализа и выход. Входной буфер содержит распознаваемую строку, за которой следует $ - правый концевой маркер, признак конца строки. Магазин содержит последовательность символов грамматики с $ на дне. Вначале магазин содержит начальный символ грамматики на верхушке и $ на дне. Таблица анализа - это двумерный массив M[A,a], где A -нетерминал, и a - терминал или символ $.

Анализатор управляется программой, которая работает следующим образом. Она рассматривает X - символ на верхушке магазина и a - текущий входной символ. Эти два символа определяют действие анализатора.

Имеются три возможности.

. Если X=a=$, анализатор останавливается и сообщает об успешном окончании разбора.

. Если X=a<>$, анализатор удаляет X из магазина и продвигает указатель входа на следующий входной символ.

. Если X - нетерминал, программа заглядывает в таблицу M[X,a]. По этому входу хранится либо правило для X, либо ошибка. Если, например, M[X,a]={X->UVW}, анализатор заменяет X на верхушке магазина на WVU {на верхушке U}. Будем считать, что анализатор в качестве выхода просто печатает использованные правила вывода. Если M[X,a]=error, анализатор обращается к подпрограмме анализа ошибок.

Пример разбора входной строки, принадлежащей заданной грамматике:


boolean k;a;

{k==0 a=1; else a=5;

}


Выходом лексического анализатора является следующая цепочка лексем:


td;td;{idrnd=n;ed=n;}


Эта цепочка подается на вход синтаксического анализатора (СА), далее часть процесса синтаксического разбора представлена на рисунке 21.


Рис. 21. Разбор строки

Выражение «Допуск» на выходной ленте свидетельствует о том, что исходная последовательность символов принадлежит заданной грамматике.

Листинг программной реализации c пояснениями представлен в Приложении 5.


1.2.5 Алгоритм построения дерева разбора

Деревом разбора грамматики G(VT,VN,P,S) называется дерево (граф), которое соответствует некоторой цепочке вывода и удовлетворяет следующим условиям:

Каждая вершина дерева обозначается символом грамматики A (VTuVN)

·корнем дерева является вершина, обозначенная целевым символом грамматики - S;

·листьями дерева (концевыми вершинами) являются вершины, обозначенные терминальными символами грамматики или символом пустой цепочки;

·если некоторый узел дерева обозначен нетерминальным символом А VN а связанные с ним узлы - символами b1,b2,bn; n>0,n>=i>0: то в грамматике G(VT,VN,P,S) существует правило А-> b1,b2,bn Р.

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

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

компилятор алгоритм польский синтаксический

Рис. 22 Часть дерева разбора


1.2.6 Реализация внутреннего представления программы

В качестве внутреннего представления выбрана обратная польская запись.

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

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

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

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

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

Для представленной выше строки обратная польская запись выглядит, как «t d t d d n r d n = d n =».


1.3 Используемые технические средства


Системные требования:

1)Операционная система Windows 98/ME/2000/XP

2)Процессор не ниже Intel Pentium 100, 32 Mb ОЗУ, клавиатура, мышь.

)Тип монитора и видеоадаптера значения не имеют.


1.4 Вызов и загрузка


Для запуска программы необходимо запустить исполняемый файл Project1.exe. В силу того, что исполняемый файл не велик большого значения объём ОЗУ на работу программы не оказывает.


1.5 Входные данные


Входными данными для компилятора являются введенные пользователем конструкции и правила грамматики, считанные из файла «Grammatika.txt».

Лексемы во входной строке должны разделятся пробелами. Селектор может быть записан как любая последовательность символов.

В качестве оператора может выступать любой оператор в языке Си. В списке значений константы можно перечислять через запятую.


1.6 Выходные данные


Выходными данными являются:

. Строка вывода, дерево вывода.

. Сообщение об ошибке, если введенная пользователем конструкция не соответствует правилам грамматики.


ЗАКЛЮЧЕНИЕ


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

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


ПРИЛОЖЕНИЕ 1


Блок-схемы алгоритма

Рис.П2.1 Общая блок-схема программа


Рис.П2.2 Блок-схема алгоритма ЛА


Рис.П2.3 Блок-схема алгоритма предикативного синтаксического анализатора

ПРИЛОЖЕНИЕ 2


Руководство пользователя


1.Общие сведения о программе

При возможности доступа к коду программа может использоваться для изучения основ работы компилятора и реализации этапов лексического и синтаксического анализов.

2.Описание запуска

Программа не требует установки. Запуск программы осуществляется открытием файла с расширением .exe. Для работы с программой необходим файл Grammatika.txt.

3.Описание пользовательского интерфейса

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


Рис. П3.1. Интерфейс программы.


4.Сообщения пользователю

1)Сообщение при нажатии на кнопку «Провести лексический анализ», если до этого не был загружен пример


2). При возникновении ошибки во время лексического анализа



). При успешном выполнении синтаксического анализа



). При возникновении ошибки во время синтаксического анализа




ПРИЛОЖЕНИЕ 3


Граф переходов конечного автомата


Рис. П4.1 Граф переходов конечного автомата


ПРИЛОЖЕНИЕ 4


Листинг программы

Unit1;


interface

, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,, StdCtrls, Buttons, Grids, ComCtrls, dxorgchr;

NT=['A'..'Z'];=^ElementS; //стек=record:char;:stack;;= class(TForm): TMemo;: TSpeedButton;: TPageControl;: TTabSheet;: TSpeedButton;: TMemo;: TLabel;: TEdit;: TLabel;: TPageControl;: TTabSheet;: TTabSheet;: TTabSheet;: TTabSheet;: TStringGrid;: TStringGrid;: TStringGrid;: TStringGrid;: TStringGrid;: TStringGrid;: TStringGrid;: TStringGrid;: TStringGrid;: TEdit;: TTabSheet;: TMemo;: TLabel;: TButton;: TButton;: TStringGrid;: TLabel;: TLabel;: TLabel;: TLabel;: TTabSheet;: TLabel;: TMemo;: TTabSheet;: TLabel;: TSpeedButton;: TSpeedButton;: TEdit;: TStringGrid;: TSpeedButton;: TTabSheet;: TdxOrgChart;: TSpeedButton;: TTabSheet;: TButton;: TMemo;: TEdit;SpeedButton1Click(Sender: TObject);FillTables;GetLexicalOutput(rez,lex_type:string);FormCreate(Sender: TObject);SpeedButton6Click(Sender: TObject);Button1Click(Sender: TObject);Button2Click(Sender: TObject);SpeedButton3Click(Sender: TObject);SpeedButton5Click(Sender: TObject);SpeedButton9Click(Sender: TObject);SpeedButton8Click(Sender: TObject);Button10Click(Sender: TObject);


{ Private declarations }

{ Public declarations };=record:char;:string;;=set of char;: TForm1;


Unit2, Unit3;


{$R *.dfm}

:integer;: TGridRect;

//правила грамматики: array ['A'..'Z',1..45] of string;:byte; //число правил:array[0..50] of char; //26: array['A'..'Z',1..45] of mnoj; //мн-во First: array['A'..'Z',1..45] of mnoj;//мн-во Follow: array[0..26] of char;,massiv:array[0..200]of string;:integer;:set of char;,STemp:Stack;: array [1..19] of string=('i','e','f','l','r','a',';',',','(',')','{','}','<','>','=','+','-','*','/');: array [1..19] of string=('3','3','3','3','6','6','-2','1','5','5','5','5','6','6','2','8','8','9','9');

TForm1.FillTables;.Cells[0,0]:='№';.Cells[1,0]:='Тип Лексемы';.Cells[2,0]:='Значение';.Cells[3,0]:='Код замены';.Cells[0,0]:='№';.Cells[1,0]:='Тип Лексемы';.Cells[2,0]:='Значение';.Cells[3,0]:='Код замены';.Cells[0,0]:='№';.Cells[1,0]:='Тип Лексемы';.Cells[2,0]:='Значение';.Cells[3,0]:='Код замены';.Cells[0,0]:='№';.Cells[1,0]:='Тип Лексемы';.Cells[2,0]:='Значение';.Cells[3,0]:='Код замены';

.Cells[0,0]:='№';.Cells[1,0]:='Тип Лексемы';.Cells[2,0]:='Значение';

SG9.Cells[3,0]:='Код замены';


//ключевые слова.Cells[0,0]:='Лексема';.Cells[1,0]:='Код замены';

SG2.Cells[0,1]:= 'if';.Cells[0,2]:= 'else';.Cells[0,3]:= '?';.Cells[0,4]:= ':';.Cells[1,1]:= 'i';.Cells[1,2]:= 'e';.Cells[1,3]:= 'f';.Cells[1,4]:= 'l';

.Cells[0,0]:='Лексема';.Cells[1,0]:='Код замены';.Cells[0,1]:= ';';.Cells[0,2]:= ',';.Cells[0,3]:= '(';.Cells[0,4]:= ')';.Cells[0,5]:= '{';.Cells[0,6]:= '}';.Cells[1,1]:= ';';.Cells[1,2]:= ',';.Cells[1,3]:= '(';.Cells[1,4]:= ')';.Cells[1,5]:= '{';.Cells[1,6]:= '}';

.Cells[0,0]:='Лексема';.Cells[1,0]:='Код замены';.Cells[0,1]:= 'id';.Cells[0,2]:= 'type';.Cells[0,3]:= 'number';.Cells[1,1]:= 'd';.Cells[1,2]:= 't';.Cells[1,3]:= 'n';

.Cells[0,0]:='Лексема';.Cells[1,0]:='Код замены';.Cells[0,1]:= '>';.Cells[0,2]:= '<';.Cells[0,3]:= '=';.Cells[0,4]:= '+';.Cells[0,5]:= '-';.Cells[0,6]:= '*';.Cells[0,7]:= '/';.Cells[0,8]:= '==';.Cells[0,9]:= '!=';.Cells[1,1]:= '>';.Cells[1,2]:= '<';.Cells[1,3]:= '=';.Cells[1,4]:= '+';.Cells[1,5]:= '-';.Cells[1,6]:= '*';.Cells[1,7]:= '/';.Cells[1,8]:= 'r';.Cells[1,9]:= 'a';;

TForm1.GetLexicalOutput(rez,lex_type:string);,j,k:integer;:string;:string;:='';

//Edit2.Text:='';rez[1]=' ' then Delete(rez,1,1);lex_type='keyword' theni:=0 to SG2.RowCount doSG2.Cells[0,i]=rez then Symbol:=SG2.Cells[1,i];

j := 1 to SG8.RowCount-1 doSG8.Cells[2,j]=rezbegin Edit2.Text:=EDit2.Text+'('+'40,'+IntTostr(j-1)+')';

//b:=false;:='';;(rez <>'') then:=SG8.RowCount;.Cells[0,i]:=IntTostr(i-1);.Cells[1,i]:=lex_type;.Cells[2,i]:=rez;.Cells[3,i]:=Symbol;.RowCount:=i+1;.FixedRows:=1;.Text:=EDit2.Text+'('+'40,'+IntTostr(i-1)+')';;

;

lex_type='razdel' theni:=0 to SG3.RowCount doSG3.Cells[0,i]=rez then Symbol:=rez;

//Symbol:=SG3.Cells[1,i]j := 1 to SG6.RowCount-1 doSG6.Cells[2,j]=rezbegin Edit2.Text:=EDit2.Text+'('+'20,'+IntTostr(j-1)+')';

//b:=false;:='';;(rez <>'') then:=SG6.RowCount;.Cells[0,i]:=IntTostr(i-1);.Cells[1,i]:=lex_type;.Cells[2,i]:=rez;.Cells[3,i]:=Symbol;.Text:=EDit2.Text+'('+'20,'+IntTostr(i-1)+')';.RowCount:=i+1;.FixedRows:=1;

;

;(lex_type='type') theni:=0 to SG4.RowCount doSG4.Cells[0,i]='type' then Symbol:=SG4.Cells[1,i];

j := 1 to SG7.RowCount-1 doSG7.Cells[2,j]=rezbegin Edit2.Text:=EDit2.Text+'('+'30,'+IntTostr(j-1)+')';

//b:=false;:='';;(rez <>'') then:=SG7.RowCount;.Cells[0,i]:=IntTostr(i-1);.Cells[1,i]:=lex_type;.Cells[2,i]:=rez;.Cells[3,i]:=Symbol;.Text:=EDit2.Text+'('+'30,'+IntTostr(i-1)+')';.RowCount:=i+1;.FixedRows:=1;

;

;(lex_type='znak') theni:=0 to SG5.RowCount doSG5.Cells[0,i]=rez then Symbol:=SG5.Cells[1,i];

j := 1 to SG1.RowCount-1 doSG1.Cells[2,j]=rezbegin Edit2.Text:=EDit2.Text+'('+'10,'+IntTostr(j-1)+')';:='';;(rez <>'') then:=SG1.RowCount;.Cells[0,i]:=IntTostr(i-1);.Cells[1,i]:=lex_type;.Cells[2,i]:=rez;.Cells[3,i]:=Symbol;.Text:=EDit2.Text+'('+'10,'+IntTostr(i-1)+')';.RowCount:=i+1;.FixedRows:=1;

;

;(lex_type='id') theni:=0 to SG4.RowCount doSG4.Cells[0,i]='id' then Symbol:=SG4.Cells[1,i];

j := 1 to SG7.RowCount-1 doSG7.Cells[2,j]=rezbegin Edit2.Text:=EDit2.Text+'('+'30,'+IntTostr(j-1)+')';

//b:=false;:='';;(rez <>'') then:=SG7.RowCount;.Cells[0,i]:=IntTostr(i-1);.Cells[1,i]:=lex_type;.Cells[2,i]:=rez;.Cells[3,i]:=Symbol;.Text:=EDit2.Text+'('+'30,'+IntTostr(i-1)+')';.RowCount:=i+1;.FixedRows:=1;

;

;(lex_type='number') theni:=0 to SG4.RowCount doSG4.Cells[0,i]='number' then Symbol:=SG4.Cells[1,i] ;

j := 1 to SG7.RowCount-1 doSG7.Cells[2,j]=rezbegin Edit2.Text:=EDit2.Text+'('+'30,'+IntTostr(j-1)+')';

//b:=false;:='';;(rez <>'') then:=SG7.RowCount;.Cells[0,i]:=IntTostr(i-1);.Cells[1,i]:=lex_type;.Cells[2,i]:=rez;.Cells[3,i]:=Symbol;.Text:=EDit2.Text+'('+'30,'+IntTostr(i-1)+')';.RowCount:=i+1;.FixedRows:=1;;;Symbol='' then

begin('Для данной лексемы ' +rez+ ' не определен код');

Exit;.text:=Edit1.text+Symbol;.Text:=Edit4.Text+Symbol+' ';:=Edit4.Text;:=length(s);(s[k-3]='>') or (s[k-3]='<') or (s[k-3]='r') or (s[k-3]='a') then.Text:=Edit4.Text+';'+' ';.Enabled:=true;

//Edit2.Text:=EDit2.Text+s1;rez <>'' then:=SG9.RowCount;.Cells[0,i]:=IntTostr(i-1);.Cells[1,i]:=lex_type;.Cells[2,i]:=rez;.Cells[3,i]:=Symbol;.RowCount:=i+1;.FixedRows:=1;;;;

LA;lab;TSost=(q0,q1,q2,q3,q4,q5,q6,q7,q8,q9,q10,q11,q12,q13,q14,q15

,q16,q17,q18,q19,q20,q21,q22,q23,q24,q25,q26,q27,q28,q29,,q31,q32,q33,q34,q35,q36,q37,q38,q70);

//--------------------------------: set of char;:boolean;, j,i,i_old:integer;:integer;:TSost;:integer;:string;,fff,OutStr,InStr,InStrClear: string; //Выходная строка:string; // Строка для псевдокода,porogd,rez,lex_type,qprev:string;:boolean;:=0; flagdel:=false;counts<=form1.Memo1.Lines.Count-1 do:=form1.memo1.Lines.Strings[counts];(counts);:=1;i<=length(s) do((s[i]=' ') and (s[i+1]=' ')) or (s[i]=#9 ) and (flagdel<>true) then(s,i,1)(s[i]='/') and (s[i+1]='/') then(s,i,length(s)-i+1);:=i-1;(s[i]=' ') and (i=length(s)) then(s,i,1)s[i]=']' then:=false ;(i);(s,1,1);(s[i]='[') or (flagdel=true) then(s,1,1);:=true;(i);;.memo4.lines.add(s);;i:=0 to Form1.Memo4.Lines.Count doForm1.Memo4.Lines[i]='' then InStrclear:=InStrclear else:=InStrclear+' '+Form1.Memo4.Lines[i];;

// InStrClear:=MakeClearStr(instr);:=InStrclear+' ';

//Начало разбора автоматом:=q0;

len:=length(InStrClear);:='';:=1;_old:=1;i<=len doSost of:InStrClear[i] of

'i': Sost:=q1;//int,if

'c' : Sost:=q3;//char

'b': Sost:=q6;//boolean

'l': Sost:=q12;//long

'f': Sost:=q15;//float

'd': Sost:=q19; //double

'e': Sost:=q26; //else

'?',':': Sost:=q29; //?-if :-else

';',',','(',')','{','}': Sost:=q33; //разделители

'=','<','>','/','+','-','*','!': sost:=q31;

' ': Sost:=q0;

'0'..'9':Sost:=q37;Sost:=q35;;

:InStrClear[i] of

'n': Sost:=q2;//int

'f':Sost:=q29; //ifSost:=q70;;:InStrClear[i] of

't': Sost:=q24;//intSost:=q70;;:InStrClear[i] of

'h': Sost:=q4;//charSost:=q70;;:InStrClear[i] of

'a': Sost:=q5;//charSost:=q70;;:InStrClear[i] of

'r': Sost:=q24;//charSost:=q70;;:InStrClear[i] of

'o': Sost:=q7;//booleanSost:=q70;;:InStrClear[i] of

'o': Sost:=q8;//booleanSost:=q70;;:InStrClear[i] of

'l': Sost:=q9;//booleanSost:=q70;;:InStrClear[i] of

'e': Sost:=q10;//booleanSost:=q70;;:InStrClear[i] of

'a': Sost:=q11;//booleanSost:=q70;;:InStrClear[i] of

'n': Sost:=q24;//booleanSost:=q70;;:InStrClear[i] of

'o': Sost:=q13;//longSost:=q70;;:InStrClear[i] of

'n': Sost:=q14;//longSost:=q70;;:InStrClear[i] of

'g': Sost:=q24;//longSost:=q70;;:InStrClear[i] of

'l': Sost:=q16;//floatSost:=q70;;:InStrClear[i] of

'o': Sost:=q17;//floatSost:=q70;;:InStrClear[i] of

'a': Sost:=q18;//floatSost:=q70;;:InStrClear[i] of

't': Sost:=q24;//floatSost:=q70;;:InStrClear[i] of

'o': Sost:=q20;//doubleSost:=q70;;:InStrClear[i] of

'u': Sost:=q21;//doubleSost:=q70;;:InStrClear[i] of

'b': Sost:=q22;//doubleSost:=q70;;:InStrClear[i] of

'l': Sost:=q23;//doubleSost:=q70;;:InStrClear[i] of

'e': Sost:=q24;//doubleSost:=q70;;:InStrClear[i] of

' ': Sost:=q25;Sost:=q35;;:InStrClear[i] of

'l': Sost:=q27;//elseSost:=q70;;:InStrClear[i] of

's': Sost:=q28;//elseSost:=q70;;:InStrClear[i] of

'e': Sost:=q29;//elseSost:=q70;;:InStrClear[i] of

' ': Sost:=q30;Sost:=q35;;

:InStrClear[i] of

' ','a'..'z','0'..'9': Sost:=q32;

'=':Sost:=q31;

// : Sost:=q32;

// :Sost:=q32;Sost:=q35;;:InStrClear[i] of

' ','a'..'d','f'..'z','0'..'9',';': Sost:=q34;

'e': Sost:=q26; //elseSost:=q35;;

:InStrClear[i] of

':','=','<','>',' ',';',',','/','+','-','*','!': Sost:=q36;

'a'..'z': Sost:=q35;

'0'..'9':Sost:=q35;Sost:=q70;;

:InStrClear[i] of

' ','=','<','>',';',',','/','+','-','*':Sost:=q38;//считаем строку цифр num

'0'..'9':Sost:=q37;Sost:=q70;;

ShowMessage('No stase');;//End of State caseSost=q70 then

begin('Произошла ошибка. Неправильный символ "'+InStrClear[i]+'"');

Sost:=q0;_old:=i;:=i-1;

// exit;;Sost=q30 then:=Copy(InStrClear,i_old,(i-i_old));(rez);_type:='keyword';.GetLexicalOutput(rez,lex_type);:=q0;_old:=i;:=i-1;;Sost=q34 then

//rez:=Copy(InStrClear,i,1);:=Copy(InStrClear,i_old,(i-i_old));(rez);_type:='razdel';

.GetLexicalOutput(rez,lex_type);:=q0;_old:=i;:=i-1;;Sost=q32 then

//rez:=Copy(InStrClear,i,1);:=Copy(InStrClear,i_old,(i-i_old));(rez);_type:='znak';.GetLexicalOutput(rez,lex_type);:=q0;_old:=i;:=i-1;;Sost=q25 then:=Copy(InStrClear,i_old,(i-i_old));(rez);_type:='type';.GetLexicalOutput(rez,lex_type);:=q0;_old:=i;:=i-1;;(Sost=q38) then:=Copy(InStrClear,i_old,(i-i_old));(rez);_type:='number';.GetLexicalOutput(rez,lex_type);:=q0;_old:=i;:=i-1;;Sost=q36 then:=Copy(InStrClear,i_old,(i-i_old));(rez);_type:='id';.GetLexicalOutput(rez,lex_type);:=q0;_old:=i;:=i-1;;(i);;;

TForm1.SpeedButton1Click(Sender: TObject);,j,q: Integer;:boolean;,lex_type,Symbol:string;

//SG1.RowCount:=1;:=0;_type:='';:='';:='';i := 0 to 3 doj := 1 to SG1.RowCount+1 do begin.Cells[i,j]:='';.RowCount:=0;;i := 0 to 3 doj := 1 to SG6.RowCount+1 do begin.Cells[i,j]:='';.RowCount:=0;;i := 0 to 3 doj := 1 to SG7.RowCount+1 do begin.Cells[i,j]:='';.RowCount:=0;;i := 0 to 3 doj := 1 to SG8.RowCount+1 do begin.Cells[i,j]:='';.RowCount:=0;;

.Text:='';.Text:='';.Text:='';.Clear;memo1.Text='' then application.MessageBox('Введите пример!','Внимание!',mb_iconwarning);;.Text:=edit1.Text;;

TForm1.FormCreate(Sender: TObject);;.StringGrid1.Cells[1,0]:='First';.StringGrid1.Cells[2,0]:='Follow';.StringGrid2.Cells[0,0]:='Стек';.StringGrid2.Cells[1,0]:='Входная лента';.StringGrid2.Cells[2,0]:='Выходная лента';;TForm1.SpeedButton6Click(Sender: TObject);f:textfile;:string;.Clear;not(Fileexists(inttostr(a)+'.txt')) then a:=1;(f,inttostr(a)+'.txt');(f);not(eof(f)) do begin(f,st);.Lines.add(st);;(f);:=a+1;.SpeedButton1.Enabled:=true;;

showgrammar(s:string);ffile:textfile; str:string;.Memo2.Clear;(ffile,s);(ffile);not Eof(ffile) do(ffile,str);.Memo2.Lines.Add(str);;(ffile);;FirstSet ();step1,step2,step3,final;,j,k:byte;

step: byte;//номер прохода по алгоритму

symbol,s:char;:string;:boolean;:=1;//первый проход

//первый шаг: begini:=0 to Form1.Memo2.Lines.Count-1 do:=notterm[i];k:=1 to Count-1 do

begin:=Rule[symbol,k];//одно правило для нетерминала symbol

if (length(temp)<>0) thentemp[1]<>'$' then First[symbol,step]:=First[symbol,step]+[temp[1]];;:='';;:=step+1;;


//второй шаг: begin:=step;i:=0 to Form1.Memo2.Lines.Count-1 do:=notterm[i];//нетерминалj:=0 to Form1.Memo2.Lines.Count-1 do

{если в First для symbol

входят другие нетерминалы, то объединяем соответствующие множества}

if(symbol<>notterm[j])and(notterm[j] in First[symbol,step-1])then[symbol,k]:=First[symbol,k-1]+First[notterm[j],step-1];:=k+1;;;[symbol,step]:=First[symbol,k-1];:=step;;;


//3 шаг: begin:=false;i:=0 to Form1.Memo2.Lines.Count-1 do

begin

//проверка мн-в на равенство

if First[notterm[i],step]<>First[notterm[i],step-1] then:=true;:=step+1;

//если не равны то вернутся к шагу 2

goto step2;;;flag=false then:=step+1;final;;;


//конец алгоритма: begin

//удаление нетерминалов из Firsti:=0 to Form1.Memo2.Lines.Count-1 do:=notterm[i];j:=0 to Form1.Memo2.Lines.Count-1 do(notterm[j] in First[symbol,step-1])then[symbol,k]:=First[symbol,k-1]-[notterm[j]];:=k+1;;;[symbol,1]:=First[symbol,k-1];:=step;;

.StringGrid1.RowCount:=Form1.Memo2.Lines.Count+1;


//вывод в таблицуi:=0 to Form1.Memo2.Lines.Count-1 do:='';s:=#32 to #125 do(s in First[notterm[i],1]) thens=#32 then temp:=temp+' '+''' ''' else temp:=temp+' '+s;.StringGrid1.Cells[0,i+1]:=notterm[i];.StringGrid1.Cells[1,i+1]:=temp;;;;

FollowSet();step1,step2,step3,step4,step5,step6,final;

//вспомогательные множества,Follow2, HelpFollow:array['A'..'Z',1..60] of mnoj;,j,k,step,n,num:byte;,str:string;,s:char;,bool:boolean;:=1;

//первоначально множества пустыs:='A' to 'Z' do

for i:=1 to 40 do Follow[s,i]:=[];


// 1 шаг: begin:=step;i:=0 to Form1.Memo2.Lines.Count-1 do:=notterm[i];

{в Follow для нетерминала symbol добавляем все символы, стоящие за symbol в правилах

из правой части грамматики}:='';

str:='';num:=0 to Form1.Memo2.Lines.Count-1 don:=1 to Count-1 do

begin:=Rule[notterm[num],n];//одно правило для нетерминала

if (length(temp)<>0) thenj:=1 to length(temp) dotemp[j]=symbol then str:=copy(temp,j+1,length(temp)-j) ;

str<>'' then beginj:=1 to length(str) do[symbol,k]:=Follow[symbol,k-1]+[str[j]];:=k+1;;:='';[symbol,step]:=Follow[symbol,k-1];:=step+1;; end; end;

end; end;;

: begin

{добавляем символ # во множество Follow стартового нетерминала}

Follow[notterm[0],step+1]:=Follow[notterm[0],step]+['#'];[notterm[0],step]:=Follow[notterm[0],step+1];:=step+1;;

: begin:=step;i:=0 to Form1.Memo2.Lines.Count-1 do:=notterm[i];//нетерминал[symbol,k-1]:=Follow[symbol,step];j:=0 to Form1.Memo2.Lines.Count-1 do

begin

{если во множество Follow для рассматриваемого нетерминала symbol

входят другие нетерминалы, то объединяем его со множеством

First входящего нетерминала}(symbol<>notterm[j])and(notterm[j] in Follow[symbol,step-1])then[symbol,k]:=Follow1[symbol,k-1]+First[notterm[j],1];:=k+1;;;[symbol,step]:=Follow1[symbol,k-1];:=step;;;

: begin:=step;i:=0 to Form1.Memo2.Lines.Count-1 do:=notterm[i];//нетерминал[symbol,k-1]:=Follow1[symbol,step];

{если во множество Follow1 для рассматриваемого нетерминала symbol

входят другие нетерминалы и существует правило B->$, то объединяем его

со мноржеством Follow1 входящего нетерминала}

for j:=0 to Form1.Memo2.Lines.Count-1 do(symbol<>notterm[j])and(notterm[j] in Follow1[symbol,step])then:=false;n:=1 to count-1 doRule[notterm[j],n]='$' then bool:=true;(bool=true) then[symbol,k]:=Follow2[symbol,k-1]+Follow1[notterm[j],step];:=k+1;;;;[symbol,step]:=Follow2[symbol,k-1];:=step;;;

: begin:=step;i:=0 to Form1.Memo2.Lines.Count-1 do:=notterm[i];//нетерминал[symbol,k-1]:=Follow2[symbol,step];j:=0 to Form1.Memo2.Lines.Count-1 do

begin

{если для некоторого терминала B существует правило

B->aSymbol, то объединяем Follow2(Symbol) и Follow2(B)}(symbol<>notterm[j])then:='';n:=1 to count-1 do:=Rule[notterm[j],n];(temp<>'')and(temp[length(temp)]=symbol) then[symbol,k]:=HelpFollow[symbol,k-1]+Follow2[notterm[j],step];:=k+1;;;;;[symbol,step]:=HelpFollow[symbol,k-1];:=step;;;

:begin:=false;i:=0 to Form1.Memo2.Lines.Count-1 do

//проверка множеств на равенствоFollow[notterm[i],step]<>Follow[notterm[i],step-1] then flag:=true;

//если множества не равны возвращаемся к шагу 3

if flag=true then:=step+1;i:=0 to Form1.Memo2.Lines.Count-1 do[notterm[i],step]:=Follow[notterm[i],step-1];

goto step3;;

//если множества равны, то переходим к завершающему этапу

if flag=false then:=step+1;final;;;


//конец алгоритма

final: begin

//удаление нетеминалов из множеств Follow

for i:=0 to Form1.Memo2.Lines.Count-1 do:=notterm[i];j:=0 to Form1.Memo2.Lines.Count-1 do(notterm[j] in Follow[symbol,step-1])then[symbol,k]:=Follow[symbol,k-1]-[notterm[j]];:=k+1;;;[symbol,1]:=Follow[symbol,k-1];

k:=step;;


//вывод в таблицу

for i:=0 to Form1.Memo2.Lines.Count-1 do:='';s:=#32 to #125 do(s in Follow[notterm[i],1]) thens=#32 then temp:=temp+' '+''' ''' else temp:=temp+' '+s;;.StringGrid1.Cells[2,i+1]:=temp;;;;


TForm1.Button1Click(Sender: TObject);,j:byte;,res:string;:char;.Clear;i:=1 to Form2.StringGrid1.RowCount doj:=1 to Form2.StringGrid1.ColCount do.Cells[j,i]:='';('gramatika.txt');:=1;i:=0 to Memo2.Lines.Count-1 do:=Form1.Memo2.Lines[i];:=4;:=temp[1];[i]:=s;:='';j<=length(temp) do(temp[j]<>'|') then

res:=res+temp[j];//считываем правило

if (temp[j]='|') or (j=length(temp))[s,count]:=res;:=count+1;:='';;:=j+1;;;();

FollowSet();;


//вставка в таблицу разбора

procedure Paste (terminal:char; neterminal:char; temp:string);,j:byte;i:=1 to Form2.StringGrid1.RowCount doForm2.StringGrid1.Cells[0,i]=neterminal thenj:=1 to Form2.StringGrid1.ColCount doForm2.StringGrid1.Cells[j,0]=terminal then.StringGrid1.Cells[j,i]:=temp;;

GetTerminal;,j:byte;

temp: string;:=[];

//для удобства массив терминалов

//сначала заполним символом #6

for i:=0 to 26 do[i]:=#6;

//терминалы введены в строку редактирования:='d t n i e f l r a ; , { } ( ) < > = + - * /';

i:=1;j:=0;i<=length(temp) do begin[j]:=temp[i];:=Terminals+[term[j]];:=j+1;:=i+2;;;TForm1.Button2Click(Sender: TObject);,j,k:byte;,s,ch:char;:string;:set of char;;:=[];//вспомогательное множество:=0;notterm[i]<>'' do:=i+1;

//число строк в таблице =числу нетерминалов

Form2.StringGrid1.RowCount:=i+1;:=0;term[i]<>#6 do:=i+1;

//число столюцов в таблице =числу терминалов

Form2.StringGrid1.ColCount:=i+1;i:=1 to Form2.StringGrid1.RowCount do.StringGrid1.Cells[0,i]:=notterm[i-1];i:=1 to Form2.StringGrid1.ColCount do.StringGrid1.Cells[i,0]:=term[i-1];k:=0 to Form1.Memo2.Lines.Count-1 do:=notterm[k];:=[];i:=1 to 45 do:=Rule[s,i];(temp<>'') then:=temp[1];

//символ входит в First(s),то заполняем соотв.ячейку т.р.

if (temp<>'$')and(symbol in First[s,1]) then(symbol,s,s+'->'+temp);:=Help+[symbol];;not(symbol in First[s,1])thench:=#32 to #125 do(ch in First[s,1])and not(ch in Help) then(ch,s,s+'->'+temp);(symbol='$') thench:=#32 to #125 do(ch in Follow[s,1])and not(ch in First[s,1]) then(ch,s,s+'->'+temp);('#',s,s+'->'+temp);;;;.SpeedButton3.Enabled:=true;.SpeedButton5.Enabled:=true;.Show;;

AddStack (var symbol:char);STop=nil then(STemp);^.inf:=symbol;^.link:=nil;:=Stemp;STop<>nil then(Stemp);^.inf:=symbol;^.link:=Stop;:=Stemp;;;

View(Input:string;Output:string;Stack:string);:=Form1.StringGrid2.RowCount;.StringGrid2.Cells[0,count-1]:=Stack;.StringGrid2.Cells[1,count-1]:=Input;.StringGrid2.Cells[2,count-1]:=OutPut;.StringGrid2.RowCount:=Form1.StringGrid2.RowCount+1;;

StackToStr():string;i:byte; str,help:string;:='';:=STop;(Stemp<>nil)do:=str+Stemp^.inf;:=Stemp^.link;;:='';i:=length(str) downto 1 do:=help+str[i];:=help;;

RemoveStack();(STop<>nil)then:=STop;:=STemp^.link;^.link:=nil;(STemp);;;

Check (terminal:char;neterminal:char):string;,j:byte;:string;i:=1 to Form2.StringGrid1.RowCount doForm2.StringGrid1.Cells[0,i]=neterminal thenj:=1 to Form2.StringGrid1.ColCount doForm2.StringGrid1.Cells[j,0]=terminal then:=Form2.StringGrid1.Cells[j,i];.Left:=j;.Right:=j;.Top:=i;.Bottom:=i;.StringGrid1.Selection:=Select;;;:=temp;;

Vivod(neterminal:char;var Right:string);:byte;(Right,1,3);i:=1 to 45 do(Rule[neterminal,i]=Right) then[step]:=Right;[step]:=neterminal+'->'+right;;:=step+1;;

TypeOfError(current:string);,j,k:byte;:set of char;

{if current[1]<>'v' then('Ожидается var',mtError, [mbYes], 0);;;current[2]<>'c' then('Ожидается идентификатор',mtError, [mbYes], 0);;; }:=['>','<','=','+','-','*','/'];:=1;i<=length(current) do(current[i] in M) then((current[i+1]<>'d')or(current[i+1]<>'n')or

(current[i-1]<>'d')or(current[i-1]<>'n'))begin('Недопустимое выражение',mtError, [mbYes], 0);;;:=i+1;;i:=1 to length(current) do(current[i]=';') then('Ожидается символ ";"',mtError, [mbYes], 0);;;i:=1 to length(current) docurrent[i]='{' then('Ожидается { ',mtError, [mbYes], 0);;;i:=1 to length(current) docurrent[i]='}' then('Ожидается }',mtError, [mbYes], 0);;;

;

TForm1.SpeedButton3Click(Sender: TObject);step1,step2,final;,temp,Stack,Input,Output: string;,ch:char;,j:byte;:string;:=nil;:=nil;i:=1 to Form1.StringGrid2.RowCount doj:=1 to Form1.StringGrid2.ColCount do.StringGrid2.Cells[j,i]:='';.StringGrid2.RowCount:=2;.StringGrid2.ColCount:=3;

: begin:=Form1.Edit3.Text+'#';:='#';(ch);(notterm[0]);:='';:=1;:='';:=StackToStr();(Input,Output,Stack);;

: begin:=input[i];(symbol=' ')then begin(Input,1,1);:=1;step2;;

(symbol='#')and(symbol=stop.inf) then:='допуск';final;;:=STop^.inf;(symbol<>'#')and(symbol=ch) then:='вытолкнуть '+symbol;();(Input,1,1);:=1;:=StackToStr;(Input,Output,Stack);step2;;

(symbol<>STop.inf)and(STop.inf='$') then();step2;;

symbol<>STop.inf then:=Check(symbol,STop.inf);temp<>'' then begin:=temp;(STop.inf,help);:='';:=temp;(temp,1,3);j:=length(temp)downto 1 do:=help+temp[j];:=help;:='';();j:=1 to length(temp) do(temp[j]);:=StackToStr();(Input,Output,Stack);step2;;

temp=''then begin:='Ошибка';:=Form1.StringGrid2.RowCount-2;:=Form1.StringGrid2.Cells[0,i];(s);final;;;;

:begin(Output='допуск') then begin('Разбор строки успешно завершен за '+IntToStr(Form1.StringGrid2.RowCount-2)+' шагов');

endShowMessage('Строка не выводима из правил грамматики');

Stack:=StackToStr();(Input,Output,Stack);.StringGrid2.RowCount:=Form1.StringGrid2.RowCount-1;;.SpeedButton9.Enabled:=true;

//form1.SpeedButton7.Enabled:=true;;

TForm1.SpeedButton5Click(Sender: TObject);step1,step2,final;,temp,Stack,Input,Output: string;,ch:char;,j:byte;:string;:=nil;:=nil;i:=1 to Form1.StringGrid2.RowCount doj:=1 to Form1.StringGrid2.ColCount do.StringGrid2.Cells[j,i]:='';.StringGrid2.RowCount:=2;.StringGrid2.ColCount:=3;

: begin:=Form1.Edit3.Text+'#';:='#';(ch);(notterm[0]);:='';:=1;:='';:=StackToStr();(Input,Output,Stack);;: begin:=input[i];(symbol=' ')then begin(Input,1,1);:=1;step2;;

(symbol='#')and(symbol=stop.inf) then:='допуск';MessageDlg('Продолжить?',mtConfirmation, [mbYes, mbNo], 0) = mrNoexit;final;;:=STop^.inf;(symbol<>'#')and(symbol=ch) then:='вытолкнуть '+symbol;MessageDlg('Продолжить?',mtConfirmation, [mbYes, mbNo], 0) = mrNoexit;();(Input,1,1);:=1;:=StackToStr;(Input,Output,Stack);step2;;

(symbol<>STop.inf)and(STop.inf='$') then();step2;;

symbol<>STop.inf then:=Check(symbol,STop.inf);temp<>'' then begin:=temp;(STop.inf,help);:='';:=temp;MessageDlg('Продолжить?',mtConfirmation, [mbYes, mbNo], 0) = mrNoexit;(temp,1,3);j:=length(temp)downto 1 do:=help+temp[j];:=help;:='';();j:=1 to length(temp) do(temp[j]);:=StackToStr();(Input,Output,Stack);step2;;

temp=''then begin:='Ошибка';:=Form1.StringGrid2.RowCount-2;:=Form1.StringGrid2.Cells[0,i];(s);MessageDlg('Продолжить?',mtConfirmation, [mbYes, mbNo], 0) = mrNoexit;final;;;;

:begin(Output='допуск') then begin('Разбор строки успешно завершен за '+IntToStr(Form1.StringGrid2.RowCount-2)+' шагов');begin ShowMessage('Строка не выводима из правил грамматики'); Form1.StringGrid2.RowCount:=Form1.StringGrid2.RowCount-1;end;:=StackToStr();(Input,Output,Stack);;.SpeedButton9.Enabled:=true;end;

ClearMemory();:=STop;(STemp<>nil) do:=Stemp^.link;(Stemp);:=STop;;;

CTree(Node:TTreeNode; str: String);:byte;: TTreeNode;:string;:=str;i:=1 to length(temp) do:=Form3.TreeView1.Items.AddChild(Node,temp[i]);temp[i] in ['A'..'Z'] then:=step+1;(InternalNode,mas[step]);;;;

BuildTree;:string;:TTreeNode;:=mas[step];.TreeView1.Items.Clear;:=Form3.TreeView1.Items.Add(nil,'S');(NewNode,temp);.TreeView1.FullExpand;;

BeautifulTree (Source:TTreeNode;Node:TdxOCNode; tv:TdxOrgChart);:byte;: TdxOCNode;: TTreeNode;Source.Count>0 then:=0;i<=Source.Count-1 do:=Source.Item[i];:=tv.AddChild(Node,nil) ;.Text:=Child.Text;.Color:=clMoneyGreen;.Width:=30;.Height:=30;.Shape := shellipse;Child.HasChildren then BeautifulTree(Child,InternalNode,tv);:=i+1;;;;

TForm1.SpeedButton9Click(Sender: TObject);,j,k,help:byte;:string;:TdxOCNode;:TTreeNode;.TreeView1.Items.Clear;.TreeView2.Items.Clear;.dxOrgChart1.Clear;.dx1.Clear;; //очищаем стек:=step;:=0;;:=Form3.TreeView1.Items.GetFirstNode();:=Form3.dxOrgChart1.AddFirst(nil,nil);.Text:=TempNode.Text;.Color:=clMoneyGreen;.Width:=30;.Height:=30;.Shape := shellipse;(TempNode,Node,Form3.dxOrgChart1);.dxOrgChart1.Font.Size:=10;.dxOrgChart1.DefaultNodeHeight:=10;

.Memo3.Clear;.Memo3.Lines.Add(notterm[0]+'->'+mas[0]);i:=0 to help do:=mas[i];:=1;j<=length(temp) dotemp[j]='$' then(temp,j,1);:=j+1;;:=1;(temp[k] in Terminals) do:=k+1;(temp,k,1);(mas[i+1],temp,k);.Memo3.Lines.Add(' '+temp);[i+1]:=temp;:='';;:=0;

i:=1 to step do[i]:='';.speedbutton8.enabled:=true;:=0;.Show;;

STree(TempNode:TTreeNode);:set of char;,j:byte;:TTreeNode;:boolean;:=false;:=['d','t','n','i','e','f','l','r','a',';',',','{','}','(',')','<','>','=','+','-','*','/'];

//M:=['=','+','-','*','/','<','>','b','e','r','t','d','i','h','c','y','n',';',',',':'];

//M:=['=','+','-','*','/','<','>','c','y','n',':'];not(TempNode.Text[1] in terminals) then(TempNode.Count=1) then begin:=TempNode.Item[0];.Text:=Child.Text;

Child.Text:='^'; //пометили на удаление

end:=0;i<=TempNode.Count-1 do:=TempNode.Item[i] ;(TempNode.Text='S') and ((Child.Text='v') or(Child.Text='b') or (Child.Text='e'))then.Text:='^';(Child.Text[1] in M)and (b=false) then begin.Text:=Child.Text;.Text:='^';//пометили на удаление:=true;;not(Child.Text[1] in M) and (Child.Text[1]in (terminals-['n']-['c']))

then begin.Text:='^';//пометили на удаление

end;Child.Text[1]='$' then Child.Text:='^';:=i+1;;;TempNode.Text[1]='$' then TempNode.Text:='^';;

MoveNode(TargetNode, SourceNode: TTreeNode; tv:TTreeView); //перемещение узла: TTreeNode;: Integer;tv do:= Items.AddChild(TargetNode, SourceNode.Text);i := SourceNode.Count - 1 downto 0 do(nodeTmp, SourceNode.Item[i],tv);;;;

BuildSyntTree;,Child,TempNode:TTreeNode;:byte;:integer;:boolean;:set of char;:=['d','t','n','i','e','f','l','r','a',';',',','{','}','(',')','<','>','=','+','-','*','/'];

//M:=['=','+','-','*','/','<','>','b','e','r','t','d','i','h','c','y','n',';',',',':'];.TreeView2.Items.Clear;.TreeView2.Items:=Form3.TreeView1.Items;.TreeView2.FullExpand;:=false;b<>true do begin:=0;i<=(Form3.TreeView2.Items.Count-1) do:=Form3.TreeView2.Items[i];(TempNode);:=i+1;;:=0;i<=Form3.TreeView2.Items.Count-1 do:=Form3.TreeView2.Items[i];

//нет потомков(TempNode.Text='^')and(TempNode.HasChildren=false) then.TreeView2.Items.Delete(Form3.TreeView2.Items.Item[i]);:=0;begin

//есть потомки(TempNode.Text='^')and(TempNode.HasChildren=true)thenj:=0 to TempNode.Count-1 do(TempNode.Parent,TempNode.Item[j],Form3.TreeView2);.DeleteChildren;.Delete;;;:=i+1;;:=0;i<=Form3.TreeView2.Items.Count-1 do:=Form3.TreeView2.Items[i];not(TempNode.Text[1]in terminals)or (TempNode.Text[1]='^') then:=false;;b:=true;:=i+1;;;

.TreeView2.FullExpand;;

TForm1.SpeedButton8Click(Sender: TObject);:TTreeNode;:TdxOCNode;

.dx1.Clear;.TreeView2.Items.Clear;:=0;;:=Form3.TreeView2.Items.GetFirstNode();:=Form1.dx1.AddFirst(nil,nil);.Text:=TempNode.Text;.Color:=clMoneyGreen;.Width:=30;.Height:=30;.Shape := shellipse;(TempNode,Node,Form1.dx1);.dx1.Font.Size:=10;.dx1.DefaultNodeHeight:=10;

end;


АННОТАЦИЯ Целью курсовой работы является: -овладение и закрепление навыков построения лексического анализатора; -овладение и закрепление навыков по

Больше работ по теме:

КОНТАКТНЫЙ EMAIL: [email protected]

Скачать реферат © 2017 | Пользовательское соглашение

Скачать      Реферат

ПРОФЕССИОНАЛЬНАЯ ПОМОЩЬ СТУДЕНТАМ