науки и высшего образования Российской Федерации
ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ
ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ
ВЫСШЕГО ОБРАЗОВАНИЯ
«ОРЕНБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ»
Аэрокосмический институт
Кафедра систем автоматизации производства
КУРСОВАЯ РАБОТА
по дисциплине «Лингвистическое и программное обеспечение систем автоматизированного проектирования»
Разработка программного обеспечения лексического анализатора
Пояснительная записка
ОГУ 09.03.01.3022.821 ПЗ
Руководитель
Канд. техн. наук, старший
преподаватель
Шамаев С. Ю.
« »
2022г.
Студент группы 20ИВТ(б)САП
Югай Г. В.
« »
2022г.
Оренбург 2022
Утверждаю
Заведующий кафедрой
систем автоматизации производства
______________
А. И. Сергеев
подпись
инициалы фамилия
«___»
2022г.
ЗАДАНИЕ
На выполнение курсовой работы
Студенту Югай Гарику Вадимовичу
по направлению подготовки (специальности) 09.03.01 – Информатика и вычислительная техника
по дисциплине «Лингвистическое и программное обеспечение систем автоматизированного проектирования»
1 Тема проекта работы:
«Разработка программного обеспечения лексического анализатора»
2 Срок сдачи студентом работы « »
2022г.
3 Цель и задачи работы:
целью курсовой работы является разработка лексического анализатора.
Курсовая работа включает в себя решение следующих задач:
1) реализовать таблицу идентификаторов;
2) реализовать конечный автомат для работы лексического анализатора со
входным языком, который содержит следующие операторы условия типа if …
then …else и if … then, разделённые символом; (точка с запятой). Операторы
условия содержат идентификаторы, знаки сравнения <,>, =, римские числа,
знак присваивания (:=).
3) реализовать конечный автомат для лексического анализатора;
4 Исходные данные к работе: Visual Studio для компиляции, язык C++, библиотека MFC, библиотеки классов для работы лексического анализатора.
Дата выдачи и получения задания « »
Руководитель «
»
Студент
»
«
2022г
2022г.
2022г.
____________
Шамаев С. Ю.
____________
Югай Г. В.
Аннотация
В данной курсовой работе описывается разработка программного обеспечения для лексического анализатора. Даются определения грамматики лексического
анализатора и описывается их работа. Также в работе описывается конечный автомат: его работа и схема.
В качестве инструментального средства разработки приложения используется среда Microsoft Visual Studio 2022. Приложение создано на языке С++, данный
язык реализует объектно – ориентированную модель программирования.
В работе приводятся результаты проделанной работы, состоящей из 4 основных частей: грамматика языка, организация таблиц идентификаторов, лексический
анализатор и разработка лексического анализатора. Работа содержит 27 листов текста, 6 рисунков и 1 приложение.
ОГУ 09.03.01.3022. 821 ПЗ
Изм Лист № докум Подп.
Разраб Югай Г.В.
Шамаев С.Ю.
Провер
–
Н.конт
Утв.
Дата
Разработка программного
обеспечения лексического
анализатора
Литер
Лист
3
Листов
25
20ИВТ(б)САП
Содержание
Введение ........................................................................................................................... 5
1 Теоретическая часть ..................................................................................................... 6
1.1 Выбор среды разработки ...................................................................................... 6
1.2 Грамматика и её формы ........................................................................................ 7
1.3 Описание лексического анализатора .................................................................. 9
2 Практическая часть реализации лексического анализатора .................................. 10
2.1 Конечный автомат ............................................................................................... 10
2.2 Руководство пользователя .................................................................................. 14
Заключение .................................................................................................................... 18
Список использованных источников .......................................................................... 19
Приложение А ............................................................................................................... 20
ОГУ 09.03.01.3022. 821 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
44
Введение
В данной курсовой работе описывается разработка программного обеспечения для лексического анализатора. Разработка лексического анализатора реализована в программе Visual Studio.
Для выполнения курсовой работы требуется написать программу, которая
выполняет лексический анализ входного языка программирования и создает
таблицу лексем с указанием их типов и значений. Текст входного языка копируется
из указанного файла.
Лексический анализатор – программа, принимающая на вход текст (последовательность символов из алфавита) и разбивающая его на подстроки (лексемы) в
соответствии с некоторым набором регулярных выражений.
Особенностью данной работы является то, что она реализована с помощью
библиотеки классов MFC. MFC является оболочкой для Win32 API и содержит многоуровневую иерархию классов. С точки зрения поставленной задачи были достигнуты все предполагаемые задания, это: организация таблицы идентификаторов,
описание конечного автомата лексического анализатора и построение грамматики.
К работе прилагаются все основные документы – это схема полного конечного автомата, код программы и описание по его использованию.
Результатом курсовой работы должна быть программа-анализатор, состоящая из лексического анализатора, считывающая файл посимвольно и составляющая отчет, о каждой лексеме и её значении.
ОГУ 09.03.01.3022. 821 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
55
1 Теоретическая часть
1.1 Выбор среды разработки
Microsoft Visual Studio - линейка продуктов компании Microsoft, включающих интегрированную среду разработки программного обеспечения и ряд других
инструментов. Данные продукты позволяют разрабатывать как консольные приложения, так и игры и приложения с графическим интерфейсом, в том числе с поддержкой технологии Windows Forms, а также веб-сайты, веб-приложения, вебслужбы как в родном, так и в управляемом коде для всех платформ, поддерживаемых: Windows, Windows Mobile, Windows CE, .NET Framework, Xbox, Windows
Phone .NET Compact Framework и Silverlight.
Visual Studio включает в себя редактор исходного кода с поддержкой технологии IntelliSense и возможностью простейшего рефакторинга кода. Встроенный
отладчик может работать как отладчик уровня исходного кода, так и отладчик машинного уровня. Остальные встраиваемые инструменты включают в себя редактор
форм для упрощения создания графического интерфейса приложения, веб-редактор, дизайнер классов и дизайнер схемы базы данных.
Visual Studio позволяет создавать и подключать сторонние дополнения (плагины) для расширения функциональности практически на каждом уровне, включая
добавление поддержки систем контроля версий исходного кода (как, например,
Subversion и Visual SourceSafe), добавление новых наборов инструментов (например, для редактирования и визуального проектирования кода на предметно-ориентированных языках программирования) или инструментов для прочих аспектов
процесса разработки программного обеспечения (например, клиент Team Explorer
для работы с Team Foundation Server).
Пакет Microsoft Foundation Classes (MFC) - библиотека на языке C++, разработанная Microsoft и призванная облегчить разработку GUI-приложений для Microsoft Windows путём использования богатого набора библиотечных классов.
Библиотека MFC, как и её основной конкурент, Borland VCL, облегчает работу с GUI путём создания каркаса приложения – «скелетной» программы, автоматически создаваемой по заданному макету интерфейса и полностью берущей на
себя рутинные действия по его обслуживанию (отработка оконных событий, пересылка данных между внутренними буферами элементов и переменными программы и т. п.). Программисту после генерации каркаса приложения необходимо
только вписать код в места, где требуются специальные действия. Каркас должен
иметь вполне определенную структуру, поэтому для его генерации и изменения в
Visual C++ предусмотрены мастера.
Кроме того, MFC предоставляет объектно-ориентированный слой обёрток
(англ. wrappers) над множеством функций Windows API, делающий несколько более удобной работу с ними. Этот слой представляет множество встроенных в систему объектов (окна, виджеты, файлы и т. п.) в виде классов и опять же берёт на
себя рутинные действия вроде закрытия дескрипторов и выделения/освобождения
памяти.
ОГУ 09.03.01.3022. 821 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
66
1.2 Грамматика и её формы
Грамматика – это описание способа построения предложений некоторого
языка или же математическая система, определяющая язык.
Язык описания констант и идентификаторов в большинстве случаев является
регулярным, то есть может быть описан с помощью регулярных грамматик. Распознавателями для регулярных языков являются КА. Существуют правила, с помощью которых для любой регулярной грамматики может быть построен КА, распознающий цепочки языка, заданного этой грамматикой.
Грамматика языка программирования содержит правила двух типов:
 первые (определяющие синтаксические конструкции языка) довольно
