Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
бАКАЛАВР_РАБОТА.docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
2.13 Mб
Скачать

4.1.4 Иерархия классов модуля

На рис. 4.2 представлена иерархия классов сканера. Где AbstractLexer- интерфейс, посредством которого осуществляется взаимодействие с синтаксическим анализатором;Lexer–реализацияклассаAbstractLexer;Token – класс, абстракция сущности токен; а Word , IntLiteral и FloatLiteralклассы производные от класса Tokenпредставляют лексемы с атрибутом – внутреннее представление лексемы,например целое число или строка.

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

Рисунок 4.2– Иерархия классов лексического анализатора

Листинг этого модуля представлен в Приложении В в разделе лексический анализатор.

4.2 Синтаксический анализатор

4.2.1 Функции синтаксического анализатора

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

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

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

4.2.2 Спецификация грамматики

Грамматика представляет собой совокупность четырех компонент:

  1. Терминалы. Они представляют собой базовые символы, из которых формируются строки. Термин “имя токена” является синонимом слова “терминал”;

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

  3. Стартовый символ.Один из нетерминалов грамматики считается стартовым символом, и множество строк, которые он обозначает, является языком, определяемым грамматикой;

  4. Продукции. Продукции грамматики определяют способ, которым терминалы и нетерминалы могут объединяться для создания строк. Каждая продукция состоит из следующих частей:

  1. Заголовок или левая часть продукции, обычно представлена нетерминалом;

  2. символ →;

  3. Тело, или правая часть, состоящая из нуля или некоторого количества терминалов и нетерминалов. Эти компоненты тела описывают один из способов, которым могут быть построены строки нетерминалов в заголовке.

По классификации Хомского разработанная грамматика языкаLISMA является контекстно-свободной(КС). На КС-грамматики распространяется единственное ограничение: вид левой части каждой продукции может быть ограничен лишь единственным нетерминальным символом. Или грамматика G[S] = <N, T, P, S> называется КС-грамматикой, если каждая ее продукция имеет вид A→β, где A N, β (N T)*.

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

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

  • отсутствие левой рекурсии, т.е. отсутствие продукций вида: A→+ Aa, где A N, а T;

  • наличие левой факторизации, т.е. отсутствие продукций вида: A→aB1 | aB2 , где A N, а T, B1 (N T)*,B2 (N T)*;

  • должна быть однозначна (обязательное требование для любой грамматики). Это означает существование единственного дерева разбора для каждой программы.

Таким образом, разработанная грамматика принадлежит к классу LL(2) без возврата с просмотром вперёд двух символов.

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

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

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

Привести грамматику полностью к классу LL(1) не удалось ввиду особенностей языка, но ситуации, где используется просмотр вперёд двух символов, сведены к минимуму.

Терминальный словарь грамматики представлен токенами из таблицы 4.2.

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

Программа - стартовый символ.Состоит из трёх частей: блока деклараций, классической части, гибридной части

  1. program → declsDefins classicPart hybridPart

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

    1. declsDefins → declsDefins1 declsDefins

    2. declsDefins → ε

    3. выбор из 2.1 или 2.2 (предо смотр 2)

Макрос (с параметром или без)

    1. declsDefins1 → MACROID param = expr ;

Явное определение константы

    1. declsDefins1 → CONST ID = sign ;

Объявление массива

    1. declsDefins1 → ARRAY ID [ NUM ] ;

Определение счётчика

    1. declsDefins1 → COUNT ID = [ list_inter ] ;

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

    1. declsDefins1 → ID index= sign;

Вспомогательные продукции

  1. list_inter → interval list_inter1

    1. list_inter1 → , interval list_inter1

    2. list_inter1 → ε

  1. interval → NUM – NUM

    1. param → [ID ]

    2. param → ε

    1. sign → ­­­–sign

    2. sign → literal

    1. index → [ expr_i ]

    2. index → ε

Классическая часть состоит из уравнений алгебраических и дифференциальных и условного оператора

    1. classicPart → classicPart1 classicPart

    2. classicPart → ε

    3. выбор 10.1 или 10.2 (предо смотр 2)

    1. classicPart1 → equation

    2. classicPart1 → condition

Условный оператор

  1. condition → IF ( bool ) THEN localValues ENDIF ;

Уравнение алгебраическое или дифференциальное

  1. equation → IDder index = expr ;

  2. localValues → localValue localValues1

    1. localValues1 → localValue localValues1

    2. localValues1 → ε

Локальное начальное условие с индексом (явное или неявное) и без него

  1. localValue → ID index = expr ;

    1. der →

    2. der → ~

Логическое выражение

  1. bool → join bool1

    1. bool1 → OR join bool1

    2. bool1 → ε

  1. join → equality join1

    1. join1 → AND equality join1

    2. join1 → ε

  1. equality → rel equality1

    1. equality1 → EQ rel

    2. equality1 → NE rel

    3. equality 1 → ε

    1. rel → expr rel1

    1. rel1 → < expr

    2. rel1 → > expr

    3. rel1 → LE expr rel1

    4. rel1 → GE expr rel1

    5. rel1 → ε

Арифметическоевыражение

  1. expr → term expr1

    1. expr1 → term expr1

    2. expr1 → + term expr1

    3. expr1 → ε

  1. term → unary term1

    1. term1 → / unary term1

    2. term1 → * unary term1

    3. term1 → ε

    1. unary → factor

    2. unary → unary

    3. unary → NOT unary

    1. factor → ( bool )

    2. factor → ID spec

    3. factor → REAL

    4. factor → NUM

    1. spec → index

Список фактических параметров функции

    1. spec → (list _expr)

  1. list _expr → expr list _expr1

    1. list _expr1 → , expr list _expr1

    2. list _expr1 → ε

Гибридная часть

    1. hybridPart → localState hybridPart

    2. hybridPart → ε

Локальное состояние

  1. localState → ver IS body FROM list_ver ;

Условно-адресная пара

  1. ver → ID cond

    1. cond → [bool ]

    2. cond → ε

Операторы – локальное начальное условие или уравнение

    1. body → body1 body

    2. body → ε

    1. body1 → localValue

    2. body1 → equation

    3. выбор 40.1 или 40.2 (предосмотр 2)

Список условно-адресных пар

    1. list_ver → ver list_ver1

    2. list_ver → ε

    1. list_ver1 → , ver list_ver1

    2. list_ver1 → ε

Вспомогательныепродукции

    1. literal → NUM

    2. literal → REAL

Арифметическиевыражениядляиндексов, введеныдляупрощения семантических проверок

  1. expr_i → term_i expr_i

    1. expr1_i → + term_i expr1_i

    2. expr1_i → term_i expr1_i

    3. expr1_i → ε

  1. term_i → unary_i term1_i

    1. term1_i → / unary_i term1_i

    2. term1_i → * unary_i term1_i

    3. term1_i → ε

    1. unary_i → factor_i

    2. unary_i unary_i

    1. factor_i → (expr_i )

    2. factor_i → ID

    3. factor_i → NUM