[Pas2js] My 1st type helper

Mattias Gaertner nc-gaertnma at netcologne.de
Wed Mar 6 14:31:18 CET 2019


On Sun, 3 Mar 2019 16:51:53 -0700 (MST)
warleyalex via Pas2js <pas2js at lists.freepascal.org> wrote:

> Ref: Pas2JS Compiler version 1.4.0RC4 [2019/03/03] for Win32 i386
> ==================================================
> Test I:
> ==================================================
> type
>  float = double;
> 
> type
>  TFloatHelper  =  type helper for Float
>    const NPI = 3.141592;
>    function ToStr: String;
>  end;    
> 
> function  TFloatHelper.ToStr: String;
> begin
>   Result:=  FloatToStr(Self);
> end;
> 
> (...)
> var
>   ReturnValue : Float;
> begin
>   ReturnValue := 2.71;
>   ReturnValue:= (ReturnValue * Float.NPI);                
>   console.log(ReturnValue.ToStr);   //8.51371432 it works for me.
> 
> a) but we can't use it straightforward like 
> //  console.log((ReturnValue *
> ReturnValue.NPI).ToStr);    //8.51371432 

Why not?
It works here.

 
> ==================================================
> Test II:
> ==================================================
> type 
>   TMyClass = class 
>     procedure MyProc; 
>     function MyFunc: Integer; 
>   end; 
> 
> type 
>   TMyClassHelper = class helper for TMyClass 
>     procedure HelloWorld; 
>     function MyFunc: Integer; 
>   end; 
> 
> implementation 
> 
> procedure TMyClass.MyProc; 
> var 
>  X: Integer; 
> begin 
>   X := MyFunc; 
> end; 
> 
> function TMyClass.MyFunc: Integer; 
> begin 
>   WriteLn('TMyClass.MyFunc called'); 
> end; 
> 
> procedure TMyClassHelper.HelloWorld; 
> begin 
>   Writeln(Self.ClassName); // Self refers to TMyClass type, not 
> TMyClassHelper 
> end; 
> 
> function TMyClassHelper.MyFunc: Integer;
> begin
> //  inherited;
>   WriteLn('TMyClassHelper.MyFunc called');
> end;  
> 
> (...) 
> 
> var 
>   X: TMyClass; 
> begin 
>   X := TMyClass.Create; 
>   X.MyProc;     // Calls TMyClass.MyProc 
>   X.HelloWorld; // Calls TMyClassHelper.HelloWorld 
>   X.MyFunc;     // Calls TMyClassHelper.MyFunc 
> ---------------------------------- 
> the console output is: 
> (* 
>   TMyClassHelper.MyFunc called
>   TMyClass 
>   TMyClassHelper.MyFunc called 
> *) 

correct

 
> a) if I declare the method "MyFunc" with the keyword "inherited"
> 
> function TMyClassHelper.MyFunc: Integer;
> begin
>   inherited;
>   WriteLn('TMyClassHelper.MyFunc called');
> end;  
> 
> the console is: 
> (* 
>   TMyClass.MyFunc called
>   TMyClassHelper.MyFunc called
>   TMyClass
>   TMyClass.MyFunc called
>   TMyClassHelper.MyFunc called
> *)

yes

 
> b) I would like to call TMyClass.MyProc method, but there's an active
> helper with the method MyProc.
> 
>   X := TMyClass.Create; 
>   X.MyProc;     // Calls TMyClass.MyProc
> 
> Hum, this is correct! it will show the console "TMyClassHelper.MyFunc
> called" instead of  "TMyClass.MyProc called" just because the class
> helper takes precedence over the actual class type.

yes

 
> ==================================================
> Test III: class helper how old are you?
> ==================================================
>   { TPessoa class }
>   type
>     TPessoa = class
>     private
>       FDataNasc: TDateTime;
>     public
>       property DataNasc: TDateTime read FDataNasc write FDataNasc;
>     end;
> 
>     TPessoaHelper = class helper for TPessoa
>       private
>         function GetIdade: integer;
>       public
>       property Idade: integer read GetIdade;
>     end;
> 
>     TIntHelper = type helper for Integer
>     public
>       function ToStr: String;
>     end;
> 
> implementation
> 
> {YearsBetween function}
> function SpanOfNowAndThen(const ANow, AThen: Float): Float;
> begin
>   if ANow < AThen then
>     Result := AThen - ANow
>   else
>     Result := ANow - AThen;
> end;
> 
> function DaySpan(const ANow, AThen: Float): Float;
> begin
>   Result := SpanOfNowAndThen(ANow, AThen);
> end;
> 
> function YearSpan(const ANow, AThen: Float): Float;
> begin
>   Result := DaySpan(ANow, AThen) / 365.25;
> end;
> 
> function YearsBetween(const ANow, AThen: Float): Integer;
> begin
>   Result := Trunc(YearSpan(ANow, AThen));
> end;
> 
> { TPessoaHelper }
> function TPessoaHelper.GetIdade: integer;
> begin
>   Result := YearsBetween(Self.DataNasc, Now);
> end;
> 
> { TIntHelper }
> function TIntHelper.ToStr: String;
> begin
>   Result := IntToStr(Self);
> end;
> 
> 
> (...)
>   oPessoa := TPessoa.Create;
>   try
>     oPessoa.DataNasc := EncodeDate(1986, 11, 26);
>     console.log(
>       (oPessoa.Idade).ToStr + ' years'
>     );
>   finally
>     oPessoa.Free;
>   end;   
> 
> a) that we are using ToStr function instead of IntToStr. I see the
> console "32 years", which is the expected! Today - BirthDate = 32

