ПЕРЕВОД ПРОГРАММ С ЯЗЫКА ФОРТРАН НА ЯЗЫК ПАСКАЛЬ

 

Постановка задачи.

В Библиотеке Численного Анализа НИВЦ МГУ (num-anal.srcc.msu.ru) помещены библиотечные программы на языках ФОРТРАН и СИ. Точность вычислений в этих языках определяется выбираемой версией счёта для данных типа Real или Double и максимальное число значащих цифр достигает 7..8 или 15..16 знаков. Однако, для точных расчётов этого бывает недостаточно и более точные вычисления может обеспечить язык Паскаль, где действует формат данных Extended, обеспечивающий точность вычислений до 19…20 десятичных цифр. В силу этого стала задача перевода программ БЧА с языка Fortran-77 на язык Object Pascal (базовый язык для системы Delphi), причём выходные файлы представляются в виде модулей определённой структуры Unit, являющихся автономно компилируемыми программными единицами. Определим подмножества языков перевода.

 

В качестве входного языка рассматривается Fortran-77 с рядом ограничений:

-          буквы в операторах должны быть только прописными;

-          оператор EQUIVALENCE в общем случае не допускается, а только в простом виде для эквивалентности двух элементов, каждый из которых является переменной или элементом массива; в данном случае эквивалентность не связывается с размещением элементов в памяти, а только с присвоением им одинаковых значений и на последующие элементы массива не распространяется;

-          оператор DATA имеет список значений, заключённый между двумя слэшами “/”;

-          метка оператора не может начинаться с незначащего 0;

-          идентификаторы не могут содержать пробелы, т.е. M 1 и M1 не идентичны;

-          параметры цикла могут быть только целыми константами без знака или простыми целыми переменными;

-          не допускаются комментарии внутри фортранного оператора при его разрыве;

-          разрыв фортранного оператора при переносе строки не должен быть посредине идентификатора или константы;

-          оператор ENTRY запрещён;

-          оператор PAUSE запрещён;

-          оператор PUNCH запрещён;

-          оператор READ запрещен;

-          оператор WRITE рассматривается как оператор PRINT;

-          операторы ENCODE и DECODE запрещены;

-          операторы END FILE, REWIND и BACKSPACE запрещены.

 

В качестве выходного языка рассматривается Object Pascal с рядом ограничений:

-          не используется объектно-ориентированное и визуальное программирование;

-          не используются классы;

-          не используются указатели и динамическая память;

-          не используется тип Variant;

-          не используются множества;

-          не используются рекурсии и опережающие описания;

-          не используются функции API Windows;

-          массивы любой размерности приводятся к одномерным, с начальным индексом равным 0;

-          ввод/вывод формируется через функцию Format;

-          компилирование программ производится стандартным образом без использования специальных директив.

 

 Процесс перевода акцентируется на следующих моментах:

 

-

анализ декларативных операторов описания типов и массивов и создание списков типов локальных переменных текущего файла;

-

анализ операторов DATA и создание списков типизированных констант;

-

анализ операторов COMMON и создание новых записей с типами элементов общего блока;

-

анализ выражения в операторах языка и генерация выражения на Паскале с проведением необходимых замен (приведение индексов, замена идентификаторов, замена операций и др.);

-

приведение типов в операторе присваивания для целочисленной арифметики;

-

создание встроенных функций, не имеющих эквивалент в языке Паскаль;

-

замена функций для реализации операций Min и Max для произвольного числа аргументов на соответствующие функции Паскаля для двух аргументов;

-

замена операций '**' (возведение в степень) и '/' (деление целых чисел) на функции Паскаля

-

анализ операторов PRINT и FORMAT для генерации форматных строк и создание на их базе списков форматов текущего файла;

 

представление массивов любой размерности в виде одномерных массивов переменной длины для передачи формальных параметров и в виде одномерных массивов фиксированной длины для локальных параметров, c начальным индексом отсчёта 0;

-

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

 

 

Схема перевода.

Перевод осуществляется при прохождении нескольких этапов:

 

1 этап.

При первом просмотре исходного файла на языке Фортран производится предварительный разбор декларативных и выполняемых операторов, на основе которого в ячейках общего архива {{n}} строятся списки, отражающие различные структуры входного и выходного языков. При этом, по окончании прохода по файлу производится анализ и коррекция списков на предмет того, входит ли идентификатор в один из списков параметров типов, либо идентификатор является массивом, либо определяется в других конструкциях Паскаля (через типизированную константу или как запись с полями, определяемыми соответствующим Common-блоком). Кроме этого строятся удобные внутренние списки представления операторов DATA, COMMON, FORMAT, PRINT.

 

2 этап.

При втором просмотре исходного файла с учетом списков, полученных на 1 этапе, производится генерация выходного файла на языке Паскаль. Полученный файл не является “полуфабрикатом” (например, как в системе For2Pas), и не требует никаких серьёзных семантических и синтаксических доработок. Файл на языке Паскаль удовлетворяет требованиям подмножества Object Pascal, используемого в системе программирования Delphi (версия 3.0 и выше).

 

3 этап.

Приведение файла на Паскале к определённым требованиям Библиотеки Численного Анализа (идентификация пятого символа имени, замена версии для типа Double на тип Extended, замена системных констант и др.).

 

4 этап.

Оформление файла на Паскале как модуля Unit – автономно компилируемая программная единица, включающая в себя различные компоненты интерфейсного (interface) и исполняющего (implementation) разделов. Раздел Uses должен содержать обязательный список модулей, с которыми устанавливается связь: SysUtils, Math, Lstruct, Lfunc , где первые два модуля принадлежат системе Delphi, третий модуль содержит структурные типы, используемые в файлах Библиотеки, а четвёртый модуль содержит дополнительные встроенные функции.

 

 

