p2js

Трансляция программ из Delphi (Pascal) в javascript

Целью проекта является создание транслятора с языка Pascal (в варианте Delphi) на язык javascript. Транслятор должен обеспечить так называемую "прозрачную" трансляцию исходного кода. Другими словами, необходимо обеспечить однозначное отображение программного кода, написанного на Pascal (Delphi) в сгенерированную на javascript программу.

Содержание

Концепция

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

В начало

Реализация

Решение было реализовано на основе компилятора языка Pascal (Delphi).

Трансляция модулей

Программы, написанные на Pascal (Delphi) имеют модульную структуру. Этого нельзя сказать про программы, написанные на javascript. Тем не менее, использую объекты, можно эмулировать такую структуру, как это показано в следующей таблице.

Pascal (Delphi) javascript
              Unit <unitname>;
              Interface
                 [interface section]
              Implementation
                 [implementation section]
              Initialization
                 [initialization section]
              End.
           
              pas.<unitname>={
               [interface section],
               impl: {
                 [implementation section],
                 $<UNITNAME>_init: function() {
                    [initialization section]
                 }
               }
              };
           
Безусловно, необходимо обеспечить наличие пустого объекта pas. Этот и другие вспомогательные объекты можно будет найти в файле rtl.js, входящим в проект(
см. более подробно). Кроме того, необходимо отметить, что секция finalization в модуле Pascal (Delphi) не поддерживается.

Главный модуль программы на Pascal (в случае Delhi располагается в файле с расширение dpr) транслируется в следующий образ.

Pascal (Delphi) javascript
              Program <unitname>;
                 [implementation section],
              Begin
                 [main code]
              End.
           
              pas.<unitname>={
               impl: {
                 [implementation section],
                 $main:function() {
                    [main code]
                 }
               }
              }
           
Предполагается, что функция pas.<unitname>.impl.$main() будет вызвана после загрузки индексной Html страницы приложения(например, в обработчике onload тэга body). Для того, чтобы это реализовать, p2js предлагает параметр -Gi(см. более подробно), позволяющий назначить шаблон индексной Html страницы, в котором можно указать место вставки вызова главной функции программы.

Прежде чем приводить пример необходимо отметить, что модули в Pascal (Delphi) подключаются и инициализируются в определенном порядке. Чтобы обеспечить такой порядок необходимо воспользоваться вспомогательной функцией rtl.module (см. более подробно).

Приведем пример:

Pascal (Delphi) javascript
              Unit MyModule;
              Interface
              Uses windows,Sysutils;
                 var  iGlob:integer;
                      sGlob:string="abc";
              Implementation
              Uses Classes;
                 procedure implMyFunc;
                 begin
                 end;
              End.
           
              rtl.module("MyModule",["System","objpas","windows","sysutils"],function(uses, unit){
               this.iGlob=0; 
               this.sGlob="abc"; 
               var $impl={
                     $MYMODULE_init:function() {
                     }, 
                     implMyFunc:function() {
                     }
               };
               this.impl=$impl;
              },["Classes","Graphics","Provider","CommonDM"]);
           

Следует отметить, что модуль System и objpas подключаются компилятором неявно. Необходимо обеспечить их наличие.

В начало

Трансляция переменных

Переменные транслируются без указания типа, так как явное указание типов в javascript не требует. Неявное указание типов возможно, если переменной происваивается начальное значение.

Приведем пример:

Pascal (Delphi) javascript
             Unit A1;
             Interface
             Uses Classes,Forms;

             const c1:integer=3;
                   c2 = 'abc';
                   c3 = 234;
                   c4 = 12.45;
                   c5 = nil;

             var   v1:string;
                   v2,v3:double;
                   v4:byte=0;
                   v5:TForm;
                   v6:TIdentMapEntry;

                   vv4:string='abcюжа';
                   vv5:set of byte=[0,1];
                   vv7:array[0..1] of byte=(1,2);
                   vv8:array of byte;
                   vv9:array[0..101] of string;

             Implementation
             End.

           
                   rtl.module("MyModule",["System","objpas","Classes","Forms"],function(uses, unit){
                    this.c1=3; 
                    this.c2="abc"; 
                    this.c3=234; 
                    this.c4=12.45; 
                    this.c5=null; 
                    this.v1=""; 
                    this.v2=0.0; 
                    this.v3=0.0; 
                    this.v4=0; 
                    this.v5=undefined; 
                    this.v6=new pas.Classes.TIdentMapEntry(); 
                    this.vv4="abcюжа"; 
                    this.vv5=3; 
                    this.vv7=[1, 2]; 
                    this.vv8=[]; 
                    this.vv9=[]; 
                    var $impl={
                          $MYMODULE_init:function() {
                          }
                    };
                    this.impl=$impl;
                   },[]);
           

