nielsAD / lape

Scripting engine with Pascal-like syntax for FPC and Delphi
112 stars 26 forks source link

Questions #173

Closed Vizit0r closed 1 year ago

Vizit0r commented 2 years ago

1) FFI usage. a) found in Simba (and using, thanks) "native" method for callbacks from Delphi b) in FFI tests found example of usage "Natify". c) found nothing about "Lapify". Can you please give any simplest example?

BTW, a bit later will commit changes in FFI for Delphi usage.

2) stupid question, but it makes me crazy. What is a correct way to add items to "Lape_Keywords"? If i add any keyword, it raise exception like "Duplicate hash bucket with "constref" and "uses" " in TLapeTokenizerBase.Create. Do i make something incorrect? in EParserToken added tk_kw_Uses, in Lape_Keywords - (Keyword: 'USES'; Token: tk_kw_Uses),

3) I've separated lpinterpreterDebug and lpcompilerDebug units for Lape debug purposes (breakpoints, step into, variables values, etc). Do you need it?

4) Is it possible to add Lape compiler option like "lcoAllowDuplicates" for name duplication?

ollydev commented 2 years ago

lcoAllowDuplicates I don't think so, I think just removing the duplicate check will cause lots of problems further on. If you're trying to add units I don't think that's the correct way to go about it.


Yes please, your debug units will be a useful resource!


Lape keyword stuff is perfect hashing - basically none of the keywords can fall into the same hash bucket. Google "perfect hashing parser". Though it instantly has collisons when adding a new keyword - I will make this better today. The "current fix" would be messing around with LapeHash and bucket size (256 for keywords right now). But as said abouve, I will improve this later today.


Lapify turns a native function into something that can be called in Lape:

External uses it internally.

// Import function from the Windows API (user32.dll)
// Syntax similar to Pascal Script
function MyMessageBox(hWnd: Integer; lpText, lpCaption: String; uType: Cardinal): Integer; external 'MessageBoxA@user32.dll stdcall';

type
  // Lape function aka a script function
  TMessageBoxLape = function(hWnd: Integer; lpText, lpCaption: String; uType: Cardinal): Integer;

  // Native function aka a system function
  TMessageBoxNative = native(TMessageBoxLape, ffi_stdcall);

var
  LapeFun: TMessageBoxLape;
  NativeFun: TMessageBoxNative;
  Lib: TLibHandle;
begin
  // Automatically imported function call!
  MyMessageBox(0, 'Hello world', 'MessageBox', 0);

  Lib := LoadLibrary('user32.dll');
  NativeFun := GetProcAddress(Lib, 'MessageBoxA');

  // We cannot call NativeFun, as it is a system function
  // NativeFun() fails

  // Convert it to a Lape function with Lapify
  LapeFun := Lapify(NativeFun);
  LapeFun(0, 'Hello Again!', 'Lape!', 0);

  FreeLibrary(Lib);
end.
Vizit0r commented 2 years ago

lcoAllowDuplicates I don't think so, I think just removing the duplicate check will cause lots of problems further on.

i know thats not good. I know, that in normal practice this should never been used.

If you're trying to add units I don't think that's the correct way to go about it.

But my users has tons of already written scripts for PascalScript, and a big part of its has overriden names in content. Right now, i write some support methods to figure this out, but not sure its cover all possible situations. Later on i will do that like branch in my fork repo to show you.

Yes please, your debug units will be a useful resource!

Same, will make it as branch.

about Lape_Keywords well understood.

BTW, built-in uses support is not in plan?


about Lapify - got it, thank you, now its clear for me.

P.S. Argh, GitHub reply form sometimes makes wonders with text format...

ollydev commented 2 years ago

What is overriden names in content ?

procedure foo;
begin
  WriteLn('I''m not here anymore');
end;

procedure foo;
begin
  WriteLn('Now I will be called');
end;

?

Vizit0r commented 2 years ago

the last one. Same, local vars-methods names have priority in comparision to global-external.

Vizit0r commented 2 years ago

https://github.com/Vizit0r/lape/commit/d79ee61011aa88c74aa95e7f2c1a25413eda5ff2 my way to handle name duplications. As you can see, i handle not all of them, only partially

ollydev commented 2 years ago

Oh, so you don't want to allow local declarations under the same name as a global?