Некоторые аспекты перевода.

Ряд операторов, конструкций и встроенных функций Фортрана не имеют полного аналога на Паскале, поэтому приняты некоторые соглашения, осуществлена подстройка под конструкции Паскаля, добавлены новые функции.

 

DATA – оператор задания начального значения переменным и массивам.

Для переменных  и массивов без индексов значения задаются в разделе

const

 IDVAR : String = ”Переменная”;                                   //  DATA IDVAR/10Hпеременная/

 IDARR : Array [0..3] of Real = ( 0.0, 0.0, 0.0, 0.0 );       //  DATA IDVAR/4*0./;

Для массива с индексами начальные значения задаются в начале тела программы

 IDARR[I-1] := 1;                                                             //  DATA  IDARR(I)/1/;

 

COMMON – оператор задания общих блоков формирует структуру, помещаемую в файл lstruct.pas :

type

 MMKRIC = record                    //  COMMON /MMKRIC/ IDVAR1,IDVAR2,IDVAR3,IDARR

  elm1: Real;

  elm2: Integer;

  elm3: Real;

  elm4: Array [0..14] of Real;

 end;

var

_MMKRIC :MMKRIC;

 

Тогда, например, обращение к 7-му элементу массива IDARR общего блока в программе будет _MMKRIC.elm4[6]

 

 

PRINT  -  оператор выдачи результатов и диагностики, как правило, используется в тестах, которые на Паскале преобразуются в функции типа String, но иногда встречается в самой программе. Результатом работы оператора является текстовая строка  выходных значений, сформатированная в соответствии с оператором FORMAT, которая записывается либо в файл результатов <Имя теста>.res, либо в диагностический файл NAL_DIAG.res .При этом следует иметь в виду, что формат строки выдачи на Паскале не является точным соответствием строки выдачи на Фортране, а только с максимально приближается к ней.

Некоторые программы из БЧА имеют в качестве параметра  обращение к функции  или процедуре (например, вычисление правых частей в программах вычисления дифференциальных уравнений) . В этом случае в качестве параметра на Паскале небходимо определить процедурный тип, который должен соответствовать по типу и числу параметров вызываемой процедуре или функции. При наличии таких конструкций перевдчик выдает  в начале переведенной программы информационный комментарий, по которому  необходимо вручную скорректировать переведенную программу на Паскале - сформировать и поместить в lstruct.pas описание соответствующего процедурного типа и задать соответствующий параметр в выходной программе. Например, заводим в модуле lstruct.pas  процедурный тип:

type

Func_F_DE  = Function (X :Real): Real;

 

и используем  его в описании  параметров  процедуры:

uses

… Lstruct;

procedure DE54RU(FF :Func_F_DE; FG :Func_F_DE; FR :Func_F_DE; M :Integer; XN :Real;

                var YN :Array of Real; XK :Real; HMIN :Real;

                EPS :Real; P :Real; var H :Real; var Y :Array of Real;

                var RA :Array of Real; var IERR :Integer);

 

 

Следует обратить внимание и на следующий аспект перевода.  В некоторых случаях при тестировании полученной программы на Паскале она выдавала неправильный результат или выходила на ошибку переполнения. Это происходило из-за того, что исходная программа на Фортране была реализована некорректно. А именно, с расчетом на то, что на исходной вычислительной системе, где отлаживалась фортранная программа, переменным и элементам массивов по умолчанию присваивалось нулевое значение, хотя Фортран, как и Паскаль требует явного задания всех начальных значений. Поэтому  такие коллизии приходится выявлять путем тестирования программ на Паскале.  

 

 

Встроенные функции –дополнение функций Паскаля, если среди встроенных функций Паскаля нет аналога для встроенной функций Фортрана (файл lstruct.txt) :

 

function Sign(Arg1,Arg2 :Extended):Extended;

//Присваивание знака аргумента Arg2 величине |Arg1|

function IntSign(Arg1,Arg2 :Integer):Integer;

//Присваивание знака аргумента Arg2 величине |Arg1|

function AMax0(Arg1,Arg2 :Integer):Extended;

//Максимум двух чисел

function EMax1(Arg1,Arg2 :Extended):Extended;

//Максимум двух чисел

function Max0(Arg1,Arg2 :Integer):Integer;

//Максимум двух чисел

function Max1(Arg1,Arg2 :Extended):Integer;

//Максимум двух чисел

function AMin0(Arg1,Arg2 :Integer):Extended;

//Минимум двух чисел

function EMin1(Arg1,Arg2 :Extended):Extended;

//Минимум двух чисел

function Min0(Arg1,Arg2 :Integer):Integer;

//Минимум двух чисел

function Min1(Arg1,Arg2 :Extended):Integer;

//Минимум двух чисел

function IDim(Arg1,Arg2 :Integer):Integer;

//Положительная разность двух чисел: Arg1 - Min(Arg1,Agr2)

function EDim(Arg1,Arg2 :Extended):Extended;

//Положительная разность двух чисел: Arg1 - Min(Arg1,Agr2)

function Emod(Arg1,Arg2 :Extended):Extended;

//Остаток от деления

 

Тип COMPLEX  -  реализуется на Паскале путем создания комплекса программ, реализующих комплексные преобразования (файл lstruct.txt) :

type

 Complex =record

           re,im: Extended

          end;

const

 _i_: Complex = (re: 0; im: 1);

 

function AddC (x,y: Complex): Complex;

//Сложение комплексных чисел

function SubC (x,y: Complex): Complex;

//Вычитание комплексных чисел

function MulC (x,y: Complex): Complex;

//Умножение комплексных чисел

