Синтаксический анализ выражений методом рекурсивного спуска | Статья в журнале «Молодой ученый»

Отправьте статью сегодня! Журнал выйдет 28 декабря, печатный экземпляр отправим 1 января.

Опубликовать статью в журнале

Автор:

Рубрика: Информационные технологии

Опубликовано в Молодой учёный №31 (373) июль 2021 г.

Дата публикации: 29.07.2021

Статья просмотрена: 1163 раза

Библиографическое описание:

Андронычева, Е. М. Синтаксический анализ выражений методом рекурсивного спуска / Е. М. Андронычева. — Текст : непосредственный // Молодой ученый. — 2021. — № 31 (373). — С. 5-10. — URL: https://moluch.ru/archive/373/83435/ (дата обращения: 18.12.2024).



В данной работе разрабатывается программный код для вычисления арифметических выражений методом рекурсивного спуска. Для понимания статьи читатель должен обладать начальными сведениями о C#: уметь компилировать и запускать приложение; знать синтаксис, основные типы данных и структуры управления.

Ключевые слова: рекурсивный спуск, синтаксический анализ, лексический анализ, арифметическое выражение.

Давайте рассмотрим следующую задачу. Пусть у нас есть строка, в которой содержится математическое выражение, например 4 + (20–3) * 2. Необходимо написать алгоритм, который вычислит значение этого выражения. Такая процедура называется синтаксический разбор выражений и является основой всех компиляторов и интерпретаторов языков, электронных таблиц и всех остальных программ, в которых требуется превращать числовые выражения в форму, понятную компьютеру.

Хоть синтаксический разбор может показаться сложным для восприятия, но является достаточно прямолинейным процессом благодаря четко определённой задаче и строгим правилам алгебры. В настоящей статье будет разработан рекурсивный нисходящий синтаксический анализатор, или синтаксический анализатор методом рекурсивного спуска (recursive-descent parser). Для этого будет использован язык программирования C#.

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

// Parser Rules

// expr: plus_minus^ EOF;

// plus_minus: mult_div ((‘+’ | ‘-‘) mult_div)^;

// mult_div: factor ((‘*’ | ‘/’) factor)^;

// factor: number | ‘(‘ expr ‘)’;

Здесь символ ‘^’ означает, что эта часть выражения может повторятся ноль или более раз; factor — подвыражение, которое может содержать число и/или выражение типа expr в скобках; mult_div — подвыражение, которое может содержать одно или несколько подвыражений factor , соединённых знаком умножения или деления; plus_minus — подвыражение, которое может содержать одно или несколько подвыражений mult_div , соединённых знаком сложения или вычитания; expr — выражение, которое содержит конец строки и может содержать подвыражение plus_minus (или не содержать его).

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

Опишем перечисление для типов лексем, которые могут встречаться в выражении. Оно будет содержать элементы, такие как LEFT_BRACKET, RIGHT_BRACKET — открывающаяся и закрывающаяся скобки, OP_PLUS, OP_MINUS, OP_MUL, OP_DIV — операции сложения, вычитания, умножения и деления, NUMBER — число, EOF — конец строки. На Рисунке 1 представлен соответствующий код.

Перечисление типов лексем [создано автором]

Рис. 1. Перечисление типов лексем [создано автором]

Для хранения сведений об лексемах опишем класс, который будет содержать два поля: тип лексемы ( LexemeType ) и саму лексему в переменной строкового типа. Код представлен на Рисунке 2.

Класс Lexeme [создано автором]

Рис. 2. Класс Lexeme [создано автором]

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

1) Если символ является математическим знаком, который поддерживает наша грамматика (т. е. это символы: ‘(’, ‘)’, ‘+’, ‘-’, ‘*’, ‘/’). Без дополнительных операций добавляем его в список с соответствующим типом лексемы.

2) Если символ является цифрой, то это значит, что мы начинаем считывать число. Для этого создаем переменную sb типа StringBuilder , в которую будем добавлять символы. Далее уместно использовать цикл do-while , в котором будем добавлять текущий символ в переменную sb и переходить к следующему символу в строке, пока текущий символ является цифрой, проверяя при этом не является ли он концом строки.

3) Если символ не является пробелом (пробелы игнорируем, они не несут смысловой нагрузки в нашем случае), то это посторонний символ, который не может встречаться в нашем выражении. Выводим ошибку.

Функция лексического анализа [создано автором]

Рис. 3. Функция лексического анализа [создано автором]

Необходимо написать еще один вспомогательный класс — буфер лексем, представленный на Рисунке 4. Вся информация, касаемая прохода по массиву, будет сосредоточена в нем, а именно: текущая позиция, рассматриваемой лексемы, в списке; само арифметическое выражение в виде списка лексем; метод для получения следующей лексемы; метод для изменения текущего положения назад.

Класс LexemeBuffer [создано автором]

Рис. 4. Класс LexemeBuffer [создано автором]

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

1) public static string expr(LexemeBuffer lexemes) — метод принимающий в качестве параметра LexemeBuffer — вычисляемую строку и возвращающий вещественное значение — результат выполнения программы, а также сообщение об ошибке типа «Пустая строка»;

2) public static double plusminus(LexemeBuffer lexemes) — метод принимающий в качестве параметра LexemeBuffer — вычисляемую строку и возвращающий вещественное значение — результат выполнения сложения или вычитания;

3) public static double multdiv(LexemeBuffer lexemes) — метод принимающий в качестве параметра LexemeBuffer — вычисляемую строку и возвращающий вещественное значение — результат выполнения умножения или деления;