procedure hello;
begin
  WriteLn('global hello');
end;

procedure test;

  procedure hello; // error here?
  begin
    writeln('hello in test');
  end;

begin
  hello();
end

begin
  test(); 
end;
Vizit0r commented 2 years ago

its very old code, i have intention to rewrite it for catching all dupe situation, send to handler code of current duplication loc, and handle it as case... of .... in handler. In my opinion, if some coder want to enable this option - he should really understand, how and where he will use this. So, he need not only to switch on lcoAllowDuplicates, but also write additional handler, where select, which names he allow to dupe (or all of them, if he crazy).

P.S. In PascalScript also allowed not all names duplication. F.e. types names duplication prohibited, etc.

ollydev commented 2 years ago

I misunderstood initially. This should be easy to add. Will get onto it.

Vizit0r commented 2 years ago

https://github.com/Vizit0r/lape/commit/0d9e967639683b6f51d4a3e792b965d8081e7e5a

lpcompilerDebug + lpinterpreterDebug.

lpinterpreterDebug calling example:

function TBaseLapeScriptThread.TryToExec(InitialVarStack: lptypes.TByteArray = nil; InitialJump: TCodePos = 0) : Boolean;
var i : Integer;
    pDoContinue: TDebugRunState;
    includeitem : String;
begin
Result := True;
  try
    if not Assigned(LapeExec) then
      LapeExec := TLapeDebugExec.Create();
    LapeExec.Reset;
    pDoContinue := dTrue;
    LapeExec.OnLineInfo := LapeScriptDebuggerLineInfo;
    LapeExec.Init(PPCode, CodeLen,pDoContinue);
    LapeExec.FilesList.Add(ScriptPath);
    if fDebugMode <> dt_DebugOff then
    begin
      if not Assigned(fBPList) then
        fBPList := TDictionary<Integer,String>.Create;
      LapeExec.OnBP := LapeScriptDebuggerBreakpoint;
      LapeExec.OnIdleCall := LapeScriptDebuggerIdle;
      LapeExec.DebugMode := True;
      case fDebugMode of
        dt_StepOver,
        dt_TraceInto: pDoContinue := dStepInto;
      end;
      if (fBPList.Count > 0) then
        for I in fBPList.Keys do
          LapeExec.BPList.Add(Cardinal(i)+1);
      if IncludesList.Count > 0 then
        for includeitem in IncludesList do
          LapeExec.FilesList.Add(includeitem);
    end;
    LapeExec.ExecRunState := pDoContinue;
    LapeExec.Run;
  except
    on e: lpException do
    begin
      OutputWithParams(e.DocPos.Line, e.DocPos.Col, e.Error, 'Runtime', e.DocPos.FileName);
     Result := False;
    end;
    on e: Exception do
    begin
      OutputWithParams(0, 0, e.message, 'Runtime', '');
      Result := False;
    end;

  end;
end;
Vizit0r commented 2 years ago

https://github.com/Vizit0r/lape/commit/e06ed667af87a6f9eced3272d0be138d796ca480

FFI Delphi fixes.

OOps, line 664 in lpffi.pas should be removed, forgot it :)

Vizit0r commented 2 years ago

BTW, built-in uses support is not in plan?

^^

Vizit0r commented 2 years ago

and one more question: which version of libffi.dll now included in Lape?

I try to use newest one, but its failed. Looks like format changed a little bit.

ollydev commented 2 years ago

3.3, I think? libffi6 on linux/macos.

unit/uses support? There are no plans for anything, if it happens it happens. it would be nice for namespaces though.

ollydev commented 2 years ago

Ok, adding to Lape_Keywords should be fixed.

Vizit0r commented 2 years ago

unit/uses support? There are no plans for anything, if it happens it happens. it would be nice for namespaces though.

yes, units without namespaces its just sort of includes.


Ok, adding to Lape_Keywords should be fixed.

it works atm, nice.

Vizit0r commented 2 years ago

Couple of helper for most popular types, maybe will be useful.

uLapeImport_Methods.pas

Vizit0r commented 2 years ago

i have a error (due to const param)

Program New;
var aaa : Array of byte;
    i : Integer;
procedure ReverseWorks( const works:Array of byte );
var index, listLength:Integer;
    temp:byte;