function DivC (x,y: Complex): Complex;

//Деление комплексных чисел

function Cabs(Arg :Complex):Extended;

//Модуль комплексного аргумента

function Creal(Arg :Complex):Extended;

//Вещественная часть комплексного аргумента

function Aimag(Arg :Complex):Extended;

//Мнимая часть комплексного аргумента

function Conjg(Arg :Complex):Complex;

//Сопряженная величина от комплексного аргумента

function Cmplx(Arg1,Arg2 :Extended):Complex;

//Объединение аргументов в комплексную величину

function Cexp(Arg :Complex):Complex;

//Экспонента

function Clog(Arg :Complex):Complex;

//Натуральный логарифм

function Csqrt(Arg :Complex):Complex;

//Корень квадратный

function Csin(Arg :Complex):Complex;

//Тригонометрический синус

function Ccos(Arg :Complex):Complex;

//Тригонометрический косинус

function Cpower(Arg :Complex, N :Integer):Complex;

//Возведение в целую степень

 

 

Способ реализации.

Реализация поставленной задачи производилась с использованием разрабатываемого в НИВЦ МГУ инструментального комплекса TeConv. Этот комплекс позволяет производить контекстную замену фрагментов текста на базе специальных средств, которые обеспечивают создание сложных сценариев контекстного поиска/замены. Сценарий для выполнения поставленной задачи охватывает все этапы редактирования файла (имя файла с полным путем доступа берется в сценарии из левого списка ListFile, который создаётся перед запуском сценария, а в правом списке RightList первой лексемой задаётся первая часть составного имени подкаталога, куда будет помещён результат):

 

!Первый этап: создание списков общего архива

!(на входе    - левый список с полными именами файлов редактирования,

!задан режим «Только протокол»)

#[ЗАМЕНА КОНТЕКСТОВ]

/1/входной текст (индекс): 3

/2/выходной текст (индекс): 0

/3/входной каталог: LeftList

/4/шаблоны входных файлов: *

/5/искать в подкаталогах (признак): 0

/6/без учета регистра (признак): 0

/7/режим замены (индекс): 0

/8/входная кодировка (индекс): 3

/9/выходной каталог: d:\forpas\rabout\

/10/рабочий каталог:

/11/сводный файл строк редакции (признак): 0

/12/только протокол (признак): 1

/13/вид протокола (индекс): 4

/14/статистика для протокола (признак): 0

/15/файл запроса: d:\forpas\_scrpt\contit1.txt

[ПАРАМЕТРЫ ВЫХОДНОГО КАТАЛОГА]

/1/запись измененных файлов (индекс): 0

/2/выходной каталог: d:\forpas\rabout\

/3/сохранять копии (признак): 0

/4/рабочий каталог: d:\forpas\rabout\

/5/копировать дерево файлов исходного каталога (признак): 0

/6/копировать в рабочий каталог (индекс): 0

/7/изменить имя файла (признак): 0

/8/переименовать после редакции (признак): 0

/9/постфиксные символы имени файла записи:

/10/расширение файла записи: 

>НАЧАТЬ: 12

 

На первом этапе таблица контекстов загружается из файла contit1.txt. Файл состоит из начального граничного контекста, где задаются начальные значения некоторых ячеек общего архива, простых контекстов для анализа некоторых конструкций Фортрана (комментарий, пустая строка, метка и др.), блоковых контекстов, в которых анализируются операторы Фортрана, и конечного граничного контекста, в котором производится основной анализ формируемых списков на базе циклов. Ниже приводятся примеры некоторых контекстов таблицы:

 

 

Контекст поиска:

Контекст замены:

Комментарий:

<!>

#["FLOAT MOD AMOD DMOD MIN0 MIN1 AMIN0 AMIN1 DMIN1 MAX0 MAX1 AMAX0 AMAX1 DMAX1 IABS ABS DABS SQRT DSQRT IFIX INT AINT EXP DEXP ALOG DLOG ALOG10 DLOG10 SIN DSIN COS DCOS ATAN DATAN ATAN2 DATAN2 SIGN ISIGN DSIGN DBLE SNGL TANH DTANH IDIM DIM DDIM IDINT DFLOAT REAL AIMAG CMPLX CONJG CABS CEXP CLOG CSIN CCOS CSQRT DREAL DIMAG DCMPLX DCONJG TAN DTAN CDABS" //{{20}}]

 

начальный граничный контекст: список имён встроенных функций Фортрана;

<!>

#["FLOAT MOD Emod Emod Min0 Min1 AMin0 EMin1 EMin1 Max0 Max1 AMax0 EMax1 EMax1 Abs Abs Abs Sqrt Sqrt Trunc Trunc Floor Exp Exp Ln Ln Log10 Log10 Sin Sin Cos Cos ArcTan ArcTan ArcTan2 ArcTan2 Sign IntSign Sign DBLE SNGL Tanh Tanh IDim EDim EDim Trunc DFLOAT Creal Aimag Cmplx Conjg Cabs Cexp Clog Csin Ccos Csqrt Csqrt Creal Aimag Cmplx Conjg Tan Tan Cabs IntPower Power Cpower AddC SubC MulC DivC Trunc"//{{21}}]

 

начальный граничный контекст: список имён встроенных функций Паскаля;

 

. . . . . . . . . . . . . . . . . . . .

 

|[{1}|~7..-1<>*loop({{30}}|*''*)/l || sysLval=loop({{1}}|*''*)/l :: {1}|~7..-1=loop({{30}}|*''*)/l || sysLval=loop({{1}}|*''*)/l]|{%????? +[A-Z][A-Z0-9]*}{=}

 