легко поддаются формальному описанию;
 вторые (определяющие семантические ограничения языка) обычно излагаются в неформальной форме.
Поэтому любое описание (или стандарт) языка программирования обычно
состоит из двух частей: вначале формально излагаются правила построения синтаксических конструкций, а потом на естественном языке дается описание семантических правил.
Формально грамматика G определяется как четверка G(VT,VN,P,S), где:
 VT – множество терминальных символов или алфавит терминальных символов;
 VN – множество нетерминальных символов или алфавит нетерминальных
символов;
 Р – множество правил (продукций) грамматики;
 S – целевой (начальный) символ грамматики S ∈ VN.
Существует три формы записи правил грамматик:
1 Форма Бэкуса–Наура (Bacus–Naur Form, BNF, русское общепринятое сокращение БНФ). Эта форма была предложена Джоном Бэкусом и модифицирована
Питером Науром, который использовал её для описания синтаксиса языка Алгол.
Со временем в БНФ были добавлены новые правила описания синтаксиса, и эта
форма получила название РБНФ – расширенная БНФ.
2 Форма Бэкуса–Наура предусматривает, как правило, также, что нетерминальные символы берутся в угловые скобки: ( ). Иногда знак ® в правилах грамматики заменяют на знак := (что характерно для старых монографий), но это всего
лишь незначительные модификации формы записи, не влияющие на ее суть.
3 Форма с использованием метасимволов предполагает, что в строке правила
грамматики могут встречаться специальные символы – метасимволы, – которые
имеют особый смысл и трактуются специальным образом. В качестве таких метасимволов чаще всего используются следующие символы: ( ) (круглые скобки), [ ]
(квадратные скобки), { } (фигурные скобки)," " (кавычки) и , (запятая).
Эти метасимволы имеют следующий смысл:
 круглые скобки означают, что из всех перечисленных внутри них цепочек
