Справочник от Автор24
Поделись лекцией за скидку на Автор24

Синтаксический анализ. Метод рекурсивного спуска

  • 👀 640 просмотров
  • 📌 596 загрузок
Выбери формат для чтения
Статья: Синтаксический анализ. Метод рекурсивного спуска
Найди решение своей задачи среди 1 000 000 ответов
Загружаем конспект в формате docx
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Конспект лекции по дисциплине «Синтаксический анализ. Метод рекурсивного спуска» docx
5. Синтаксический анализ. Метод рекурсивного спуска Синтаксический анализатор – это часть компилятора, которая выполняет выявление синтаксических конструкций входного языка, анализ их корректности. Как правило, синтаксис языков программирования описывается с помощью КС-грамматик. В процессе работы синтаксический анализатор строит дерево разбора, корнем которого является аксиома, листьями - терминалы языка, а узлами – нетерминалы, причем для вершины А и ее потомков а1а2…ак в грамматике должно существовать правило А-> а1а2…ак. В зависимости от метода грамматического разбора – восходящего или нисходящего, различают различные виды синтаксических анализаторов. К нисходящим анализаторам относятся анализатор, работающий по методу рекурсивного спуска (по методу синт.диаграмм), LL(k)-анализатор. К восходящим анализаторам относятся анализаторы предшествования, LR(k)-анализаторы. Все эти методы предназначены для грамматик, на правила которых введен ряд ограничений для упрощения анализа. Рассмотрим метод СД, предназначенный для небольших языков программирования. Остальные методы – для больших языков. Синтаксической диаграммой называется граф, содержащий вершины двух видов – прямоугольники для нетерминалов и скругленные блоки – для терминалов, и строящийся по правилам грамматики следующим образом: 1) каждому нетерминалу соответствует своя СД. Если есть правила А->A1| A2 | … | An, то диаграмма имеет вид: А 2) для каждого правила А->a1a2…ak диаграмма имеет вид А Если построить диаграммы непосредственно по правилам грамматики, то их нельзя использовать для синтаксического анализа, так как они являются леворекурсивными и недетерминированными. В связи с этим СД необходимо преобразовать. Рассмотрим способы преобразования СД. Преобразование СД 1. Если ветви СД при разветвлениях имеют одинаковые блоки в начале или окончании, то их можно вынести: А A 2. Подстановка диаграммы в диаграмму. Это имеет смысл делать для сокращения количества СД, если такая подстановка не увеличивает сложность новой диаграммы. 3. Устранение левой рекурсии. А Правило А->A |  Для устранения левой рекурсии рассмотрим вывод А=>A=>A=>*. Обозначим * через T. A T Выполнив подстановку диаграммы в диаграмму, получим: А Если цепочка =, то возможны два типа диаграмм: или Диаграмма второго типа используется только в том случае, если  не несет семантической нагрузки, например, если это запятая в списке идентификаторов. Если это операция, соединяющая два операнда – то нужна диаграмма первого типа. Устранение правой рекурсии осуществляется аналогично. Разметка ветвей СД. Функции firstk(),followk() Для того чтобы запрограммировать точки ветвления в СД, необходимо идентифицировать ветви диаграммы. Для этого используются специальные функции. Функции firstk() соответствует множество терминальных цепочек длины не более k, выводимых из цепочки . Функции followk() соответствует множество терминальных цепочек длины не более k, которые при выводе следуют за цепочкой . firstk(A)={x| xVT*  A*x (|x|=k  |x|xR (|x|=k  |x|а1а2..аm. Например: S->while (V) O O-> {P} | a=V; P->PO | e V->V+E | E E-> a | c | (V) Построим first1. S O P V E i=0 while { a e a c ( i=1 while { a { a e a c ( a c ( i=2 while { a { a e a c ( a c ( Для построения функции followk необходимо дополнить анализируемый текст k знаками конца #, а начинать анализ надо с цепочки S#k. Для S#k cтроятся всевозможные выводы длины k+1 вида a1Afa2, при этом цепочки f, следующие за А, и составляют функцию follow(A). Алгоритм основан на том, что в процессе вывода мы рассматриваем цепочки одинаковой длины k+1. Это означает, что грамматика для построения функции follow должна быть преобразована к эквивалентной неукорачивающей форме. Для нашего примера follow(S)= # follow(O)= # } follow(P)= } a { follow(V)= ; ) + follow(E)= ; ) + Проще всего определять функции по диаграммам, нужно смотреть, с какой терминальной цепочки будет начинаться конструкция, соответствующая нетерминалу (это first), и какие терминальные цепочки будут при порождении находиться за нетерминалом (follow). Программирование СД Правила программирования СД: 1. Каждой СД А ставится в соответствие функция void A(void) { } 2. Ввести в проект кроме scanner модуль analis дополнить defs.h кодами синтаксических ошибок. 3. Функцию PrintError(int err) реализовать как switch по номеру ошибки, дополнив новыми кодами синтаксических ошибок по сравнению с лексическими ошибками сканера. Функция PrintError вызывается в тексте синтаксического анализатора в точках, где находится неверная лексема, причем в синтаксическом анализаторе, работающем до первой ошибки, эта функция должна вызывать завершение работы всей программы. 4. Если в диаграмме встретился нетерминальный элемент, то осуществляется вызов соответствующей функции. 5. Все переменные, используемые при программировании СД, должны быть локальными для соответствующей функции. void S(void){int uk1,t;LEX l; … } 6. Если в СД терминальный символ, то его обработка, если он несет смысловую нагрузку, выглядит следующим образом: t=Scaner(l); if (t==<нужный тип>) {семант.обработка} else PrintError(код); Выдача ошибки осуществляется в виде – «Ожидался тип …» Если терминал не несет семантической нагрузки, то код анализатора: t=Scaner(l); if (t!=<нужный тип>) PrintError(код); 7. Если в диаграмме идут подряд блоки, то им при программировании соответствует последовательность обработки терминальных и нетерминальных символов. 8. Если в диаграмме имеется разветвление, и все они начинаются терминалами, то диаграмме соответствует код t=Scaner(l); if (t==) {реализ.ветви 1 без 1 терминала} else … if (t==) {реализ.ветви n без 1 терминала} else PrintError(код); В PrintError выдается ошибка «Ожидался один из first разветвления» или «неверная конструкция». 9. Если в диаграмме разветвление, и хотя бы 1 ветвь начинается нетерминалом, то для того, чтобы был корректный вход в функции, реализующие нетерминалы, необходимо в локальной переменной сохранять старое значение указателя (uk1=GetUk();) и восстановить его в начале каждой ветви, начинающейся нетерминалом. (SetUk(uk1)) 10. Для диаграммы вида возможны два случая. Если ветвь начинается терминалом, то t=Scaner(l); do {реализ.ветви  без сканирования 1 терминала uk1=GetUk(); t=Scaner(l); } while (t==); SetUk(uk1); Если ветвь начинается с нетерминала, то do {реализ.ветви  uk1=GetUk(); t=Scaner(l); SetUk(uk1); } while (t==); 11. Если ветвь начинается нетерминалом uk1=GetUk(); t=Scaner(l); SetUk(); while (t==) { (); uk1=GetUk(); t=Scaner(l); SetUk(); } Для терминального начала восстановление указателя выносится из цикла: uk1=GetUk(); t=Scaner(l); while (t==) { реализ.ветви  без сканирования 1 терминала uk1=GetUk(); t=Scaner(l); } SetUk(uk1); 12. В main вызвать функцию, соответствующую аксиоме.
«Синтаксический анализ. Метод рекурсивного спуска» 👇
Готовые курсовые работы и рефераты
Купить от 250 ₽
Решение задач от ИИ за 2 минуты
Решить задачу
Найди решение своей задачи среди 1 000 000 ответов
Найти
Найди решение своей задачи среди 1 000 000 ответов
Крупнейшая русскоязычная библиотека студенческих решенных задач

Тебе могут подойти лекции

Смотреть все 493 лекции
Все самое важное и интересное в Telegram

Все сервисы Справочника в твоем телефоне! Просто напиши Боту, что ты ищешь и он быстро найдет нужную статью, лекцию или пособие для тебя!

Перейти в Telegram Bot