#[{{30}} & {{1}}|r*''r* & " "//{{30}} :: || BlockLexm(delpatlex,{{57}},,{1}|~7..-1); BLockOper(setst)]

 

 

формирование списка {{30}} изменяемых формальных параметров (находятся в правой части оператора присваивания); если идентификатора нет в списке, то он добавляется, а в случае наличия его в списке изменяющих своё значение через параметры оператора Call {{57}}, параметр исключается из списка;

|[{1}|~1..-1~<>""]|{%?????}

 

#[{{18}} & "_" & {1}|~1..-1~ & ","//{{18}}; BlockOper(setst)]

 

формирование списка меток {{18}} (к исходной метке файла добавляется префикс ‘_’);

 

. . . . . . . . . . . . . . . . . . . .

 

|2|%%{%      +INTEGER}

 

#[{0}|1..72~//{0}; ""//sysvar1; BlockOper(setst,,,{1}_+1)]

 

начало блока оператора INTEGER; выделяются первые 72 значащих символов оператора;

|2|{%     [~ 0]}

 

#[{0}|1..72~//{0}; BlockOper(setst,,,{1}_+1)]

 

строка-продолжение оператора;

|2|{%C}

 

#[BlockOper(setst); BlockOper(setcn,,,+4)]

 

строка-комментарий;

|2|{[A-Z][A-Z0-9]* *[,\0d]}

 

#[{{2}} & {1}|1..-2~ & " "//{{2}}]

 

 

формирование списка {{2}} – добавление переменной типа Integer;

|2|{[A-Z][A-Z0-9]*(}{?*) *[,\0d]}

#[{{2}} & {1}|1..-2~ & " "//{{2}}; "true"//sysvar1]

 

формирование списка {{2}} – добавление массива типа Integer, устанавливается признак массива true в sysvar1;

|2|{% *\0d\0a}

 

#[BlockOper(setcn)]

 

пропуск пустых строк;

|2|$${%????? }|{%C}

 

#[BlockOper(setst)]

 

конец блока - начало следующего оператора или комментария;

|2[sysvar1="true"]|%$

 

#[BlockOper(reted,,,7)]

 

при наличии в списке элемента массива – уход на блок |7|;

|2|%$

 

#[BlockOper(setcn,0,0,1)]

 

при выходе из блока устанавливаем номер строки чтения по таблице контекстов;

|7|%%{%      +DIMENSION}

 

#[{0}|1..72~//{0}; ""//sysvar2; BlockOper(setst,,,{1}_+1)]

 

начало блока оператора DIMENSION, выделяются первые 72 значащих символов оператора;

|7.2.3.4.5.6|%%{%      +INTEGER}|{%      +REAL}|{%      +DOUBLE PRECISION}|{%      +COMPLEX}|{%      +COMPLEX*16}|{%      +LOGICAL}

 

#[""//sysvar2; BlockOper(setst,,,{1}_+1)]

 

вход в блок при обращении из других блоков (в частности, из блока |2|);

|7|{%     [~ 0]}

 

#[{0}|1..72~//{0}; BlockOper(setst,,,{1}_+1)]

 

строка-продолжение оператора;

|7[{1}|1..-2~=loop({{1}}|*''*)/l :: else]|{[A-Z][A-Z0-9]* *(}

 

#[{{8}} & {1}|1..-2~ & " "//{{8}}; "true"//sysvar3; "0"//sysvar4 :: {{12}} & {1}|1..-2~ & " "//{{12}}; "0"//sysvar3 || "0"//sysvar4; "true"//sysvar2]

 

поиск имени массива в списке формальных или локальных параметров и установка в true признака sysvar3 для формального параметра или “0” для локального;

|7[sysvar2="" :: sysvar3="true" :: else]|{[A-Z0-9]+ *,}

 

#[:: sysvar4+1//sysvar4; {{9}} & {1}|1..-2~ & ","//{{9}} ::  sysvar4+1//sysvar4; {{13}} & {1}|1..-2~ & ","//{{13}}]

 

добавление текущей размерности массива в список {{9}} или {{13}} для формального или локального массива; sysvar4 – счётчик общей размерности массива;

|7[sysvar3="true" :: else]|{[A-Z0-9]+ *)}

 

#[{{10}} & (sysvar4 + 1) & " "//{{10}}; {{9}} & {1}|1..-2~ & " "//{{9}} :: {{14}} & (sysvar4 + 1) & " "//{{14}};  {{13}} & {1}|1..-2~ & " "//{{13}} || ""//sysvar2]

 

добавление текущей размерности массива в список {{9}} или {{13}} для последнего элемента описания размерности массива, а общая размерность заносится в списки {{10}} или {{14}};

|7|{% *\0d\0a}

 

#[BlockOper(setcn)]

 

пропуск пустых строк;

|7|$${%????? }|{%C}

 

#[BlockOper(setst); BlockOper(setcn,,,1)]

 

конец блока - начало следующего оператора или комментария;

 

. . . . . . . . . . . . . . . . . . . .

 

<!>repeat

#["1"//sysvar1]

конечный граничный контекст: анализ списка фактических параметров операторов CALL {{38}}, являющихся переменными;

<!>break|[{{38}}=""]|

 

 

контроль пустого списка;

<!>

#[{{38}}|sysvar1''sysvar1//sysvar6; ""//sysvar3]

 

занесение в sysvar6 лексемы sysvar1 (идентификатора) списка {{38}};

<!>repeat

 

#["1"//sysvar2]

 

начало внутреннего цикла;

<!>break|[sysvar6=loop({{sysvar2}}|*''*)/l]|

 

#["true"//sysvar3]

 

контроль наличия идентификатора в списках типов локальных параметров {{2}} – {{7}}; если он найден в одном из списков, производится выход из цикла (оператор break);

