Учебный исследовательский проект реализации алгоритмических языков: описания и окружения

Мартыненко Борис Константинович

УЧЕБНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ ПРОЕКТ РЕАЛИЗАЦИИ АЛГОРИТМИЧЕСКИХ ЯЗЫКОВ: ОПИСАНИЯ И ОКРУЖЕНИЯ

Аннотация

Рассматривается представление описаний простых видов в форме объектов-конструкций, а также окружений, реализуемых в виде иерархии участков в стеке, заполненных индикаторами, посредством которых программа получает доступ к значениям.

Описывается сам этот метод доступа.

Ключевые слова: окружение, последовательное предложение, стек, участок.

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

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

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

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

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

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

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

end;

if ROperand <> nil then begin ROperandA.Run;

ROperandValue := PIntegralValue

end;

if LAddr <> nil

then begin LAddra.GetValue; LOperandValue := PIntegralValue (UV) end; if RAddr <> nil

then begin RAddrA.GetValue; ROperandValue := PIntegralValue

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

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 информатики математико-механического факультета СПбГУ,

mbk@ctinet.ru

© Наши авторы, 2009. Our authors, 2009.