Сети ЭВМ и телекоммуникации
Содержание
- Постановка задачи 2
- Теоретические основы разработки трансляторов 3
- Построение лексического анализатора 3
- Построение синтаксического анализатора 4
- Описание синтаксических конструкций …………………………………………...6
- Грамматика, описывающая язык…………………………………………………… 8
- Управляющая таблица 8
- Листинг программы 9
- Результаты работы программы 17
- Список литературы 22
1.Постановка задачи:
Создать грамматику, описывающую конструкцию языка Pascal-оператор if. Для этой грамматики разработать интерпретатор, обеспечивающий показ промежуточных шагов анализа (лексический анализ, синтаксический анализ, построение дерева вывода и синтаксическое дерево).
2.Теоретические основы разработки трансляторов
Процесс интерпретации исходной программы происходит в несколько этапов:
- лексический анализ;
- синтаксический и семантический анализ;
- выполнение программы
2.1 Построение лексического
На этапе лексического анализа символы, составляющие исходную программу, считываются и группируются в отдельные лексические элементы, называемые лексемами. Лексический анализ важен по следующим причинам:
- замена в программе
- уменьшается длина программы, т.к. из нее устраняются несущественные пробелы и комментарии.
С точки зрения реализации процесса сканирования различают два подхода - прямой и непрямой лексический анализ. При прямом лексическом анализе требуется найти одну из многих лексем, которые заданы в описании данного языка.
Моделью прямого лексического анализатора служит множество работающих параллельно конечных автоматов (КА), каждый из которых распознает лексемы заданного типа. Эти КА можно представить и реализовать как один конечный преобразователь, моделирующий работу всех КА и выдающий сигнал о том, какой из них распознал очередную лексему.
При непрямом лексическом анализе требуется, прочитав цепочку символов, определить, образует ли эта цепочка лексему некоторого конкретного типа. В этом случае сканер работает вместе с синтаксическим анализатором, как некоторая программная процедура SCAN
Синтаксический анализатор обращается к SCAN всякий раз, когда ему нужен новый символ при анализе текста программы и построения ее внутреннего представления. В ответ на вызов, SCAN распознает очередную лексему в исходной программе и передает ее анализатору через таблицу лексем.
Непрямой сканер более экономичен ( в смысле экономии памяти), т.к. он не создает полной таблицы лексем для всего исходного текста программы.
Распознавание лексем выполняется следующим образом:
- входная цепочка считывается до тех пор, пока КА не достигнет заключительного состояния;
- по достижению заключительного состояния КА сигнализирует о нахождению лексемы данного типа и сканер заносит информацию о ней в таблицу имен (символов).
Таким образом, проблему построения непрямого лексического анализатора для данного типа лексем можно представить как проблему построения и реализации КА, который по достижению заключительного состояния, выдает на выходе лексему ( в этом смысле его можно рассматривать и как конечный преобразователь
При создании нового языка часто
удобно описывать его конструкции
с помощью синтаксических диаграмм,
а затем преобразовать в какую-
Для выполнения такого преобразования надо разметить синтаксическую диаграмму нетерминальными символами , используя следующие правила :
- вершина синтаксической диаграммы помечается начальным символом грамматики S ;
- между двумя подряд идущими терминальными символами вставляется нетерминальный символ AÎ N ;
- перед альтернативой (разветвлением на несколько ветвей) ставиться только один нетерминальный символ AÎN ;
- перед выходящими ветвями итерации (цикла ) ставится один нетерминальный символ .
Далее следует записать правила грамматики для каждого нетерминального символа. Все правила должны иметь вид : A® aB или A® a, где A , B Î N , a Î S È {e} (регулярная грамматика ) . При задании условий следует пользоваться следующими правилами :
1. Если два нетерминала А и В связаны одной ветвью направленной от А к В и содержащей терминал а, то правило имеет вид А ® аВ.
2. Если два нетерминала А и В связаны несколькими ветвями, направленными от А к В и содержащими терминалы a,b,..c, то правило имеет вид А® аВ½bB½...½cB.
3. Если среди ветвей, связывающих нетерминалы А и В, содержится пустая (не содержащая терминалов) ветвь и имеется ветвь, связывающая В и С, содержащая терминал а , тогда правило имеет вид А® аС , то есть нетерминал В пропускается .
В процессе работы сканера должна быть сформирована таблица имен для хранения информации о лексемах. Эта информация используется в дальнейшем для семантического контроля исходного текста программы и при генерации кода (при конструировании компилятора).
Таким образом, после распознавания каждой лексемы, сканер должен занести в таблицу имен их характеристики (атрибуты). К этим атрибутам относятся: класс лексемы (обозначаемый обычно во внутреннем представлении целым числом), фактическое значение лексемы (фактическое символическое имя), дополнительная информация.
2.2 Построение синтаксического анализатора
Синтаксический анализ подразумевает процесс анализа символьных цепочек с целью распознавания “правильных” или “неправильных” языковых конструкций. получение и представлении этой информации в виде дерева разбора или другой структуры. Эта структура называется промежуточной программой и используется для генерации кода.
Синтаксический анализ, в ходе которого
уясняется структура
Левым разбором цепочки a называется последовательность правил, примененных при левом выводе цепочки a из S.
Правым разбором цепочки a называется обращение последовательности правил, примененных при правом выводе цепочки a из S.
Эти разборы можно представить в виде последовательности номеров из множества правил анализируемой грамматики.
Большинство методов синтаксического анализа делят на два класса разбора, – нисходящего и восходящего. Анализаторы, реализующие методы нисходящего разбора, выдают на выходе левые разборы и называются левыми анализаторами, а реализующие методы восходящего разбора, выдают правые разборы и называются правыми анализаторами. Эти анализаторы можно моделировать при помощи соответствующих МП - преобразователей.
Попытка реализации МП-преобразователей для широкого класса КС-грамматик приводит к так называемым алгоритмам с “возвратами”, которые требуют слишком больших затрат времени. На практике обычно ограничивают классы грамматик таким образом, чтобы сделать процесс разбора полностью детерминированным. Требованиям детерминированности левых анализаторов наилучшим образом удовлетворяют так называемые LL(k)-грамматики, для которых левый анализатор работает детерминировано, если позволить ему принимать во внимание k входных символов, расположенных справа от текущей входной позиции. Входная цепочка считывается таким анализатором один раз слева направо и в процессе анализа не происходит возвратов к уже прочитанной части цепочки. Такие анализаторы называются однопроходными. Иначе говоря, G будет LL(k)-грамматикой, если для данной цепочки w AaÎ(NUE)* и первых k символов (если они есть), выводящихся из Aa, существует не более одного правила, которое можно применить к A, чтобы получить вывод какой-нибудь терминальной цепочки, начинающейся с w и продолжающейся упомянутыми k терминальными символами.
Разбор для LL(k)-грамматики удобно осуществлять с помощью так называемого k-предсказывающего алгоритма разбора, k-предсказывающий алгоритм А для КС-грамматики G=(N,S,P,S) использует входную ленту, магазин и выходную ленту и, по существу, моделирует работу МП-преобразователя, опустошающего магазин.
При чтении анализируемой цепочки, находящейся на входной ленте, входная головка может “заглядывать вперед” на k очередных символов.
На каждом такте сначала определяется аванцепочка u и верхний символ магазина Х. Затем рассматривается элемент М(Х, u) управляющей таблицы и в соответствии с его содержанием выполняется одно из 4 действий:
1) если М(Х, u) = (b, i), то (x, Xa, p) (x, ba, p i), т.е. верхний символ магазина заменяется цепочкой b и к выходу добавляется номер правила i, входная головка не сдвигается;
2) если М(а, u)= «выброс» и x=a , то (x, aa, p) ( , a, p), т.е. если верхний символ магазина совпадает с текущим входным символом, то он выбрасывается из магазина и входная головка сдвигается на один символ вправо;
3) если алгоритм достигает
4) если M(Х, u)= «ошибка», то разбор прекращается и выдается сообщение об ошибке, а соответствующая конфигурация (x, Xa, p) называется ошибочной.
Для построения корректной управляющей таблицы М для заданной LL(1)-грамматики можно использовать следующий алгоритм:
Вход алгоритма: заданная LL(1)-грамматика G=(N, S, P, S).
Выход алгоритма: управляющая таблица М.
Описание алгоритма.
$ — маркер дна магазина. Таблица М определяется на множестве (N È S È {$})´(S È {e}) следующим образом:
1) если А®a — правило из P с номером i, то М(А, а) = (a, i) для всех (а¹е)ÎFIRST1(a); если еÎFIRST1(a), то М(А, b) = (a, i) для всех bÎFOLLOW1(A);
2) М(а, а) = «выброс» для всех аÎS;
3) М($, е) = «допуск»;
4) в остальных случаях М(X, а)= «ошибка» для XÎ N È S È {$} и аÎS È {e}.
3. Описание синтаксических конструкций
Синтаксическая диаграмма оператора присваивания:
Выражение оператора условия:
Синтаксическая диаграмма
Синтаксическая диаграмма
4 Грамматика, описывающая язык.
1) S -> VAR PER; IF A THEN B ELSE B 12) I->+TI
2) PER->K: INTEGER; M: DOUBLE; 13) I->-TI
3) K -> F,F
4) F-> P
5) M-> F,F
6) A->E>E
7) A->E<E
8) E->TI
9) T->FJ
10) F->P
11) F->(E)
5.Управляющая таблица такого анализатора построена по приведённому выше алгоритму и приведена ниже.
6.Листинг программы:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, ADODB, Grids, ComCtrls, StdCtrls, Buttons, XPMan, ExtCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
BitBtn1: TBitBtn;
AT: TADOTable;
ADOConnection1: TADOConnection;
DataSource1: TDataSource;
Button2: TButton;
Kontr: TPageControl;
TabSheet1: TTabSheet;
StringGrid1: TStringGrid;
TabSheet2: TTabSheet;
StringGrid_UprTabl: TStringGrid;
TabSheet4: TTabSheet;
TV: TTreeView;
OpenDialog1: TOpenDialog;
TabSheet3: TTabSheet;
TVV: TTreeView;
TabSheet5: TTabSheet;
Memo2: TMemo;
TabSheet6: TTabSheet;
Memo3: TMemo;
GroupBox1: TGroupBox;
GroupBox2: TGroupBox;
Image1: TImage;
Image2: TImage;
procedure BitBtn1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure SintaxTree(T:TTreenode; l,r: integer);
procedure BuildSintaxTree;
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
StrCount :integer;
Pri :array [1..255] of integer;
Str :array [1..255] of string;
procedure RecBuildTree(T: TTreeNode; i :integer);
public
{ Public declarations }
end;
var
Form1: TForm1;
globalX,GlobalY:integer;
implementation
var typ:array [3..10] of string=('Îïåðàòîð ïðèñâàèâàíèÿ','Çíàê ñðàâíåíèÿ','Çíàê
îïåðàöèè','Ïåðåìåííàÿ','Öåëîå ÷èñëî','Ñêîáêà','
list:array [1..100] of string;
listZ:array [1..100] of integer;
list_cur:integer;
{$R *.dfm}
procedure LoadTiGrid();
var i,j,b,bj:integer;
begin
form1.AT.First;
for j:=0 to 28 do begin
for i:=0 to 17 do begin
form1.StringGrid_UprTabl.
end;
form1.AT.Next;
end;
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
form1.StringGrid_UprTabl.
end;
Function GetString(s:string;var state:integer):string;
var i:char;max:integer;
begin
if s='' then
begin
GetString:='';state:=1;
exit;
end;
if s[1]=' ' then
begin
GetString:=' ';state:=2;
exit;
end;
if length(s)>1 then
if copy(s,1,2)=':=' then
begin
GetString:=':=';state:=3;
exit;
end;
if length(s)>1 then
if (copy(s,1,2)='<>')or(copy(s,1,
begin
GetString:=copy(s,1,2);state:=
exit;
end;
if (s[1]='(')or(s[1]=')') then
begin
GetString:=s[1];state:=8;
exit;
end;
if (s[1]='<')or(s[1]='>') then
begin
GetString:=s[1];state:=4;
exit;
end;
if (s[1]='/')or(s[1]='*')or(s[1]=
begin
GetString:=s[1];state:=5;
exit;
end;
if length(s)>1 then
if copy(s,1,2)='IF' then
begin
GetString:='IF';state:=9;
exit;
end;
if length(s)>3 then
if copy(s,1,4)='THEN' then
begin
GetString:='THEN';state:=9;
exit;
end;
if length(s)>3 then
if copy(s,1,4)='ELSE' then
begin
GetString:='ELSE';state:=9;
exit;
end;
if length(s)>2 then
if copy(s,1,3)='VAR' then
begin
GetString:='VAR';state:=9;
exit;
end;
if length(s)>6 then
if copy(s,1,7)='INTEGER' then
begin
GetString:='INTEGER';state:=9;
exit;
end;
if length(s)>5 then
if copy(s,1,6)='DOUBLE' then
begin
GetString:='DOUBLE';state:=9;
exit;
end;
if ((s[1]>='A')and(s[1]<='Z')) then
begin
max:=1000;
for i:=#0 to #255 do
begin
if not(((i>='A')and(i<='Z'))or((
if (pos(i,s)>0)and(pos(i,s)<max) then max:=pos(i,s);
end;
if max=1000 then max:=length(s);
GetString:=copy(s,1,max-1);
exit;
end;
if (s[2]='.') then
begin
max:=1000;
for i:=#0 to #255 do
begin
if not(((i>='0')and(i<='9'))or((
if (pos(i,s)>0)and(pos(i,s)<max) then max:=pos(i,s) ;
end;
if max=1000 then max:=length(s);
GetString:=copy(s,1,max-1);
exit;
end;
if (s[1]='0')or(s[1]='1')or(s[1]=
begin
GetString:=s[1];state:=7;
exit;
end;
state:=0;
GetString:='error';
end;
procedure TForm1.BitBtn1Click(Sender: TObject);
var
s,s1:string;
i,state:integer;
begin
list_cur:=0;
stringgrid1.RowCount:=2;
for i:=0 to memo1.Lines.Count do
begin
s:=' ';
s1:=memo1.Lines[i];
while (s<>'')and(s<>'error') do
begin
s:=GetString(UpperCase(s1),
if (length(s)<=length(s1))and(
begin
delete(s1,1,length(s));
if (s<>' ')and(s<>'error') then
begin
stringgrid1.Cols[0][
stringgrid1.Cols[1][
stringgrid1.RowCount:=
inc(list_cur);
list[list_cur]:=s;
listZ[list_cur]:=state;
end;
end;
if s='error' then messagebox(0,'error',0,0);
end;
end;
end;
Function Find(x,y:string):string;
var i,j,bi,bj:integer;
begin
form1.AT.RecNo:=1;
bi:=100;bj:=100;
for i:=1 to form1.at.FieldCount-1 do
if x=form1.AT.Fields.Fields[i].
for j:=1 to form1.at.RecordCount do begin
if j>1 then form1.AT.RecNo:=form1.AT.
if y=form1.at.Fields.Fields[0].
end;
form1.AT.RecNo:=bj;
if (bi<100)and(bj<100) then
Find:=form1.at.Fields.Fields[
else Find:='Error';
globalX:=bi;
GlobalY:=bj;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
stringgrid1.Cols[0][0]:='Type'
stringgrid1.Cols[1][0]:='
LoadTiGrid();
end;
procedure TForm1.Button2Click(Sender: TObject);
var
st:string;
Rez,inP:string;
stek:array [1..100] of string;
stek_cur,i,j,k,m{,s_c}:
bExt,bDel,bRez:boolean;
cur,cur1,cu,cu1:TTreeNode;
m_i:integer;
s_temp_stek,s_temp_str,s_temp_
ruls:integer;
begin
s_temp_rules:='';
BitBtn1Click(Sender);
bRez:=True;
tv.Items.Clear;
tv.Items.Add(nil,'Äåðåâî âûâîäà');
cur:=tv.Items.Item[0];
cu:=tvv.Items.Item[0];
Stek[1]:='$';
Stek[2]:='START';
Stek_cur:=2;
inc(list_cur);
list[list_cur]:='e';
i:=1;
bExt:=false;
while not(bExt) do begin
case listz[i] of
4:inP:='ZN';
6,7,10:inP:='P';
5:begin
if List[i]=';' then inp:=';';
if List[i]=',' then inp:=',';
if List[i]=':' then inp:=':';
if (List[i]='+')or(List[i]='-') then inp:='+';
if (List[i]='/')or(List[i]='*') then inp:='*';
end;
else inP:=List[i];
end;
Rez:= Find(inP,Stek[stek_cur]); //s_temp_stek,s_temp_str
//////////////////////////////
s_temp_stek:='';
s_temp_str:='';
for m_i:=1
to stek_cur do s_temp_stek:=s_temp_stek+Stek[
for m_i:=i
to list_cur-1 do s_temp_str:=s_temp_str+list[m_
//form1.Memo2.Lines.Add(s_
//form1.Memo2.Lines.Add(s_
//////////////////////////////
if (Rez='Error')or(Rez='') then begin
bExt:=True;
bRez:=False;
MessageBox(0,'Îøèáêà àíàëèçà òåêñòà','',0);
end;
if (Rez='DOPUSK') then begin
bExt:=True;
bRez:=True;
form1.Memo2.Lines.Add('['+s_
// MessageBox(0,'Òåêñò ïðîàíàëèçèðîâàí','',0);
end;
if (Rez='VYBROS') then begin
cur.Text:=list[i];
inc(i);
dec(stek_cur);
cur1:=cur.GetNext;
cur:=cur1;
form1.Memo2.Lines.Add('['+s_
end
else if not(bExt) then begin
dec(stek_cur);
k:=2;
for j:=1 to length(rez) do
if rez[j]=' ' then k:=k+1;
m:=stek_cur;
if Rez[1]<>'e' then begin
if (globaly = 18) then globaly:=7;
if (globaly = 19) then globaly:=8;
s_temp_rules:=s_temp_rules+' '+inttostr(globaly);
while pos(' ',rez)>0 do begin
inc(stek_cur);
Stek[k+m+m-stek_cur]:=copy(
delete(rez,1,pos(' ',rez));
end;
inc(stek_cur);
Stek[{stek_cur}m+1]:=rez;
for j:=stek_cur downto m+1 do
cur1:=tv.Items.AddChild(cur,
cur:=cur1.Parent.
end
else begin
tv.Items.AddChild(cur,'e');
cur:=cur.GetNext.GetNext;
end;
end;
end;
//if bRez then BuildSintaxTree;
end;
procedure TForm1.RecBuildTree(T: TTreeNode; i :integer);
var
j :integer;
begin
if T.Count=0 then
begin
if T.Text = 'e' then
Exit;
if T.Text = ')' then
Exit;
if T.Text = '(' then
Exit;
if T.Text = ';' then
Exit;
if T.Text = ',' then
Exit;
if T.Text = ':' then
Exit;
if T.Text = 'VAR' then
Exit;
if T.Text = 'INTEGER' then
Exit;
if T.Text = 'DOUBLE' then
Exit;
if T.Text = 'THEN' then
Exit;
if T.Text = 'ELSE' then
Exit;
Inc(StrCount);
Pri[StrCount]:=i;
Str[StrCount]:=T.Text;
end
else
for j:=T.Count-1 downto 0 do
RecBuildTree(T.Item[j], i+1);
end;
procedure TForm1.BuildSintaxTree;
var i: integer;
T: TTreeNode;
begin
StrCount:=0;
RecBuildTree(TV.TopItem, 0);
for i:= 1 to StrCount do
if (Str[i]<>'+')and(Str[i]<>'*')
TVV.Items.Clear;
if StrCount = 1 then TVV.Items.AddChild(nil,str[1])
else
begin
T:= nil;
SintaxTree(T,1,StrCount);
end;
end;
procedure TForm1.SintaxTree(T: TTreenode; l, r: integer);
var i, min,m,mmin,ii,jj: integer;
T1,T2: TTreeNode;
begin
if r-l < 2 then
begin
T.Text:=str[l];
end
else
begin
min:=maxInt;
for i:= l to r do
if (pri[i] <= min)and(pri[i]<>0) then begin
min:= pri[i];
m:=i;
end;
i:=m;
if T = nil then
begin
T1:=TVV.Items.AddChild(T,str[
TVV.Items.AddChild(T1,'');
TVV.Items.AddChild(T1,'');
T:= T1;
end
else
begin
T.Text:=str[i];
TVV.Items.AddChild(T,'');
TVV.Items.AddChild(T,'');
end;
if T.Text<>'IF' then begin
if T.Text<>':=' then begin
SintaxTree(T.Item[1],l,i-1);
SintaxTree(T.Item[0],i+1,r);
end else begin
SintaxTree(T.Item[0],i+1,r);
mmin:=0;
for ii:=l to i-1 do
if str[ii]=':=' then mmin:=ii;
if mmin>0 then begin
SintaxTree(T.Item[1],mmin+2,i-
T2:=TVV.Items.AddChild(T.
SintaxTree(T2,l,mmin+1);
end
else SintaxTree(T.Item[1],l,i-1);
end;
end else begin
jj:=1;
while(pri[jj]>0)or(pri[jj+1]>
SintaxTree(T.Item[0],l,{l+2}
SintaxTree(T.Item[1],{l+3}jj+
end;
end;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
if OpenDialog1.Execute then memo1.Lines.LoadFromFile(
end;
end.
7. Результаты работы программы
8. Список литературы
- Иванченко А.Н., Гавриков М.М., Гринченков Д.В. Теоретические основы разработки компиляторов. Учеб. пос. – Новочеркасск: ЮРГТУ, 2006.