<!>until|[sysvar2 + 1>7 :: else]|

 

#[:: sysvar2 + 1//sysvar2]

 

контроль конца внутреннего цикла по значению sysvar2;

<!>|[sysvar3="true" :: sysvar6=loop({{12}}|*''*)/l]|

 

#[:: "true"//sysvar3]

 

контроль наличия идентификатора в списке локальных массивов {{12}};

<!>|[sysvar3="true" :: sysvar6|1..1~=loop({{59}}|*''*)/l || sysvar6<>{{16}}|1''1 :: sysvar6|1..1~<>*loop({{59}}|*''*)/l] || sysvar6<>{{16}}|1''1 |

 

#[:: BlockLexm(addlex,{{2}},,sysvar6) ::  BlockLexm(addlex,{{3}},,sysvar6)]

 

добавление идентификатора в случае если он не является именем функции, хранящемся в первой лексеме списка {{16}}, в в список параметров типа Integer  {{2}} или Real {{3}} в зависимости от первой буквы идентификатора, сравниваемой со списком букв {{59}}, задающих по умолчанию целый тип;

<!>until|[{{38}}|sysvar1+1''sysvar1+1="" :: else]|

 

#[:: sysvar1 + 1//sysvar1]

 

контроль конца внешнего цикла по значению sysvar1;

 

 

!Второй этап: генерация программы на Паскале

!(на входе    - левый список с полными именами файлов редактирования,

! на выходе - файлы в рабочем каталоге d:\forpas\\rabout)

#[ЗАМЕНА КОНТЕКСТОВ] {6}

/1/входной текст (индекс): 3

/2/выходной текст (индекс): 0

/3/входной каталог: LeftList

/4/шаблоны входных файлов: *

/5/искать в подкаталогах (признак): 0

/6/без учета регистра (признак): 0

/7/режим замены (индекс): 0

/8/входная кодировка (индекс): 3

/9/выходной каталог: d:\forpas\rabout\

/10/рабочий каталог:

/11/сводный файл строк редакции (признак): 0

/12/только протокол (признак): 0

/13/вид протокола (индекс): 4

/14/статистика для протокола (признак): 0

/15/файл запроса: d:\forpas\_scrpt\contit2.txt

[ПАРАМЕТРЫ ВЫХОДНОГО КАТАЛОГА]

/1/запись измененных файлов (индекс): 0

/2/выходной каталог: d:\forpas\rabout\

/3/сохранять копии (признак): 0

/4/рабочий каталог: d:\forpas\rabout\

/5/копировать дерево файлов исходного каталога (признак): 0

/6/копировать в рабочий каталог (индекс): 0

/7/изменить имя файла (признак): 0

/8/переименовать после редакции (признак): 0

/9/постфиксные символы имени файла записи:

/10/расширение файла записи: 

>НАЧАТЬ: 12

 

На втором этапе таблица контекстов загружается из файла contit2.txt. Файл состоит из начального граничного контекста, где на базе списков общего архива, генерируется головная часть выходного файла (объявление локальных переменных, типов, констант, описания вложенных операторные функции Фортрана), простых контекстов для генерации некоторых конструкций  Паскаля (комментарий, метка и др.) и блоковых контекстов для генерации операторов тела функции на Паскале. Ниже приводятся примеры некоторых контекстов таблицы:

 

 

Контекст поиска:

Контекст замены:

Комментарий:

<!>|[{{16}}="" :: {{16}}|2''2="" || {{60}}<>"" :: {{16}}|2''2="" :: else]|

 

#["function TEST: String"//sysvar49 :: "function " & {{16}}|1''1//sysvar49; BlockLexm(addlex,{{16}},,"String") :: "procedure " & {{16}}|1''1//sysvar49 :: "function " & {{16}}|1''1//sysvar49]

 

генерация начала заголовка procedure или function  в переменной sysvar49, используя информацию ячейки {{16}}:
[имя программы][тип функции]
и число операторов
PRINT в файле, содержащееся в {{60}};

<!>repeat

 

#["1"//sysvar2]

 

цикл генерации списка параметров процедуры/функции;

<!>|[{{1}}|sysvar2''sysvar2=loop({{30}}|*''*)/l || {{17}}|sysvar2''sysvar2<>"String" :: {{1}}|sysvar2''sysvar2=loop({{8}}|*''*)/l || {{17}}|sysvar2''sysvar2<>"Array of String":: else]|

 

#["var "//sysvar :: "var "//sysvar :: ""//sysvar || {{1}}|sysvar2''sysvar2//sysvar3]

 

контроль добавления префикса var в описание формального параметра: идентификатор содержится в списке изменяемых параметров {{30}} и не имеет тип String, для всех массивов не имеющих тип Array of String;

<!>|[sysvar3=loop({{37}}|*''*)/l]|

 

#[sysvar3 & "_"//sysvar3]

 

контроль идентификатора с зарезервированными словами Паскаля {{37}} (при совпадении к идентификатору добавляется префикс ‘_’);

<!>continue

 

#[sysvar49 & sysvar & sysvar3 & " :" & {{17}}|sysvar2''sysvar2//sysvar49; sysvar2 + 1//sysvar2]

 

формирование вида записи формального параметра с типом, хранящимся в списке {{17}}; счётчик параметров sysvar2 увеличивается на 1;

<!>until|[{{1}}|sysvar2''sysvar2="" :: else]|

 

#[sysvar49 & ")"//sysvar49 :: sysvar49 & "; "//sysvar49]

 

конец цикла генерации списка параметров;

<!>|[{{16}}|2''2<>"" :: else]|

 

#[sysvar49 & ": " & {{16}}|2''2 & ";"//sysvar49 :: sysvar49 & ";"//sysvar49]

 