В начало

Трансляция типов

В javascript типизация в декларативном виде отсутствует, кроме типов, которые можно отнести к объектным (в таком случае используются так называемые прототипы). Именно поэтому все производные от простых типов Pascal никак не транслируются. Сложные типы Pascal (классы, записи(структуры) или массивы) транслируются в объекты или массивы javascript.

В начало

Трансляция типа "запись"

Тип запись(record) транслируются в javascript объект, конструктор которого формирует структуру полей данного типа. Например:

Pascal (Delphi) javascript
              Unit MyModule;
              Interface
                type
                     t3 = record
                        t3_f1 :integer;
                        t3_f2 :string;
                        t3_f3 :TDateTime;
                     end;

              Implementation
                var  r1:t3;
              Initialization
                r1.t3_f1 := 11;
              End.
           
              rtl.module("MyModule",["System","objpas"],function(uses, unit){
               this.t3=
                function() {
                 this.clean_fields = function() {
                 this.t3_f1=0; 
                 this.t3_f2=""; 
                 this.t3_f3=0.0; 
                 };
                 this.clean_fields();
                }; 
               var $impl={
                     $MYMODULE_init:function() {
                      $impl.r1.t3_f1=11; 
                     }, 
                     r1:new pas.MyModule.t3()
               };
               this.impl=$impl;
              },[]);
           
Следует обратить внимание на то, что объявление в Pascal переменной типа "record" эквивалентно созданию javascript объекта.

В начало

Трансляция типа "массив"

Любые типы массивов Pascal(имеется ввиду как статические так и динамические) транслируются в javascript массивы. При этом, контроль границ массива отсутствует, т.к. все массивы в javascript являются динамическими и к тому же автоматическими(т.е. автоматически расширяют свои границы при необходимости).

Приведем пример:

Pascal (Delphi) javascript
             Unit MyModule;
             Interface

             type
                   t1 = array [0..21] of byte;
                   t2 = array of TObject;
             var
                   a1: array of string;

             Implementation

             end.
           
             rtl.module("MyModule",["System","objpas"],function(uses, unit){
              this.t1=new Array(22); 
              this.t2=[]; 
              this.a1=[]; 
              var $impl={
                    $MYMODULE_init:function() {
                    }
              };
              this.impl=$impl;
             },[]);
           

В начало

Трансляция типа "класс"

Классы в общепринятом понимании этого слова в javascript отсутствуют. В некотором смысле заменой классам является понятие прототипа. Чтобы с помощью прототипов эмулировать концепцию классов будем пользоваться вспомогательной функцией rtl.extend (см. более подробно)

Приведем пример:

Pascal (Delphi) javascript
             Unit MyModule;
             Interface

             type
                    MyCls=class
                      FId:integer;
                      FZOrder:byte;
                      function getZOrder:byte;
                    end;

             Implementation

               function MyCls.getZOrder:byte;
               begin
                 Result:=FZOrder;
               end;

             end.
           
             rtl.module("MyModule",["System","objpas"],function(uses, unit){
              this.MyCls=rtl.extend(pas.System.TObject, {
                constructor: function() {
                  pas.System.TObject.apply(this, arguments);
                },
                FId:0, 
                FZOrder:0, 
                getZOrder:function() {
                 var $result=0; 
                 $result=this.FZOrder; 
                 return $result;
                }
               }); 
              var $impl={
                    $MYMODULE_init:function() {
                    }
              };
              this.impl=$impl;
             },[]);
           
Для классов которые реализуют интерфейсы необходимо во втором аргументе rtl.extend передать массив идентификаторов этих интерфейсов. Например:

Pascal (Delphi) javascript
           Unit MyModule;
           Interface

           type

              TMyObj=class(TObject,IUnknown)
               function QueryInterface(const guid: TGuid;out obj):LongInt; StdCall;
               function _AddRef:LongInt; StdCall;
               function _Release:LongInt; StdCall;
              end;

           Implementation
           Uses SysUtils;

              function TMyObj.QueryInterface(const guid: TGuid;out obj):LongInt; 
              begin

              end;

              function TMyObj._AddRef:LongInt; 
              begin

              end;

              function TMyObj._Release:LongInt;
              begin

              end;

           var  obj:TMyObj;
           initialization
             obj:=TMyObj.Create;
             if Supports(obj,IUnknown) then;
           end.
           
           rtl.module("MyModule",["System","objpas"],function(uses, unit){
            this.TMyObj=rtl.extend(pas.System.TObject, 
                      ["{00000000-0000-0000-C000-000000000046}"], {
              constructor: function() {
                pas.System.TObject.apply(this, arguments);
              },
              QueryInterface:function(guid, obj) {
               var $result=0; 
               return $result;
              }, 
              _AddRef:function() {
               var $result=0; 
               return $result;
              }, 
              _Release:function() {
               var $result=0; 
               return $result;
              }
             }); 
            var $impl={
                  $MYMODULE_init:function() {
                   $impl.obj=new pas.MyModule.TMyObj(); 
                   if(pas.sysutils.Supports$3.call(pas.sysutils,$impl.obj, 
                       "{00000000-0000-0000-C000-000000000046}")) ; 
                  }, 
                  obj:undefined
            };
            this.impl=$impl;
           },["sysutils","Graphics","Provider","windows","CommonDM"]);
           

В начало

Трансляция перечисляемых типов и множеств

Перечисляемый тип образует поля в модуле. Имена этих полей соответствуют именам элементов перечисляемого типа, а значения - позиции элемента в объявлении этого типа. Например:

Pascal (Delphi) javascript
           Unit MyModule;
           Interface
           type 
                t1 = (t31,t32,t33);
           Implementation
           End.
           
           rtl.module("MyModule",["System","objpas"],function(uses, unit){
            this.t1=rtl.enumtype('t31','t32','t33'); 
            var $impl={
                  $MYMODULE_init:function() {
                  }
            };
            this.impl=$impl;
           },[]);
           

После такого объявления появляются поля pas.MyModule.t31 и т.д. с соответствующими значениями

Множества представляются целым числом или массивом целых чисел. Операции на множествах видоизменяются. Например,

Pascal (Delphi) javascript
           Unit MyModule;
           Interface

           Implementation

             var  abc:set of byte;
                  r1 :byte;
                  r2 :byte;

             function f1:boolean;
             begin
               abc := [1,5,25];
               r1:=0;r2:=1;
               Result:=1 in [r1,r2];
             end;

           initialization
           end.
           
           rtl.module("MyModule",["System","objpas"],function(uses, unit){
            var $impl={
                  $MYMODULE_init:function() {
                  }, 
                  abc:undefined, 
                  r1:0, 
                  r2:0, 
                  f1:function() {
                   var $result=false; 
                   $impl.abc=[33554466,131072,512,2]; 
                   $impl.r1=0; 
                   $impl.r2=1; 
                   $result=rtl.inset((([] | ($impl.r1<<1)) | ($impl.r2<<1)), 1); 
                   return $result;
                  }
            };
            this.impl=$impl;
           },[]);
           

В начало

Трансляция циклов

Трансляция циклов типа while и repeat ничем особенным не отличается. Это не относится к трансляции цикла for. Дело в том, что в javascript условие окончания цикла for вычисляется на каждом цикле, что не так в языке pascal. В pascal условие окончания цикла вычисляется до начала его выполения и, поэтому не может быть изменено в процессе выполнения цикла. С целью реализации данной особенности p2js создает вспомогательную переменную, значение которой вычисляется до начала цикла. Например:

Pascal (Delphi) javascript
           Unit MyModule;
           Interface

           Implementation

             var  arr :array of string;

             function f1:string;
             var i:integer;
             begin
               i:=0;
               while i<10 do 
               begin
                arr[i]:='abc';
                i:=i+1;
               end;
               repeat 
                arr[i]:='dce';
                i:=i+1;
               until i>=12;
             end;

             procedure f2;
             var i:integer;
             begin
               for i:=0 to 12 do arr[i]:='abc';
               for i:=12 downto 0 do begin 
                  arr[i]:='dce';
                  if(i=12) then arr[i]:='fgt';
               end;
             end;

           end.
           
           rtl.module("MyModule",["System","objpas"],function(uses, unit){
            var $impl={
                  $MYMODULE_init:function() {
                  }, 
                  arr:[], 
                  f1:function() {
                   var i=0; 
                   var $result=""; 
                   i=0; 
                   while ((i < 10)) {
                    $impl.arr[i]="abc"; 
                    i=(i + 1); 
                   }; 
                   do {
                    $impl.arr[i]="dce"; 
                    i=(i + 1); 
                   } while (!(i >= 12)); 
                   return $result;
                  }, 
                  f2:function() {
                   var i=0; 
                   {i=0; for(var $efor_176_5=12; i<=$efor_176_5; i++) {
                    $impl.arr[i]="abc"; 
                   }
                   }; 
                   {i=12; for(var $efor_177_5=0; i>=$efor_177_5; i--) {
                    $impl.arr[i]="dce"; 
                    if((i == 12)) {
                     $impl.arr[i]="fgt"; 
                    }; 
                   }
                   }; 
                  }
            };
            this.impl=$impl;
           },[]);
           

В начало

Особенности трансляции прочих операторов

Трансляция многих операторов языка pascal не имеет никаких особенностей. Поэтому в данном пункте будут упомянуты только те операторы, трансляция которых не очевидна или имеет ньюансы.

В начало

Оператор try...except

В операторе javascript catch (аналог except) необходимо определить имя переменной, которая будет содержать объект-исключительную ситуацию. Таким именем в p2js является имя exceptObject. Имя выбрано не случайно. Оно совпадает с именем функции в pascal, которая возвращает указатель на объект типа Exception. Соответственно, все вызовы данной функции заменяются на обращение к переменной exceptObject. Пример:

Pascal (Delphi) javascript
           Interface

           Implementation
           Uses SysUtils,Dialogs;

             function f2(a:integer):double;
             var s:string;
             begin
               try
                 Result:=10/a;
               except
                 on E:EZeroDivide do result:=0;
                 on E:EOverflow do result:=0;
                 else 
                   begin
                    s:=(ExceptObject as Exception).Message;
                    raise Exception.Create('abort');
                   end;
               end;
             end;

           end.
           
           rtl.module("MyModule",["System","objpas"],function(uses, unit){
            var $impl={
                  $MYMODULE_init:function() {
                  }, 
                  f2:function(a) {
                   var s=""; 
                   var $result=0.0; 
                   try {
                    $result=(10 / a); 
                   }
                   catch(exceptObject){
                    var E=exceptObject; 
                    if(E instanceof pas.sysutils.EZeroDivide) {
                     $result=0; 
                    } else {
                     {
                      if(E instanceof pas.sysutils.EOverflow) {
                       $result=0; 
                      } else {
                       {
                        s=exceptObject.fmessage; 
                        throw new pas.sysutils.Exception("abort"); 
                       }; 
                      }; 
                     }; 
                    }; 
                   }; 
                   return $result;
                  }
            };
            this.impl=$impl;
           },["sysutils","Dialogs","Graphics","Provider","windows","CommonDM"]);
           

В начало

Вызов обработчика событий

Обрабочик событий (к ним пречисляются поля объекта процедурного типа) должен быть вызван от имени объекта, которому он принадлежит. Другими словами поле Self внутри обработчика должно обязательно указывать на владельца этого метода. С этой целью, в p2js все обработчики становятся объектами, которые должны создаваться методом rtl.createEvent(). При явном вызове обработчика p2js транслирует его в вызов функции callback(...) объекта-обработчика. Устанавливать значение обработчика необходимо с помощью функции set(self,callback) объекта-обработчика. Приведем пример:

Pascal (Delphi) javascript
           Unit MyModule;
           Interface

           Implementation

           Type
                 TMyFuncObj=function(a:integer):boolean of object;

             TObj=class
              FMyFunc:TMyFuncObj;
              function handler(a:integer):boolean;
             end;
            
             function TObj.handler(a:integer):boolean;
             begin
             end;

           Var  Obj:TObj;

             procedure mySet;
             begin
               Obj.FMyFunc:=Obj.handler;
             end;

             function myCall:boolean;
             begin
               Result:=Obj.FMyFunc(20);
             end;

           end.
           
           rtl.module("MyModule",["System","objpas"],function(uses, unit){
            var $impl={
                  $MYMODULE_init:function() {
                  }, 
                  TMyFuncObj:function($self, a){}, 
                  TObj:rtl.extend(pas.System.TObject, {
                    constructor: function() {
                      pas.System.TObject.apply(this, arguments);
                      this.FMyFunc=rtl.createEvent();
                    },
                    FMyFunc:undefined, 
                    handler:function(a) {
                     var $result=false; 
                     return $result;
                    }
                   }), 
                  Obj:undefined, 
                  mySet:function() {
                   $impl.Obj.FMyFunc.set($impl.Obj, $impl.Obj.handler); 
                  }, 
                  myCall:function() {
                   var $result=false; 
                   $result=$impl.Obj.FMyFunc.callback(20); 
                   return $result;
                  }
            };
            this.impl=$impl;
           },[]);
           

В начало

Передача данных по ссылке

Известно, что в javascript нельзя передавать данные из функции по ссылке. Иными словами, трансляция аргументов функций/методов с модификаторами var/out требует специальных способов обработки. Рассмотрим пример:

Pascal (Delphi) javascript
              procedure f1(var b:byte);
              begin
                b:=-1;
              end;
           
              f1:function(b) {
                   b.set(-1);
              }
           
Здесь предполагается, что вместо аргумента простого типа передается объект с методом set. При вызове функции f с помощью p2js произойдет следующее
Pascal (Delphi) javascript
              function f2:byte;
              begin
                f1(Result);
              end;
           
              f2:function() {
               var $result;
               this.f1({get:function(){return $result;},set:function(v){$result=v;}});
               return $result;
              }
           
При этом используется замыкание. Ведь метод set, вызванный из тела f1 будет выполнятся в контексте f2, что эквивалентно передачи значения аргумента на более высокий уровень управления.

В начало

Рефакторинг модальных диалогов

Модальные диалоги с пользователем не поддерживаются большинством браузеров. Это вызывает определенную проблему при трансляции из программ, написанных на языке Pascal, которые используют модальность. Здесь пойдет речь об истинной, а не видимой модальности. Под истинной модальностью мы подразумеваем организацию в любом месте программы замкнутого цикла обработки сообщений, что позволяет программисту написать подобный код:

             {a}
             if f.ShowModal=mrOk then {b}
                                 else {c};
             {d}
           
После того как пользователь осуществит ввод данных в модальном диалоге и покинет цикл обработки сообщений у программы будет два альтернативных пути: {b,d} или {c,d}. Наиболее простым способом рефакторинга является преобразование вышеуказанного кода в следующую форму:
             {a}
             f.ShowModal( function($modalresult) 
                          {
                            if $modalresult=mrOk then {b}
                                                 else {c};
                            {d}
                          }
                         );
           
Пока мы намеренно не учитываем ряд ньюансов, которые возникнут чуть позже. Итак модальный вызов становится немодальным, а все возможные исходы({b,d} или {c,d}) заключены в функцию, которую f.ShowModal вызовет при вводе данных пользователем. Такую функцию будем называть дополнением. В аргументе $modalresult данной функции будет передано значение, которое характеризует результат работы модального диалога (Например, если пользователь нажмет кнопку Ok в $modalresult буде передано значение mrOk).

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

             if {x} then
             begin
               {a}
               if f.ShowModal=mrOk then {b}
                                   else {c};
               {d}
             end else
               {y};
             {z};
           
Возможными путями являются {x,y,z}, {x,a,b,d,z}, {x,a,c,d,z}. Обратим внимание, что {z} входит во все возможные пути, среди которых есть путь как с модальным диалогом, так и без него. Это означает, что действуют два взаимоисключающих требования: перенести {z} в функцию-дополнение и оставить ее на месте. Эта и многие другие особенности учитываются в реализации функции rtl.modal_if. А оператор if при трансляции заменяется на вызов rtl.modal_if. Например:

Pascal (Delphi) javascript
           Unit MyModule;
           Interface

           Implementation
           Uses SysUtils,Dialogs;

             function f2(a:integer):double;
             begin
               if a<0 then ShowMessage('А value is less than zero')
                      else Result:=sqrt(a);
             end;

           end.
           
           rtl.module("MyModule",["System","objpas"],function(uses, unit){
            var $impl={
                  $MYMODULE_init:function() {
                  }, 
                  f2:function(a, $scope, $callback, $topLevel) {
                   var $result=0.0; 
                   rtl.modal_topLevel(this, function($context){
                    $context(function(){
                     rtl.modal_if($context, (a < 0), function($context){
                      $context(function(){
                       pas.Dialogs.ShowMessage("А value is less than zero", this, 
                        function($modalresult){
                        $context(function(){
                         $context.ret(); 
                        }); 
                       }, $context); 
                      }); 
                     }, function($context){
                      $context(function(){
                       $result=Math.sqrt(a); 
                       $context.ret(); 
                      }); 
                     }, function($context){
                      $context(function(){
                       $context.ret(); 
                      }); 
                     }); 
                     
                    }); 
                   }, $scope, $callback, function(){
                    return $result; 
                   }, $topLevel); 
                   return $result;
                  }
            };
            this.impl=$impl;
           },["sysutils","Dialogs","Graphics","Provider","windows","CommonDM"]);
           

Выглядит устрашающе. Кстати, следует обратить внимание, что тело модальной функции помещается в специализированную функцию rtl.modal_topLevel. Эта функция необходима для правильной передачи управления при выходе из модального диалога.

Далее необходимо учесть, что функции вложены друг в друга. Функция, в которой есть вызов модальной функции также будет модальной. Приведем пример:

             function X:byte;
             begin
               {a0}
               if f.ShowModal=mrOk then {b0}
                                   else {c0};
               {d0}
             end;

             procedure Y;
             begin
               {a1}
               if X=0 then {b1}
                      else {c1}
               {d1}
             end;
           
Рассмотрим следующую трансляцию:
                  function X($scope, $callback, $topLevel) {
                   var $result=0; 
                   rtl.modal_topLevel(this, function($context){
                    $context(function(){
                     {a0}; 
                     $impl.f.ShowModal(this, function($modalresult){
                      $context(function(){
                       if(($modalresult == 1)) {
                        {b0}; 
                       } else {
                        {c0}; 
                       }; 
                       {d0}; 
                       $context.ret(); 
                      }); 
                     }, $context); 
                    }); 
                   }, $scope, $callback, function(){
                    return $result; 
                   }, $topLevel); 
                   return $result;
                  };
            
                  function Y($scope, $callback, $topLevel) {
                   rtl.modal_topLevel(this, function($context){
                    $context(function(){
                     {a1}; 
                     $impl.X(this, function($modalresult){
                      $context(function(){
                       if(($modalresult == 0)) {
                        {b1}; 
                       } else {
                        {c1}; 
                       }; 
                       {d1}; 
                       $context.ret(); 
                      }); 
                     }, $context); 
                    }); 
                   }, $scope, $callback, function(){
                    return; 
                   }, $topLevel); 
                  };
           
Общим принципом трансляции с учетом структуры программы является следующее: функция-дополнение определенного уровня управления (в данном примере Y) передается на нижележайший уровень управления (в данном примере X) и вызывается после функции-дополнения текущего уровня. Таким способом функции-дополнения образуют правильный порядок вызова. Необходимо отметить, что благодаря замыканиям, реализованным в javascript функции-дополнения вызываются в правильном контексте (т.е. в том, который задумывался в исходной программе на Pascal).

Любая функция или метод может быть модальной. Для этого p2js расширяет синтаксис Pascal и использует модификатор modal. Например:

             ...
             procedure MyModal;modal;
             ...
           

В начало

Опции командной строки

Командная строка выглядит следующим образом

p2js [options] <inputfile>