true

 
> ==================================================
> Test IV: Multiple class helper (multihelpers) are supported
> ==================================================
> We add 2 more helpers in the Test III:
> 
>   TPessoaFisicaHelper = class helper (TPessoaHelper) for TPessoa
>     public
>       function isValidCPF():Boolean;
>   end;
> 
>   TPessoaJuridicaHelper = class helper (TPessoaFisicaHelper) for
> TPessoa public
>       function isValidCNPJ():Boolean;
>   end;
> 
> implementation
> 
> { TPessoaFisicaHelper }
> function TPessoaFisicaHelper.isValidCPF(): Boolean;
> begin
>  WriteLn('CPF is valid');
> end;
> 
> { TPessoaJuridicaHelper }
> function TPessoaJuridicaHelper.isValidCNPJ(): Boolean;
> begin
>  WriteLn('CNPJ is valid');
> end;
> 
> (...)
>   oPessoa := TPessoa.Create;
>   try
>     oPessoa.DataNasc := EncodeDate(1986, 11, 26);
>     console.log(
>       (oPessoa.Idade).ToStr + ' years'
>     );
>     txt :=  (oPessoa.Idade).ToStr;
>     console.log(txt);
>     oPessoa.isValidCPF;
>     oPessoa.isValidCNPJ;
> 
>   finally
>     oPessoa.Free;
>   end;  
> 
> we have the expected console:
> (*
>   32 years
>   32
>   CPF is valid
>   CNPJ is valid
> *)

good

 
> We can not use the following syntax, when defining TPessoaFisicaHelper
> helper, for instance:
>   TPessoaFisicaHelper = class helper for TPessoa

Yes, you can.
 
> I'm getting this msg: 
>   Error: identifier not found "Idade"

Please show a complete example.

 
> to disappear this error, we have to define this way:
>   TPessoaFisicaHelper = class helper (TPessoaHelper) for TPessoa
>        and
>   TPessoaJuridicaHelper = class helper (TPessoaFisicaHelper) for
> TPessoa

That is recommended.
If not possible use multihelpers.

 
> That is, MORE THAN ONE class helper for a given class may be in scope
> at any time. For instance, if you have two helpers in scope, ALL OF
> THEM will be recognised by the compiler. 

Could be right. Depends on what you mean with "recognised".

 
> ==================================================
> Test V: Helpers on functions pointers are not supported.

Correct. Same as FPC and Delphi.
Reason is probably that there are some confusing cases. For example

aFuncVar.Proc

Which could mean
aFuncVar().Proc

So it is unclear which helper to call. The function type helper or the
function result type helper.


> ==================================================
> type
>    TFunc = function: Integer;
> 
>    { TIntegerHelper }
>     TIntegerHelper = type helper (TIntHelper) for Integer
>        function ToString : String;
>     end;
> 
> implementation
> 
> { TIntegerHelper }
> function TIntegerHelper.ToString: String;
> begin
>   Result := IntToStr(Self);
> end;
> 
> (*
>   { TFuncHelper }
>   function TFuncHelper.ToString: String;
>   begin
>     Result := IntToStr(Self);
>   end;  
> *)
> 
> We can not define a helper like this:
> 
>    { TFuncHelper }
>    (*
>    TFuncHelper = type helper for TFunc // this is not allowed
>        function ToString : String;
>     end;
>     *)
> 
> You've got this msg:
> Error: Type "TFunc" cannot be extended by a type helper. 
> so Helper on function pointer are not supported.
> 
> (...)
> 
> function twenty: Integer;
> begin
>   Result := 20;
> end;
> 
> var
>   f: TFunc;
> begin
>   f:= @twenty;
> 
>   console.log(
>     f.ToString   
>   );
> 
> the console is "20". Correct!
> 
> if we comment this line f:= @twenty;
> Uncaught TypeError: $mod.f is not a function // this is correct.