приформирование типа функции (содержится во второй лексеме ячейки {{16}}); 

 

. . . . . . . . . . . . . . . . . . .

 

|[{1}|1..5="     " :: else]|{%[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9] +END?*}

#["end;"//{1} :: "exit;$end;"//{1}]

 

анализ оператора END :
-если метки перед оператором нет, то он заменяется оператором
 
end;
-если метка есть – двумя операторами
 
exit;
 end;

 

. . . . . . . . . . . . . . . . . . .

 

|7[{4}<>"1"]|%%{%?????[ 0] *DO +[0-9]}{= *[A-Z0-9]+ *,}{[A-Z0-9]+ *,}{[A-Z0-9]+}

 

#[{0}|1..72~//{0}; {1}|~7..-1//{1}; BlockOper(setst,0,0,1)]

 

анализ оператора DO вида DO  m  I=n1,n2,n3:
  контекст поиска оператора разбит на отдельные группы, берутся только первые 72 символа строки, из которых убирается поле метки и курсор устанавливается в начало полученной строки;


 

|7|{[0-9]+}{[A-Z][A-Z0-9a-z]* *=}{[A-Z0-9]+ *,}{[A-Z0-9]+ *,}{[A-Z0-9]+}

 

#[sysvar6 & "  inc(" & {2}|1..-2~ & "," & {5} & ");$" & sysvar6 & " end;"//sysvar; BlockLexm(addlex,{{27}},{{27}},{1}); BlockLexm(addlex,{{28}},{{28}},"sysvar"); sysvar6 & {2}|1..-2~ & " := " & {3}|1..-2~ & ";$" & sysvar6 & "while ( " & {2}|1..-2~ & "<=" & {4}|1..-2~ & " ) do$" &  sysvar6 & " begin"//{0}]

 

поиск конструкций оператора, начиная с метки: строятся два синхронных списка: {{27}} – список меток, определяющих конец цикла, {{28}} – список операторов Паскаля, вставляемых в место окончания цикла; sysvar6 – поле начальных пробелов оператора; весь найденный контекст заменяется операторами
I := n1;

while ( I <= n2 ) do

 begin
операторы конца цикла
inc (I,n3);

end;

|7|{% *\0d\0a}

 

#[BlockRepl(""""); BlockOper(setcn)]

 

удаление пустых строк;

|7|$${%[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9][ 0]}|{%C}

 

#[sysvar6 & "  "//sysvar6; ""//sysvar5; BlockOper(setst)]

 

конец блока - начало следующего оператора или комментария; увеличивается поле начальных пробелов оператора sysvar6;

|7|%$

 

#[BlockOper(reted,,,20)]

 

уход на блок |20|;

|7|%$

 

#[BlockOper(setcn,0,0,1)]

 

установка указателя на первую ячейку таблицы контекстов;

|20.3.7.8.19|%%{[A-Z][A-Z0-9]*}

 

#["true"//sysvar3; BlockOper(setst)]

 

начало блока контроля имени идентификатора (допускается вход в блок только из блоков |3|,|7|,|8|,|19|,);

|20[_{1}=1 || sysvar3="" :: {0}|_{1}-1="_" :: {0}|{1}_+1="_" :: {1}|1..-1~=loop({{46}}|*''*)/l :: sysLval=loop({{37}}|*''*)/l :: sysLval=_>loop({{54}}|*''*)/l || sysLval=_>loop({{53}}|*''*)/l || sysLval<>"FLOAT" || sysLval<>"DFLOAT" || sysLval<>"MOD" || sysLval<>"SIGN" || {0}|{1}_+1<>"(" :: else]|{[A-Za-z][A-Z0-9a-z]* *}

 

#[BlockOper(setst) :: BlockOper(setst) :: BlockOper(setst) :: BlockFind(lngtxt,{1}|1..-1~,sysvar7); BlockFind(lngtxt,{1},sysvar8); {{47}}|r*''r* & {1}|sysvar7+1..sysvar8//{1}; ""//sysvar3 :: {1}|1..-1~ & "_"//{1}; ""//sysvar3 :: {1}|1..-1~ & "_"//{1}; ""//sysvar3 :: ""//sysvar3]

 

контроль идентификатора со списком всех элементов COMMON-общих блоков {{46}},  со списком служебных идентификаторов Паскаля {{37}}, со списком встроенных функций Паскаля {{54}} и Фортрана {{53}};

|20[sysvar3=""]|$${%[ 0-9][ 0-9][ 0-9][ 0-9][ 0-9][ 0]}|{%C}}

 

#[BlockOper(setst)]

 

конец блока - начало следующего оператора или комментария;

 

 

!Третий этап: корректировка перекодированных программ для БЧА

!(на входе    - левый список с полными именами файлов редактирования,

! на выходе -  файлы в исходном каталоге)

#[ЗАМЕНА КОНТЕКСТОВ] {8}

/1/входной текст (индекс): 3

/2/выходной текст (индекс): 0

/3/входной каталог: LeftList

/4/шаблоны входных файлов: *

/5/искать в подкаталогах (признак): 0

/6/без учета регистра (признак): 1

/7/режим замены (индекс): 0

/8/входная кодировка (индекс): 3

/9/выходной каталог: ListFiles (путь к каталогу в списке)

/10/рабочий каталог:

/11/сводный файл строк редакции (признак): 0

/12/только протокол (признак): 0

/13/вид протокола (индекс): 4

/14/статистика для протокола (признак): 0

/15/файл запроса: d:\forpas\_scrpt\contit3.txt

[ПАРАМЕТРЫ ВЫХОДНОГО КАТАЛОГА]