4) public static double factor(LexemeBuffer lexemes) — метод принимающий в качестве параметра LexemeBuffer — вычисляемую строку и возвращающий вещественное значение — число. А также возвращает такие сообщения об ошибках как «Деление на ноль» и «Отсутствует закрывающаяся скобка».

Класс синтаксического анализа [создано автором]

Рис. 5. Класс синтаксического анализа [создано автором]

Для проверки демонстрации использования анализатора, напишем функцию Main(), представленную на Рисунке 6. В ней будем считывать строку с клавиатуры и выводить форматированный результат на экран.

Функция Main() [создано автором]

Рис. 6. Функция Main() [создано автором]

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

32 + 16 / 2

При вызове функции exp() — входной точки анализатора — из входной строки выбирается лексема. Если она является пустой строкой, то функция выводит ошибку «Пустая строка» и завершает работу. Однако в данном случае лексемой является число 32. Поскольку это не пустая строка, вызывается функция plusminus() . В результате функция plusminus() вызывает multdiv() , а та в свою очередь вызывает factor() . Затем функция factor() может либо рекурсивно вызвать функцию exp() (в случае выражения заключенного в скобки), либо определить значение числа. В нашем случае она возвращает число 32, и управление возвращается функции plusminus() .

Затем происходит выборка следующей лексемы, которой становится оператор + и которая сохраняется в переменную lexeme , и спуск по цепочке начинается снова. Как и раньше, вызывается функция factor() , которая возвращает значение 16. В функции multdiv() считывается следующая лексема — оператор /. Аналогично возвращается последняя лексема 2 и выполняется первая арифметическая операция — деление 16 на 2. Полученный результат возвращается функции plusminus() , где выполняется сложение. В результате вычитания в ответе получается 40.

Литература:

  1. Герберт Шилдт — C# 2.0. Полное руководство, 4-е изд.
  2. Руководство по C# от Microsoft [Электронный ресурс] — https://docs.microsoft.com/
  3. Курс лекции по дисциплине «Технологии программирования» Молчанова С. И. 2021
Основные термины (генерируются автоматически): EOF, арифметическое выражение, вещественное значение, вычисляемая строка, качество параметра, лексический анализ, функция, конец строки, рекурсивный спуск, символ, синтаксический анализ.


Ключевые слова

рекурсивный спуск, синтаксический анализ, лексический анализ, арифметическое выражение

Похожие статьи

Асинхронное выполнение SQL-запросов на языке программирования PHP

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

Теоретические аспекты создания обозревателя файловой директории с применением кроссплатформенного фреймворка Qt

В статье подробно разобран пример программы, написанной на языке C++ на основе кроссплатформенного фреймворка Qt. Программа InterView написана программистами компании Qt, и входит в состав примеров, поставляемых вместе с пакетом Qt Creator. На её при...

Применение векторизации слов для нечеткого поиска

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

Оценка скорости вычисления тригонометрических функций на Си

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

Методы использования регулярных выражений для грамматических ситуаций Казахско-Английского машинного перевода

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

Построение графиков функций в решении задач по общей физике с помощью программы Excel (на примере домашнего задания по теме «Электромагнитная индукция»)

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

Программное средство верификации конфигурационных файлов сетевого оборудования

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

Упрощение текстов через чат GPT при обучении иностранным языкам

Данная статья рассматривает применение чата GPT для упрощения текстов в контексте обучения иностранному языку. Цель статьи — исследование процесса упрощения сложных текстов с помощью чата GPT Prompt Perfect версии 3.5, который делает их более доступн...

Имитационное моделирование квантового алгоритма решения систем линейных уравнений в прикладной программе MATLAB

Целью статьи является ознакомление с квантовым алгоритмом решения систем линейных алгебраических уравнений (СЛАУ) и программой для его моделирования на ЭВМ с помощью программы MATLAB. В работе представлен код программы модели и результат её работы.

Комбинированный алгоритм оптимизации вредных выбросов промышленного предприятия

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

Похожие статьи

Асинхронное выполнение SQL-запросов на языке программирования PHP

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

Теоретические аспекты создания обозревателя файловой директории с применением кроссплатформенного фреймворка Qt

В статье подробно разобран пример программы, написанной на языке C++ на основе кроссплатформенного фреймворка Qt. Программа InterView написана программистами компании Qt, и входит в состав примеров, поставляемых вместе с пакетом Qt Creator. На её при...

Применение векторизации слов для нечеткого поиска

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

Оценка скорости вычисления тригонометрических функций на Си

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

Методы использования регулярных выражений для грамматических ситуаций Казахско-Английского машинного перевода

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

Построение графиков функций в решении задач по общей физике с помощью программы Excel (на примере домашнего задания по теме «Электромагнитная индукция»)

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

Программное средство верификации конфигурационных файлов сетевого оборудования

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

Упрощение текстов через чат GPT при обучении иностранным языкам

Данная статья рассматривает применение чата GPT для упрощения текстов в контексте обучения иностранному языку. Цель статьи — исследование процесса упрощения сложных текстов с помощью чата GPT Prompt Perfect версии 3.5, который делает их более доступн...

Имитационное моделирование квантового алгоритма решения систем линейных уравнений в прикладной программе MATLAB

Целью статьи является ознакомление с квантовым алгоритмом решения систем линейных алгебраических уравнений (СЛАУ) и программой для его моделирования на ЭВМ с помощью программы MATLAB. В работе представлен код программы модели и результат её работы.

Комбинированный алгоритм оптимизации вредных выбросов промышленного предприятия

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

Задать вопрос