CodeNet / Приложения / Алгоритмы / Разбор выражений. Компиляторы и интерпретаторы. / Компилятор пишется так...
Пример 2. Таблица имен.
Type EnumKey = (KeyProc, KeyMain, KeyEnd, KeyInt,
KeyRecord, KeyIf, KeyThen, KeyElse,
KeyClose, KeyWhile, KeyDo, KeyPrint);
var CommonTop : integer;
procedure Compile;
begin
Scan;
(* Сначала считаем описания переменных,
доступных всей программе
*)
while LexType LexKey
or LexVal KeyProc do
Declare;
ProgInit; (* Вывести стандартный
заголовок программы на Паскале *)
CommonTop := TabLen;
while not LogEOF do
ThisProc; (* Одна процедура *)
ProgClose; (* Вывести стандартное
окончание программы на Паскале *)
end;
(* Скомпилировать одну процедуру *)
procedure ThisProc;
begin
TabLen := ThisProc;
while (LexType = LexKey) and
(LexVal = ord (KeyInt)
or LexVal = ord (KeyRecord)) do
Declare;
ProcInit; (* Заголовок процедуры *)
Code;
ProcClose; (* Окончание процедуры *)
end;
(* Обработать одно описание *)
procedure Declare;
var Y:integer;
begin
if (LexType=LexKey)
and (LexVal=ord (KeyInt)
then NameList (TagInt,0)
else if (LexType=LexKey)
and (LexVal=ord (LexRecord))
then begin
PutIn ('');
TableOfNames [TabLen].Fields := 0;
Y := TabLen;
ReadFields (Y);
NameList (TagRecord,Y);
end;
end;
(* Считать список имен переменных после описателя
и запихнуть эти имена в таблицу имен*)
procedure NameList (TagVal, FldVal : integer);
begin
repeat
Scan;
With TableOfNames [LexVal] do
begin
Tag := TagVal;
Fields := FldVal;
Ref := 0;
end;
Scan;
until (LexType=LexSpecs)
and (LexVal=ord (SemiColn));
end;
(* Обработать поля записи *)
procedure ReadFields (Y0 : integer);
var X,Y,Pre : integer;
begin
Y := Y0;
repeat
Pre := Y;
Dcl1 (X,Y);
TableOfNames [Pre].Ref := X;
until (LexType=LexKey)
and (LexVal=ord (KeyEnd));
Table OfNames [Y].Ref := 0;
end;
(* Dcl1 делает почти тоже, что и Declare,
только описание, которое он считывает,
является полем записи.
Поэтому вместо NameList используется
другая процедура
*)
procedure Dcl1 (var X,Y : integer);
var C:integer;
begin
if (LexType=LexKey)
and (LexVal=ord (KeyInt))
then NameList1 (TagInt,0)
else if (LexType=LexKey)
and (LexVal=ord (LexRecord))
then begin
PutIn ('');
TableOfNames [TabLen].Fields := 0;
C := TabLen;
ReadFields (C);
NameList1 (TagRecord,C,X,Y);
end;
end;
(* Разница между NameList и NameList1 в том,
что последняя устанавливает "указатели" Ref.
*)
procedure NameList1 (TagVal,FldVal : integer;
var X,Y : integer);
begin
X:=0;
Pre:=0;
repeat
Scan;
if X=0 then X:=LexVal;
Y:=LexVal;
With TableOfNames [LexVal] do
begin
Tag :=TagVal;
Fields :=FldVal;
end;
if Pre0 then
TableOfNames [Pre].Ref := LexVal;
Pre:=LexVal;
Scan;
until (LexType=LexSpecs)
and (LexVal=ord (SemiColn));
end;
(* Скомпилировать все операторы в процедуре *)
procedure Code;
begin
repeat
Statement;
until (LexType=LexKey)
and (LexVal=ord (KeyEnd));
end;
(* Скомпилировать один оператор *)
procedure Statement;
begin
if LexType = LexKey then
(*Большинство операторов можно распознать
по первому ключевому слову*)
begin
if LexVal = ord (KeyIf)
then SempIf
else if LexVal = ord (KeyElse)
then SempElse
else if LexVal = ord (KeyWhile)
then SempWhile
else if LexVal = ord (KeyClose)
then SempClose
else if LexVal = ord (KeyPrint)
then SempPrint
end
else if (LexType=LexNames)
then SempAssignOrCall;
(* Присваивание или вызов процедуры *)
end;
(* Семантические подпрограммы *)
(* If...then *)
procedure SempIf;
begin
WriteLn ('if');
Scan;
WriteLn (Buf);
WriteLn ('then begin');
Scan; (**)
Scan;
end;
(* Else *)
procedure SempElse;
begin
WriteLn ('end else begin');
Scan;
end;
(* Print...*)
procedure SempPrint;
begin
Scan;
Write ('WriteLn');
id := TableOfNames [LexVal].Pname;
Write ('(','''',id,' = ','''',',',id,')');
Scan; (*;*)
Scan;
end;
(* По первой лексеме нельзя отличить
вызов процедуры от присваивания.
Поэтому отлдичаем одно от другого
в одной семантической подпрограмме
по ходу дела
*)
procedure SempAssignOrCall;
begin
Write (TableOfNames [LexVal].Pname);
Scan; (*:=*)
if (LexType = LexSpecs) and (LexVal = ord (Assign))
(* Присваивание *)
then begin Scan;
WriteLn (' := ',Buf,';');
Scan; (*;*)
end
else if (LexType = LexSpecs) and (LExVal = ord (SemiColn))
then WriteLn (';'); (* Вызов процедуры *)
Scan;
end;
(* While...do *)
procedure SempWhile;
begin
WriteLn ('While');
Scan;
WriteLn (Buf);
WriteLn ('do begin');
Scan;
Scan;
end;
(* Close *)
procedure SempClose;
begin
WriteLn ('end;');
Scan;
end;
Краткий обзор языка SIMPLE
Программа на языке SIMPLE есть:
описания переменных,
общих для всей программы;
procedure имя;
begin
описание переменных процедуры;
операторы;
end;
....
procedure имя;
....
Одна из процедур носит имя main.
Эта процедура и есть программа.
Описание
int список переменных через запятую;
record описание;
....
описание;
end список переменных через запятую;
Описание
Присваивание: Имя := [....];
Вызов процедуры: Имя;
Печать: Print имя;
Условный: if [....] then операторы else операторы close;
Цикла: while [....] do операторы close;
Ключевые слова
(LexType = LexKey)
procedure then
main else
end close
int while
record do
if print
Имена
(LexType = LexNames)
Например:
sight
gh218
slow1a6
Числа
(LexType = LexNumber)
Например:
17
0218
33
1
Спецсимволы
(LexType = LexSpecs)
:=
,
:
"Нечто в []"
(LexType = LexExpr)
Например:
[a*(8+d)}
[d mod h + 12]