/1/запись измененных файлов (индекс): 1

/2/выходной каталог: ListFiles (путь к каталогу в списке)

/3/сохранять копии (признак): 0

/4/рабочий каталог:

/5/копировать дерево файлов исходного каталога (признак): 0

/6/копировать в рабочий каталог (индекс): 0

/7/изменить имя файла (признак): 0

/8/переименовать после редакции (признак): 0

/9/постфиксные символы имени файла записи:

/10/расширение файла записи: 

>НАЧАТЬ: 12

 

На третьем этапе таблица контекстов загружается из файла contit3.txt и содержит следующие основные контексты:

 

 

Контекст поиска:

Контекст замены:

Комментарий:

<!>

#[""//sysvar6; ""//sysvar7; ""//sysvar8]

начальные значения переменных

|[sysvar8="" || {2}<>&2|RightList:-1 || &2|RightList:-1 <>""]|{function}{[A-Z0-9]+}

 

#[&2|RightList:-1 //{2}; "true"//sysvar8; BlockOper(setln)]

 

замена имени функции на вторую лексему текущей строки правого списка; оператор BlockOper(setln) осуществляет возврат по файлу на чтение этой же строки;

|[{2}<>&2|RightList:-1 || &2|RightList:-1 <>""]|{procedure}{[A-Z0-9]+}

 

#[&2|RightList:-1 //{2}]

 

замена имени процедуры на вторую лексему текущей строки правого списка;

|3|%%{function UT[A-Z0-9]+}

 

#[{1}|10..-1//sysvar7]

 

начало блока для установки признака обработки диагностической функции UT..:  sysvar7 – имя функции;

|3|$${): String;\0d}

 

#["true"//sysvar6]

 

конец блока: sysvar6=true - признак функции UT…;

|4|%%{function T[A-Z0-9]+}

 

#[{1}|10..-1//sysvar7]

 

начало блока для установки признака обработки теста.:  sysvar7 – имя теста;

|4|$${: String;\0d}

 

#["true1"//sysvar6]

 

конец блока: sysvar6=true1 - признак теста;

{[0-9.]D[0-9@-@+]}

 

#["e"//{1}|2..2]

 

замена порядка констант D на E;

{[ :]Double[ );]}

 

#["Extended"//{1}|2..-2]

 

замена типа Double на Extended;