символов в данном месте правила грамматики может стоять только одна цепочка;
ОГУ 09.03.01.3022. 821 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
77
 квадратные скобки означают, что указанная в них цепочка может встречаться, а может и не встречаться в данном месте правила грамматики (то есть может
быть в нем один раз или ни одного раза);
 фигурные скобки означают, что указанная внутри них цепочка может не
встречаться в данном месте правила грамматики ни одного раза, встречаться один
раз или сколь угодно много раз;
 запятая служит для того, чтобы разделять цепочки символов внутри круглых скобок;
 кавычки используются в тех случаях, когда один из метасимволов нужно
включить в цепочку обычным образом – то есть когда одна из скобок или запятая
должны присутствовать в цепочке символов языка;
 форма записи правил в графическом виде вся грамматика представляется
в форме, набора специальным образом построенных диаграмм. Эта форма была
предложена при описании грамматики языка Pascal, а затем она получила широкое
распространение в литературе. Она доступна не для всех типов грамматик, а только
для тех типов, где в левой части правил присутствует не более одного символа, но
этого достаточно, чтобы ее можно было использовать для описания грамматик известных языков программирования.
В такой форме записи каждому нетерминальному символу грамматики соответствует диаграмма, построенная в виде направленного графа.
Любой КА может быть задан с помощью пяти параметров: M(Q, , , q0, F),
где:
Q – конечное множество состояний автомата;
 – конечное множество допустимых входных символов (входной алфавит
КА);
 – заданное отображение множества Q· во множество подмножеств P(Q) :
Q· → P(Q) (иногда  называют функцией переходов автомата);
q0 ∈ Q – начальное состояние автомата;
F ∈ Q – множество заключительных состояний автомата.
Другим способом описания КА является граф переходов – графическое представление множества состояний и функции переходов КА.
Граф переходов КА – это нагруженный однонаправленный граф, в котором
вершины представляют состояния КА, дуги отображают переходы из одного состояния в другое, а символы нагрузки (пометки) дуг соответствуют функции перехода
КА. Если функция перехода КА предусматривает переход из состояния q в q' по
нескольким символам, то между ними строится одна дуга, которая помечается
всеми символами, по которым происходит переход из q в q'.
ОГУ 09.03.01.3022. 821 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
88
1.3 Описание лексического анализатора
Лексический анализатор (или сканер) — это часть компилятора, которая читает литеры программы на исходном языке и строит из них слова (лексемы) исходного языка. На вход лексического анализатора поступает текст исходной программы, а выходная информация передается для дальнейшей обработки компилятором на этапе синтаксического анализа и разбора.
Лексема (лексическая единица языка) — это структурная единица языка, которая состоит из элементарных символов языка и не содержит в своем составе других структурных единиц языка. Лексемами языков программирования являются
идентификаторы, константы, ключевые слова языка, знаки операций и т. п. Состав
возможных лексем каждого конкретного языка программирования определяется
синтаксисом этого языка.
Результатом работы лексического анализатора является перечень всех
найденных в тексте исходной программы лексем с учетом характеристик каждой
лексемы. Каждой лексеме в таблице лексем соответствует некий уникальный
условный код, зависящий от типа лексемы, и дополнительная служебная информация.
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
Лист
9
2 Практическая часть реализации лексического анализатора
2.1 Конечный автомат
Конечный автомат (КА) – модель вычислительного устройства с фиксированным и конечным объемом памяти, которое читает и обрабатывает цепочку входных
символов, принадлежащих некоторому конечному множеству. X Конечные автоматы различают в зависимости от того, какой результат они дают на выходе.
Конечный распознаватель – модель устройства с конечным числом состояний, которое отличает допустимые (правильно образованные) цепочки от недопустимых.
На фрагментах графа переходов КА, изображенных на рисунках 1, 2, приняты
следующие изображения:
 C – любой алфавитно – цифровой символ;
 П – любой незначащий символ (пробел, знак табуляции, перевод строки, возврат каретки);
 Б – любая буква английского алфавита (прописная или строчная) или символ
