Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции Бахты.doc
Скачиваний:
82
Добавлен:
12.02.2015
Размер:
654.34 Кб
Скачать

Интерпретатор математических формул, реализованный на основе метода Дейкстры.

Unit Aintr;

Interface

Uses Astream, Astack, Apro;

Type {Если мы будем проверять правильность соседства лексем, то во флаге удобно держать тип предыдущей лексемы. Для этого объявим новый тип.}

LexType = (opar { ( }, cpar { ) }, sgn {знак}, value{значение});

Type Pintr = ^Cintr;

Cintr = object

Public constructor Init (Pst:PFStreamTxt); {Передается указатель на поток}

Destructor Done; virtual;

Procedure Interpretate;

Private

P:PFStreamTxt;

Pro:Cpro;

St:CstackCh; {стек Дейкстры}

Oldlex:lextype; {флаг Дейкстры}

Procedure Failure (n:word);

Procedure Lexem; {процедура обработки очередной лексемы}

Procedure Number; {интерпретатор числа }

Function Weight (x:char):byte; {возвращает приоритет операции или скобки}

Function Right (x:byte):boolean; {возвращает значение ассоциативности; на входе – вес, на выходе истина если операции с таким весом правоассоциативны и ложь в противном случае}

Procedure PushPop (x:char); {организует выталкивание из стека Дейкстры}

End;

Implementation

Uses Crt;

Constructor Cintr.Init;

Begin

P:=PST;

Writeln(‘Pro.Create’);

Pro.Create;

St.Create;

{oldlex будет установлена в процессе интерпритации}

end;

Destructor Cintr.Done;

Begin

St.Destroy;

Writeln (‘Pro.Destroy’);

Pro.Destroy;

P:=nil;

End;

Procedure Cintr.Interpretate;

Var S:string;

Begin

St.Push ( ‘(‘ ); {помещаем первую неявную скобку}

Oldlex := opar;

While not p^.IsEnd do Lexem;

If not ((oldlex=value) or (oldlex=cpar)) then failure (1);

PushPop ( ‘)’ );

If not st.IsEmpty then failure(2);

Writeln (‘Pro.Show’);

Pro.Top2Str (s); {вершина стекового процессора преобразуется в строковую переменную}

Writeln (‘Ответ =’,s);

{далее необязательная часть}

Pro.Del; {чистим стековый процессор}

If not Pro.IsEmpty then Failure(3);

End;

Procedure Cintr.Lexem;

Var c:char;

Begin

C:= p^.getchar;

Case c of

‘0’..’9’: begin

if not ((oldlex=opar) or (oldlex=sgn)) then failure(11);

number;

oldlex :=value;

end;

‘(‘: begin if not ((oldlex=opar) or (oldlex=sgn)) then failure(12);

st.Push ( c );

p^.skip;

oldlex:=opar;

end;

‘)’:begin If not ((oldlex=value) or (oldlex=cpar)) then failure (13);

PushPop ( c );

P^.Skip;

Oldlex:=cpar;

End;

‘+’,’-‘,’*’,’/’:begin

if (c=’+’) and ((oldlex=opar) or (oldlex=sgn)) then

begin

if not ((weight(‘p’)>weight(st.top)) or

(((weight(‘p’)=weight(st.top)) and

right(weigth(‘p’))) then failure (14);

p^.skip;

Oldlex:=sgn;

End else

if (c=’-’) and ((oldlex=opar) or (oldlex=sgn)) then

begin

if not ((weight(‘m’)>weight(st.top)) or

(((weight(‘m’)=weight(st.top)) and

right(weigth(‘m’))) then failure (15);

PushPop(‘m’);

p^.skip;

Oldlex:=sgn;

End else if c in [‘+’,’-’,’*’,’/’] then

Begin

If not ((oldlex=value) or (oldlex=cpar)) then

failure (16);

PushPop( c );

P^.skip;

Oldlex:=sgn;

End {else if одноместные операции} ;

End;

Else failure(17);

End;

End;

{Текст можно было несколько сократить в части обработки операций, если запоминать значение веса вершины стека. Удобнее Top заменить на GetTop.}

procedure Cintr.PushPop;

var y:char;

begin

while true do

begin

if st.IsEmpty then failure(21);

if ((weight(x)>weight(st.top)) or

(((weight(x)=weight(st.top)) and right(weigth(x))) then break

else begin

st.pop(y);

case y of

‘m’:begin writeln (‘Pro.Inv’); pro.Inv; end;

‘+’: begin writeln (‘Pro.Add’); pro.Add; end;

‘-‘:begin writeln (‘Pro.Sub’); pro.sub; end;

‘*’ :begin writeln (‘Pro.Mul’); pro.Mul; end;

‘/’ :begin writeln (‘Pro.Div’); pro._Div; end;

end;

end;

end;

if x in [‘m’,’+’,’-‘,’*’,’/’] then st.Push(x);

else if x=’)’ then begin

if not (st.top=’(‘) then failure(22);

st.Pop(y);

end;

end;

function Cintr.Weight;

begin

case x of

‘(‘:weight:=0;

‘)’:weight:=1;

‘+’,’-‘:weight:=2;

‘/’:weight:=3;

‘*’:weight:=4;

‘p’,’m’:weight:=5;

else failure(31);

end;

end;

function Cintr.Right;

begin

case x of

2:Right:=false;

3:right:=true;

4:right:=false;

5:right:=true;

else right:=false;

end;

end;

procedure Cintr.Number;

…….

End;

Procedure Cintr.Failure;

Begin

……

halt(1);

end;

end.

Может возникнуть вполне резонный вопрос: какой из представленных двух компиляторов лучше? Ответ на него очень прост: первый проще создавать, а второй изменять. Если вы программировали первый вариант, то обратили внимание, что в нем практически пишешь программу по грамматике, при этом лишается всякая самостоятельность. Тогда как во втором компиляторе

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

Похожим образом создаются языки программирования. Только там компилятор обеспечивает перевод программы с языка программирования на машинный язык. Кроме этого в нашей работе используется однопроходной вариант, не содержащий отдельного блока лексического анализа. Обычно вначале файл преобразуется: лексемы заменяются кодами. В наше случае лексемы достаточно просты поэтому дополнительное преобразование не понадобилось. В работе настоящего компилятора не ограничиваются однопроходным вариантом. Например, в языке Alpha компилятор выполняет около 30 проходов (притом из них 15 - оптимизация). В результате получается программа по скорости работы опережающая программу на Assembler'е.