|[_{1}>1 || {0}|_{1}-1=A^^Z :: {1}|1..2<>"UT" || {1}|1..-2~<>*loop({{1}}|*''*)/l]|{[A-Z][A-Z][A-Z0-9][A-Z0-9]D[A-Z0-9]* *(}

 

#[:: "E"//{1}|5..5]

 

замена версии D на E  - пятый символ имени процедуры/функции (если не формальный параметр из списка {{1}});

{%SYS[0-9][0-9][0-9] :}{= [0-9.ED@+@-]+}

 

#[{1}|1..-2~//sysvar; {2}//sysvar1; BlockFile(extr,"d:\forpas\_scrpt\sysconst.txt", sysvar1,sysvar,"",sysvar,\0d); sysvar1|2''2//{2}|3..-1; BlockOper(setst,,,{1}_-1); BlockOper(setcn,,,+1)]

 

замена системных констант: системная константа SYSnnn определяется на Паскале как типизированая  и её новое значение (вторая лексема переменной sysvar1) выбирается из файла sysconst.txt по начальному  sysvar,""  и конечному  sysvar,\0d  контекстам поиска (в sysvar перед этим занесено имя константы); если константы в файле нет, то переменная sysvar1 содержит во второй лексеме старое значение константы; затем значение константы меняется на вторую лексему sysvar1;

|[sysvar6="true1"]|{% *exit;}

 

#[BlockFind(lngtxt,{1},sysvar1); BlockFind(lngtxt,{1}|~1..-1,sysvar2); loop(" "#sysvar1-sysvar2)/i//sysvar3; sysvar3 & "UtRes('" & sysvar7 & "',Result);  //вывод результатов в файл " & sysvar7 & ".res$"//{1}/b]

 

вставка строки выдачи диагностики в тесты (sysvar6=true1) перед оператором exit; (перед этим в sysvar3 формируется строка начальных пробелов); вместо sysvar7 подставляется имя файла;

|[sysvar6="true"]|{% *exit;}

 

#[BlockFind(lngtxt,{1},sysvar1); BlockFind(lngtxt,{1}|~1..-1,sysvar2); loop(" "#sysvar1-sysvar2)/i//sysvar3; sysvar3 & "UtDiag(Result,IERR);$"//{1}/b]

 

вставка строки выдачи диагностики в программы UT.. (sysvar6=true) перед оператором exit; (перед этим в sysvar3 формируется строка начальных пробелов);.

|5[sysvar6="true"]|%%{%begin}

 

#[BlockOper(setst)]

 

начало блока: определение строки begin , для вставки после неё комментария;

|5[block=1 :: else]|{%begin}

 

#[{1} & "$//Функция " & sysvar7 & " выводит сообщения в файл NAL_DIAG.res"//{1} ::]

 

вставка перед строкой комментария только для первого найденного блока; вместо sysvar7 подставляется имя файла;

|5|$$

 

#[BlockOper(setcn,,,1); BlockOper(setst)]

 

конец блока;

 

 

!Четвертый этап: создание Unit-файла для проекта Delphi

!(левый список содержит полные имена редактируемых файлов,

!адрес результата редактирования строится на основе первой лексемы

!текущего значения правого списка, к нему добавляются символы ‘_prj

!и образуется имя выходного каталога)

#[ЗАМЕНА КОНТЕКСТОВ] {13}

/1/входной текст (индекс): 3

/2/выходной текст (индекс): 0

/3/входной каталог: LeftList

/4/шаблоны входных файлов: *

/5/искать в подкаталогах (признак): 0

/6/без учета регистра (признак): 1

/7/режим замены (индекс): 0

/8/входная кодировка (индекс): 3

/9/выходной каталог: d:\forpas\&1|RightList:-1 _prj\

/10/рабочий каталог:

/11/сводный файл строк редакции (признак): 0

/12/только протокол (признак): 0

/13/вид протокола (индекс): 4

/14/статистика для протокола (признак): 0

/15/файл запроса: d:\forpas\_scrpt\contit4.txt

[ПАРАМЕТРЫ ВЫХОДНОГО КАТАЛОГА]

/1/запись измененных файлов (индекс): 0

/2/выходной каталог: d:\forpas\&1|RightList:-1 _prj\

/3/сохранять копии (признак): 0

/4/рабочий каталог: d:\forpas\&1|RightList:-1 _prj\

/5/копировать дерево файлов исходного каталога (признак): 0

/6/копировать в рабочий каталог (индекс): 0

/7/изменить имя файла (признак): 0

/8/переименовать после редакции (признак): 0

/9/постфиксные символы имени файла записи:

/10/расширение файла записи: 

>НАЧАТЬ: 12

 

На четвёртом этапе таблица контекстов загружается из файла contit4.txt и содержит следующие основные контексты:

 

Контекст поиска:

Контекст замены:

Комментарий:

<!>repeat

 

#["1"//sysvar1; {{39}}//sysvar9;]

 

начало цикла формирования общего списка обращений к внешним программам; в sysvar9 заносим  список программ, описанных в EXTERNAL и CALL {{39}};

<!>break|[{{48}}=""]|

 

 

контроль непустого списка вызываемых программ через формальный параметр {{48}};

<!>

#[BlockLexm(addchklex,sysvar9,sysvar9, {{48}}|sysvar1''sysvar1)]

 

добавление в sysvar9 программ из списка {{48}}, если их там нет;

<!>until|[{{48}}|sysvar1+1''sysvar1+1="" :: else]|

 

#[:: sysvar1 + 1//sysvar1]

 

конец цикла;

<!>repeat

 

#["1"//sysvar1; ""//sysvar3; ""//sysvar4]

 

начало цикла для анализа списка внешних вызываемых программ sysvar9;

<!>break|[sysvar9=""]|

 

 

контроль непустого списка sysvar9;

<!>

#[sysvar9|sysvar1''sysvar1//sysvar2]

 

выбор элемента списка в переменную sysvar2;

<!>|[sysvar2<>*loop({{1}}|*''*)/l || sysvar2<>*loop({{30}}|*''*)/l || sysvar2<>*loop(sysvar4|*''*)/l :: sysvar2<>*loop({{1}}|*''*)/l || sysvar2<>*loop(sysvar4|*''*)/l :: else]|

 

#["true"//sysvar5 :: "true1"//sysvar5 :: "false"//sysvar5]

 

анализ sysvar2: не формальный параметр {{1}}, не меняет своё значение в программе {{30}}, не входит в sysvar4 (sysvar5=true); не формальный параметр {{1}}, не входит в sysvar4 (sysvar5=true1); иначе (sysvar5=false);

<!>|[sysvar5="true" || sysvar2|1..2<>"UT" || sysvar2|5..5="D"]|

 

#["E"//sysvar2|5..5]

 

замена версии D на E в именах программ ( для неформальных параметров и не функций UT…);

<!>|[sysvar5<>"false"]|

 

#[sysvar3 & ", " & sysvar2 & "_p"//sysvar3; sysvar4 & sysvar2 & " "//sysvar4]

 

запись имени внешней программы в список sysvar4 и с добавленным постфиксом _p в список sysvar3;

<!>until|[sysvar9|sysvar1+1''sysvar1+1="" :: else]|

 

#[:: sysvar1 + 1//sysvar1]

 

конец цикла;

 

. . . . . . . . . . . . . . . . . . .

 

|1|%%{%procedure}{[A-Z][A-Z0-9]*}

 

#[""//sysvar; ""//sysvar6; {2}//sysvar1; ""//sysvar2; BlockOper(setst)]

 

начало блока для процедуры Паскаля;

|1[{1}|1..2="UT" :: {1}|1="T" :: else]|%%{%function}{[A-Z][A-Z0-9]*}

 

#["true"//sysvar6 :: "true1"//sysvar6 :: ""//"//sysvar; {2}//sysvar1; ""//sysvar2; BlockOper(setst)]

 

начало блока для функции Паскаля:
функция UT(sysvar6=true),
 
тест  (sysvar6=true1);

|1|$$$

 

конец блока, совпадающий с концом файла;

|1[sysvar6="true :: sysvar6="true1" :: else]|%$

 

#["SysUtils, Math, LStruct, Lfunc, UtDiag_p"//sysvar7 :: "SysUtils, Math, LStruct, Lfunc, UtRes_p"//sysvar7 :: "SysUtils, Math, Lstruct, Lfunc"//sysvar7]

 

построение списка имён внешних программ sysvar7 для разных видов программ;

|1|%$

 

#["Unit " & sysvar1 & "_p;$" & "interface$" & "uses$" & sysvar7 & sysvar3 & ";$$" & sysvar2 & "$$implementation$$"//sysvar3; BlockOper(insb,sysvar3,{10}); "$end."//sysvar4; BlockOper(inse,sysvar4,{10},,,True)]

 

генерация модуля Unit;

 

 

Полный текст сценария:  teconv.srcc.msu.ru/resurs/forpascrpt0.html

 

Иллюстрация  перекодировки программы и теста для  AFB2C :