Мартыненко Борис Константинович
УЧЕБНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ ПРОЕКТ РЕАЛИЗАЦИИ АЛГОРИТМИЧЕСКИХ ЯЗЫКОВ: ОПИСАНИЯ И ОКРУЖЕНИЯ
Аннотация
Рассматривается представление описаний простых видов в форме объектов-конструкций, а также окружений, реализуемых в виде иерархии участков в стеке, заполненных индикаторами, посредством которых программа получает доступ к значениям.
Описывается сам этот метод доступа.
Ключевые слова: окружение, последовательное предложение, стек, участок.
1. ВВЕДЕНИЕ
В [1] была сформулирована тема моделирования гипотетического вычислителя, используемого в [2] для описания семантики алгоритмического языка типа Алгол 68, в терминах понятий объектно-ориентированного программирования (ООП). При этом объекты: конструкт, значение, участок, окружение или сцена, с которыми манипулирует этот гипотетический вычислитель, отображаются в соответствующие классы объектов.
В [3] описаны основы и значения простых видов в качестве результатов исполнения этих конструкций, а также использование сцен в переходах.
Здесь же мы рассмотрим представление описаний в форме объектов-конструкций, а также окружений, реализуемых в виде иерархии участков стека, заполненных индикаторами. Эти индикаторы возникают в результате исполнения описаний и используются для доступа к значениям.
Основная проблема состоит в том, как организовать взаимодействие конструкций программы (синтаксис) и текущего окружения программы (семантика). Естественно использовать обычный приём ООП: передачу статической информации, извлекаемой из входного текста программы во время его анализа (синтаксис) в объект-конструкцию при его создании посредством конструктора (Init) этого объекта, с учётом этой информации в методе (Run), реализующем исполнение этой конструкции во время счёта (семантика). В интересующем нас случае речь идёт о статических координатах значения в стеке данных, которые представляются уровнем блока и относительным номером индикатора на участке, созданным этим блоком. Индикатор имеет поле, связывающее его со значением, которым он обладает.
© Б.К. Мартыненко, 2009
Все эксперименты были проведены на компьютере с использованием системы программирования FREE PASCAL [4].
2. ОПИСАНИЯ ДАННЫХ И ИНДИКАТОРЫ
Согласно [2], данные любых видов определяются с помощью конструкции описание тождества (см. объект типа TIdentityDeclaration в модуле DECLARATIONS). Она связывает индикатор (см. объект типа TTag) с некоторой конструкцией, относящейся к группе основ, вид которой специфицируется соответствующим описателем. В расширенном варианте конструкции описание тождества может иметь несколько пар (индикатор - основа) (см. объект типа TTagList). Исполнение этой конструкции устанавливает связь индикатора со значением основы. Всякое применение индикатора в других конструкциях означает использование значения, связанного с ним по описанию тождества.
В терминах описываемой модели эта связь воспроизводится путём образования пар (индикатор - значение) на участке, образуемом блоком, в котором находится описание тождества. Доступ к этим значениям из других конструкций реализуется посредством статических адресов индикаторов, то есть пар (I, n), где I - уровень блока, а n - относительный номер определяющего вхождения индикатора в данном блоке.
Общее число индикаторов по всем описаниям блока определяет статический аппетит блока, то есть число элементов коллекции, представляющей его участок.
3. РЕАЛИЗАЦИЯ КОНСТРУКЦИИ ОПИСАНИЕ ТОЖДЕСТВА
Конструкция описание тождества в модельном представлении определяется в модуле
unit DECLARATIONS; { Реализация описаний } interface
uses objects, Strings, VALUES, PLAIN_VALUES, ENVIRON, STANDART, CONSTRUCTS;
type
{ ВСПОМОГАТЕЛЬНЫЕ СИНТАКСИЧЕСКИЕ СТРУКТУРЫ }
{ Тег в описании тождества }
PTag = ATTag;
TTag = object (TObject)
identifier : string; { Идентификатор константы, переменной или
индикатор операции } Source : PConstruct; { Основа, конструкция, результат которой
связывается с данным индикатором } is_IdentityDeclaration : Boolean; { Флажок выбора символа '=' или ':='
для представления аннотации} constructor Init (is_id: Boolean; id : string; c : PConstruct); function Show : PChar; virtual; procedure Run; virtual; end;
{ Список тегов в описаниях тождеств } PTagList = ATTagList; TTagList = object (TCollection) procedure Run; virtual; function Show : PChar; virtual; end;
{ Расширенное описание тождеств } PIdentityDeclaration = ATIdentityDeclaration; TIdentityDeclaration = object (TConstruct) mode : string;
TagList : PTagList;
constructor Init (md : string; tl : PTagList) ; procedure Run; virtual; function Show : PChar; virtual; end;
implementation
{ ВСПОМОГАТЕЛЬНЫЕ СИНТАКСИЧЕСКИЕ СТРУКТУРЫ }
{ Теги в описаниях тождеств, в частности, описаниях переменных } constructor TTag.Init (is_id : Boolean; id : string; c : PConstruct); begin is_IdentityDeclaration := is_id; identifier := id; Source := c end; function TTag.Show : PChar; var Bf: array [0..511] of char; begin StrPCopy (Bf, identifier) ; if Source <> Nil
then begin if is_IdentityDeclaration then StrCat(Bf,' = ') else "StrCat(Bf,':= '); StrCat(Bf, SourceA.Show) end;
Show := Bf
end;
procedure TTag.Run; var Couple : PCouple; begin
if Source <> Nil then begin SourceA.Run;
Couple := New (PCouple, Init (identifier, UV)); CoupleA.PutValue (UV)
end
else Couple := New (PCouple, Init (identifier, Nil)); CurrentEnvironA.AddCouple (Couple); end;
{ Список тегов в описаниях тождеств } procedure TTagList. Run-
procedure ExecItem (Item : PTag); far; begin ItemA.Run end; begin ForEach (@ExecItem)end; function TTagList.Show : PChar; var Bf : array [0. .511] of char; l : Longint; procedure PrintItem (Item : PTag); far; begin StrCat (Bf, ItemA.Show); StrCat (Bf, ', ') end; begin Bf[0] := #0; ForEach (@PrintItem); l := StrLen (Bf)-2; if Bf[l] = ',' then Bf[l] := #0; Show := Bf end;
{ Расширенное описание тождеств } constructor TIdentityDeclaration.Init (md : string; tl : PTagList); begin mode := md; TagList:= tl end; procedure TIdentityDeclaration.Run;
{ Выкладывает пары (индикатор, указатель на значение основы тега)
на участок текущего блока } begin TagLista.Run end;
function TIdentityDeclaration.Show : PChar; var Bf, B : array [0. .511] of char; begin StrPCopy (Bf, mode + ' '); B := TagListA.Show; StrCat (Bf,B); Show := Bf
end; end.
Заметим, что определение этого модуля не зависит от вида конструкции описание тождества по существу.
Рассмотрим простейший пример программы, использующей конструкцию описание тождества:
.begin .int one = 001, two = 02; one + two .end
Связь между определяющими вхождениями индикаторов one и two в этом описании тождества и их использующими вхождениями в формуле one + two устанавливается на стадии идентификации при контекстном анализе.
Планирование размещения индикаторов на участке блока - задача конвертера. Для этого в тексте программы достаточно информации: уровень блока определяется как число блоков, объемлющих данный, а номер определяющего вхождения индикатора в данном блоке устанавливается по тексту входной программы на стадии бесконтекстного синтаксического анализа.
Значения обоих операндов формулы one + two в момент их использования операцией '+' находятся в участке блока уровня О под индексами О и 1 и доступны через указатели этих индикаторов.
Пары (О, О) и (О, l) - статические адреса индикаторов one и two соответственно. Они используются для доступа к значениям, которыми эти индикаторы обладают в текущий момент исполнения программы, то есть при исполнении упомянутой формулы.
Конструкция типа TAddr, как и другие средства создания и управления динамикой текущего окружения исполняемой программы, описываются в модуле ENVIRON.
4. РЕАЛИЗАЦИЯ ОКРУЖЕНИЙ
Модельное представление понятия окружения включает:
- стек участков (Stack),
- таблицу (Dislpay),
- четыре параметра, характеризующих его текущее состояние: CurrentLevel - текущий блочный уровень, CurrentEnviron - текущий участок стека,
UCouple - индикатор значения текущей конструкции, UV - значения текущей конструкции. Оно описывается в модуле
Unit ENVIRON; i Реализация окружений} interface
uses objects, Strings, Values;
i Напоминание i элементы коллекций индексируются от 0 до
значения поля Count-1 включительно.} type
i Представление индикатора } PCouple = лтcouple; TCouple = object (TObject) Indicator i string; !Внешнее представление ИНДИКАТОРА во входной программе.} Value i PValue; i Указатель на объект-значение. } constructor Init (indic i String; val i PValue); i Инициализирует индикатор по параметру indic и
указателю на объект-значение val. destructor Done; virtual;
i ИНТИКАТОР уничтожается, но значение, которым он обладает, сохраняется.}
function Show : PChar; virtual;
{ Даёт строку, представляющий данный ИНДИКАТОР.
Эта строка содержит внешнее представление индикатора и значение, которым он обладает.
Если данная пара представляет переменную, то за идентификатором этой переменной в сформированной строке следует текущее значение переменной, а не имя (в смысле Алгола 68), которым она обладает.} function GetValue : PValue; virtual;
{ Даёт указатель на объект-значение, представленного данной парой.} function GetIndicator : string; virtual;
{ Даёт внешнее представление индикатора.} procedure PutValue (v : PValue); virtual; { Помещает указатель на объект-значение v в данную пару.} end;
{ Представление участка стека (locale) } PLocale = ATLocale; TLocale = object (TCollection) Name : string;
RangeLevel : integer; { Блочный уровень (>= 0) блока,
образовавшего данный участок.} Prev: PLocale; { Указатель на участок объемлющего блока.} constructor Init (lev, max, inc : integer) ; { Инициализация участка блочного уровня lev.} destructor Done; virtual;
{ Участок уничтожается вместе со значениями, размещёнными на нём.} procedure AddCouple (Couple : PCouple); function CoupleNmb : integer; function Show : PChar; virtual; end;
{ Представление стека участков } PStack = ATStack; TStack = object (TCollection) constructor Init (NmbOfLocales : integer);
{ Создание стека данных первоначально на NmbOfLocales участков
с последующим расширением на половину этой величины.} destructor Done; virtual; { Уничтожение всех участков.} procedure AddLocale (Locale : PLocale); { Поместить участок Locale на вершину стека.} procedure RemoveTopLocale; { Удалить участок с вершины стека.} procedure RemoveLocale (lr : PLocale) ;
{ Удалить участок с вершины стека пока его указатель не равен lr. Используется при исполнении перехода. Display корректируется соответственно. Здесь lr характеризует сцену метки.} function Show : PChar; virtual; end;
{ Таблица Dislpay - текущее окружение } PDisplay = ATDisplay; TDisplay = object (TCollection) constructor Init (Max : integer); { Создание таблицы Display на Max элементов.} destructor Done; virtual; procedure Fix (Locale : PLocale);
{ Фиксирует в таблице Display участок, созданный блоком,
в качестве нового текущего окружения.} procedure UpDate (LocaleReference : PLocale); { Обновление таблицы Display по ссылке на участок,
заданный параметром lr } function Show : PChar; virtual;
{ В виде строки представляет текущее окружение } end;
{ Статический адрес значения в стеке данных } PAddr = Л TAddr ; TAddr = object (TObject) RLevelNmb, { Блочный уровень блока, к которому относится данный адрес.} ItemNmb { Номер ИНДИКАТОРА на участке уровня RLevelNmb.}: integer; constructor Init (l, n : integer); { Инициализирует:
(a) номер блочного уровеня RLevelNmb по параметру l и
(b) номер ИНДИКАТОРА на участке уровня RLevelNmb в стеке данных по параметру n.}
function Show : PChar; virtual;
{ Готовит строку, представляющую данный статический адрес.} function GetCouple : PCouple; { По статическому адресу ИНДИКАТОРА даёт
указатель на него в стеке данных.} function GetIndic : string;
{ По статическому адресу элемента стека данных дает
внешнее представление индикатора - первую компоненту пары.} function GetValue : PValue; virtual;
{ По статическому адресу ИНДИКАТОРА дает
указатель на значение - вторую компоненту пары.} procedure PutValue (v : PValue); virtual; { Указатель v вставляет в стек данных на место, задаваемое данным статическим адресом,
не меняя внешнего представления индикатора получателя.} function GetScope : integer; virtual; { Даёт область действия значения,
размещенного по данному адресу в стеке данных.} end;
var
Stack : PStack; { Стек участков }
Display : PDisplay; { Таблица Display }
CurrentLevel : integer; { Текущий блочный уровень }
CurrentEnviron : PLocale; { Текущее окружение }
UCouple : PCouple; { Индикатор значения текущей конструкции }
UV : PValue; { Универсальное значение любого вида -
собственно значение текущей конструкции } Jump elaborated : Boolean; { Флажок прерывание исполнения основ
текущего блока. Используется при исполнении переходов. }
implementation
{ РЕАЛИЗАЦИЯ TStack } constructor TStack.Init (NmbOfLocales : integer); { Создание стека данных первоначально на NmbOfLocales участков с последующим расширением на половину этой величины } begin inherited Init (NmbOfLocales, NmbOfLocales div 2 + 1) end; destructor TStack.Done; begin while count > 0 do
begin count := count - 1; Free (At (count))end; DeleteAll; inherited Done
end;
procedure TStack.AddLocale (Locale : PLocale); { Добавление нового участка на вершину стека данных } var CE : PLocale; begin
CE := CurrentEnviron; Insert (Locale); CurrentEnviron := At (count-1); CurrentEnvironA.Prev := CE; CurrentLevel:= CurrentLevel + 1; CurrentEnvironA.RangeLevel := CurrentLevel; DisplayA.Fix (CurrentEnviron) end;
procedure TStack.RemoveTopLocale; { Удалить участок с вершины стека } begin
if count > 0
then
begin
Free (At (count-1)); DisplayA.count := CurrentLevel; CurrentLevel := CurrentLevel - 1; if CurrentLevel < 0 then CurrentEnviron := nil else CurrentEnviron := CurrentEnvironA.Prev end
end;
procedure TStack.RemoveLocale (lr : PLocale);
{ Удалить участок с вершины стека пока его указатель не равен lr. Используется при исполнении перехода. Display корректируется соответственно. Здесь lr характеризует сцену метки. } begin while CurrentEnviron <> lr do RemoveTopLocale end; function TStack.Show : PChar; var s : string; i : integer;
Bf : array [0. .400] of char; B : array [0.. 127] of char; procedure PrintItem (Item: PLocale); far; begin inc (i); str (i, s); StrPCopy (B, s);
{s := s + #10#13'Stack [' + s1 + '] = ' + LA.Show} StrCat (Bf, #10#13' Stack ['); StrCat (Bf, B); StrCat (Bf, '] = '); StrCat (Bf, ItemA.Show)
end;
begin i := -1; Bf[0] := #0; ForEach (@PrintItem);
if Bf[0] = #0 then StrPCopy (Bf, ' EMPTY'); Show := Bf end; { РЕАЛИЗАЦИЯ TDislpay } constructor TDisplay.Init (Max : integer); { Сознание таблицы Display на Max элементов } begin inherited Init (Max, 0) end; function TDisplay.Show : PChar; var s : string; i : integer;
Bf : array [0. .40 95] of char; B : array [0 ..4095] of char; procedure PrintItem (Item: PLocale); far; begin
inc (i); str (i, s); StrPCopy (B, s);
{s := s + #10#13'Display [' + s1 + '] :: ' + LA.Show } StrCat (Bf, #10#13' Display ['); StrCat (Bf, B); StrCat (Bf, '] :: '); StrCat (Bf, ItemA.Show) end;
begin i := -1; Bf[0] := #0; ForEach (@PrintItem);
if Bf[0] = #0 then StrPCopy (Bf, #13#10' ПУСТО'); Show := Bf
end;
procedure TDisplay.Fix (Locale : PLocale); { Фиксирует в таблице Display участок, созданный блоком,
в качестве нового текущего окружения. } var cl : integer; begin
cl := LocaleA.RangeLevel; Insert (Locale);
CurrentLevel := cl; CurrentEnviron := Locale end;
procedure TDisplay.UpDate (LocaleReference : PLocale);
var cv : integer; e : PLocale; { Параметр цикла продвижения по Display.} begin
CurrentEnviron := LocaleReference; { Установка текущего окружения
по параметру процедуры.} CurrentLevel := CurrentEnvirona.RangeLevel; { Определение уровня
нового окружения.}
count := CurrentLevel+1; e := CurrentEnviron; cv := CurrentLevel; while cv > 0 do
begin DisplayA.AtPut (cv-1, eA.Prev);
e := eA.Prev; cv := cv - 1
end end;
destructor TDisplay.Done; begin inherited Done end;
{ РЕАЛИЗАЦИЯ TLocale } constructor TLocale.Init (lev, max, inc : integer); { Создание нового участка блочного уравня lev >= 0
на max элементов и приращением на inc элементов } begin
RangeLevel := lev ; Prev := CurrentEnviron; inherited Init (max, inc) end;
destructor TLocale.Done; begin DeleteAll end;
function TLocale.CoupleNmb : integer; begin CoupleNmb := count end;
procedure TLocale.AddCouple (Couple : PCouple); begin Insert (Couple) end; function TLocale.Show : PChar; var s : string; n : integer;
Bf : array [0. .4095] of char; B : array [0. .511] of char; procedure PrintItem (Item: PCouple); far; begin inc (n) ; str (n, s) ; StrPCopy (B, s) ;
StrCat (Bf, #10#13' ['); StrCat (Bf, B); StrCat (Bf, '] '); StrCat (Bf, ItemA.Show)
end;
begin n := -1; Bf[0] := #0; ForEach (@PrintItem); Show := Bf end; { РЕАЛИЗАЦИЯ TCouple }
constructor TCouple.Init (indic : String; val : PValue); begin Indicator := indic; Value:= val end;
destructor TCouple.Done;
begin Dispose (Value, Done) end;
function TCouple.GetValue : PValue;
begin GetValue := Value end;
function TCouple.GetIndicator : string;
begin GetIndicator := INDICATOR end;
procedure TCouple.PutValue (v : PValue);
begin Value := v end;
function TCouple.Show : PChar;
var Bf : array [0. .127] of char;
begin StrPCopy (Bf, INDICATOR); StrCat(Bf,' => '); if Value = nil then StrCat(Bf, 'nil')
else StrCat(Bf, ValueA.Show);
Show := Bf
end;
{ РЕАЛИЗАЦИЯ TAddr } constructor TAddr.Init (l, n : integer); begin RLevelNmb := l; ItemNmb := n end; function TAddr.Show : PChar; var s, s1, s2 : string;
s1x, s2x : array [0. .127] of char; Bf : array [0. .63] of char; begin str (RLevelNmb, s1); s1 := s1 + ', '; str (ItemNmb, s2);
StrPCopy (s1x, s1); StrPCopy (s2x, s2); StrPCopy (Bf, '< '); StrCat (Bf, s1x); StrCat (Bf, s2x); StrCat (Bf, ' >'); Show := Bf
end;
function TAddr.GetCouple : PCouple; { По статическому адресу элемента стека данных
дает указатель на пару в стеке данных.} begin GetCouple := PLocale(DisplayA.At(RLevelNmb))a.At(ItemNmb) end; function TAddr.GetIndic : string;
{ По статическому адресу элемента стека данных дает
внешнее представление индикатора - первую компоненту пары.} var Couple : PCouple;
begin Couple := GetCouple; GetIndic := CoupleA.GetIndicator end; function TAddr.GetValue : PValue;
{ По статическому адресу элемента стека данных дает
указатель на значение - вторую компоненту пары.} var Couple : PCouple;
begin Couple := GetCouple; GetValue := CoupleA.GetValue end; procedure TAddr.PutValue (v : PValue); { Указатель v вставляет в стек данных на место, задаваемое данным статическим адресом,
не меняя внешнего представления индикатора получателя.} var Couple : PCouple; { Пара-получатель } begin Couple := GetCouple; CoupleA.PutValue (v) end; function TAddr.GetScope : integer; begin GetScope := RLevelNmb end;
begin CurrentLevel := -1; CurrentEnviron := nil; UCouple := nil end.
Статические адреса индикаторов используются для инициализации объекта-конструкции типа TAddr, который при её исполнении преобразует статический адрес значения в стеке в указатель на собственно значение в реальной куче. Фактически, тип TAddr представляет конструкцию применённое вхождение индикатора. Семантически она открывает доступ к значению, которым обладает соответствующий индикатор для той конструкции, в которой он используется.
А теперь самое время вспомнить о способе передачи статической информации в объект-конструкцию при создании посредством конструктора Init и использовании этой информации в методе Run, реализующем исполнение этой конструкции.
Рассмотрим использование статических адресов индикаторов для доступа к значениям, которыми они обладают, в качестве значений операндов формул.
Заметим, что в [3] описан способ передачи значений операндов в формулу, который метафорически уместно назвать «из рук в руки», поскольку результат конструкции-операнда передаётся в конструкцию-формулу через локальные поля объектов Loperand и Roperand, минуя стек.
Если в программе используются описания тождеств, то способ обмена значениями «из рук в руки» не достаточен и придётся чуть изменить описание формул, а именно, ввести статические адреса операндов LAddr, RAddr.
PFormula = ATformula; TFormula = object (TConstruct) { Наследуемые поля данных: Representation : PChar ; }
Loperand, Roperand : PConstruct { конструкции-операнды }; LAddr, RAddr : PAddr { статические адреса значений операндов в стеке } ; function Show : PChar; virtual; procedure Run; virtual; end;
с реализацией методов
function TFormula.Show : PChar; begin abstract end; procedure TFormula.Run; begin abstract end;
При создании конкретного экземпляра формулы её конструктор (Init) получает обе пары ссылок на операнды, то есть ссылки на конструкции в роли операндов формулы и в виде статических адресов значений операндов в стеке данных.
Метод (Run), реализующий исполнение формулы, сам анализирует фактическую ситуацию: формула унарная или бинарная и какой способ доступа к значениям операндов применять.
Для примера, покажем описание формул с операциями вида (integral, integral) integral.
constructor Tintegral integral integral Formula.Init
(r : PChar; { Внешнее обозначение операции } op : Tintegral integral integral Routine; { Стандартная операция, реализующая формулу } lo, ro : PConstruct;
{ Конструкции в роли операндов формулы }
la, ra : PAddr { Статические адреса значений операндов } );
begin
Representation := r; Routine := op; if lo <> nil
then LOperand := lo else if la <> nil then LAddr := la; if ro <> nil then ROperand := ro else RAddr := ra; end;
function Tintegral integral integral Formula.Show : PChar; var Bf, Bf1 : array [0..512] of char;
begin if LOperand <> nil then strPCopy (Bf, LOperandA.Show); if ROperand <> nil
then begin StrPCopy (Bf1, Representation);
StrCat (Bf1, ROperandA.Show); StrCat (Bf, Bf1)
end;
{ Если операнд формулы представлен статическим адресом, то при создании конструкции «формула» стеком пользоваться нельзя, ибо он ещё не существует!} if LAddr <> nil
then begin StrPCopy (Bf, LAddrA.Show); if RAddr <> nil
then begin StrPCopy (Bf1, Representation);
StrCat (Bf1, RAddrA.Show); StrCat (Bf, Bf1) end
end;
Show := Bf end;
procedure Tintegral integral integral Formula.Run; begin if LOperand <> nil
then begin LOperandA.Run;
LOperandValue := PIntegralValue (UV)
end;
if ROperand <> nil then begin ROperandA.Run;
ROperandValue := PIntegralValue (UV)
end;
if LAddr <> nil
then begin LAddra.GetValue; LOperandValue := PIntegralValue (UV) end; if RAddr <> nil
then begin RAddrA.GetValue; ROperandValue := PIntegralValue (UV) end; UV := Routine (LOperandValue, ROperandValue)
end;
Здесь учитывается число операндов формулы и какой метод используется для доступа к их значениям. Если адрес LOperand или ROperand не равны nil, то используется метод «из рук в руки», а иначе значения операндов формулы достаются из стека данных с использованием статических адресов.
Демонстрация вышесказанного приведена в листинге I и на рис. 1. В этом примере доступ к значениям операндов формулы one + two производится с использованием статических адресов (0, 0) и (0, 1), поскольку они соответствуют нулевому и первому вхождению индикаторов на участке уровня 0. Эти адреса преобразуются в указатели на значения, которыми обладают индикаторы one и two (см. метод TAddr.GetValue в модуле ENVIRON). Значение формулы вычисляется, но нигде не используется.
5. ПРИСВАИВАНИЕ
Рассмотрим ещё один пример, в котором показывается, как значение формулы замещает прежнее значение в стеке. Однако для этого потребуется описать реализацию ещё одной конструкции, а именно, присваивания.
Листинг I. Программа построения семантического дерева входной программы на Алголе 68: .begin .int one = 001, two = 02; one + two .end и её интерпретации
program Itentity_Declaration_test; { Тестирование программы Алгола 68: .begin .int one = 001, two = 02; one + two .end } uses CRT, objects, Strings,
VALUES, PLAIN_VALUES, STANDART, CONSTRUCTS, CLAUSES, ENVIRON, DECLARATIONS; var id1s, id2s, ones, twos, Addr00s, Addr01s, fs : string; id1, id2 : PIntegralDenotation; cl0 : PConstructList; Range0 : PRange; one, two : PTag; Addr00, Addr01 : PAddr; TagList : PTagList; IdentityDeclaration : PIdentityDeclaration; Routine : Tintegral integral integral Routine; f : Pintegral integral integral Formula; main : PClosedClause; var0, var1 : PVariable; begin ClrScr;
writeln (#13#10' Тестирование программы Алгола 68:'); writeln (' .begin .int one = 001, two = 02; one + two .end'#13#10); writeln (' ПРОСТРАНСТВО ДАННЫХ:'#10#13); { СОЗДАНИЕ СТЕКА ДАННЫХ }
Stack := New (PStack, Init (1));
writeln (' Стек на ', StackA.Limit, ' участка'); { СОЗДАНИЕ ТАБЛИЦЫ DISPLAY }
Display := New (PDisplay, Init (1));
writeln (' Display на ', DisplayA.Limit,' участка'#13#10); writeln ('======= СОЗДАНИЕ СЕМАНТИЧЕСКОГО ДЕРЕВА ПРОГРАММЫ =======');
{ СОЗДАНИЕ КОНСТРУКЦИЙ ПРОГРАММЫ }
id1s := '001'; id1 := New (PIntegralDenotation, Init (@id1s[1], 1));
writeln (' Изображение целого ', id1A.Show);
writeln (' Создание Tag'^ one ');
one := New (PTag, Init (true, 'one', id1));
writeln (' Tag ', oneA.Show);
id2s := '02'; id2 := New (PIntegralDenotation, Init (@id2s[1], 2));
writeln (' Изображение целого ', id2A.Show);
writeln (' Создание Tag'^ two ');
two := New (PTag, Init (true, 'two', id2));
writeln (' Tag ', twoA.Show);
{ Создание списка тегов }
writeln (' Создание списка Tag''ов ...');
TagList := New (PTagList, Init (2, 0)); TagListA.Insert (one); TagListA.Insert (two);
writeln (' Список Tag''ов создан: ', TagListA.Show); writeln;
{ Создание конструкции описание тождества: .int one = 001, two = 02 }
IdentityDeclaration := New (PIdentityDeclaration, Init ('.int', TagList));
writeln (' Конструкция описание тождества создана: '); writeln;
writeln (' ', IdentityDeclarationA.Show); writeln;
Routine := @PlusRoutine; fs := ' + ';
Addr00 := New (PAddr, Init (0, 0));
writeln (' Создан адрес Addr00: ', Addr00A.Show);
Addr01 := New (PAddr, Init (0, 1));
writeln (' Создан адрес Addr01: ', Addr01A.Show);
{ Создание формулы с операцией вида (. int,.int).int }
f := New (Pintegral integral integral Formula,
Init (@fs[1], Routine, NIL, NIL, Addr00, Addr01)); writeln (' Создана формула вида (integral,integral)integral: ', fa.Show); { Создание списка конструкций блока 0 } cl0 := New (PConstructList, Init (2, 0)); cl0A.Insert (IdentityDeclaration); { Описание тождества } cl0A.Insert (f); { Формула }
{ Создание последовательного предложения - блока уровня 0 } RangeO := New (PRange, Init (0, 10, cl0)); { Создание замкнутого предложения - собственно программы } main := New (PClosedClause, Init (Range0)) ;
writeln (#13#10' СЕМАНТИЧЕСКОЕ ДЕРЕВО ПРОГРАММЫ СОЗДАНО:', mainA.Show); writeln (#13#10' Программа начала ... '); mainA.Run;
writeln (' Программа выполнена!');
writeln (' UV = ', UVA.Show); writeln (' СТОП !!!'); readln end.
Тестирование нрограммы Алгола 68:
.begin .int cine = §Ш.„ two - 02; one •>■ two -end
пространство данные
Стек на 1. участка Display на 1 участка
СОЗИЯНИЕ СЕПЙНТИЧЕСКОГО ДЕРЕБй ПРОГРЙНЯУ
Изображение целого ШШ1 Создание Tag'a one Tag one Sil Изображение целого 02 Создание Tag'a two Tag two = 02
Создание сгшска Tag5os ...
Список Tag*ов создан: one = two - 02
Конструкция описание тождества создана:
. int огне - 001,
0 > i >
Создан адрес ñddr00: < Й Создан адрес 8dd¡r0i: < Ш,
constructor Tintegral..JLntegi*al....integi»al JForraula, In it начал ... Representation = ♦ LßtJdif =<§,©> Rñddr = < Ш„ i >
constructor T integral...integral...írite,gral...ForRula. I п it закончил ?
Создана формула »ида <integral, integral) Integral:: <
СЕШШТИЧЕСКОЕ ДЕРЕВО ПРОГРЙМНУ СОЗДЙНО: BEGIN<0>
[0] -int one - Sil, two - 02 Ш <§„§>* < 0, i > ENB<0>
> + <
i >
Программа начала .-. Construс t List:
[ШЗ .int one - ШШ1. two [Í] < 0, 0 > + < 0, i >
02
ТЕКШЕЕ ОКРУЖЕНИЕ Display [01 :: ES J one => 1
СЛЕ ИСПОЛНЕНИЯ IXag.Kun
ТЕКУЩЕЕ ОКРУЖЕНИЕ ПОСЛЕ ИСПОЛНЕНИЯ Hag. Ran Display [0] :: E0] one => i [1J two 2
ТЕКУЩЕЕ ОКРУ}*СЕНИЕ ПОСЛЕ ВЫХОДЯ ИЗ БЛОКЙ ТЕКУЩЕГО УРОВНЯ ПУСТО
Программа кончилаТ UU - 3 СТОП ?*?
Рис. 1. Протокол исполнения программы на Алголе 68: .begin .int one = 001, two = 02; one + two .end
Эффект исполнения присваивания состоит в замещении значения получателя (Destination) значением источника (Source). Реализация этой конструкция во многом аналогична реализации формул.
Конструкция присваивание представляется как объект типа TAssignment:
PAssignment = ATAssignment; TAssignment = object (TConstruct) (* Наследуемые поля данных: Representation : PChar; *)
Destination { Аналог LOperand в формулах }, Source { Аналог ROperand в формулах } : PConstruct; DestinationAddr { Аналог LAddr в формулах }, SourceAddr { Аналог RAddr в формулах } : PAddr; function Show : PChar; virtual; procedure Run; virtual; end;
от которого наследуются присваивания конкретных видов, в частности, логического вида:
PBooleanAssignment = ATBooleanAssignment; TBooleanAssignment = object (TAssignment) (* Наследуемые поля данных: Representation : PChar; Destination, Source: PConstruct; DestinationAddr, SourceAddr: PAddr;
*)
DestinationValue, SourceValue : PBooleanValue;
constructor Init (r : PChar; D, S : PConstruct; da, sa : PAddr) ; function Show : PChar; virtual; procedure Run; virtual; end;
Реализации абстрактных присваиваний в разделе implementation модуля CONSTRUCTS имеет вид:
function TAssignment.Show : PChar; begin abstract end; procedure TAssignment.Run; begin abstract end;
а конкретных вида boolean:
constructor TBooleanAssignment.Init
(r : PChar; D, S : PConstruct; da, sa : PAddr) ; begin Representation := r;
Destination := D; Source := S; DestinationAddr := da; SourceAddr := sa
end;
function TBooleanAssignment.Show : PChar; var Bf, Bf1 : array [0..512] of char; begin if Destination <> nil
then StrPCopy (Bf, DestinationA.Show) else StrPCopy (Bf, DestinationAddrA.Show); { Destination представлен в виде PChar } if Source <> nil then
begin StrPCopy (Bf1, Representation); StrCat (Bf1, SourceA.Show) end
else begin StrPCopy (Bf1, Representation) ;
StrCat (Bf1, SourceAddrA.Show)end; { Source представлен в виде PChar } StrCat (Bf, Bf1);
Show := Bf end;
procedure TBooleanAssignment.Run; begin
if Destination <> nil then begin DestinationA.Run;
DestinationValue := PbooleanValue (UV) end; if Source <> nil then begin SourceA.Run;
SourceValue := PbooleanValue (UV) end; if DestinationAddr <> nil then begin DestinationAddra.GetValue;
DestinationValue := PbooleanValue (UV) end; if SourceAddr <> nil then begin SourceAddrA.GetValue;
SourceValue := PbooleanValue (UV) end; if DestinationAddr <> nil
then DestinationAddra.PutValue (SourceValue); if DestinationValueA.Scope >= SourceValueA.Scope then UV := DestinationValue
else { Недопустимое отношение областей действия получателя и источника } Halt (1);
end;
И вот обещанный пример (см. листинг II, рис. 2).
Поясним дополнительно, что конструкция описания тождества .bool b в этой программе определяет логическую переменную. Это выясняется на этапе синтаксического анализа программы с учётом расширенного варианта грамматики языка Алгол 68. При этом пропущенная основа логического вида после идентификатора b в описании тождества воспринимается как генератор, исполнение которого даёт имя неопределённого логического значения. Поскольку из двух возможных значений false или true, согласно семантике языка Алгол 68, можно выбрать любое, то в нашей реализации выбрано false, в соответствии с бытующей традицией реализации алгоритмических языков (см. Листинг II).
Генераторы и имена - значения, выдаваемые этими конструкциями, - тема следующей публикации.
5. ЗАКЛЮЧЕНИЕ
Подведём краткие итоги.
1. Описания дают ещё один способ передачи значений между конструкциями в программе. При этом стек играет роль канала связи. Не будь описаний, не возможен был бы обмен значениями между конструкциями иначе как «из рук в руки». Рекурсивные процедуры также не могли бы существовать.
2. Средством передачи значений между конструкциями через стек служат статические адреса индикаторов. Они применяются при создании конструкций, использующих значения в стеке. Фактически, тип TAddr представляет конструкцию применённое вхождение индикатора. Семантически она открывает доступ к значению, которым обладает соответствующий индикатор для той конструкции, в которой он используется.
Листинг II. Программа построения семантического дерева входной программы .begin .bool b; b := ~ b .end и её интерпретации
program Itentity_Declaration2xx;
{ Тестирование программы Алгола 68: .begin .bool b; b := ~ b .end } uses CRT, objects, Strings,
VALUES, PLAIN_VALUES, STANDART, CONSTRUCTS, CLAUSES, ENVIRON, DECLARATIONS; var bgs, Addr00s, fs : string;
bg : PBooleanDenotation; cl0 : PConstructList; Range0 : PRange; b : PTag; Addr00 : PAddr; TagList : PTagList;
IdentityDeclaration : PIdentityDeclaration; Routine : Tboolean boolean Routine; f : Pboolean boolean Formula; Assignment : PAssignment; main : PClosedClause; begin ClrScr;
writeln (#13#10' Тестирование программы Алгола 68:'); writeln (' .begin .bool b; ~ b .end'#13#10); writeln (' ПРОСТРАНСТВО ДАННЫХ:'#10#13); { СОЗДАНИЕ СТЕКА ДАННЫХ }
Stack := New (PStack, Init (1));
writeln (' Стек на ', StackA.Limit, ' участка'); { СОЗДАНИЕ ТАБЛИЦЫ DISPLAY }
Display := New (PDisplay, Init (1));
writeln (' Display на ', DisplayA.Limit,' участка'#13#10);
writeln ('======= СОЗДАНИЕ СЕМАНТИЧЕСКОГО ДЕРЕВА ПРОГРАММЫ ======'#13#10);
{ СОЗДАНИЕ СЕМАНТИЧЕСКОГО ДЕРЕВА ПРОГРАММЫ } writeln (' Создание Tag''а b '); bgs : = 'false';
{ Создание конструкции генератор логического .loc.bool } bg := New (PBooleanGenerator, Init (@bgs[1])); { Создание тега 'b = .loc.bool' } b := New (PTag, Init (false, 'b', bg)); writeln (' Tag ', bA.Show); { Создание списка тегов }
writeln (' Создание списка Tag''ов ...'); TagList := New (PTagList, Init (1, 0)); TagListA.Insert (b);
writeln (' Список Tag''ов создан: ', TagListA.Show); writeln; { Создание конструкции описание тождества '.bool b := false' } IdentityDeclaration := New (PIdentityDeclaration, Init ('.bool', TagList)); writeln (' Конструкция описание тождества создана: '); writeln; writeln (' ', IdentityDeclarationA.Show); writeln;
{ Создание стандартной операции вида .op (.bool).bool ~ = @NotRoutine } Routine := @NotRoutine; fs := ' ~ ';
{ Создание статического адреса переменной b }
Addr00 := New (PAddr, Init (0, 0));
writeln (' Создан адрес Addr00: ', Addr00A.Show);
{ Создание логической формулы ~ b }
f := New (Pboolean boolean Formula,
Init (@fs[1], Routine, NIL, NIL, NIL, Addr00)); writeln (#13#10' Создана формула вида (boolean)boolean: ', fa.Show); { Создание присваивания b := ~ b }
Assignment := New (PBooleanAssignment, Init (':=', nil, f, Addr00, nil));
{ Создание списка конструкций блока 0 }
cl0 := New (PConstructList, Init (2, 0));
cl0A.Insert (IdentityDeclaration) ;
cl0A.Insert (Assignment);
{ Создание последовательного предложения - блока уровня 0:
.bool b; b := ~ b }
Range0 := New (PRange, Init (0, 10, cl0));
{ Создание замкнутого предложения - собственно программы:
.begin .bool b; b := ~ b .end }
main := New (PClosedClause, Init (Range0) ) ;
writeln (#13#10' СЕМАНТИЧЕСКОЕ ДЕРЕВО ПРОГРАММЫ СОЗДАНО:', mainA. Show);
writeln (#13#10' Программа начала ... '#13#10);
{ ЗАПУСК ИНТЕРПРЕТАЦИИ СЕМАНТИЧЕСКОГО ДЕРЕВА ПРОГРАММЫ }
mainA.Run;
writeln (#13 #10' Программа кончила!');
writeln (#13#10' СТОП !!!'); readln
end.
Тестирование программы Йлгола 68:
СОЗДЙНИЕ СЕИЙНТИЧЕСКОГО ВЕРЕВЙ ПРОГРАММ —
Конструкция описание тождества создана:
Создам« Формула вика (boolean)boo lean: < 0, Ш >
С|тНТИЧЕСКО£ ДЕРЕВО ПРОГРЙНМЫ создано:
После окончания IBoo le an fi a s о«м;пt.Кшп
ТЕКУЩЕЕ ОКРУЖЕНИЕ ЯО ВШОДЙ ИЗ БЛОКА ТЕКУЩЕГО УРОВНЯ
ТЕКУЩЕЕ ОКРУЖЕНИЕ ПОСЛЕ ЁЖ(?№ ИЗ БПОКЙ ТЕКУЩЕГО УРОВНЯ
Рис. 2. Протокол исполнения программы: .begin .bool b; b := ~b.end
3. Преобразование статических адресов индикаторов в указатели на значения, которыми они обладают во время исполнения программы, и детали реализации динамики окружений программы, описаны в модуле ENVIRON.
4. Ясно также, что конструкции присваивание и описание тождества лучше всего рассматривать совместно с окружением программы, что и было сделано.
Литература
1. Мартыненко Б.К. Учебный исследовательский проект реализации алгоритмических языков // Компьютерные инструменты в образовании, 2008. № 5. С. 3-18.
2. Под ред. А. ван Вейнгаарден, Б. Майу, Дж. Пек, К. Костер и др. Пересмотренное сообщение об Алголе 68. М., 1979. 533 с.
3. Мартыненко Б.К. Учебный исследовательский проект реализации алгоритмических языков: значения и конструкции // Компьютерные инструменты в образовании, 2009. № 1. С. 10-25.
4. Michael Van Canneyt. Reference guide for Free Pascal. 2002. 188 p.
Abstract
The article describes representation of plain mode declarations as object-oriented constructs, and implementation of environments in the form of hierarchy of stack frames. The indicators stored in a stack frame are used for the program to have access to valid values. The access method to the values in question is described.
Мартыненко Борис Константинович, доктор физико-математических наук, профессор кафедры1 информатики математико-механического факультета СПбГУ,
© Наши авторы, 2009. Our authors, 2009.