begin
    listLength := Length( works );

    for index := 0 to ( listLength div 2 ) - 1 do
    begin
        temp := works[ index ];
        works[ index ] := works[ listLength - index - 1 ];
        works[ listLength - index - 1 ] := temp;
    end;
end;

begin
  SetLength(aaa, 10);
  for i := 0 to 9 do
    aaa[i] := i + 1;
  ReverseWorks(aaa);
end.

Error is:

15:28:22:698 Compiler: [Compile] (D:\Stealth\Output\Scripts\telegram2.sc at 13:14): Target cannot be assigned to

Its absolutely uniformative, isnt it?

Vizit0r commented 2 years ago

when you make few overloads like

  for i := GetTypeData(TypeInfo(TPacketEvent)).MinValue to GetTypeData(TypeInfo(TPacketEvent)).MaxValue do
  begin
    EnumName := GetEnumName(TypeInfo(TPacketEvent), i);
    if Assigned(Compiler.getGlobalType('T'+EnumName+'CallBack')) then
    Compiler.addGlobalMethod('procedure SetEventProc(Eventname : TPacketEvent;ProcAddress : '
         +'T'+EnumName+'CallBack); overload;',@LapeWrap_SetEventProcByAddress_ByRef,Self);
  end;
+
    Compiler.addGlobalMethod('procedure SetEventProc(Eventname : TPacketEvent;ProcAddress : '
         +'Pointer); overload;',@LapeWrap_SetEventProcByAddress_Ptr,Self);

where

    AddGlobalType('procedure (ItemID:Cardinal)', 'TEvItemInfoCallBack', FFI_DEFAULT_ABI);
    AddGlobalType('procedure (Text, SenderName : String; SenderID:Cardinal)', 'TEvSpeechCallBack', FFI_DEFAULT_ABI);
    AddGlobalType('procedure (ItemID, ContainerID:Cardinal)', 'TEvAddItemToContainerCallBack', FFI_DEFAULT_ABI);
   //etc...

in TLapeType_OverloadedMethod.getMethodIndex the overloaded method with Pointer as 2nd param has ALWAYS less weight, than casted methods. But thats incorrect, pointer should have bigger weight. Otherwise instead of casted methods, it will all the times use only pointer.

Vizit0r commented 2 years ago

3.3, I think? libffi6 on linux/macos.

really 3.2 btw, 3.0, 3.1 and 3.2 are called libffi6. But 3.0 and 3.1 didnt work with Lape, only 3.2

Vizit0r commented 2 years ago

for last commit need 2 changes:

1) lptypes.pas 1286 T := GetMem(ElSize); change to GetMem(T, ElSize);

2) lpvartypes.pas 709 function getTypeArray(Arr: array of TLapeType): TLapeTypeArray; {$IFDEF Lape_Inline}inline;{$ENDIF} change to function getTypeArray(Arr: array of TLapeType): TLapeTypeArray; {$IF DEFINED (Lape_Inline) AND DEFINED (FPC)}inline;{$ENDIF}

(Delphi cant inline methods with open arrays as param).

Vizit0r commented 2 years ago

can you add default property for object ( props with arrays)? right now obj[3] takes obj Pointer address with shift 3. User has no errors about missing default property, but has error about "Can't assign "Pointer" to "UnicodeString"", which have no sense for him.

Vizit0r commented 2 years ago

bug, can be reproduced very easy:

function Test1: array of String;
begin
  Result := ['aaa','bbb','ccc'];
end;
procedure CommaText(const Value: string);  overload;
begin
  WriteLn('SetCommaText: ' + Value);
end;

function CommaText: string; overload;
begin
  WriteLn('GetCommaText called');
end;

var s: String;

begin
 s := Test1[1];
 CommaText := s; //thats OK
 //CommaText := Test1[1]; //thats error!
end;

error is: Don't know which overloaded method to call with params (unknown)

if make it little bit different:

function Test1: array of String;
begin
  Result := ['aaa','bbb','ccc'];
end;
procedure CommaText(const Value: string);  
begin
  WriteLn('SetCommaText: ' + Value);
end;

var s: String;

begin
 s := Test1[1];
 CommaText := s; //thats OK
 CommaText(Test1[1]); /thats OK too
 //CommaText := Test1[1]; //thats error!
end;

error also changing: Operator "Index" not compatible with types "function():array of UnicodeString" and "Int32"