, где inputfile - имя файла с исходным кодом на языке Pascal, options - необязательные опции. Список опций:

  • -G опреледяет режим вывода
    • -G- отключение режима генерации javascript
    • -Ga включает режим добавления сгенерированного кода к существующему файлу (по умолчанию - отключено)
    • -Gt<x> устанавливает <x> в качестве имени выходного файла (по умолчанию "<inputfile>.js")
    • -Gi<x> устанавливает <x> в качестве имени индексного html-файла. Играет роль при генерации из файла проекта(*.dpr).
    • -Gs устанавливает режим генерации заглушек (без секции реализации модуля)(по умолчанию - отключено)
    • -Gd<x> устанавливает <x> в качестве каталога, в который осуществляется вывод
    • -Gu<x> устанавливает <x> в качестве имени файла, в который будут выводится словари (*.dict и *.udict) (по умолчанию - словари не формируются)
    • -Gp<x> устанавливает <x> в качестве имени файла, который используется как словарь подстановки
    • -Ge<x> устанавливает <x> в качестве списка имен, исключаемых из результата модулей
    • -Gm устанавливает режим перестраивания программного кода, связанный с модальными диалогами(по умолчанию режим выключен)
  • -v опреледяет режим информирования о процессе генерации
    • -vg подробный вывод информации
    • -vm вывод информации об используемых модулях

В начало

Словари

Существует три вида словарей:

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

Записи отделяются друг от друга символом конца строки и перевода каретки.

Словарь подстановки имеет дополнительные элементы синтаксиса. В последних двух полях, после указания имени метода можно написать [=<Имя заменителя>[()]]. Если круглые скобки указаны, то заменяющий вызов будет вызовом метода, иначе обращением к полю. Например:

           Forms	TApplication	Hint	FHint=getAlt()	SetHint
           

В начало

Генерация индексного файла

В качестве индексного html-файла может выступать любой текстовый файл-шаблон. Например:

<html>
            <head>
              <script type="text/javascript" src="ext/ext-all.js"></script>
              <script type="text/javascript" src="?app=$SUBS-APPNAME?r=jsproject.js?v=$SUBS-VERSION"></script>
              <script type="text/javascript">
                 Ext.onReady(function(){$SUBS-MAIN});
              </script>
            </head>
           <body>
           </body>
           </html>
При этом необходимо учитывать, что в шаблоне можно использовать следующие переменные подстановки:
Имя переменной Описание
$SUBS-APPNAMEИмя приложения(проекта Delphi)
$SUBS-VERSIONВерсия проекта. Значение будет сформировано при нахождении рядом с файлом проекта(*.dpr) файла c именем ver.rc в котором размешен исходный код ресурса Windows
$SUBS-MAINКоманда вызова главной функции проекта. В большинстве случаев она выглядит как <Имя приложения>.impl.$main()

В начало

Вспомогательные объекты javascript

Для демонстрации возможностей p2js отправте нам любой проект, состоящий из одного модуля. В демонстрационном прмере существуют следующие ограничения:

Данные ограничения не является ограничениями технологии. Они является ограничениями среды исполнения javascript в демонстрационном примере.

Примеры

Простая форма

Исходный код: sample_source_1.zip
Рабочий пример: Запуск

Модальный диалог

Исходный код: sample_source_2.zip
Рабочий пример: Запуск

Списки

Исходный код: sample_source_2.zip
Рабочий пример: Запуск

Команда p2js готова помочь в создании кроссплатформенных приложений. Напишите нам p2js@mail.ru. Язык общения русский или английский.

Лицензирование и ограничения использования

p2js выпускается по лицензии GNU General Public License. Вы можете скачивать и использовать его в любых разработках, в том числе коммерческих.

Стороннее програмное обеспечение, используемое в проекте:

Услуги

Команда проекта оказывает услуги по сопровождению и поддержке пользователей p2js. Для того, чтобы получить поддержку, необходимо ознакомится с ее условиями, выбрать тариф, оплатить услуги.

ТарифыБазовыйПродвинутыйОсобый
Перечень услуг
Консультирование по e-mail*
Информирование Заказчика о выпуске обновлений. Информирование Заказчика о расширении функциональности в обновленных версиях.
Устранения дефектов по заявке в течении 5 рабочих дней
Срочное устранения дефектов по заявке
Максимальное время реагирования на обращение в службу поддержки - 40 часов 16 часов
Прием заявок на доработку функциональности. Расчет стоимости и сроков.
Стоимость, €/мес.2060140

* - адрес службы технической поддержки p2js@mail.ru. Язык общения русский или английский.

Расчетным периодом оказания услуги является календарный месяц. После того, как оплата поступает на счет службы поддержки услуга начнает действовать в текущем календарном месяце. Служба технической поддержки будет оказывать услуги, запрашиваемые с электронного адреса, указанного при оплате.