ok

 
> ==================================================
> Test VI
> ==================================================
> type
>    Vec2  =  array  [0..1]  of  Float;
> 
> type
>    TVec2Helper  =  type helper  for  Vec2
>      function  Add(const  v:  Vec2):  Vec2;  overload;
>      function  Add(const  f:  Float):  Vec2;  overload;
>    end;
> 
> implementation
> 
> { TVec2Helper }
> function  TVec2Helper.Add(const  v  :  Vec2):  Vec2;
> begin
>   Result:=[Self[0]+v[0],  Self[1]+v[1]];
> end;
> 
> function  TVec2Helper.Add(const  f  :  Float):  Vec2;
> begin
>   Result:=[Self[0]+f,  Self[1]+f];
> end;   
> 
> (...)
> var
>   v1:  Vec2  =  (1,  2);
>   v2:  Vec2  =  (10,  20);
>   v: array of  Float;
> 
> begin
>   v1 :=  [1,  2];
>   v2 :=  [10,  20];
> 
>   //v  :=  v1  +  v2;    // Error: Operator is not overloaded:
> "Unit3.Vec2"
> + "Unit3.Vec2"

The array operator + works only for dynamic arrays, not static arrays.

 
>   v:=[v1[0]+v2[0],  v1[1]+v2[1]];
> 
>   WriteLn(v[0]); // 11
>   WriteLn(v[1]); // 22
> 
> In this test, I'd like to test helper methods with operator
> overloading, but it's not supported yet.

True.


> ==================================================
> 
> ==================================================
> Test VII - helper class to TJSObject
> ==================================================
> { TJSValueHelper }
> 
> TJSValueHelper = type helper for TJSObject
>   public
>     function valid(ref: JSValue): boolean; assembler;
> end;
> 
> var
>   this: TJSObject; external name 'this';
> 
> implementation
> 
> { TJSValueHelper }
> function TJSValueHelper.valid(ref: JSValue): boolean; assembler;
> asm
>   return !( (ref == undefined) || (ref == null) );
> end;  
> 
> (...)
> var
> dt : TDateTime;
> 
> begin
>   dt := TDateTime(now);
>   if this.valid(dt) then
>     console.log(DateTimetoStr(dt)) else  // 2019-03-03 18:16:49
>       dt := 0;
>       console.log(dt);  
> 
> I think this very useful function!  and it works for me.

Why not use "isObject(dt)" or "if jsvalue(dt) then"?

 
> **Note: I think we have a bug here at "dt: TDateTime" initialization!
> for instance: If I comment
> 
>  //dt := TDateTime(now);

pas2js initializes all variables. doubles with 0.0.


>   if this.valid(dt) then
>     console.log(DateTimetoStr(dt)) else  
>     //dt := 0;
>     console.log(dt); 
> 
> the console outputs "1899-12-30" - I think the correct is "0"

Looks correct to me. It is FPC compatible.

 
> ==================================================
> Test VIII - helper for array of record
> ==================================================
> type
>   TFunction = function: TJSPromise;
> 
> type
>   TModelClassRegistration = record
>     TableName: string;
>     CreateFunc: TFunction;
>   end;
> 
> type
>   TModelClassRegistrations = array of TModelClassRegistration;
> 
>   { TTestHelper }
>   TTestHelper = type helper for TModelClassRegistrations
>     procedure Add(rec: TModelClassRegistration);
>   end;
> 
> implementation
> 
> { TTestHelper }
> procedure TTestHelper.Add(rec: TModelClassRegistration);
> begin
>   TJSArray(Self).push(rec);
> end;
> 
> (...)
> 
> function CreateServer: TJSPromise;
> begin
> result:=
>   TJSPromise.New(
>   procedure (resolve, reject: TJSPromiseResolver)
>   begin
>     console.log('test');
>   end);
> end;   
> 
> var
>   rec: TModelClassRegistration;
>   RegisteredModelClasses: TModelClassRegistrations;
>   i: integer;                  
> 
> begin
>   rec.TableName := 'devil_report';
>   rec.CreateFunc := @CreateServer;
>   RegisteredModelClasses.Add(rec);
>   rec.TableName:= 'flowers_report';
>   rec.CreateFunc := @CreateServer;
>   RegisteredModelClasses.Add(rec);
>   rec.TableName := 'cards_report';
>   rec.CreateFunc := @CreateServer;
>   RegisteredModelClasses.Add(rec);
> 
>   for i:= Low(RegisteredModelClasses) to High(RegisteredModelClasses)
> do console.log(RegisteredModelClasses[i])
> 
> We have the expected object output.

Nice.

Thanks for testing.

Mattias


More information about the Pas2js mailing list