Студопедия КАТЕГОРИИ: АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция |
Основная программа анализатораСтр 1 из 3Следующая ⇒
Содержание Введение. 4 ЛАБОРАТОРНАЯ РАБОТА 5 ПОСТРОЕНИЕ СИНТАКСИЧЕСКОГО АНАЛИЗАТОРА.. 5 1. Цель работы.. 5 2. Теоретическая часть. 5 2.1. Анализатор многочленов. 5 2.2. Умножение многочленов. 12 2.3. Табличный LL(1) – анализатор. 22 2.4. Табличный транслятор многочленов. 24 2.5. Реализация стека. 25 2.6. LL(1) – драйвер. 27 3. Порядок выполнения работы.. 29 4. Контрольные вопросы.. 30 Список литературы.. 31
Введение
Синтаксический анализатор (синтаксический разбор) – это часть компилятора, которая отвечает за выявление и проверку синтаксических конструкций входного языка. В задачу синтаксического анализа входит: · поиск и выделение синтаксических конструкций в тексте исходной программы; · установка типа и проверка правильности каждой синтаксической конструкции; · представление синтаксических конструкций в виде, удобном для дальнейшей генерации текста результирующей программы. В основе синтаксического анализатора лежит распознаватель текста исходной программы, построенный на основе грамматики входного языка. Как правило, синтаксические конструкции языков программирования могут быть описаны с помощью КС – грамматик, реже встречаются языки, которые могут быть описаны с помощью регулярных грамматик. К классу контекстно-свободных (КС) относятся грамматики, у которых не накладывается никаких ограничений на вид правых частей их правил, а левая часть каждого правила – единственный нетерминал. Для проектирования языков программирования используются синтаксические диаграммы, которые играют роль схемы алгоритма. В теоретической части данной лабораторной работы рассмотрены основные этапы построения синтаксического анализатора многочленов. ЛАБОРАТОРНАЯ РАБОТА ПОСТРОЕНИЕ СИНТАКСИЧЕСКОГО АНАЛИЗАТОРА Цель работы Целью лабораторной работы является закрепление теоретических знаний и приобретение практических навыков при построении синтаксического анализатора методом рекурсивного спуска. Теоретическая часть
Анализатор многочленов
Пользуясь методом рекурсивного спуска, запрограммируем синтаксический анализатор многочленов. Будем считать, что анализатор должен просто отвечать на вопрос, является ли введенная пользователем строка правильно записанным многочленом. Язык многочленов Определим правила записи многочленов от x с постоянными целочисленными коэффициентами, с помощью синтаксических диаграмм. Примеры таких многочленов: 199 Последний пример не содержит переменной x. Условимся считать такую запись правильным многочленом нулевой степени. Чтобы запись многочленов могла быть обработана компьютерной программой (транслятором), предусмотрим возможность записи символов без надстрочных показателей степени: . Построим синтаксические диаграммы, определяющие правила записи многочленов. Первой будет диаграмма для начального нетерминала, который есть не что иное как «Многочлен». Многочлен состоит из отдельных слагаемых, между которыми записываются знаки операций. Перед первым слагаемым может быть записан знак. Слагаемых должно быть не меньше одного. С учетом этого получается следующая диаграмма (рис.1). Рис. 1. Синтаксическая диаграмма многочлена
Построим теперь диаграмму для нетерминала «Слагаемое». Одна ветвь должна соответствовать полному варианту слагаемого, когда присутствуют все его элементы: коэффициент, буква x, знак возведения в степень и сама степень. Другие ветви (обходные) должны позволять предусмотреть такие варианты слагаемого, когда нет коэффициента, буквы x и последующей степени, или только степени. При этом на диаграмме не должно появиться такого пути, пройдя по которому мы минуем как коэффициенты, так и x. Рис. 2. Синтаксическая диаграмма слагаемого
«Целое» без знака есть последовательность, состоящая из одной и более цифр (рис.3, а). Диаграмма для нетерминала «Цифра» показана на рис.3, б. Поскольку арабские цифры используются в самых разнообразных языках, было бы неудобно каждый раз приводить диаграмму, подобную изображенной на рис. 3, б. В дальнейшем будем вместо нетерминального блока «Цифра» использовать на диаграммах овал (рис.3,в). Рис. 3. Синтаксическая диаграмма для целого и для цифры Основная программа анализатора Вначале необходимо подготовить текст для чтения анализатором. Было бы неправильно делать так, чтобы в основной программе и распознающих процедурах отражались особенности представления входной цепочки, т.е. программировать таким образом, чтобы основные части анализатора зависели от того, считываются ли символы из файла, вводятся с терминала, извлекаются из окна редактора текста или получаются как-либо по-другому. Эта специфика будет скрыта в нескольких процедурах, которые только и будут зависеть от конкретного представления входной цепочки. Предусмотрим, что подготовка входного текста к чтению выполняется процедурой ResetText: Begin ResetText; Далее вызываем распознающую процедуру начального нетерминала, которым в нашей задаче является «Многочлен»: Polynom; Данная процедура считывает символы входной цепочки, проверяя, соответствует ли их порядок синтаксису многочленов. Если необходимо она обращается к другим распознающим процедурам. В случае, когда входная цепочка действительно содержит многочлен, процедура Polynom оставляет текущим символ, следующий за многочленом. Если при анализе многочлена обнаруживается ошибка, вызывается процедура Error, останавливающая работу анализатора. Для завершения анализа остается проверить, не содержится ли за правильно записанным многочленом во входной цепочке лишних символов: if Ch <> EOT then Error(‘Ожидается конец текста’) else WriteLn (‘Правильно’); WriteLn; end. Вспомогательные процедуры Запишем начало программы с описанием констант и переменных. Кроме глобальной переменной Ch, обозначающей текущий символ, предусмотрим глобальную переменную Pos, которая будет хранить номер этого символа во входной цепочке. program ParsePoly: const EOT = chr(0); var Ch:char; Pos: integer; Взаимодействие с входным текстом будут выполнять процедуры ResetText – готовит входную цепочку к считыванию распознавателем, и NextCh – читает очередной символ входной цепочки, перемещая его в переменную Ch. Предусмотрим чтение исходных данных из стандартного входного файла. Сообщения распознавателя будут выводиться в стандартный выходной файл (т.е. на экран). В этом случае процедура, подготавливающая текст, будет выглядеть следующим образом: procedure ResetText; begin WriteLn(‘Введите многочлен от x с целыми коэффициентами’); Pos:=0; NextCh; end; При чтении очередного символа будем игнорировать пробелы, считая их не значащими. Это позволит при записи исходного многочлена применять пробелы, например для отделения одного слагаемого от другого. procedure NextCh; begin repeat Pos:=Pos+1; if not eoln then Read(Ch) else begin ReadLn; Ch:=EOT; end; until Ch<>’ ‘; end; Такой пропуск пробелов делает их допустимыми в любом месте записи многочлена. Это не соответствует соглашениям современных языков программирования, но все же предпочтительней полного запрета пробелов. Процедура, реагирующая на ошибку, тоже зависит от соглашений по вводу и выводу. Она будет выдавать следующее сообщение на экране: procedure Error(Message:string); { Message – сообщение об ошибке} begin WriteLn(‘^’: Pos); WriteLn(‘Синтаксическая ошибка:’, Message); Halt; {Прекращение работы анализатора} end; Вывод знака ‘^’ в позиции Pos позволяет указать стрелкой на символ, вызывающий ошибку. Диалог с анализатором может быть таким: Введите многочлен от x с целыми коэффициентами 2x + 1.2 ^ Синтаксическая ошибка: Ожидается конец текста Распознающие процедуры Для каждого нетерминала грамматики многочленов, т.е. для каждой синтаксической диаграммы (рис.1-3) запишем одну распознающую процедуру. Синтаксическая диаграмма для нетерминала «Многочлен» неудобна для программирования – она содержит цикл с выходом из середины. Заменим диаграмму эквивалентной, содержащей цикл с предусловием. Рис.4. Преобразованная диаграмма «Многочлен»
Теперь можно записать распознающую процедуру. На диаграмме три последовательных участка – в процедуре три оператора, выполняемых один за другим: if, вызов процедуры Addend, цикл while. procedure Polynom; begin if Ch in [‘+’, ‘-‘] then NextCh; Addend; {Слагаемое} while Ch in [‘+’, ‘-‘] do begin NextCh; Addend; end; end; Следующий нетерминал – «Слагаемое». Однако диаграмма, показанная на рис. 2, не разделяется на типовые фрагменты, что затрудняет программирование распознавателя. Неструктурированность обусловлена фрагментом, который помечен на рисунке знаком «?». Преобразуем диаграмму в эквивалентную, но состоящую только из совокупности типовых структур (рис.5). Для этого изобразим отдельно две ветви: одна соответствует слагаемому, начинающемуся с числа, другая – с буквы x. Фрагмент, выделенный на исходной диаграмме пунктирной рамкой, преобразуем в нетерминал «Степень». Рис. 5. Синтаксические диаграммы «Слагаемое» и «Степень» Программирование распознающих процедур Addend (слагаемое) и Power (степень) теперь легко выполнить: диаграммы служат схемами алгоритма. procedure Addend; begin if Ch = ‘x’ then begin NextCh; Power; end else begin Number; {Целое} if Ch = ‘x’ then begin NextCh; Power; end; end; end; procedure Power; begin if Ch = ‘^’ then begin NextCh; Number; end; end; Последняя распознающая процедура – для «Целого». Ее также можно преобразовать, отделив блок, соответствующий первой цифре (обязательной), от остальных блоков.
Рис.6. Синтаксическая диаграмма «Целое»
procedure Number; begin if Ch in [‘0’.. ‘9‘] then NextCh else Error(‘Число начинается не с цифры’); while Ch in [‘0’.. ‘9‘] do NextCh; end; Распознаватель готов. Осталось только расположить процедуры в правильном порядке.
Умножение многочленов Постановка задачи Составим программу, вводящую в символьной форме два многочлена и выводящую их произведение в символьной форме в порядке убывания степеней. Пример работы программы: 1-й многочлен: 3x^2+3x-5 2-й многочлен: x^3-2x Произведение: 3x^5+3x^4-11x^3-6x^2+10x Приступим к решению поставленной задачи. Сформулируем общий план: 1. Напечатать заголовок. 2. Вывести текст первого многочлена, проанализировать его запись и преобразовать в удобную для дальнейших вычислений форму. Для выполнения действий с многочленами удобно хранить их в программе в виде массива коэффициентов, количество которых соответствует степени многочленов, а индекс каждого коэффициента равен степени соответствующего слагаемого. 3. Ввести текст второго многочлена, проанализировать его запись и преобразовать в удобную для дальнейших вычислений форму. 4. Перемножить многочлены. 5. Напечатать результат. Запишем начало программы, в котором определим необходимые константы, типы данных и переменные. program PolyMult; const nmax = 255; {Максимальная степень} type tPoly = record {Тип многочленов} n: integer; {Степень} a: array [0..nmax] of integer; {Коэффициенты} end; var P1, P2, Q: tPoly; {Сомножители и произведение} Теперь, пользуясь предварительно составленным планом, запишем основную программу. begin WriteLn (‘Перемножение многочленов’); WriteLn; WriteLn(‘Первый многочлен’); GetPoly(P1); WriteLn(‘Второй многочлен’); GetPoly(P2); MultPoly(P1, P2, Q); {Перемножение} WriteLn; WriteLn(“Произведение’); WritePoly (Q); {Печать результатов} WriteLn; end. Умножение и вывод Детализацию начнем с процедуры, которая выполняет перемножение. И входные, и выходные параметры процедуры являются многочленами, представленные в виде совокупности массива коэффициентов и величины, задающей степень многочлена, т.е. относятся к типу tPoly. Обозначим сомножители x и y, а результат – z, Запишем заголовок процедуры. procedure MultPoly(X, Y: tPoly; var Z: tPoly); Основная идея вычисления коэффициентов многочлена-произведения состоит в том, что при попарном перемножении слагаемых первого и второго многочленов получающееся произведение участвует в формировании того слагаемого многочлена-результата, степень которого равна сумме степеней сомножителей. Т.е. при перемножении i-го члена многочлена X и j-го члена многочлена Y получается величина, которая должна быть добавлена к i+j-му слагаемому Z. Такое вычисление необходимо выполнить для всех сочетаний i и j, не забыв присвоить нулевые начальные значения коэффициентам z. Получаем: procedure MultPoly(X, Y: tPoly; var Z: tPoly); var i, j: integer; begin ClearPoly(Z); for i:=0 to X.n do for j:=0 to Y.n do Z.a[i+j]:= Z.a[i+j] + X.a[i]*Y.a[j]; {Определении степени многочлена} Z.n:=nmax; while (Z.n>0) and (Z.a[Z.n]=0) do Z.n:=Z.n-1; end; Процедура ClearPoly выполняет обнуление многочлена. ClearPoly(var P:tPoly); var i:integer; begin for i:=0 to nmax do P.a[i]:=0; P.n:=0; end; Теперь рассмотрим как будет осуществляться печать многочлена. Слагаемые должны выводиться в порядке убывания степеней. Слагаемому может предшествовать знак. Коэффициент печатается, если он не равен 0 или 1. Буква x выводится для ненулевых степеней, а значение показателя степени - если эта степень больше единицы. procedure WritePoly(P: tPoly); var i:integer; begin while P do for i:=n downto 0 do begin if (a[i]>0) and (i<>n) then Write(‘ + ’) else if (a[i]<0) and (i=n) then Write(‘-‘) else if a[i]<0 then Write(‘ - ‘); if (abs(a[i])>1) or (i=0) and (a[i]<>0) or (n=0) then Write(abs(a[i])); if (i>0) and (a[i]<>0) then Write(‘x’); if (i>1) and (a[i]<>0) then Write(‘^’,i) end; end. Транслятор многочленов Реализуем теперь транслятор (процедура GetPoly), преобразующий введенную запись многочлена в массив коэффициентов. Его задача – считывать символы из входной строки, выполнять распознавание многочлена и вычислять его коэффициенты. Распознающие процедуры будут вложены внутрь GetPoly, а сама эта процедура лишь подготовит чтение входной строки, вызовет распознаватель и убедиться, что за многочленом во входной строке ничего не содержится. procedure GetPoly(var P: Poly); const EOT = chr(0); {Конец текста} var Pos: integer; {номер символа} Ch: char; {очередной символ} … {Здесь разместятся процедуры распознавателя} … begin Pos:=0; NextCh; Poly(P); if Ch<> EOT then Error (‘Ожидается конец текста’); end; Разместим на синтаксических диаграммах значки семантических процедур и определим содержание этих процедур. Семантические процедуры – это встраиваемые в распознаватель действия, предназначенные для смысловой обработки входного текста.
Рис. 7. Синтаксическая диаграмма многочлена с семантическими процедурами
Перед началом обработки массив коэффициентов многочлена заполняется нулями, а его степень принимается равной нулю. Эту работу выполняет семантическая процедура , вызвав процедуру ClearPoly. : ClearPoly(P); и отвечают за запоминание знака перед слагаемым. Знак сохраняется в локальной переменной Op. : Op:=Ch; : Op:=’+’; Каждое слагаемое многочлена имеет вид . Транслятор слагаемого вычислит значения коэффициента a и степени k. Получив эти значения, семантическая процедура в зависимости от знака добавит или отнимет значение a из k –й ячейки массива коэффициентов: : if Op = ‘+’ then P.a[k]:= P.a[k] + a else P.a[k]:= P.a[k] – a; Было бы неправильно просто записывать значение коэффициента в a[k], поскольку в записи многочлена могут быть несколько членов с одинаковой степенью x – синтаксис этого не запрещает. Складывая или вычитая каждый коэффициент с предыдущей суммой, транслятор как бы приводит подобные. Определение степени n многочлена P выполняет семантическая процедура . : P.n:= nmax; while (P.n >0) and (P.a[P.n] = 0) do P.n:=P.n-1; Теперь пользуясь диаграммой и вставляя в текст анализатора в соответствующих местах определенные семантические процедуры, запишем распознаватель многочленов. procedure Poly(var P: tPoly); var a: integer; {Модуль коэффициента} k: integer; {Степень слагаемого} Op : char; {Знак операции} begin ClearPoly(P); {P3} if Ch in [‘+’, ‘-‘] then begin Op:=Ch; {P4} NextCh; end else Op:=’+’; {P5} Addend(a, k,) if Op = ‘+’ then {P6} P.a[k]:= P.a[k] + a else P.a[k]:= P.a[k] – a; while Ch in [‘+’,’-‘] do begin Op:=Ch {P4} NextCh; Addend(a, k,) if Op = ‘+’ then {P6} P.a[k]:= P.a[k] + a else P.a[k]:= P.a[k] – a; end; P.n:= nmax; {P7} while (P.n >0) and (P.a[P.n] = 0) do P.n:=P.n-1; end; Задача транслятора слагаемого состоит в определении коэффициента a и степени k. В соответствии с этим предусмотрим следующие семантические процедуры (рис. 8) Рис. 8 Синтаксическая диаграмма слагаемого с семантическими процедурами
Процедура присваивает коэффициенту a значение, равное величине целого числа, которое вычисляет транслятор целого. В программе это будет реализовано подстановкой переменной a в качестве параметра при вызове процедуры Number. Если коэффициент отсутствует, он принимается равным единице. : a:=1; Значение степени либо берется равным вычисленному распознавателем степени, либо нулевым, если в записи слагаемого отсутствует x. : k:= значение степени; : k:=0; Запишем теперь распознаватель – транслятор для слагаемого. procedure Addend (var a, k : integer); begin if Ch in [‘0’..’9’] then begin Number(a); {P8} if Ch = ‘x’ then begin NextCh; Power(k); {P10} end else k:=0 {P11} end else if Ch = ‘x’ then begin a:=1; {P9} NextCh; Power(k); {P10} end else Error(‘ Ожидается число или “x”’); end; Реализация распознавателя нетерминала «Степень» выполняется по соответствующей синтаксической диаграмме. Рис. 9. Синтаксическая диаграмма степени с семантическими процедурами
Чтобы защитить программу от ошибки при обращении к массиву коэффициентов, в процедуре предусмотрен контроль величины показателя степени. Вычисляемая степень обозначается p. : p:= значение целого; if p>nmax then Error (‘Слишком большая степень’); : p:=1; Семантическая процедура расположена на ветви диаграммы, где не было терминальных и нетерминальных блоков. В этом случае она сама играет роль блока, что меняет структуру программы-распознавателя (в нашем примере появляется ветвь else). procedure Power (var p: integer); begin if Ch = ‘^’ then begin NextCh; Number(p); {P12} if p>nmax then Error (‘Слишком большая степень’); end else p:=1; {P13} end; |
||||
Последнее изменение этой страницы: 2018-05-31; просмотров: 284. stydopedya.ru не претендует на авторское право материалов, которые вылажены, но предоставляет бесплатный доступ к ним. В случае нарушения авторского права или персональных данных напишите сюда... |