подчёркивания (_);
 Б(*) – любая буква английского алфавита (прописная или строчная) или символ подчёркивания (_), кроме перечисленных в скобках;
 Ц – любая цифра от 0 до 9;
 F – функция обработки таблицы лексем, вызываемая при переходе КА из одного состояния в другое. Обозначения её аргументов:
v – переменная, запомненная при работе КА;
s – константа, запомненная при работе КА;
a – текущий входной символ КА;
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
10
10
Рисунок 1 – Фрагмент графа переходов КА для ключевых слов
switch (sCurStr[j])
{
case 'i': posCur = AP_IF1; break;
case 't': posCur = AP_THEN1; break;
case 'e': posCur = AP_ELSE1; break;
///////////////////////////////////////////////////////////////////////
case ':': posCur = AP_ASSIGN; break;
///////////////////////////////////////////////////////////////////////
case '(': listLex.emplace_back(LEX_OPEN, iAll, i, j); break;
case ')': listLex.emplace_back(LEX_CLOSE, iAll, i, j); break;
case ';': listLex.emplace_back(LEX_SEMI, iAll, i, j); break;
case '<': listLex.emplace_back(LEX_MEN, iAll, i, j); break;
case '>': listLex.emplace_back(LEX_BOL, iAll, i, j); break;
case '=': listLex.emplace_back(LEX_RAV, iAll, i, j); break;
case '{': posCur = AP_COMM;
case ' ': case 10: case 13: case 9: break;
///////////////////////////////////////////////////////////////////////
default:
if (isupper(sCurStr[j]) || 'a' == sCurStr[j] || 'b' <= sCurStr[j] && sCurStr[j] <= 'd' ||
'f' <= sCurStr[j] && sCurStr[j] <= 'h' ||
'j' <= sCurStr[j] && sCurStr[j] <= 'n' ||
'p' <= sCurStr[j] && sCurStr[j] <= 's' ||
'u' <= sCurStr[j] && sCurStr[j] <= 'w' ||
'y' <= sCurStr[j] && sCurStr[j] <= 'z' ||
sCurStr[j] == '_')
{
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
11
11
if (sCurStr[j] == 'X' || sCurStr[j] == 'I' || sCurStr[j] == 'C'
|| sCurStr[j] == 'V' || sCurStr[j] == 'M')
posCur = AP_CONST;
else
posCur = AP_VAR;
}
else
posCur = AP_ERR;
// case list
}; break;
Рисунок 2 – Фрагмент графа переходов КА для распознавания строковой
константы и присвоения
case AP_CONST:
switch (sCurStr[j])
{
case ':': AddConstToList(AP_ASSIGN, j); break;
case '(': AddConstKeyToList(LEX_OPEN, AP_START); break;
case ')': AddConstKeyToList(LEX_CLOSE, AP_START); break;
case ';': AddConstKeyToList(LEX_SEMI, AP_START); break;
case '<': listLex.emplace_back(LEX_MEN, iAll, i, j); break;
case '>': listLex.emplace_back(LEX_BOL, iAll, i, j); break;
case '=': listLex.emplace_back(LEX_RAV, iAll, i, j); break;
///////////////////////////////////////////////////////////////////////
case ' ': case 10: case 13: case 9: AddConstToList(AP_START, j); break;
default:
if (sCurStr[j] == _T('I') || sCurStr[j] == _T('V') || sCurStr[j] == _T('X')
|| sCurStr[j] == _T('L') || sCurStr[j] == _T('C'))
posCur = AP_CONST;
else
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
12
12
posCur = AP_ERR;
} // case list
break;
С учётом этих обозначений, полностью КА можно описать следующими следующим образом:
М(Q,  ,, q0, F):
Q = {H, V, D1, D2, G, F, E1, E2, E3, E4, I1, I2, T1, T2, T3, T4}
=С
q0 = H
F = {F}
Из начального состояния КА литеры «i» «e» «t» ведут в начало цепочек состояний, каждая из которых соответствует ключевому слову:
 cостояние I1, I2 – ключевому слову if;
 cостояние E1, E2, E3, E4 – ключевому слову else;
 cостояние T1, T2, T3, T4– ключевому слову then;
