науки и высшего образования Российской Федерации ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ «ОРЕНБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ» Аэрокосмический институт Кафедра систем автоматизации производства КУРСОВАЯ РАБОТА по дисциплине «Лингвистическое и программное обеспечение систем автоматизированного проектирования» Разработка программного обеспечения лексического анализатора Пояснительная записка ОГУ 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