Остальные ведут к состоянию, соответствующему переменной (идентификатору), – V. Если в какой-то из цепочек встречает литера, не соответствующая ключевому слову, или цифра, то КА также переходит в состояние V, а если встречается
граница лексемы – запоминает уже прочитанную часть ключевого слова как переменную.
Буквы I, V, X, C, L, M ведут в состояние D. Состояние D соответствует лексеме «константа». В состояние D КА переходит, получив на вход символы I, V, X,
C, L, M. G соответствует лексеме «знак присваивания». В него КА переходит, получив на вход двоеточие, и ожидает в этом состоянии символа «равенство».
Состояние H – начальное состояние КА, а состояние F – конечное. Поскольку
КА работает с непрерывным потоком лексем, перейдя в конечное состояние, он
возвращается в начальное, но на графах это не представлено.
На графе не обозначено состояние «ошибка», чтобы не загромождать граф.
В это состояние КА переходит всегда, когда получает на вход символ, по которому
нет переходов из текущего состояния.
Функция F, которой помечены дуги КА на графе, соответствует выполнению
записи данных в таблицу лексем. Аргументы функции зависят от текущего состояния КА. В реализации программы моделирования этой функции соответствует несколько функций, вызываемые в зависимости от текущего состояния и входного
символа.
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
13
13
2.2 Руководство пользователя
Приложение написано с помощью программы Visual Studio. При запуске
программы появляется диалоговое окно, изображенное на рисунке 3.
Рисунок 3 – Диалоговое окно с двумя вкладкам
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
14
14
При нажатии на кнопку «Выбрать файл» на экране появляется окно с выбором нужного пути для загрузки .txt файла (рисунок 4).
Рисунок 4 – Диалоговое окно загрузки файла
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
15
15
После нажатия на кнопку «Загрузить файл» происходит загрузка файла, и на
окне выводится содержимое файла (рисунок 5).
Рисунок 5 – Диалоговое окно с выведенным текстом из файла
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
16
16
При переходе на вкладку «Таблица лексем» в приложении видим построенную таблицу лексем с данными, взятыми из загруженного файла, как это показано
на рисунке 6.
Рисунок 6 – Вкладка диалогового вывода на экран таблицу лексем
Для выхода из программы необходимо нажать на кнопку «Закрыть работу с
программой», находящуюся внизу программы. Программа обрабатывает ключевые слова, переменные, разделители, знак присвоения, открывающиеся и закрывающиеся скобки.
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
17
17
Заключение
В ходе выполнения курсовой работы был изучен материал по разработке лексического анализатора, конечного автомата, организации таблицы идентификаторов.
Также были решены следующие задачи:
 разработана регулярная грамматика и ее правила;
 разработан полный конечный автомат;
 организована таблица идентификаторов.
Результатом лексического анализатора является структура данных, которая
представляет таблицу лексем.
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
18
18
Список использованных источников
1 СТО 02069024.101–2015. Работы студенческие. Общие требования и правила оформления
2 Молчанов, А. Ю. Системное программное обеспечение / А. Ю. Молчанов –
С–П.: Питер, 2005. - 282 с.
3 Гусенков А. М. Специализированные языки обработки информации /
А. М. Гусенков – Казань: Казань. Ун-т, 2018. - 151 с.
4 Aw А, Ульман Дж. Теория синтаксического анализа, перевода и компиляции. М.: Мир, 1978. - Т. 1,612 с. Т. 2,487 с.
5 Бржезовский А. В., Корсакова И. В., Филъчаков В. В. Лексический и синтаксический анализ. Формальные языки и грамматики. – Л.: ЛИАП, 1990. – 31 с.
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
19
19
Приложение А
(обязательное)
Исходный код программы
#include "pch.h"
#include "LexAuto.h"
#include "FncTree.h"
using namespace FncTree;
// Функция создания списка лексем по исходному тексту программы
int LexAuto::MakeLexList(CString listFile, int iCountStr, vector<CLexem>&
listLexem)
{
listLex = listLexem;
// Обнуляем общий счетчик символов и результат функции
int result = 0;
iAll = 0;
iStComm = 0;
// Устанавливаем начальное состояние конечного автомата
posCur = AP_START;
// Цикл по всем строкам входного файла
// iCnt = listFile.GetLength() - 1;
// for (int i = 0; i <= iCnt; i++)
for (i = 0, iStr = 0; i <= iCountStr; i++)
{
// выделяем текущую строку, убираем незначащие пробелы в начале и в
конце строки
int iPos = listFile.Find('\n', iStr); // \r - 13 \n - 10
// Запоминаем текущую строку
sCurStr = listFile.Mid(iStr, iPos - iStr);
iStr = iPos + 1;
// Позиция начала лексемы - первый символ
iStart = 1;
// Цикл по всем символам текущей строки
for (j = 0; j < sCurStr.GetLength(); j++)
{
// Увеличиваем общий счетчик символов
iAll++;
///////////////////////////////////////////////////////////////////////
// Моделируем работу КА в зависимости от состояния автомата и
текущего символа входной строки
///////////////////////////////////////////////////////////////////////
switch (posCur)
{
// В начальном состоянии запоминаем позицию начала лексемы
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
20
20
case AP_START:
iStart = j;
iStComm = iAll - 1;
///////////////////////////////////////////////////////////////////////
switch (sCurStr[j])
{
case 'i': posCur = AP_IF1; break;
case 't': posCur = AP_THEN1; break;
case 'e': posCur = AP_ELSE1; break;
///////////////////////////////////////////////////////////////////////
case ':': posCur = AP_ASSIGN; break;
///////////////////////////////////////////////////////////////////////
case '(': listLex.emplace_back(LEX_OPEN, iAll, i, j);
break;
case ')': listLex.emplace_back(LEX_CLOSE, iAll, i, j);
break;
case ';': listLex.emplace_back(LEX_SEMI, iAll, i, j);
break;
case '<': listLex.emplace_back(LEX_MEN, iAll, i, j); break;
case '>': listLex.emplace_back(LEX_BOL, iAll, i, j); break;
case '=': listLex.emplace_back(LEX_RAV, iAll, i, j); break;
case '{': posCur = AP_COMM;
case ' ': case 10: case 13: case 9: break;
///////////////////////////////////////////////////////////////////////
default:
if (isupper(sCurStr[j]) || 'a' == sCurStr[j] || 'b' <=
sCurStr[j] && sCurStr[j] <= 'd' ||
'f' <= sCurStr[j] && sCurStr[j] <= 'h' ||
'j' <= sCurStr[j] && sCurStr[j] <= 'n' ||
'p' <= sCurStr[j] && sCurStr[j] <= 's' ||
'u' <= sCurStr[j] && sCurStr[j] <= 'w' ||
'y' <= sCurStr[j] && sCurStr[j] <= 'z' ||
sCurStr[j] == '_')
{
if (sCurStr[j] == 'X' || sCurStr[j] == 'I' || sCurStr[j] == 'C'
|| sCurStr[j] == 'V' || sCurStr[j] == 'M')
posCur = AP_CONST;
else
posCur = AP_VAR;
}
else
posCur = AP_ERR;
// case list
}; break;
///////////////////////////////////////////////////////////////////////
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
21
21
case
case
case
case
case
case
case
case
case
case
AP_IF1: KeyLetter('f', AP_IF2); break;
AP_IF2: KeyFinish(LEX_IF); break;
AP_THEN1: KeyLetter('h', AP_THEN2); break;
AP_THEN2: KeyLetter('e', AP_THEN3); break;
AP_THEN3: KeyLetter('n', AP_THEN4); break;
AP_THEN4: KeyFinish(LEX_THEN); break;
AP_ELSE1: KeyLetter('l', AP_ELSE2); break;
AP_ELSE2: KeyLetter('s', AP_ELSE3); break;
AP_ELSE3: KeyLetter('e', AP_ELSE4); break;
AP_ELSE4: KeyFinish(LEX_ELSE); break;
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
case AP_ASSIGN:
if (sCurStr[j] == '=')
AddKeyToList(LEX_ASSIGN, AP_START);
else
posCur = AP_ERR;
break;
///////////////////////////////////////////////////////////////////////
case AP_VAR:
switch (sCurStr[j])
{
case ':': AddVarToList(AP_ASSIGN, j); break;
case '(': AddVarKeyToList(LEX_OPEN, AP_START); break;
case ')': AddVarKeyToList(LEX_CLOSE, AP_START); break;
case ';': AddVarKeyToList(LEX_SEMI, AP_START); break;
case '<': listLex.emplace_back(LEX_MEN, iAll, i, j); break;
case '>': listLex.emplace_back(LEX_BOL, iAll, i, j); break;
case '=': listLex.emplace_back(LEX_RAV, iAll, i, j); break;
case ' ': case 10: case 13: case 9: AddVarToList(AP_START,
j); break;
default:
if (isalnum(sCurStr[j]) || sCurStr[j] == '_')
posCur = AP_VAR;
else
posCur = AP_ERR;
} // case list
break;
///////////////////////////////////////////////////////////////////////
case AP_CONST:
switch (sCurStr[j])
{
case ':': AddConstToList(AP_ASSIGN, j); break;
case '(': AddConstKeyToList(LEX_OPEN, AP_START); break;
case ')': AddConstKeyToList(LEX_CLOSE, AP_START); break;
case ';': AddConstKeyToList(LEX_SEMI, AP_START); break;
case '<': listLex.emplace_back(LEX_MEN, iAll, i, j); break;
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
22
22
case '>': listLex.emplace_back(LEX_BOL, iAll, i, j); break;
case '=': listLex.emplace_back(LEX_RAV, iAll, i, j); break;
///////////////////////////////////////////////////////////////////////
case ' ': case 10: case 13: case 9: AddConstToList(AP_START, j); break;
default:
if (sCurStr[j] == _T('I') || sCurStr[j] == _T('V') ||
sCurStr[j] == _T('X')
|| sCurStr[j] == _T('L') || sCurStr[j] == _T('C'))
posCur = AP_CONST;
else
posCur = AP_ERR;
} // case list
break;
///////////////////////////////////////////////////////////////////////
case AP_COMM:
if (sCurStr[j] == '}')
posCur = AP_START;
// case list
}
// case pos
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// Проверяем, не достигнут ли конец строки
///////////////////////////////////////////////////////////////////////
if (j == sCurStr.GetLength() - 1)
{
// Конец строки - это конец текущей лексемы
switch (posCur)
{
case AP_IF2: AddKeyToList(LEX_IF, AP_START); break;
case AP_THEN4: AddKeyToList(LEX_THEN, AP_START); break;
case AP_ELSE4: AddKeyToList(LEX_ELSE, AP_START); break;
case AP_CONST: AddConstToList(AP_START, j + 1); break;
case AP_ASSIGN: posCur = AP_ERR; break;
case AP_IF1: case AP_THEN1: case AP_THEN2: case AP_THEN3:
case AP_ELSE1:
case AP_ELSE2: case AP_ELSE3: case AP_OR1: case AP_XOR1:
case AP_XOR2:
case AP_AND1: case AP_AND2: case AP_VAR: AddVarToList(AP_START, j + 1); break;
}
// case pos2
};
// Проверяем не была ли ошибка в лексемах
if (posCur == AP_ERR)
{
// Если была ошибка, вычисляем позицию ошибочной лексемы
iStart = (j - iStart) + 1;
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
23
23
// и запоминаем ее в виде фиктивной лексемы в начале списка
// для детальной диагностики ошибки
//CLexem* lex = new CLexem(_T("Недопустимая лексема"), iAll
- iStart, i, iStart);
//listLex.insert(listLex.begin(), lex);
listLex.emplace_back(_T("Недопустимая лексема"), iAll iStart, i, iStart);
rotate(listLex.rbegin(), listLex.rbegin() + 1,
listLex.rend());
// Если ошибка, прерываем цикл
break;
}
}
// for j
// В конце строки увеличиваем общий счетчик символов на 2: конец
строки и возврат каретки
iAll++; // += 2
// Если ошибка, запоминаем номер ошибочной строки и прерываем цикл
if (posCur == AP_ERR)
{
result = i + 1;
break;
}
}
// for i
/*
// Если комментарий не был закрыт, то это ошибка
if (posCur == AP_COMM)
{
//listLex.Insert(0, TLexem.CreateInfo('Незакрытый комментарий',
iStComm, iCnt, iAll - iStComm));
listLex.emplace_back(_T("Незакрытый комментарий"), iStComm, iCnt,
iAll - iStComm);
rotate(listLex.rbegin(), listLex.rbegin() + 1, listLex.rend());
result = iCnt;
}
else
// Если КА не в начальном состоянии - это неверная лексема
if (posCur != AP_START || posCur != AP_ERR)
{
//listLex.Insert(0, TLexem.CreateInfo('Незавершенная лексема',
iAll - iStart, iCnt, iStart));
listLex.emplace_back(_T("Незавершенная лексема"), iAll iStart, iCnt, iStart);
rotate(listLex.rbegin(), listLex.rbegin() + 1, listLex.rend());
result = iCnt;
}
*/
listLexem = listLex;
return result;
}
// Процедура добавления переменной в список
void LexAuto::AddVarToList(TAutoPos posNext, int iP)
{
// Выделяем имя переменной из текущей строки
//sTmp = System.Copy(sCurStr, iStart, iP - iStart);
sTmp = sCurStr.Mid(iStart, iP - iStart);
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
24
24
// При создании переменной она сначала заносится в таблицу идентификаторов,
// а потом ссылка на нее - в таблицу лексем
// listLex.Add(TLexem.CreateVar(AddTreeVar(sTmp), iStComm, i, iStart));
listLex.emplace_back(*AddTreeVar(sTmp), iStComm, i, iStart);
iStart = j;
iStComm = iAll - 1;
posCur = posNext;
}
///////////////////////////////////////////////////////////////////////
// Процедура добавления переменной и разделителя в список
///////////////////////////////////////////////////////////////////////
void LexAuto::AddVarKeyToList(TLexType keyAdd, TAutoPos posNext)
{
// Выделяем имя переменной из текущей строки
//sTmp = System.Copy(sCurStr, iStart, j - iStart);
sTmp = sCurStr.Mid(iStart, j - iStart);
// При создании переменной она сначала заносится в таблицу идентификаторов,
// а потом ссылка на нее - в таблицу лексем
// listLex.Add(TLexem.CreateVar(AddTreeVar(sTmp), iStComm, i, iStart));
// listLex.Add(TLexem.CreateKey(keyAdd, iAll, i, j));
listLex.emplace_back(*AddTreeVar(sTmp), iStComm, i, iStart);
listLex.emplace_back(keyAdd, iStComm, i, iStart);
iStart = j;
iStComm = iAll - 1;
posCur = posNext;
}
///////////////////////////////////////////////////////////////////////
// Процедура добавления константы в список
void LexAuto::AddConstToList(TAutoPos posNext, int iP)
{
// Выделяем константу из текущей строки }
// sTmp: = System.Copy(sCurStr, iStart, iP - iStart);
sTmp = sCurStr.Mid(iStart, iP - iStart);
// Заносим константу в список вместе с ее значением
// listLex.Add(TLexem.CreateConst(StrToInt(sTmp), iStComm, i, iStart));
listLex.emplace_back(iStComm, i, iStart, (sTmp));
iStart = j;
iStComm = iAll - 1;
posCur = posNext;
}
// Процедура добавления константы и разделителя в список
void LexAuto::AddConstKeyToList(TLexType keyAdd, TAutoPos posNext)
{
sTmp = sCurStr.Mid(iStart, j - iStart);
// При создании переменной она сначала заносится в таблицу идентификаторов,
// а потом ссылка на нее - в таблицу лексем
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
25
25
// listLex.Add(TLexem.CreateVar(AddTreeVar(sTmp), iStComm, i, iStart));
// listLex.Add(TLexem.CreateKey(keyAdd, iAll, i, j));
listLex.emplace_back(iStComm, i, iStart, (sTmp));
listLex.emplace_back(keyAdd, iStComm, i, iStart);
iStart = j;
iStComm = iAll - 1;
posCur = posNext;
}
// Процедура добавления ключевого или разделителя слова в список
void LexAuto::AddKeyToList(TLexType keyAdd, TAutoPos posNext)
{
// listLex.Add(TLexem.CreateKey(keyAdd, iStComm, i, iStart));
listLex.emplace_back(keyAdd, iStComm, i, iStart);
iStart = j;
iStComm = iAll - 1;
posCur = posNext;
}
// Процедура добавления ключевого слова и разделителя в список подряд
void LexAuto::Add2KeysToList(TLexType keyAdd1, TLexType keyAdd2, TAutoPos
posNext)
{
//listLex.Add(TLexem.CreateKey(keyAdd1, iStComm, i, iStart));
//listLex.Add(TLexem.CreateKey(keyAdd2, iAll, i, j));
listLex.emplace_back(keyAdd1, iStComm, i, iStart);
listLex.emplace_back(keyAdd2, iAll, i, j);
iStart = j;
iStComm = iAll - 1;
posCur = posNext;
}
// Процедура проверки очередного символа ключевого слова
void LexAuto::KeyLetter(TCHAR chNext, TAutoPos posNext)
{
switch (sCurStr[j])
{
case ':': AddVarToList(AP_ASSIGN, j); break;
case '(': AddVarKeyToList(LEX_OPEN, AP_START); break;
case ')': AddVarKeyToList(LEX_CLOSE, AP_START); break;
case ';': AddVarKeyToList(LEX_SEMI, AP_START); break;
case '{': AddVarToList(AP_COMM, j); break;
case ' ': case 10: case 13: case 9: AddVarToList(AP_START, j); break;
// #
default:
if (sCurStr[j] == chNext)
posCur = posNext;
else
if (sCurStr[j] =='_'|| isalnum (sCurStr[j]))// ['0'..'9',
'A'..'Z', 'a'..'z', '_']
posCur = AP_VAR;
else
posCur = AP_ERR;
// case list };
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
26
26
}
}
// Процедура проверки завершения ключевого слова
void LexAuto::KeyFinish(TLexType keyAdd)
{
TCHAR ch = sCurStr[j];
switch (sCurStr[j])
{
case ':': AddKeyToList(keyAdd, AP_ASSIGN); break;
case '(': Add2KeysToList(keyAdd, LEX_OPEN, AP_START); break;
case ')': Add2KeysToList(keyAdd, LEX_CLOSE, AP_START); break;
case ';': Add2KeysToList(keyAdd, LEX_SEMI, AP_START); break;
case '{': AddKeyToList(keyAdd, AP_COMM); break;
case ' ': case 10: case 13: case 9: AddKeyToList(keyAdd, AP_START);
break;
default:
if (sCurStr[j] == '_' || isalnum(sCurStr[j]))// ['0'..'9',
'A'..'Z', 'a'..'z', '_']
posCur = AP_VAR;
else
posCur = AP_ERR;
}
// { case list };
}
ОГУ 09.03.01.3022. 506 ПЗ
Изм. Лист
№ докум
Подп.
Дата
Лист
27
27