{
  20050712 jgv
  add parsing of untyped parameter (with warning)
  fix parsing of overloaded procedure/function (ie not method)
  add parsing of class method member
  add parsing of "array of const"
  fix parsing of constant expression for chr and ord case.
  add parsing of reintroduced property specifier (stored)
}

unit ParserU;

{$MODE Delphi}

{version: 20041219}

interface
  uses uPSUtils, SysUtils, StrUtils, ParserUtils, Classes, IniFiles;

type
  TProcAttr = set of (PublicProc, IsDone, IsHelper);

(*----------------------------------------------------------------------------*)
  TProcList = class(TStringList)
  public
    ProcAttr : TProcAttr;
    Name     : string;
  end;

  TWriteln = procedure(const S: string) of object;
  TReadln = procedure(var S: string; const Promote, Caption: string) of object;

(*----------------------------------------------------------------------------*)
  TPasToken = record
    ID: TPSPasToken;
    Data: string;
    Org : String;
    row, col: integer;
  end;

  TProcDeclOptions = set of (IsMethod, IsPointer);
  TProcDeclInfo    = set of (IsVirtual, IsAbstract, IsConstructor, IsDestructor, IsFunction, IsCallHelper);

  // CompileTime RunTime
  TTimeMode = (CompileTime, RunTime);

  TClassInheritance = class
  public
    FClassParent: string;
    FClassName: string;
  end;

(*----------------------------------------------------------------------------*)
  TUnitParser = class
  private
    fWriteln: TWriteln;
    fReadln: TReadln;
    FUnitPrefix,
    FCompPage,
    fCompPrefix : String;
    FSingleUnit: Boolean;
    FCompileTimeFunctions : Boolean;
    FAfterInterfaceDeclaration: string;
    FAutoRenameOverloadedMethods: Boolean;
    FClassInheritances: TList; // TODO free this and its children
    procedure AddClassInheritance(const classParent, className: string);
    function GetChildClass(const className: string): string;
    procedure SetWriteln(aWriteln: TWriteln);
    procedure SetReadln(aReadln: TReadln);
  private
    fParser: TPsPascalParser;
    fToken, fPrevToken: TPasToken;
//    fprevOrgToken: string;
    Ini: TIniFile;
    FRenamingHelper: Integer;
  private
    LastTokens: array of TPasToken;
    FTail, FHead, TokenHistoryCount, TokenHistoryLength: integer;
    procedure AddToTokenHistory(const aToken: TPasToken);
    function RemoveFromTokenHistory(var aToken: TPasToken): boolean;
  private
    property TokenID: TPsPasToken read fToken.ID;
    property PrevTokenID: TPsPasToken read fPrevToken.ID;
    property TokenRow: integer read fToken.Row;
    property TokenCol: integer read fToken.Col;
   // Property PrevTokenPos : integer read fPrevToken.Pos;
    property Token: string read fToken.data;
    property OrgToken: string read fToken.Org;
//    property PrevToken: string read fPrevToken.data;
    property PrevOrgToken: string read fPrevToken.Org;

    procedure SetToken(aTokenID: TPsPasToken; aToken: string; aTokenRow, aTokenCol: integer);
    procedure NextToken;

    procedure skipToSemicolon; //Birb
    function IfMatch(atoken: TPsPasToken): boolean;
    procedure Match(atoken: TPsPasToken; err: string = '');
  private
    FOutUnitList            : TStringList;    // teo

    FRunTimeProcList        : TStringList;
    FCompileTimeProcList    : TStringList;
    fCurrentDTProc          : TProcList;
    fCurrentRTProc          : TProcList;

    fRunTimeUnitList        : TStringList;
    fRunTimeUnitListImp     : TStringList;
    fCompileTimeUnitList    : TStringList;
    fCompileTimeUnitListImp : TStringList;

    RunTimeProcType: set of (InterfaceImporter, ClassImporter, RoutineImporter); //Birb
    procedure AddRequiredUnit(const UnitName: string;
      TimeMode: TTimeMode;
      InterfaceSection: boolean);
    function RegisterProc(const ProcName: string; TimeMode: TTimeMode; Attr: TProcAttr): TProcList;
  //  Procedure AddVarToProc(const ProcName : string;CompileTime : TCompileTime;
  //                         const VarString : string);
    procedure FinishProcs;
  private
    procedure StartParse;
    procedure FinishParse;
    procedure FinishParseSingleUnit;   // teo

    procedure ParseUnitHeader;
    procedure ParseGlobalDeclarations;

    function  GetConstantType: string;
    function  GetAsString(const ConstType, ConstValue: string): string;

    function  ParseConstantExpression(var ExpType: string): string;
    procedure ParseConstants;
    procedure ParseVariables;
    procedure ParseRoutines;
    procedure ParseTypes;
    // parses a type delcaration
    procedure ParseType(aTokenID: TPsPasToken; const TypeName: string;
      var TypeDescriptor: string; var CheckSemiColon: boolean);
    // helper method which parses a class or interface definition //Birb
    procedure ParseClassOrInterfaceDef(const aClassName: string; const isInterface: boolean); //Birb
    // helper method which parses a interface definition
    procedure ParseInterfaceDef(const aInterfaceName: string);
    // helper method which parses a class definition
    procedure ParseClassDef(const aClassName: string);
    // helper method which parses a routine decl (after the procedure name)
    function ParseProcDecl(var ProcName, decl, CallingConvention: string;
      Options: TProcDeclOptions; OwnerClass:String=''): TProcDeclInfo;
  public
    constructor Create(const IniFilename: string; aTokenHistoryLength: Integer = 5);
    destructor Destroy; override;
  public
    // output
    Unitname, OutputRT, OutputDT: string;
    // Controls went registering classes, at design time
    // the actual class type is used
    WriteDefines, UseUnitAtDT: boolean;
    // Class this to parse a unit
    procedure ParseUnit(const Input: string);
    procedure ParserError(Parser: TObject; Kind: TPSParserErrorKind);

    function  UnitNameRT: string;
    function  UnitNameCT: string;
    function  UnitNameCmp: string;
    procedure SaveToPath(const Path: string);

    property Writeln: TWriteln read fWriteln write SetWriteln;
    property Readln: TReadln read fReadln write setReadln;

    property SingleUnit : Boolean read FSingleUnit write FSingleUnit;  // teo
    property UnitPrefix : string read FUnitPrefix write FUnitPrefix;   // teo
    property CompPage   : String read FCompPage write FCompPage; // Niels
    property CompPrefix : String read FCompPrefix write FCompPrefix; // Niels
    property AfterInterfaceDeclaration: string read FAfterInterfaceDeclaration write FAfterInterfaceDeclaration;
    property AutoRenameOverloadedMethods: Boolean read FAutoRenameOverloadedMethods write FAutoRenameOverloadedMethods;


    property OutUnitList : TStringList read FOutUnitList;              // teo
  end; {TUnitParser}

const
  MaxSearchCount = 100;

implementation

const
 STR_IINTERFACE='IUNKNOWN';

procedure DefReadln(var S: string; const Promote, Caption: string);
begin
  S := '';
end; {DefReadln}

procedure DefWriteln(const S: string);
begin
end; {DefWriteln}

// Some method declaratinos may be very long.
// Delphi can't compile these, so we need to spit them manually.
function SplitIntoLines(AString: string): string;
const
  cSplitPosition: Byte = 200;
begin
  Result := '';
  while (Length(AString) > cSplitPosition) do
  begin
    Result := Result + LeftStr(AString, cSplitPosition) +
      ''' +' + #13 + #10  + '      ''';
    Delete(AString, 1, cSplitPosition);
  end;
  Result := Result + AString;
end;

constructor TUnitParser.Create(const IniFilename: string; aTokenHistoryLength: Integer = 5);
begin
  inherited create;
  FClassInheritances := TList.Create;
  FUnitPrefix := 'uPS';
  FCompPage   := 'Pascal Script';
  FCompPrefix := 'TPSImport';
  FCompileTimeFunctions := false;
  Writeln := nil;
  Readln := nil;
  Ini := TIniFile.Create(IniFilename);
  //Ini.FlagDropApostrophes := True;
  //ini.FlagDropCommentLines := True;
  //Ini.FlagTrimRight := True;
  //Ini.FlagFilterOutInvalid := True;

  fParser := TPSPascalParser.create;
  TokenHistoryLength := aTokenHistoryLength;
  if TokenHistoryLength > 0 then
    Setlength(LastTokens, TokenHistoryLength);

  FOutUnitList := TStringList.Create;
  FSingleUnit := True;
end; {Create}

destructor TUnitParser.Destroy;
begin
  FOutUnitList.Free;
  fParser.free;
  ini.free;
  inherited;
end; {Destroy}

procedure TUnitParser.AddClassInheritance(const classParent, className: string);
var
  ci: TClassInheritance;
begin
  ci := TClassInheritance.Create;
  ci.FClassParent := classParent;
  ci.FClassName := className;
  FClassInheritances.Add(ci);
end;

function TUnitParser.GetChildClass(const className: string): string;
var
  i: integer;
  ci: TClassInheritance;
begin
  Result := '';
  for i := 0 to FClassInheritances.Count - 1 do
  begin
    ci := TClassInheritance(FClassInheritances[i]);
    if ci.FClassParent = className then
    begin
      Result := ci.FClassName;
      Exit;
    end;
  end;
end;

procedure TUnitParser.SetWriteln(aWriteln: TWriteln);
begin
  if assigned(aWriteln) then
    fWriteln := aWriteln
//else
//  fWriteln := DefWriteln;
end; {SetWriteln}

procedure TUnitParser.SetReadln(aReadln: TReadln);
begin
  if assigned(aReadln) then
    fReadln := aReadln
//else
//  fReadln := DefReadln;
end; {SetWriteln}

procedure TUnitParser.AddToTokenHistory(const aToken: TPasToken);
begin
  if TokenHistoryLength <= 0 then exit;
  LastTokens[FTail] := aToken;
  FTail := (FTail + 1) mod TokenHistoryLength;
  if FTail = FHead then
    FHead := (FHead + 1) mod TokenHistoryLength
  else
    inc(TokenHistoryCount);
end; {AddToTokenHistory}

function TUnitParser.RemoveFromTokenHistory(var aToken: TPasToken): boolean;
begin
  Result := (TokenHistoryLength > 0) and (TokenHistoryCount <> 0);
  if result then
  begin
    aToken := LastTokens[FHead];
    FHead := (FHead + 1) mod TokenHistoryLength;
    dec(TokenHistoryCount);
  end;
end; {RemoveFromTokenHistory}
(*----------------------------------------------------------------------------*)
procedure TUnitParser.SetToken(aTokenID: TPSPasToken; aToken: string;  aTokenRow, aTokenCol: integer);
begin
  fToken.ID := aTokenID;
  fToken.data := Uppercase(aToken);
  fToken.Org  := aToken;
  fToken.row := aTokenRow;
  fToken.col := aTokenCol;
  AddToTokenHistory(fToken);
end; {InsertToken}

procedure TUnitParser.NextToken;
begin
  fPrevToken := fToken;
//  fprevOrgToken := fparser.OriginalToken;
  fParser.next;
  fToken.ID := fParser.CurrTokenID;
  fToken.data := fParser.GetToken;
  fToken.Org  := fParser.OriginalToken;
  fToken.row := fParser.Row;
  fToken.col := fParser.Col;
  AddToTokenHistory(fToken);
end; {NextToken}

// -----------------------------------------------------------------------------

procedure TUnitParser.skipToSemicolon; //Birb
begin
  while not ifmatch(CSTI_SemiColon) do //assuming EOF checks aren't needed since everywhere in this code it's done similarly (maybe parser throws exception at EOF so that loops similar to this one don't go on forever)
    NextToken;
end;

function TUnitParser.Ifmatch(atoken: TPSPasToken): boolean;
begin
  Result := TokenID = atoken;
  if result then
    NextToken;
end; {Ifmatch}

procedure TUnitParser.Match(atoken: TPSPasToken; err: string = '');
var
  Errormsg: string;
  TokenList: string;
  OldToken: TPasToken;
begin
  if not Ifmatch(atoken) then
  begin
    if err = '' then
      err := GetTokenName(atoken);
    Errormsg := 'Expecting Token ''' + err + ''' but ';
    case TokenID of
      CSTI_Identifier: Errormsg := Errormsg + 'Identifier ''' + Token;
      CSTI_Integer: Errormsg := Errormsg + 'Integer number ''' + Token;
      CSTI_Real: Errormsg := Errormsg + 'Floatpoint number ''' + Token;
      CSTI_String: Errormsg := Errormsg + 'String ''' + Token;
      CSTI_Char: Errormsg := Errormsg + 'Character ''' + Token;
      CSTI_HexInt: Errormsg := Errormsg + 'Hexadecimal number ''' + Token;
    else Errormsg := Errormsg + 'token ''' + GetTokenName(TokenID);
    end;
  // build the list of tokens
    TokenList := '';
    while RemoveFromTokenHistory(OldToken) do
    begin
      if OldToken.ID in [CSTI_Identifier, CSTI_Integer, CSTI_Real,
        CSTI_String, CSTI_Char, CSTI_HexInt] then
        TokenList := TokenList + OldToken.Data + ' '
      else
        TokenList := TokenList + GetTokenName(OldToken.ID) + ' ';
    end;
    RaiseError(Errormsg + ''' found' + NewLine + 'Previous tokens : ''' + TokenList + '''', TokenRow, TokenCol);
  end;
end; {Match}

// -----------------------------------------------------------------------------

procedure TUnitParser.ParseUnit(const Input: string);
begin
  UnitName := '';
  FOutUnitList.Clear;
  fparser.OnParserError := ParserError;
  fParser.SetText(Input);
  try
    StartParse;
    ParseUnitHeader;
    ParseGlobalDeclarations;
  finally
    case FSingleUnit of
      False : FinishParse;
      True  : FinishParseSingleUnit;
    end;
  end;
end; {ParseUnit}
(*----------------------------------------------------------------------------*)
procedure TUnitParser.AddRequiredUnit(const UnitName: string; TimeMode: TTimeMode; InterfaceSection: boolean);
var
  Unitlist : TStringList;  { ref }
  Index    : integer;
begin
// choice the correct list to Add it to
  Unitlist := nil;
  case TimeMode of
    CompileTime  : if InterfaceSection then
                     Unitlist := fCompileTimeUnitList
                   else Unitlist := fCompileTimeUnitListImp;
    RunTime      : if InterfaceSection then
                     Unitlist := fRunTimeUnitList
                   else Unitlist := fRunTimeUnitListImp;
    else         RaiseError('Unable to deterimine which used unit list' + ' to Add the unit ''' + UnitName + ''' to', TokenRow, TokenCol);
  end;
  Index := Unitlist.Indexof(UnitName);
  if Index = -1 then
    Unitlist.Add(UnitName)
end; {AddRequiredUnit}
(*----------------------------------------------------------------------------*)
function TUnitParser.RegisterProc(const ProcName: string; TimeMode: TTimeMode; Attr: TProcAttr): TProcList;
var
  proclist: TStringList;
  Index: integer;
begin
  if ProcName = '' then
    RaiseError('Invalid procedure name', TokenRow, TokenCol);

  if TimeMode = CompileTime then
    proclist := fCompileTimeproclist
  else proclist := fRunTimeProclist;
  
  assert(proclist <> nil);
  Index := proclist.IndexOf(ProcName);
  if Index = -1 then
  begin
    Result := TProcList.create;
    try
      Result.Add(ProcName);
      if not (IsHelper in Attr) then
        Result.Add('begin');
      Result.ProcAttr := Attr;
      proclist.AddObject(ProcName, Result);
    except
      Result.free;
      raise
    end;
  end
  else
    Result := proclist.Objects[Index] as TProcList;
end; {RegisterProc}
(*----------------------------------------------------------------------------*)
procedure TUnitParser.FinishProcs;
var
  Index: integer;
  obj: TObject;
begin
  if FRunTimeProcList <> nil then
    for Index := FRunTimeProcList.count - 1 downto 0 do
    begin
      obj := FRunTimeProcList.Objects[Index];
      if (obj is TProcList) and
        not (IsHelper in TProcList(obj).ProcAttr) then
        TProcList(obj).Add('end;');
    end;
  if FCompileTimeProcList <> nil then
    for Index := FCompileTimeProcList.count - 1 downto 0 do
    begin
      obj := FCompileTimeProcList.Objects[Index];
      if (obj is TProcList) and
        not (IsHelper in TProcList(obj).ProcAttr) then
        TProcList(obj).Add('end;');
    end;
end; {FinishProcs}

(*
Procedure TUnitParser.AddVarToProc(const ProcName : string;
                                   CompileTime : TCompileTime;
                                   const VarString : string);
var
  proc : TStringList;
begin
proc := RegisterProc(ProcName,CompileTime,false);
If Proc = nil then
  RaiseError('Procedure :"'+ProcName+'" can not be found');
If fastUppercase(Proc[1]) = 'VAR' then
  Proc.Insert(2,VarString)
else
  Proc.Insert(1,'var'+newline+VarString)
end; {AddVarToProc}
*)
(*----------------------------------------------------------------------------*)
procedure TUnitParser.StartParse;
begin
  SetToken(fParser.CurrTokenID, fParser.OriginalToken, fParser.Row, FParser.Col);
  OutputDT := '';
  OutputRT := '';

  FRunTimeProcList := TStringList.create;
  FCompileTimeProcList := TStringList.create;

  fRunTimeUnitList := TStringList.create;
  fRunTimeUnitListImp := TStringList.create;
  fCompileTimeUnitList := TStringList.create;
  fCompileTimeUnitListImp := TStringList.create;
end; {StartParse}
(*----------------------------------------------------------------------------*)
function  TUnitParser.UnitNameRT: string;
begin
  Result := Format('%sR_%s.pas', [FUnitPrefix, UnitName]);
end;
(*----------------------------------------------------------------------------*)
function  TUnitParser.UnitNameCT: string;
begin
  Result := Format('%sC_%s.pas', [FUnitPrefix, UnitName]);
end;

function TUnitParser.UnitNameCmp: string;
begin
  Result := Format('%sI_%s.pas', [FUnitPrefix, UnitName]);
end;


(*----------------------------------------------------------------------------*)
procedure TUnitParser.SaveToPath(const Path: string);
var
  List   : TStringList;
begin
  if SingleUnit then
  begin
    FOutUnitList.SaveToFile(Path + UnitNameCmp);
  end else begin
    List := TStringList.Create;
    try
      List.Text := OutputRT;
      List.SaveToFile(Path + UnitnameRT);

      List.Text := OutputDT;
      List.SaveToFile(Path + UnitnameCT);
    finally
      List.Free;
    end;
  end;
end;
(*----------------------------------------------------------------------------*)
procedure TUnitParser.FinishParse;
var
  OutPut  : TStringList;
  obj         : TObject;
  Index       : integer;
  S           : string;
begin
  try
    FinishProcs;

    {===================================================================================}
    // write out the design time unit
    if FCompileTimeProcList <> nil then
    begin
      OutPut := TStringList.create;
      try
        // insert the front of the text body   //FUnitPrefix
        //OutPutList.Add('unit ifpii_' + UnitName + ';');
        OutPut.Add('unit ' + ChangeFileExt(UnitNameCT, '') + ';');
        OutPut.Add(GetLicence);
//        OutPut.Add('{$I PascalScript.inc}');
        OutPut.Add('interface');
        OutPut.Add(FAfterInterfaceDeclaration);
        OutPut.Add(GetUsedUnitList(fCompileTimeUnitList) + Newline);

        for Index := FCompileTimeProcList.count - 1 downto 0 do
        begin
          obj := FCompileTimeProcList.objects[Index];
          if (obj is TProcList) and
            (PublicProc in TProcList(obj).ProcAttr) then
            OutPut.Add(FCompileTimeProcList[Index]);
        end;

        OutPut.Add('implementation');
        // insert the Designtime unit importer into the used unit list
        S := GetUsedUnitList(fCompileTimeUnitListImp);
        if S <> '' then
        begin
          Delete(S, length(S), 1);
          OutPut.Add(S);
(*          if WriteDefines then
          begin
            OutPut.Add('{$IFDEF USEIMPORTER}');
            OutPut.Add('  ,CIImporterU');
            OutPut.Add('{$ENDIF};');
          end;*)
          OutPut.Add(';');
        end else  begin
(*          if WriteDefines then
          begin
            OutPut.Add('{$IFDEF USEIMPORTER}');
            OutPut.Add('  uses CIImporterU;');
            OutPut.Add('{$ENDIF}');
          end;*)
        end;

        //OutPut.Add('');
        //Output.Add('const IID__DUMMY: TGUID = ''{00000000-0000-0000-0000-000000000000}'';'); //Birb (!!!could set some attribute to avoid spitting that out when not needed)
        //Output.Add('');

        // reinsert the main text body
        for Index := FCompileTimeProcList.count - 1 downto 0 do
        begin
          obj := FCompileTimeProcList.objects[Index];
          if (obj is TProcList) and
            (IsHelper in TProcList(obj).ProcAttr) then
            OutPut.Add(TStringList(obj).text);
        end;

        for Index := FCompileTimeProcList.count - 1 downto 0 do
        begin
          obj := FCompileTimeProcList.objects[Index];
          if (obj is TProcList) and
            (PublicProc in TProcList(obj).ProcAttr) then
            OutPut.Add(TStringList(obj).text);
        end;

        // insert the Runtime unit importer code into the end of the unit
(*        if WriteDefines then
        begin
          OutPut.Add('{$IFDEF USEIMPORTER}');
          OutPut.Add('initialization');
          OutPut.Add('CIImporter.AddCallBack(@SIRegister_' + UnitName + ',PT_ClassImport);');
          OutPut.Add('{$ENDIF}');
        end;*)
        OutPut.Add('end.');

      finally
        if OutPut <> nil then
          OutputDT := OutPut.text;
        OutPut.free;
      end;
    end;



   {===================================================================================}
   // write out the run time import unit
    if FRunTimeProcList <> nil then
    begin
      OutPut := TStringList.create;
      try
        OutPut.Add('unit ' + ChangeFileExt(UnitNameRT, '') + ';');
        OutPut.Add(GetLicence);
//        OutPut.Add('{$I PascalScript.inc}');
        OutPut.Add('interface');
        OutPut.Add(FAfterInterfaceDeclaration);        
        OutPut.Add(GetUsedUnitList(fRunTimeUnitList) + Newline);
        for Index := FRunTimeProcList.count - 1 downto 0 do
        begin
          obj := FRunTimeProcList.objects[Index];
          if (obj is TProcList) and
            (PublicProc in TProcList(obj).ProcAttr) then
            OutPut.Add(FRunTimeProcList[Index]);
        end;

        OutPut.Add('');
        OutPut.Add('implementation');

        // insert the Runtime unit importer into the used unit list
        S := GetUsedUnitList(fRunTimeUnitListImp);
        if RunTimeProcType <> [] then
        begin
          if S <> '' then
          begin
            Delete(S, length(S), 1);
            OutPut.Add(S);
(*            if WriteDefines then
            begin
              OutPut.Add('{$IFDEF USEIMPORTER}');
              OutPut.Add('  ,RIImporterU');
              OutPut.Add('{$ENDIF};');
            end;*)
            OutPut.Add(';');
          end
          else
          begin
(*            if WriteDefines then
            begin
              OutPut.Add('{$IFDEF USEIMPORTER}');
              OutPut.Add('  uses RIImporterU;');
              OutPut.Add('{$ENDIF}');
            end;*)
          end;
        end
        else OutPut.Add(S);

         // reinsert the main text body --IsHelper
        for Index := FRunTimeProcList.count - 1 downto 0 do
        begin
          obj := FRunTimeProcList.objects[Index];
          if (obj is TProcList) and
            (IsHelper in TProcList(obj).ProcAttr) then
            OutPut.Add(TProcList(obj).text);
        end;

        // reinsert the main text body  --PublicProc
        for Index := FRunTimeProcList.count - 1 downto 0 do
        begin
          obj := FRunTimeProcList.objects[Index];
          if (obj is TProcList) and
            (PublicProc in TProcList(obj).ProcAttr) then
            OutPut.Add(TProcList(obj).text);
        end;

        // Add the ending of the unit
        // insert the Runtime unit importer code into the end of the unit
        if RunTimeProcType <> [] then
        begin
(*          if WriteDefines then
          begin
            OutPut.Add('{$IFDEF USEIMPORTER}');
            OutPut.Add('initialization');
            if RoutineImporter in RunTimeProcType then
              OutPut.Add('RIImporter.AddCallBack(RIRegister_' + UnitName + '_Routines);');
            if ClassImporter in RunTimeProcType then
              OutPut.Add('RIImporter.Invoke(RIRegister_' + UnitName + ');');
            OutPut.Add('{$ENDIF}');
          end;*)
        end;
        OutPut.Add('end.');
      finally
        if OutPut <> nil then
          OutputRT := OutPut.text;
        OutPut.free;
      end;
    end;
  finally

    for Index := FRunTimeProcList.Count - 1 downto 0 do
    begin
      FRunTimeProcList.Objects[Index].Free;
    end;
    FreeAndNil(FRunTimeProcList);
    for Index := FCompileTimeProcList.Count - 1 downto 0 do
    begin
      FCompileTimeProcList.Objects[Index].Free;
    end;

    FreeAndNil(FCompileTimeProcList);
    FreeAndNil(fRunTimeUnitList);
    FreeAndNil(fRunTimeUnitListImp);
    FreeAndNil(fCompileTimeUnitList);
    FreeAndNil(fCompileTimeUnitListImp);
  end;
end; {FinishParse}

(*----------------------------------------------------------------------------*)
procedure TUnitParser.FinishParseSingleUnit;
  {-------------------------------------------}
  procedure ProcessUsesList(List: TStrings);
  var
    i : Integer;
    s: string;
  begin
    // TODO this is just a hack, provide a proper solution
    {$ifndef WINDOWS}
    if List.Count > 0 then
    for i := List.Count - 1 downto 0 do
    begin
      s := LowerCase(List[i]);
      if (s = 'windows') or (s = 'messages') or (s = 'ddeserver') or (s = 'fileassoc') then
        List.Delete(i);
    end;
    {$endif}

    if List.Count > 0 then
    begin
      List[0] := '   ' + List[0];

      for i := 1 to List.Count - 1 do
      begin
        if List[i] <> '' then
          List[i] := '  ,' + List[i];
      end;

      List.Insert(0, 'uses');
      List.Add('  ;')
    end;
  end;
  {-------------------------------------------}
  procedure AddToUsesList(UsesList, CheckList: TStrings; const AUnitName: string);
  var
    i : Integer;
    S : string;
  begin
    S := UpperCase(AUnitName);

    if Assigned(CheckList) then
    begin
      for i := 0 to CheckList.Count - 1 do
        if UpperCase(CheckList[i]) = S then
          Exit; //==>
    end;

    for i := 0 to UsesList.Count - 1 do
      if UpperCase(UsesList[i]) = S then
       Exit; //==>

    UsesList.Add(AUnitName);
  end;
var
  OutPutList  : TStringList;
  InterfaceUsesList       : TStringList; { helper }
  ImplementationUsesList  : TStringList; { helper }
  List                    : TStringList;
  obj         : TObject;
  Index       : integer;
  //S           : string;
  i           : Integer;
  sClassName  : string;
begin
  OutPutList := TStringList.Create;
  ImplementationUsesList       := TStringList.Create;
  InterfaceUsesList            := TStringList.Create;
  List                         := TStringList.Create;

//  ImplementationUsesList .CaseSensitive := False;
//  InterfaceUsesList      .CaseSensitive := False;
  try
    FinishProcs;

    { unit name, etc. }
    OutPutList.Add('unit ' + ChangeFileExt(UnitNameCmp, '') + ';');
    OutPutList.Add(GetLicence);
//    OutPutList.Add('{$I PascalScript.inc}');
    OutPutList.Add('interface');
    OutPutList.Add(' ');
    OutPutList.Add(FAfterInterfaceDeclaration);
    OutPutList.Add(' ');    

    { interface uses clause list }
    AddToUsesList(InterfaceUsesList, nil, 'SysUtils');
    AddToUsesList(InterfaceUsesList, nil, 'Classes');
    AddToUsesList(InterfaceUsesList, nil, 'uPSComponent');
    AddToUsesList(InterfaceUsesList, nil, 'uPSRuntime');
    AddToUsesList(InterfaceUsesList, nil, 'uPSCompiler');

    if Assigned(FCompileTimeProcList) then
      for i := 0 to FCompileTimeUnitList.Count - 1 do
        AddToUsesList(InterfaceUsesList, nil, FCompileTimeUnitList[i]);

    if Assigned(FRunTimeProcList) then
      for i := 0 to FRunTimeUnitList.Count - 1 do
        AddToUsesList(InterfaceUsesList, nil, FRunTimeUnitList[i]);

    List.Assign(InterfaceUsesList);
    ProcessUsesList(List);
    OutPutList.AddStrings(List);
    OutPutList.Add(' ');

    sClassName := FCompPrefix + '_' + UnitName ;
    OutPutList.Add('type ');
    OutPutList.Add('(*----------------------------------------------------------------------------*)');
    OutPutList.Add(Format('  %s = class(TPSPlugin)', [sClassName]));
    OutPutList.Add('  public');
//  OutPutList.Add('    procedure CompOnUses(CompExec: TPSScript); override;');
//  OutPutList.Add('    procedure ExecOnUses(CompExec: TPSScript); override;');
    OutPutList.Add('    procedure CompileImport1(CompExec: TPSScript); override;');
//  OutPutList.Add('    procedure CompileImport2(CompExec: TPSScript); override;');
    OutPutList.Add('    procedure ExecImport1(CompExec: TPSScript; const ri: TPSRuntimeClassImporter); override;');
//  OutPutList.Add('    procedure ExecImport2(CompExec: TPSScript; const ri: TPSRuntimeClassImporter); override;');
    OutPutList.Add('  end;');
    OutPutList.Add(' ');
    OutPutList.Add(' ');


    { compile-time function declarations }
      if Assigned(FCompileTimeProcList) then
      begin
        OutPutList.Add('{ compile-time registration functions }');
        for Index := FCompileTimeProcList.count - 1 downto 0 do
        begin
          obj := FCompileTimeProcList.objects[Index];
          if (obj is TProcList) and
            (PublicProc in TProcList(obj).ProcAttr) then
            OutPutList.Add(FCompileTimeProcList[Index]);
        end;
      end;

      OutPutList.Add('');

      { run-time function declarations }
      if Assigned(FRunTimeProcList) then
      begin
        OutPutList.Add('{ run-time registration functions }');
        for Index := FRunTimeProcList.count - 1 downto 0 do
        begin
          obj := FRunTimeProcList.objects[Index];
          if (obj is TProcList) and
            (PublicProc in TProcList(obj).ProcAttr) then
            OutPutList.Add(FRunTimeProcList[Index]);
        end;
      end;

    OutPutList.Add('');

    OutPutList.Add('procedure Register;');

    OutPutList.Add('');
    OutPutList.Add('implementation');
    OutPutList.Add('');
    OutPutList.Add('');


    { implementation uses clause }
     if Assigned(FCompileTimeProcList) then
       for i := 0 to FCompileTimeUnitListImp.Count - 1 do
         AddToUsesList(ImplementationUsesList, InterfaceUsesList, FCompileTimeUnitListImp[i]);


    if Assigned(FRunTimeProcList) then
       for i := 0 to FRunTimeUnitListImp.Count - 1 do
         AddToUsesList(ImplementationUsesList, InterfaceUsesList, FRunTimeUnitListImp[i]);

(*    if WriteDefines then
    begin
      ImplementationUsesList.Add('CIImporterU');
      if RunTimeProcType <> [] then
        ImplementationUsesList.Add('RIImporterU');
    end;*)

    List.Assign(ImplementationUsesList);
    ProcessUsesList(List);
    (*
    i := List.IndexOf('CIImporterU');
    if i <> -1 then
    begin
      if i = 1 then
        List[i] := '{$IFDEF USEIMPORTER} CIImporterU {$ENDIF}'
      else List[i] := '{$IFDEF USEIMPORTER} ,CIImporterU {$ENDIF}';
    end;
    i := List.IndexOf('RIImporterU');
    if i <> -1 then
    begin
      if i = 1 then
        List[i] := '{$IFDEF USEIMPORTER} RIImporterU {$ENDIF}'
      else List[i] := '{$IFDEF USEIMPORTER} ,RIImporterU {$ENDIF}';
    end;

*)


    OutPutList.AddStrings(List);
    OutPutList.Add(' ');
    OutPutList.Add(' ');
          OutPutList.Add('procedure Register;');
          OutPutList.Add('begin');
          OutPutList.Add('  RegisterComponents('''+FCompPage+''', ['+FCompPrefix + '_' + UnitName+']);');
          OutPutList.Add('end;');
          OutPutList.Add('');

    { compile-time function definitions }
    if Assigned(FCompileTimeProcList) then
    begin
      OutPutList.Add('(* === compile-time registration functions === *)');

      // reinsert the main text body
      for Index := FCompileTimeProcList.count - 1 downto 0 do
      begin
        obj := FCompileTimeProcList.objects[Index];
        if (obj is TProcList) and (IsHelper in TProcList(obj).ProcAttr) then
        begin
          OutPutList.Add('(*----------------------------------------------------------------------------*)');
          OutPutList.Add(TStringList(obj).text);
        end;
      end;

      for Index := FCompileTimeProcList.count - 1 downto 0 do
      begin
        obj := FCompileTimeProcList.objects[Index];
        if (obj is TProcList) and (PublicProc in TProcList(obj).ProcAttr) then
        begin
          OutPutList.Add('(*----------------------------------------------------------------------------*)');
          OutPutList.Add(TStringList(obj).text);
        end;
      end;
    end;

    { run-time function definitions }
    if Assigned(FRunTimeProcList) then
    begin
      OutPutList.Add('(* === run-time registration functions === *)');

      // reinsert the main text body --IsHelper
      for Index := FRunTimeProcList.count - 1 downto 0 do
      begin
        obj := FRunTimeProcList.objects[Index];
        if (obj is TProcList) and (IsHelper in TProcList(obj).ProcAttr) then
        begin
          OutPutList.Add('(*----------------------------------------------------------------------------*)');
          OutPutList.Add(TProcList(obj).text);
        end;
      end;

      // reinsert the main text body  --PublicProc
      for Index := FRunTimeProcList.count - 1 downto 0 do
      begin
        obj := FRunTimeProcList.objects[Index];
        if (obj is TProcList) and (PublicProc in TProcList(obj).ProcAttr) then
        begin
          OutPutList.Add('(*----------------------------------------------------------------------------*)');
          OutPutList.Add(TProcList(obj).text);
        end;
      end;
    end;

    OutPutList.Add(' ');
    OutPutList.Add(' ');
    OutPutList.Add(Format('{ %s }', [sClassName]));
    OutPutList.Add('(*----------------------------------------------------------------------------*)');
    OutPutList.Add(Format('procedure %s.CompileImport1(CompExec: TPSScript);', [sClassName]));
    OutPutList.Add('begin');
    OutPutList.Add(Format('  SIRegister_%s(CompExec.Comp);', [UnitName]));
    OutPutList.Add('end;');
    OutPutList.Add('(*----------------------------------------------------------------------------*)');
    OutPutList.Add(Format('procedure %s.ExecImport1(CompExec: TPSScript; const ri: TPSRuntimeClassImporter);', [sClassName]));
    OutPutList.Add('begin');

    // Birb: (!!!) should fix it so that this line is never added if there's
    // no RIRegister...routine (e.g. if unit has just constants)
    if (ClassImporter in RunTimeProcType) then
      OutPutList.Add(Format('  RIRegister_%s(ri);', [UnitName]));

    if RoutineImporter in RunTimeProcType then
      OutPutList.Add(Format('  RIRegister_%s_Routines(CompExec.Exec); // comment it if no routines', [UnitName]));

    OutPutList.Add('end;');
    OutPutList.Add('(*----------------------------------------------------------------------------*)');


    OutPutList.Add(' ');
    OutPutList.Add(' ');

    OutPutList.Add('end.');
  finally
    for Index := FRunTimeProcList.Count - 1 downto 0 do
    begin
      FRunTimeProcList.Objects[Index].Free;
    end;
    FreeAndNil(FRunTimeProcList);
    for Index := FCompileTimeProcList.Count - 1 downto 0 do
    begin
      FCompileTimeProcList.Objects[Index].Free;
    end;

    FreeAndNil(FCompileTimeProcList);
    FreeAndNil(fRunTimeUnitList);
    FreeAndNil(fRunTimeUnitListImp);
    FreeAndNil(fCompileTimeUnitList);
    FreeAndNil(fCompileTimeUnitListImp);

    List.Free;
    ImplementationUsesList.Free;
    InterfaceUsesList.Free;
    FOutUnitList.Assign(OutPutList);
    OutPutList.Free;
  end;

end;
(*----------------------------------------------------------------------------*)
procedure TUnitParser.ParseUnitHeader;
begin
// parser 'Unit <identifier>;'
  Match(CSTII_Unit);
  Match(CSTI_Identifier);
  Unitname := prevOrgToken;
  Match(CSTI_SemiColon);
  Match(CSTII_Interface);
// parser the units clause 'uses <identifier>[, <identifier>];'
  if IfMatch(CSTII_uses) then
  begin
    repeat
      Match(CSTI_Identifier);
      AddRequiredUnit(PrevOrgToken, RunTime, false);
//    AddRequiredUnit(PrevToken,CompileTime,false);
      if TokenID = CSTI_SemiColon then
      begin
        Match(CSTI_SemiColon);
        break;
      end
      else
        Match(CSTI_Comma, ','' or '';');
    until false;
  end;

  AddRequiredUnit(UnitName, RunTime, false);
  fCurrentDTProc := RegisterProc('procedure SIRegister_' + UnitName +
                       '(CL: TPSPascalCompiler);', CompileTime, [PublicProc]);
  AddRequiredUnit('uPSCompiler', CompileTime, True);
  AddRequiredUnit('uPSRuntime', RunTime, true);

  RunTimeProcType := [];
  fCurrentRTProc := nil;
end; 
(*----------------------------------------------------------------------------*)
procedure TUnitParser.ParseGlobalDeclarations;
begin
  while not IfMatch(CSTII_Implementation) do
    case TokenID of
      CSTII_var       : ParseVariables;
      CSTII_const     : ParseConstants;
      CSTII_type      : ParseTypes;
      CSTII_procedure ,
      CSTII_function  : ParseRoutines;
      CSTI_Identifier : RaiseError('Declaration expected but identifier ''' + OrgToken + ''' found', TokenRow, TokenCol);
      else NextToken;
//      else              RaiseError('Unknown keyword ''' + GetTokenName(TokenID) + '''', TokenRow, TokenCol);
    end;
end;
(*----------------------------------------------------------------------------*)
function TUnitParser.GetConstantType: string;
var
  value: int64;
begin
  Result := '';
// determine the constant type
  case TokenID of
    CSTI_Integer:
      begin
        value := StrToInt64(Token);
        if (value < low(Longint)) then
          Result := 'Int64'
        else if value > High(Longint) then
        begin
          if value > High(longword) then
            Result := 'Int64'
          else
            Result := 'LongWord'
        end
        else
          Result := 'LongInt';
      end;
    CSTI_HexInt: Result := 'LongWord';
    CSTI_Real: Result := 'Extended';
    CSTI_String: Result := 'String';
    CSTI_Char: Result := 'Char';
    CSTI_Identifier:
      begin // unknown identifier
        if (Token = 'FALSE') or
          (Token = 'TRUE') then
          Result := 'Boolean';
      end;
  else RaiseError('Expected valid type, but found ''' + GetTokenName(TokenID) + '''', TokenRow, TokenCol);
  end; {case}
end; {GetConstantType}

function TUnitParser.ParseConstantExpression(var ExpType: string): string;
var
  BracketCount: integer;
  BlkBracketCount: integer;
begin
  result := '';
  BracketCount := 0;
  BlkBracketCount := 0;
  repeat
  // generate the actual string
    case TokenID of
      CSTI_OpenBlock:
        BEGIN
          ExpType := 'ConstSet';
          Inc(BlkBracketCount);
        end;
      CSTI_Comma: If BlkBracketCount>0 then
        Result := Result + ' or ';
      CSTI_CloseBlock:
        begin // prevent adding brakets when there should not be
          if blkBracketCount <> 0 then
          begin
            dec(BlkBracketCount);
            if (Result = '') and (BlkBracketCount = 0) then Result := '0';
          end
          else break;
        end;
      CSTI_Integer, CSTI_HexInt, CSTI_Real,
        CSTI_String, CSTI_Char, CSTI_Identifier:
        begin
          if (TokenId = CSTI_Identifier) and (BlkBracketCount>0) then begin
            result := result + 'ord('+orgtoken+')';
          end else begin
            ExpType := GetConstantType;
            // convert sveral consecutive characters into a string
            if (PrevTokenID = CSTI_Char) and
              (TokenID = CSTI_Char) then
            begin
              Result := Result + orgtoken;
              ExpType := 'String';
            end
            else
              Result := Result + ' ' + orgToken;
          end;
        end;
      CSTI_Equal: Result := Result + ' =';
      CSTI_NotEqual: Result := Result + ' <>';
      CSTI_Greater: Result := Result + ' >';
      CSTI_GreaterEqual: Result := Result + ' >=';
      CSTI_Less: Result := Result + ' <';
      CSTI_LessEqual: Result := Result + ' <=';
      CSTI_Plus: Result := Result + ' +';
      CSTI_Minus: Result := Result + ' -';
      CSTI_Divide: begin Result := Result + ' /'; ExpType := 'Extended'; end;
      CSTII_div: Result := Result + ' div';
      CSTI_Multiply: Result := Result + ' *';
      CSTI_AddressOf: Result := Result + ' @';
      CSTI_Dereference: Result := Result + ' ^';
      CSTII_and: Result := Result + ' and';
      CSTII_mod: Result := Result + ' mod';
      CSTII_not: Result := Result + ' not';
      CSTII_or: Result := Result + ' or';
      CSTII_shl: Result := Result + ' shl';
      CSTII_shr: Result := Result + ' shr';
      CSTII_xor: Result := Result + ' xor';
      CSTII_Chr: begin
                   // jgv
                   Result := Result + ' char(';
                   NextToken;
                   Match (CSTI_OpenRound);
                   Result := Result + ParseConstantExpression(ExpType) + ')';
                   Match (CSTI_CloseRound);
                   break;
                   //Result := Result + ' char(' + ParseConstantExpression(ExpType) + ')';
                 end;
      CSTII_Ord: begin
                   // jgv
                   Result := Result + ' ord(';
                   NextToken;
                   Match (CSTI_OpenRound);
                   Result := Result + ParseConstantExpression(ExpType);
                   Match (CSTI_CloseRound);
                   break;
                   //Result := Result + ' ord(' + ParseConstantExpression(ExpType) + ')';
                 end;
      CSTI_OpenRound: begin Result := Result + ' ('; inc(BracketCount); end;
      CSTI_CloseRound:
        begin // prevent adding brakets when there should not be
          if BracketCount <> 0 then
          begin
            Result := Result + ' )';
            dec(BracketCount)
          end
          else break;
        end;
    end;
    NextToken; 
  until (not (TokenID in [CSTI_Period, CSTI_Identifier, CSTI_Integer, CSTI_HexInt, CSTI_Real, CSTI_String, CSTI_Char,
    CSTI_Equal, CSTI_NotEqual, CSTI_Greater, CSTI_GreaterEqual,
      CSTI_Less, CSTI_LessEqual, CSTI_Plus, CSTI_Minus, CSTI_Divide,
      CSTII_div, CSTI_Multiply, CSTI_AddressOf, CSTI_Dereference,
      CSTII_and, CSTII_mod, CSTII_not, CSTII_or, CSTII_shl, CSTII_shr,
      CSTII_xor, CSTII_Chr, CSTII_Ord, CSTI_OpenRound, CSTI_CloseRound])) and (((BlkBracketCount=0)and(BracketCount=0)) or
      not (TokenID in [CSTI_COmma,CSTI_Colon,CSTI_SemiColon, CSTI_OpenBlock, CSTI_CloseBlock ])) ;
end; {ParseConstantExpression}

function TUnitParser.GetAsString(const ConstType, ConstValue: string): string;
begin
  if ConstType = 'BOOLEAN' then
  begin
    with RegisterProc('Function BoolToStr(value : boolean) : string;', CompileTime, [IsHelper]) do
    begin
      if IsDone in ProcAttr then exit;
      include(ProcAttr, IsDone);
      Add('Begin If value then Result := ''TRUE'' else Result := ''FALSE'' End;');
    end;
    Result := '.setbool(' + ConstValue + ')';
  end
  else If ConstType = 'CONSTSET' then
    result := '.Value.ts32 := '+ConstValue
//  Result := ConstValue
//else If ConstType = 'CHAR' then
//  Result := ''
  else
  begin
    AddRequiredUnit('Sysutils', CompileTime, false);
    if (ConstType = 'BOOLEAN') then
      Result := '.SetInt(Ord(' + Constvalue + '))'
    else if (ConstType = 'LONGINT') or (ConstType = 'INTEGER') then
      Result := '.SetInt(' + ConstValue + ')'
    else if (ConstType = 'INT64') then
      Result := '.SetInt64(' + ConstValue + ')'
    else if (ConstType = 'LONGWORD') or (ConstType = 'BYTE') or (ConstType = 'WORD') then
      Result := '.SetUInt(' + ConstValue + ')'
    else if (ConstType = 'EXTENDED') or (ConstType = 'DOUBLE') or (ConstType = 'SINGLE') then
      Result := '.setExtended(' + ConstValue + ')'
    else
      Result := '.SetString(' + ConstValue + ')';
  end;
end; {GetAsString}

procedure TUnitParser.ParseConstants;
var
  ConstName, ConstType, ConstValue, Expression, msg: string;
  l: Longint;
begin
  Match(CSTII_const);
  repeat
    try
      Match(CSTI_Identifier);
      ConstName := PrevOrgToken;
      if IfMatch(CSTI_Colon) then
      begin
        ConstType := OrgToken;
        NextToken;
        Match(CSTI_Equal);
        Expression := ParseConstantExpression(Expression);
      end else begin
        Match(CSTI_Equal, ':'' or ''=');
        Expression := ParseConstantExpression(ConstType);
      end;
      Match(CSTI_SemiColon);
      if UseUnitAtDT then
        ConstValue := ConstName
      else
        ConstValue := Expression;
      if ConstType = '' then
        ReadLn(ConstType, 'Expression (' + Expression + ') :', 'Unable to determine expression type');
      // now output the value   //   String(   //teo
      If ConstType = 'ConstSet' then
        fCurrentDTProc.Add(' CL.AddConstantN(''' + ConstName + ''',' + '''LongInt'')' + GetAsString(FastUppercase(ConstType), ConstValue) + ';')
      else
        fCurrentDTProc.Add(' CL.AddConstantN(''' + ConstName + ''',' + '''' + ConstType + ''')' + GetAsString(FastUppercase(ConstType), ConstValue) + ';');
    except
   // Hack: We cannot succesfully parse this, but that doesn't mean we should stop.
      on e: Exception do
      begin
        msg := '?';
        if (e <> nil) then
          msg := e.Message;
//        Writeln('Warning, could not parse const: ' + msg); // this crashes...
        l := 0;
        while TokenId <> CSTI_Eof do
        begin
          NextToken;
          if TokenId = CSTI_OpenBlock then
            inc(l)
          else if TokenId = CSTI_CloseBlock then
            Dec(l)
          else if TokenId = CSTI_OpenRound then
            inc(l)
          else if TokenId = CSTI_CloseRound then
            Dec(l)
          else if (TokenId = CSTI_SemiColon) and (l <= 0) then
            break;
        end;
        Match(CSTI_Semicolon);
      end;
    end;
  until (TokenID <> CSTI_Identifier);
end; {ParseConstants}

procedure TUnitParser.ParseVariables;
begin
{todo 3-cWishList : generate wrapper code to allow a script to access a variable}
  Match(CSTII_var);
  repeat
    Match(CSTI_Identifier);
//    Match(CSTI_Colon);
//    NextToken;
//    if IfMatch(CSTI_Equal) then
//      NextToken;
    skipToSemicolon;
  until (TokenID <> CSTI_Identifier);
end; {ParseVariables}

function TUnitParser.ParseProcDecl(var ProcName, decl, CallingConvention: string;
  Options: TProcDeclOptions; OwnerClass:String=''): TProcDeclInfo;
var
  VarListFirst: boolean;
  FinishedProcDecl: boolean;
  ParamNames: TStringList;
  Olddecl, OldProcName, ParamStr,
  s, Decl2: string;
  Index: integer;
  CheckSemiColon: boolean;
  Proc: TProcList;
begin
  Result := [];
  if IfMatch(CSTII_function) then
  begin
    Include(Result, IsFunction);
    decl := 'Function ';
  end
  else if IfMatch(CSTII_Procedure) then
    decl := 'Procedure '
  else if IfMatch(CSTII_Constructor) then
  begin
    if not (IsMethod in Options) then
      RaiseError('Constructor directive only applies to methods: '+OwnerClass, TokenRow, TokenCol);
    Include(Result, IsConstructor);
    decl := 'Constructor '
  end
  else if IfMatch(CSTII_Destructor) then
  begin
    if not (IsMethod in Options) then
      RaiseError('Destructor directive only applies to methods: '+OwnerClass, TokenRow, TokenCol);
    Include(Result, IsDestructor);
    decl := 'Destructor '
  end
  else
    Match(CSTII_Procedure, 'Function'' Or ''Procedure');

  if not (Ispointer in Options) then
  begin
    Match(CSTI_Identifier);
    ProcName := PrevOrgToken;
    decl := decl + PrevOrgToken;
  end
  else
    ProcName := '';
  ParamNames := TStringList.create;
  try
    if IfMatch(CSTI_OpenRound) then
    begin
      decl := decl + '( ';
      while not IfMatch(CSTI_CloseRound) do
      begin
        if IfMatch(CSTII_var) then
          decl := decl + 'var '
        else if Ifmatch(CSTII_out) then //Birb
          decl := decl + 'out '         //Birb
        else if Ifmatch(CSTII_const) then
          decl := decl + 'const ';
    // get the list of variable names
        VarListFirst := true;
        repeat
          Match(CSTI_Identifier);
          if VarListFirst then
          begin
            VarListFirst := false;
            decl := decl + PrevOrgToken;
          end
          else
            decl := decl + ', ' + PrevOrgToken;
          ParamNames.Add(PrevOrgToken);
          if TokenID = CSTI_Colon then
            Break;

          //-- jgv untyped parameters
          if TokenID in [CSTI_CloseRound, CSTI_SemiColon] then begin
            //Writeln('Untyped Pointers parameters are not supported, this declaration will fail. At position :' + inttostr(TokenRow) + ':' + inttostr(TokenCol));
            if TokenID = CSTI_SemiColon then begin
              NextToken;
              Continue;
            end
            else
              Break; // jgv untyped parameter
          end;

          IfMatch(CSTI_Comma);
        until false;

        // jgv untyped parameter
        if not (TokenID in [CSTI_CloseRound, CSTI_SemiColon]) then begin
          Match(CSTI_Colon);
          // get the type
          decl := decl + ' : ';
          CheckSemiColon := true;
          ParseType(TokenID, ProcName, decl, CheckSemiColon);
        end; //-- end jgv

        if TokenID = CSTI_Equal then
        begin // stip the default part of the varaible declaration
          NextToken;
          ParseConstantExpression(ParamStr);
        end;
        if CheckSemiColon and Ifmatch(CSTI_SemiColon) and
          (TokenID <> CSTI_CloseRound) then
          decl := decl + '; '
      end;
      decl := decl + ')';
    end;
// parse the ' : <typename>' part of a function
    if IsFunction in Result then
    begin
      Match(CSTI_Colon);
      Match(CSTI_Identifier);
      decl := decl + ' : ' + PrevOrgToken;
    end;
// parse Calling Conventions & other misc bits that are taken to
// the end of a routine declaration
    CallingConvention := 'cdRegister';
    FinishedProcDecl := false;
// check if we are a method pointer
    if IsPointer in Options then
    begin
      if Ifmatch(CSTII_of) then
      begin
        if (TokenID <> CSTI_Identifier) or
          (Token <> 'OBJECT') then
          RaiseError('Expecting Token ''Object'' but found ''' + GetTokenName(TokenID) + '''', TokenRow, TokenCol)
        else NextToken;
      end
      else
    {todo 1 -cWishList : normal function pointers are not supported by the script, only method pointers}
        Decl := '';
    end;
    Match(CSTI_Semicolon);
    repeat
      case TokenID of
        CSTII_External:
          begin
            if (IsPointer in Options) or
              (IsMethod in Options) then
              RaiseError('External directive only applies to routines ('+OwnerClass + ProcName + ')', TokenRow, TokenCol);
            NextToken;
            skipToSemicolon; // dll
          end;
        CSTII_Export:
          begin
            if (IsPointer in Options) or
              (IsMethod in Options) then
              RaiseError('Export directive only applies to routines (' + OwnerClass +ProcName + ')', TokenRow, TokenCol);
            NextToken;
            Match(CSTI_Semicolon);
          end;
        CSTII_Forward:
          begin
            if (IsPointer in Options) or
              (IsMethod in Options) then
              RaiseError('Forward directive only applies to routines (' + OwnerClass +ProcName + ')', TokenRow, TokenCol);
            NextToken;
            Match(CSTI_Semicolon);
          end;
        CSTII_Override:
          begin
            if not (IsMethod in Options) then
              RaiseError('Override directive only applies to methods (' + OwnerClass +ProcName + ')', TokenRow, TokenCol);
            decl := '';
            NextToken;
            Match(CSTI_Semicolon);
          end;
        CSTII_Virtual:
          begin
            if not (IsMethod in Options) then
              RaiseError('Virtual directive only applies to methods (' + OwnerClass +ProcName + ')', TokenRow, TokenCol);
            NextToken;
            Match(CSTI_Semicolon);
            include(Result, IsVirtual);
            if Token = 'ABSTRACT' then
            begin
              NextToken;
              Match(CSTI_Semicolon);
              include(Result, IsAbstract);
            end;
          end;
        CSTI_Identifier:
          begin
    // check for calling conversion
            if Token = 'MESSAGE' then
            begin
              if not (IsMethod in Options) then
                RaiseError('Message directive only applies to methods (' + OwnerClass +ProcName + ')', TokenRow, TokenCol);
              NextToken;
              Match(CSTI_Identifier);
              Match(CSTI_Semicolon);
            end else
              if Token = 'DYNAMIC' then
              begin
                if not (IsMethod in Options) then
                  RaiseError('Method directive only applies to methods (' + OwnerClass + ProcName + ')', TokenRow, TokenCol);
                NextToken;
                Match(CSTI_Semicolon);
                include(Result, IsVirtual);
                if Token = 'ABSTRACT' then
                begin
                  NextToken;
                  Match(CSTI_Semicolon);
                  include(Result, IsAbstract);
                end;
              end else if Token = 'PASCAL' then
              begin
                CallingConvention := 'cdPascal';
                NextToken; ;
                Match(CSTI_Semicolon);
              end else if Token = 'REGISTER' then
              begin
                CallingConvention := 'cdRegister';
                NextToken;
                Match(CSTI_Semicolon);
              end else if Token = 'CDECL' then
              begin
                CallingConvention := 'CdCdecl';
                NextToken;
                Match(CSTI_Semicolon);
              end else if (Token = 'STDCALL') or
                (Token = 'SAFECALL') then
              begin
      // map a safecall to stdcall
      // (safecall cause special wrapper code to be implemented client side)
                CallingConvention := 'CdStdCall';
                NextToken;
                Match(CSTI_Semicolon);

              end else if not (Ispointer in Options) then
              begin
                if (token = 'OVERLOAD') then
                begin
//                  if (IsPointer in Options) then
//                    RaiseError('overload directive does not applies to function/method pointers', TokenRow, TokenCol);
//                  Writeln('Overloading isnt supported. Remapping of name required '+OwnerClass +Decl); // this crashes
                  OldProcName := ProcName;
                  Olddecl := decl;
                  s := '';
                  ProcName := OldProcName + InttoStr(FRenamingHelper);
                  Inc(FRenamingHelper);
                  repeat
                    if FAutoRenameOverloadedMethods then
                    begin
//                      Writeln('Auto-remapped');
                    end
                    else
                    begin
//                      Readln(ProcName, s+'Current declaration :' + '''' + OwnerClass +decl + '''', 'Enter new name.');
                    end;
                      
                    if ProcName = '' then
                      ProcName := OldProcName;

                    // create a tmp procedure to handle the overload.
                    decl2 := decl;

                    If (IsMethod in Options) then
                      if (Pos('(',decl)=0)then
                        decl2 := StringReplace(decl, OldProcName, OldProcName+'(Self: '+Ownerclass+')', [rfIgnoreCase])
                      else
                        decl2 := StringReplace(decl, OldProcName+'(', OldProcName+'(Self: '+Ownerclass+'; ', [rfIgnoreCase]);

                    decl2 := StringReplace(decl2, OldProcName, OwnerClass+ProcName(*+'_P'*), [rfIgnoreCase]);
                    decl := StringReplace(decl, OldProcName, ProcName, [rfIgnoreCase])+';';
                    If (IsConstructor in Result) then begin
                      decl2 := StringReplace(decl2, 'Constructor', 'Function', [rfIgnoreCase]);
                      decl2 := StringReplace(decl2, ')', '):TObject', [rfIgnoreCase]);
                      decl2 := StringReplace(decl2, 'Self: '+Ownerclass, 'Self: TClass; CreateNewInstance: Boolean', [rfIgnoreCase]);
                    end;
                    If (IsDestructor in Result) then
                      decl2 := StringReplace(decl2, 'Destructor', 'Procedure', [rfIgnoreCase]);
                    decl2 := decl2 +';';
                    Proc := RegisterProc(decl2, RunTime, [IsHelper]);
                    if {not} (IsDone in Proc.ProcAttr) then
                    begin
                      If S = '' then
                        S := 'Procedure name has been used, enter a new one'^m;
                      ProcName := OldProcName;
                      decl := Olddecl;
                    end
                    else break;
                  until false;
                  Include(result,IsCallHelper);
                  Include(Proc.ProcAttr, IsDone);
//                  Writeln('New Name :''' + ProcName + '''');
                  with Proc do
                  begin
                    ParamStr := '';
                    if ParamNames.count <> 0 then
                    begin
                      for Index := 0 to ParamNames.count - 1 do
                        ParamStr := ParamStr + ', ' + ParamNames[Index];
                    end;
                    system.Delete(ParamStr,1,2);
                    s := '';
                    If (IsFunction in Result) then s := 'Result := ';
                    If ParamStr <> '' then ParamStr := '('+ParamStr +')';
                    If (IsConstructor in Result) then
                      Add('Begin Result := '+OwnerClass+'.' + OldProcName+ParamStr+'; END;')
                    else
                    If (IsMethod in Options) then
                      Add('Begin '+S+'Self.' + OldProcName+ParamStr+'; END;')
                    else
                      Add('Begin '+s+self.UnitName + '.' + OldProcName +ParamStr+ '; END;');
                  end;
                end;
                NextToken;
                Match(CSTI_Semicolon);
              end
              else
                exit;
    // get the next token
          end;
      else FinishedProcDecl := true;
      end;
    until FinishedProcDecl;
  finally
    ParamNames.free;
  end;
end; {ParseProcDecl}

procedure TUnitParser.ParseRoutines;
var
  decl, ProcName, CallingConvention: string;
begin
  AddRequiredUnit('uPSRuntime', RunTime, true);
  include(RunTimeProcType, RoutineImporter);
  fCurrentRTProc := RegisterProc('procedure RIRegister_' + UnitName + '_Routines(S: TPSExec);', RunTime, [PublicProc]);
// build the function declaration
  ParseProcDecl(ProcName, Decl, CallingConvention, []);
  if decl <> '' then
  begin
    fCurrentDTProc.Add(' CL.AddDelphiFunction(''' + decl + ''');');  // teo -undeclared identifier RegisterDelphiFunctionC
    fCurrentRTProc.Add(' S.RegisterDelphiFunction(@' + ProcName + ', ''' +  ProcName + ''', ' + CallingConvention + ');');
  end;
end; {ParseRoutines}

procedure TUnitParser.ParseClassOrInterfaceDef(const aClassName: string; const isInterface: boolean); //Birb
var
  CurrPos: (cp_private, cp_Protected, cp_public, cp_published);
  aClassParent: string;

  procedure ProcessProc;
  var
    decl, ProcName, callingConvention, PProcname, childClass: string;
    ProcDeclInfo: TProcDeclInfo;
  begin
    ProcDeclInfo := ParseProcDecl(ProcName, decl, callingConvention, [IsMethod], aClassName);
    if (decl = '') or
      (CurrPos in [cp_private, cp_Protected]) or
      (IsDestructor in ProcDeclInfo) then
      Exit;

    if isInterface then //Birb
     fCurrentDTProc.Add('    RegisterMethod(''' + SplitIntoLines(decl) + ''', '+callingConvention+');')
    else
     fCurrentDTProc.Add('    RegisterMethod(''' + SplitIntoLines(decl) + ''');');

    if IsCallHelper in ProcDeclInfo then
      PProcName := aClassname + ProcName(*+'_P'*)
    else
      PProcName := aClassname + '.' + ProcName;

    if not isInterface then //Birb
      if IsVirtual in ProcDeclInfo then
      begin
        if IsConstructor in ProcDeclInfo then
          fCurrentRTProc.Add('    RegisterVirtualConstructor(@' +
            PProcName + ', ''' + ProcName + ''');')
        else
        begin
          if IsAbstract in ProcDeclInfo then
          begin
            fCurrentRTProc.Add('    RegisterVirtualAbstractMethod('+aClassname +
              '_Impl, @'+aClassname+'_Impl.' + ProcName + ', ''' + ProcName + ''');');
          end
          else
            fCurrentRTProc.Add('    RegisterVirtualMethod(@' + PProcName +
              ', ''' + ProcName + ''');')
        end;
      end
      else
      begin
        if IsConstructor in ProcDeclInfo then
          fCurrentRTProc.Add('    RegisterConstructor(@' + PProcName +
            ', ''' + ProcName + ''');')
        else
          fCurrentRTProc.Add('    RegisterMethod(@' + PProcName +
            ', ''' + ProcName + ''');')
      end;
  end; {ProcessProc}

  procedure ProcessVar;
  var
    VarType: string;

    procedure CreateFieldReadFunc(const VarName: string);
    begin
      with RegisterProc('procedure ' + aClassname + VarName + '_R(Self: ' + aClassname +
        '; var T: ' + VarType + ');', RunTime, [Ishelper]) do
      begin
        if IsDone in ProcAttr then RaiseError('Duplicate reader for field :' + aClassname + VarName, TokenRow, TokenCol);
        include(ProcAttr, IsDone);
        Add('Begin T := Self.' + VarName + '; end;');
      end;
    end; {CreateFieldReadFunc}

    procedure CreateFieldWriteFunc(const VarName: string);
    begin
      with RegisterProc('procedure ' + aClassname + VarName + '_W(Self: ' + aClassname +
        '; const T: ' + VarType + ');', RunTime, [Ishelper]) do
      begin
        if IsDone in ProcAttr then RaiseError('Duplicate writer for field :' + aClassname + VarName, TokenRow, TokenCol);
        include(ProcAttr, IsDone);
        Add('Begin Self.' + VarName + ' := T; end;');
      end;
    end; {CreateFieldWriteFunc}
  var
    VarNames: TStringList;
    Index: integer;
    CheckSemiColon: boolean;
  begin {ProcessVar}
    VarNames := TStringList.Create;
    try
      VarNames.Add(OrgToken);
      NextToken;
      while TokenId = CSTI_Comma do
      begin
        NextToken;
        Match(CSTI_Identifier);
        VarNames.Add(PrevorgToken);
      end;
      Match(CSTI_Colon);
      CheckSemiColon := true;
      ParseType(TokenID, '', VarType, CheckSemiColon);
      if CheckSemiColon then
        Match(CSTI_SemiColon);
      if CurrPos in [cp_public, cp_published] then
      begin
        for Index := 0 to Varnames.Count - 1 do
        begin
          CreateFieldReadFunc(Varnames[Index]);
          CreateFieldWriteFunc(Varnames[Index]);
          fCurrentDTProc.Add('    RegisterProperty(''' + varnames[Index] + ''', ''' +
            vartype + ''', iptrw);');
          if not isInterface then //Birb
            fCurrentRTProc.Add('    RegisterPropertyHelper(' +
              '@' + aClassname + varnames[Index] + '_R,' +
              '@' + aClassname + varnames[Index] + '_W,' +
              '''' + varnames[Index] + ''');');
        end;
      end;
    finally
      VarNames.Free;
    end;
  end; {ProcessVar}

  procedure ProcessProp;
  var
    ParamTypes: TStringList;
    PropertyName: string;
    read, write: Boolean;
    IsDefaultProp : Boolean; // teo

    function FindProperty: Boolean;
    var
      e, ReadString: string;
      SearchCount: integer;
    begin
      ReadString := aClassParent;
      Result := False;
      SearchCount := MaxSearchCount;
      while True do
      begin
        if SearchCount = 0 then RaiseError('While searching for property in property list, the maxium number of searchs allowed was reached', TokenRow, TokenCol);
        dec(SearchCount);
        e := Ini.ReadString(ReadString, PropertyName, '~');
        if e = '~' then
        begin
          ReadString := Ini.ReadString(ReadString, 'PARENT-CLASS', '');
      // check in the parent for the property
          if ReadString = '' then exit;
        end
        else
        begin
          if e = '' then
          begin
            PropertyName := '';
            Result := True;
            exit;
          end;
          if pos(' ', e) = 0 then exit;
          ReadString := copy(e, 1, pos(' ', e) - 1);
          Delete(e, 1, length(ReadString) + 1);
          ParamTypes.Text := Stringreplace(e, ' ', #13#10, [rfReplaceAll]);
          if ReadString = 'READ' then
            Read := True
          else if ReadString = 'WRITE' then
            Write := True
          else if ReadString = 'READWRITE' then
          begin
            Read := True;
            Write := True;
          end
          else exit;
          Result := True;
          exit;
        end;
      end;
    end; {FindProperty}

    procedure CreateReadFunc(Fake: Boolean);
    var
      decl: string;
      Index: Longint;
    begin
      decl := 'procedure ' + aClassname + PropertyName + '_R(Self: ' + aClassname +
        '; var T: ' + ParamTypes[0];
      for Index := 1 to ParamTypes.Count - 1 do
        decl := decl + '; const t' + inttostr(Index) + ': ' + ParamTypes[Index];
      decl := decl + ');';
      with RegisterProc(decl, RunTime, [Ishelper]) do
      begin
        if IsDone in ProcAttr then RaiseError('Duplicate property :' + aClassname + PropertyName + '_R', TokenRow, TokenCol);
        include(ProcAttr, IsDone);
        if Fake then Insert(1, '{');
        decl := 'begin T := Self.' + PropertyName;
        if ParamTypes.Count > 1 then
        begin
          decl := decl + '[t1';
          for Index := 2 to ParamTypes.Count - 1 do
            decl := decl + ', t' + inttostr(Index);
          decl := decl + ']';
        end;
        Add(decl + '; end;');
        if Fake then Add('}');
      end;
    end; {CreateReadFunc}

    procedure CreateWriteFunc(Fake: Boolean);
    var
      decl: string;
      Index: Longint;
    begin
      decl := 'procedure ' + aClassname + PropertyName + '_W(Self: ' + aClassname +
        '; const T: ' + ParamTypes[0];
      for Index := 1 to ParamTypes.Count - 1 do
        decl := decl + '; const t' + inttostr(Index) + ': ' + ParamTypes[Index];
      decl := decl + ');';
      with RegisterProc(decl, RunTime, [Ishelper]) do
      begin
        if IsDone in ProcAttr then RaiseError('Duplicate property :' + aClassname + PropertyName + '_W', TokenRow, TokenCol);
        include(ProcAttr, IsDone);
        if Fake then Insert(1, '{');
        decl := 'begin Self.' + PropertyName;
        if ParamTypes.Count > 1 then
        begin
          decl := decl + '[t1';
          for Index := 2 to ParamTypes.Count - 1 do
            decl := decl + ', t' + inttostr(Index);
          decl := decl + ']';
        end;
        Add(decl + ' := T; end;');
        if Fake then Add('}');
      end;
    end; {CreateWriteFunc}

  var
    Readstr, Writestr, decl: string;
    ParamCount: Longint;

  begin {ProcessProp}
    IsDefaultProp := False;
    ParamTypes := TStringList.Create;
    try
      NextToken;
      Match(CSTI_Identifier);
      PropertyName := PrevOrgToken;
      if Token = 'NODEFAULT' then
        NextToken;
      case TokenId of
        CSTI_Semicolon:
          begin // A property is being introduced that is present in the parent object
            NextToken;
            if FindProperty then
            begin
              if (PropertyName = '') or
                not (CurrPos in [cp_public, cp_published]) then Exit;
              decl := StringReplace(ParamTypes.Text, #13, '', [rfreplaceAll]);
              decl := StringReplace(decl, #10, ' ', [rfreplaceAll]);
              decl := trim(decl);
        // build the design time declaration
              decl := '    RegisterProperty(''' + PropertyName + ''', ''' + decl + ''', ipt';
              if Read then decl := decl + 'r';
              if Write then decl := decl + 'w';
              fCurrentDTProc.Add(decl + ');');
              if CurrPos <> cp_published then
              begin
          // write out the runtime version
                if Read then
                begin // create the helper function to read from the property
                  CreateReadFunc(False);
                  Readstr := '@' + aClassName + PropertyName + '_R';
                end
                else Readstr := 'nil';
                if Write then
                begin // create the helper function to write to the property
                  CreateWriteFunc(False);
                  Writestr := '@' + aClassName + PropertyName + '_W';
                end
                else Writestr := 'nil';
          // select which Property helper to use (relys on events following the syntax (ON...))
                if copy(PropertyName, 1, 2) <> 'ON' then
                  decl := '    RegisterPropertyHelper('
                else
                  decl := '    RegisterEventPropertyHelper(';
                if not isInterface then //Birb
                  fCurrentRTProc.Add(decl + Readstr + ',' + Writestr + ',''' + PropertyName + ''');')
              end;
            end
            else if PropertyName <> '' then
              Exit;
          end;
        CSTI_OpenBlock:
          begin // a pseudo array (indexed) property
            NextToken;
            while TokenID <> CSTI_CloseBlock do
            begin
              ParamCount := 0;
              repeat
                if (TokenID = CSTII_Const)
                   or (TokenID = CSTII_Var)
                   or (TokenID = CSTII_Out) //Birb
                then
                  NextToken;
                Match(CSTI_Identifier);
                inc(ParamCount);
                if TokenID = CSTI_Comma then
                  NextToken
                else Break;
              until False;
              Match(CSTI_Colon);
              Match(CSTI_Identifier);
              while ParamCount > 0 do
              begin
                ParamTypes.Add(PrevOrgToken);
                Dec(ParamCount);
              end;
              if TokenId = CSTI_Semicolon then
              begin
                NextToken;
                Continue;
              end;
            end;
            NextToken;
          end;
      end;

      //-- jgv reintroduce a property specifier
      if token = 'STORED' then begin
        If (CurrPos <> cp_published) then Exit;
        NextToken;
        Match(CSTI_Identifier);
        If TokenID = CSTI_SemiColon then begin
          Match (CSTI_SemiColon);
          Exit;
        end;
      end;

      if Token = 'DEFAULT' then
      begin
        NextToken;
        while TokenID <> CSTI_Semicolon do
          NextToken;
        NextToken;
        if FindProperty then
        begin
          if (PropertyName = '') or
            not (CurrPos in [cp_public, cp_published]) then Exit;
          decl := StringReplace(ParamTypes.Text, #13, '', [rfreplaceAll]);
          decl := StringReplace(decl, #10, ' ', [rfreplaceAll]);
          decl := trim(decl);
        // build the design time declaration
          decl := '    RegisterProperty(''' + PropertyName + ''', ''' + decl + ''', ipt';
          if Read then decl := decl + 'r';
          if Write then decl := decl + 'w';
          fCurrentDTProc.Add(decl + ');');
          if CurrPos <> cp_published then
          begin
          // write out the runtime version
            if Read then
            begin // create the helper function to read from the property
              CreateReadFunc(False);
              Readstr := '@' + aClassName + PropertyName + '_R';
            end
            else Readstr := 'nil';
            if Write then
            begin // create the helper function to write to the property
              CreateWriteFunc(False);
              Writestr := '@' + aClassName + PropertyName + '_W';
            end
            else Writestr := 'nil';
          // select which Property helper to use (relys on events following the syntax (ON...))
            if copy(PropertyName, 1, 2) <> 'ON' then
              decl := '    RegisterPropertyHelper('
            else
              decl := '    RegisterEventPropertyHelper(';
            if not isInterface then //Birb
              fCurrentRTProc.Add(decl + Readstr + ',' + Writestr + ',''' + PropertyName + ''');')
          end;
        end
        else if PropertyName <> '' then
          Exit;
      end;
      Match(CSTI_Colon);
      Match(CSTI_Identifier);
      ParamTypes.Insert(0, PrevOrgToken);
    // handle various property declarations
      read := false; write := false;

      //-- 20050707_jgv
      if Token = 'INDEX' then begin
        NextToken;
        Match (CSTI_Integer);
      end;
      //-- end jgv

      if Token = 'READ' then
      begin
        repeat
          NextToken; Match(CSTI_Identifier);
        until TokenID <> CSTI_Period;
        read := true;
      end;
      if Token = 'WRITE' then
      begin
        repeat
          NextToken; Match(CSTI_Identifier);
        until TokenID <> CSTI_Period;
        Write := true;
      end;
      if TokenID = CSTI_SemiColon then
        NextToken
      else
      begin
        if (Token = 'STORED') then
        begin
          NextToken;
          NextToken; // skip this
          if TokenId = CSTI_Semicolon then
            Match(CSTI_Semicolon);
        end;
        if (Token = 'DEFAULT') then
        begin
          NextToken;
          while TokenID <> CSTI_Semicolon do
            NextToken;
          Match(CSTI_SemiColon);
        end;
      end;
      if Token = 'DEFAULT' then
      begin
        IsDefaultProp := True;
        NextToken;
        Match(CSTI_Semicolon);
      end;
      if UseUnitAtDT and (CurrPos <> cp_public) or
        not (CurrPos in [cp_public, cp_published]) then
        exit;
      decl := StringReplace(ParamTypes.Text, #13, '', [rfreplaceAll]);
      decl := StringReplace(decl, #10, ' ', [rfreplaceAll]);
      decl := trim(decl);
    // build the design time declaration
      decl := '    RegisterProperty(''' + PropertyName + ''', ''' + decl + ''', ipt';
      if Read then decl := decl + 'r';
      if Write then decl := decl + 'w';
      fCurrentDTProc.Add(decl + ');');
    // write out the runtime version
      if Read then
      begin // create the helper function to read from the property
        CreateReadFunc(False);
        Readstr := '@' + aClassName + PropertyName + '_R';
      end
      else Readstr := 'nil';
      if Write then
      begin // create the helper function to write to the property
        CreateWriteFunc(False);
        Writestr := '@' + aClassName + PropertyName + '_W';
      end
      else Writestr := 'nil';
    // select which Property helper to use (relys on events following the syntax (ON...))
      if copy(PropertyName, 1, 2) <> 'ON' then
        decl := '    RegisterPropertyHelper('
      else
        decl := '    RegisterEventPropertyHelper(';
      if not isInterface then //Birb
        fCurrentRTProc.Add(decl + Readstr + ',' + Writestr + ',''' + PropertyName + ''');');

      if IsDefaultProp then   //teo
        fCurrentDTProc.Add('    SetDefaultPropery(''' + PropertyName + ''');');

    finally
      ParamTypes.Free;
    end;
  end; {ProcessProp}

var
  OldDTProc, OldRTProc: TProcList;
begin {ParseClassDef}
  if isInterface //Birb
   then Match(CSTII_interface) //Birb
   else Match(CSTII_class);
//CreateRegClasProc;
// check for forward declaration
  if TokenID = CSTI_Semicolon then
  begin
    exit; // do nothing
//  NextToken;  the semicolon is removed by the caller
    if UseUnitAtDT then
      if isInterface //Birb
        then fCurrentDTProc.Add('  CL.AddInterface(CL.FindInterface('''+STR_IINTERFACE+'''),' + aClassname + ', '''+aClassname+''');') //this is a forward declaration that will be overriden later on
        else fCurrentDTProc.Add('  CL.AddClass(CL.FindClass(''TOBJECT''),' + aClassname + ');')
    else
      if isInterface //Birb
        then fCurrentDTProc.Add('  CL.AddInterface(CL.FindInterface('''+STR_IINTERFACE+'''),' + aClassname + ', '''+aClassname+''');') //this is a forward declaration that will be overriden later on
        else fCurrentDTProc.Add('  CL.AddClassN(CL.FindClass(''TOBJECT''),''' + aClassname + ''');');
    if isInterface then //Birb
      Include(RuntimeProcType, InterfaceImporter) //Birb
    else //Birb
      begin //Birb
      if fCurrentRTProc = nil then
      begin
        Include(RunTimeProcType, ClassImporter);
        fCurrentRTProc := RegisterProc('procedure RIRegister_' + UnitName +
          '(CL: TPSRuntimeClassImporter);', RunTime, [PublicProc]);
      end;
      fCurrentRTProc.Add('  with CL.Add(' + aClassname + ') do');
      end; //Birb
    exit;
  end

  else if IfMatch(CSTII_of) then
  begin
    Match(CSTI_Identifier);
    //teo --compiler complains when it comes to register a TClass type
    fCurrentDTProc.Add('  //CL.AddTypeS(''' + aClassname + ''', ''class of ' + PrevOrgToken + ''');');
    exit;
  end

  else if IfMatch(CSTI_OpenRound) then
  begin
    Match(CSTI_Identifier);
    aClassParent := PrevOrgToken;
    AddClassInheritance(aClassParent, aClassname);

    if not isInterface then
     while IfMatch(CSTI_Comma) do
      Match(CSTI_Identifier); //Birb (ignore possible list of implemented interfaces after class ancestor)

    Match(CSTI_CloseRound);

    ///////////////////

    if TokenId = CSTI_Semicolon then //??? //Birb: I think this is an impossible case!
    begin
      if UseUnitAtDT then
        if isInterface //Birb
          then fCurrentDTProc.Add('  CL.AddInterface(CL.FindInterface(''IUNKNOWN''),' + aClassname + ', '''+aClassname+''');')
          else fCurrentDTProc.Add('  CL.AddClass(CL.FindClass(''TOBJECT''),' + aClassname + ');')
      else
        if isInterface //Birb
          then fCurrentDTProc.Add('  CL.AddInterface(CL.FindInterface(''IUNKNOWN''),' + aClassname + ', '''+aClassname+''');')
          else fCurrentDTProc.Add('  CL.AddClassN(CL.FindClass(''TOBJECT''),''' + aClassname + ''');');
      if isInterface then //Birb
        Include(RuntimeProcType, InterfaceImporter) //Birb
      else //Birb
        begin //Birb
          if fCurrentRTProc = nil then
          begin
            Include(RunTimeProcType, ClassImporter);
            fCurrentRTProc := RegisterProc('procedure RIRegister_' + UnitName +
              '(CL: TPSRuntimeClassImporter);', RunTime, [PublicProc]);
          end;
          fCurrentRTProc.Add('  with CL.Add(' + aClassname + ') do');
        end; //Birb
      exit;
    end;

   ///////////////////

  end

  else
    if isInterface //Birb
     then aClassParent := STR_IINTERFACE //Birb (Delphi interfaces descent from IInterface if no ancestor is specified)
     else aClassParent := 'TOBJECT';

  if isInterface then //Birb
    begin //Birb
    Include(RuntimeProcType, InterfaceImporter); //Birb
    OldRTProc := fCurrentRTProc; //Birb (using to avoid compiler warning later on - maybe can just use "nil" here)
    end //Birb
  else //Birb
    begin //Birb
    if fCurrentRTProc = nil then
    begin
      Include(RunTimeProcType, ClassImporter);
      fCurrentRTProc := RegisterProc('procedure RIRegister_' + UnitName +
        '(CL: TPSRuntimeClassImporter);', RunTime, [PublicProc]);
    end;
    OldRTProc := fCurrentRTProc;
    fCurrentRTProc := RegisterProc('procedure RIRegister_' + aClassname +
      '(CL: TPSRuntimeClassImporter);', RunTime, [PublicProc]);
    fCurrentRTProc.Add('  with CL.Add(' + aClassname + ') do');
    fCurrentRTProc.Add('  begin');
    end; //Birb

  OldDTProc := fCurrentDTProc;
  fCurrentDTProc := RegisterProc('procedure SIRegister_' + aClassname +
    '(CL: TPSPascalCompiler);', CompileTime, [PublicProc]);
  if UseUnitAtDT then
  begin
    AddRequiredUnit(UnitName, CompileTime, false);

    if isInterface //Birb
      then fCurrentDTProc.Add('  with CL.AddInterface(CL.FindInterface(''' + aClassParent + '''),' + aClassname + ', '''+aClassname+''') do')
      else fCurrentDTProc.Add('  with CL.AddClass(CL.FindClass(''' + aClassParent + '''),' + aClassname + ') do');
    fCurrentDTProc.Add('  begin');

    if not isInterface then //Birb (note that Delphi does support interface properties, but on only for Delphi objects, not external objects [!!!should fix uPSCompiler to support that too - with some RegisterProperties method, since there's no published section at interface declarations])
     fCurrentDTProc.Add('    RegisterPublishedProperties;');
  end
  else
  begin
    if isInterface then //Birb
     begin
     fCurrentDTProc.Add('  //with RegInterfaceS(CL,''' + aClassParent + ''', ''' + aClassname + ''') do'); //Birb
     fCurrentDTProc.Add('  with CL.AddInterface(CL.FindInterface(''' + aClassParent + '''),' + aClassname + ', '''+aClassname+''') do')
     end
    else
     begin
     fCurrentDTProc.Add('  //with RegClassS(CL,''' + aClassParent + ''', ''' + aClassname + ''') do');  // teo
     fCurrentDTProc.Add('  with CL.AddClassN(CL.FindClass(''' + aClassParent + '''),''' + aClassname + ''') do');
     end;
    fCurrentDTProc.Add('  begin');
  end;
  CurrPos := cp_public;

  if isInterface then //Birb
    if not IfMatch(CSTI_OpenBlock) then //Birb: GUID string needed at interface declarations cause "CL.AddInterface" has a TGUID parameter above (!!!should have a PGUID so that we could maybe pass nil to it - else maybe should see if '' is accepted for a TGUID and peek ahead to see if a GUID is available, else use '')
      RaiseError('Found ''' + GetTokenName(TokenID) + ''' instead of [''GUID-string'']', TokenRow, TokenCol)
    else
      begin //Birb: ignore ['GUID-string']
      Match(CSTI_String);
      Match(CSTI_CloseBlock);
      end;

  while not IfMatch(CSTII_End) do
    case TokenID of
      CSTII_Private:
        begin
          CurrPos := cp_private;
          NextToken;
        end;
      CSTII_Protected:
        begin
          CurrPos := cp_Protected;
          NextToken;
        end;
      CSTII_Public:
        begin
          CurrPos := cp_public;
          NextToken;
        end;
      CSTII_Published:
        begin
          CurrPos := cp_published;
          NextToken;
        end;
      CSTII_Procedure, CSTII_Function, CSTII_Constructor, CSTII_Destructor:
        ProcessProc;
      CSTI_Identifier:
        ProcessVar;
      CSTII_Property:
        if isInterface then //Birb
          begin
          skipToSemicolon; //Birb (note that Delphi does support interface properties, but on only for Delphi objects, not external objects [!!!should fix uPSCompiler to support that too])
          if Token='DEFAULT' then //Birb: ignore optional "default;" specifier that may follow indexed/array properties
           begin
           NextToken;
           Match(CSTI_SemiColon);
           end
          end
        else
          ProcessProp; //Birb: (!!!) do check if this works ok with "default;" specifier for indexed/array property declarations (since that one is after the property declaration's ending ";")

      CSTII_Class:
        begin
          // jgv: class procedure/function  
          NextToken;
          If Not (TokenID in [CSTII_Procedure, CSTII_Function]) then
            RaiseError ('class must be followed by "function" or "procedure"', TokenRow, TokenCol);
        end;

    else RaiseError('Unknown keyword ''' + GetTokenName(TokenID) + '''', TokenRow, TokenCol);

    end;
  if not isInterface then //Birb
    fCurrentRTProc.Add('  end;');
  fCurrentDTProc.Add('  end;');
  if OldDTProc <> nil then
    fCurrentDTProc := OldDTProc;
  fCurrentDTProc.Add('  SIRegister_' + aClassname + '(CL);');
  if not isInterface then //Birb
    begin
    if OldRTProc <> nil then
      fCurrentRTProc := OldRTProc;
    fCurrentRTProc.Add('  RIRegister_' + aClassname + '(CL);');
    end;
end; {ParseClassOrInterfaceDef} //Birb

procedure TUnitParser.ParseInterfaceDef(const aInterfaceName: string);
begin
{  Writeln('Interface Declaration not suported at position: ' + Inttostr(TokenRow) + ':' + Inttostr(TokenCol));
  while not (TokenId in [CSTI_EOF, CSTII_End]) do
    NextToken;
  NextToken; // skip the END
//todo 4 -cRequired : Allow parsing of interfaces
}
 ParseClassOrInterfaceDef(aInterfaceName,true); //Birb
end; {ParseInterfaceDef}

procedure TUnitParser.ParseClassDef(const aClassName: string); //Birb
begin
 ParseClassOrInterfaceDef(aClassName,false); //Birb
end; {ParseClassDef} //Birb

procedure TUnitParser.ParseType(aTokenID: TPSPasToken;
  const TypeName: string;
  var TypeDescriptor: string;
  var CheckSemiColon: boolean);
var
  S: string;
  b: boolean;
begin
  CheckSemiColon := True;
  if aTokenID = CSTII_packed then
   begin
    NextToken;
    aTokenID := TokenID;
   end;
  case aTokenID of
    CSTI_Integer: // range
      begin
        TypeDescriptor := TypeDescriptor + 'Integer';
        while not (TokenId in [CSTI_EOF, CSTI_Semicolon]) do
        begin
          NextToken;
        end;
        Match(CSTI_Semicolon);
        CheckSemicolon := False;
      end;
    CSTI_Identifier: // simple type by name  (MyInt = Integer)
      begin
        Match(CSTI_Identifier);
        if IfMatch(CSTI_Less) then // generic, not supported
        begin
          TypeDescriptor := '';
          skipToSemicolon;
          CheckSemiColon:=false;
        end else
        if IfMatch(CSTI_OpenBlock) then // string[...], not supported
        begin
          TypeDescriptor := '';
          skipToSemicolon;
          CheckSemiColon:=false;
        end else
        if IfMatch(CSTI_TwoDots) then // 12..32, not supported
        begin
          TypeDescriptor := '';
          skipToSemicolon;
          CheckSemiColon:=false;
        end else
          TypeDescriptor := TypeDescriptor + PrevOrgToken;
      end;
    CSTI_Dereference: // ie 'PInteger = ^Integer'
      begin { todo 3-cWishList : When pointers are supported by ROPPS, supported them or provide emulation }
        Match(CSTI_Dereference);
        TypeDescriptor := TypeDescriptor + ' ^';
        ParseType(CSTI_Identifier, TypeName, TypeDescriptor, CheckSemiColon);
//        Writeln('Pointers are not supported, this declaration will fail. At position :' + inttostr(TokenRow) + ':' + inttostr(TokenCol)); // this crashes
        TypeDescriptor := TypeDescriptor + ' // will not work';
      end;
    CSTII_type: // type identity (MyInt = type Integer)
      begin
        Match(CSTII_type);
//    TypeDescriptor := TypeDescriptor + 'type';
        ParseType(CSTI_Identifier, TypeName, TypeDescriptor, CheckSemiColon);
      end;
    CSTII_procedure,
      CSTII_function: // parse a routine/method pointer
      begin
        ParseProcDecl(S, TypeDescriptor, S, [IsPointer]);
        CheckSemiColon := false;
      end;
    CSTI_OpenRound: // enums  (somename,somename2,..)
      begin
        Match(CSTI_OpenRound);
        TypeDescriptor := TypeDescriptor + '( ';
        b := false;
        repeat
          Match(CSTI_Identifier);
          if b then
            TypeDescriptor := TypeDescriptor + ', ' + PrevOrgToken
          else
          begin
            b := true;
            TypeDescriptor := TypeDescriptor + PrevOrgToken;
          end;
          if TokenID = CSTI_CloseRound then
          begin
            NextToken;
            TypeDescriptor := TypeDescriptor + ' ) ';
            break;
          end
          else
            Match(CSTI_Comma);
        until false;
      end;
    CSTII_record: // records (rec = record a : integer; end;)
      begin
        Match(CSTII_record);
        TypeDescriptor := TypeDescriptor + 'record ';
        if (TokenID = CSTI_Identifier) and (Token = 'HELPER') then
        begin
          NextToken;
          Match(CSTII_for);
          Match(CSTI_Identifier);
        end;
        b := false;
        while TokenID = CSTI_Identifier do
        begin
          TypeDescriptor := TypeDescriptor + OrgToken + ' : ';
          NextToken;
          Match(CSTI_Colon);
          ParseType(TokenId, TypeName, TypeDescriptor, CheckSemiColon);
          if TypeDescriptor = '' then
            b := true; // invalidat this type
          Match(CSTI_SemiColon);
          TypeDescriptor := TypeDescriptor + '; ';
        end;
        TypeDescriptor := TypeDescriptor + 'end ';
        if b then TypeDescriptor := '';
        while not IfMatch(CSTII_end) do NextToken;
      end;
    CSTII_set: // sets    (set of (...))
      begin // parse a set declaration
        Match(CSTII_set);
        Match(CSTII_of);
        TypeDescriptor := TypeDescriptor + 'set of ';
        ParseType(TokenID, TypeName, TypeDescriptor, CheckSemiColon);

   { todo 1 -cWishList : When Sets are supported by ROPS, supported them }
//    RaiseError('Sets are not supported',TokenPos);
      end;
    CSTII_array: // arrays  (array [<const expression>..<const expression>] of ...)
      begin
        Match(CSTII_array);
        b := false;
        TypeDescriptor := TypeDescriptor + 'array ';
        if Ifmatch(CSTI_OpenBlock) then
        begin
          TypeDescriptor := TypeDescriptor + '[ ';
          repeat
            TypeDescriptor := TypeDescriptor + ParseConstantExpression(S);
            if IfMatch(CSTI_TwoDots) then
            begin
  //            Match(CSTI_Period, '..');
              TypeDescriptor := TypeDescriptor + ' .. ' + ParseConstantExpression(S);
            end;
            if IfMatch(CSTI_Comma) then
              TypeDescriptor := TypeDescriptor + ', ';
          until IfMatch(CSTI_CloseBlock);
          TypeDescriptor := TypeDescriptor + '] ';
      { TODO 1 -cWishList : When static arrays are supported by ROPS, supported them }
          b := true;
        end;
        Match(CSTII_of);
        TypeDescriptor := TypeDescriptor + 'of ';
        //-- jgv parse array of const
        If TokenID = CSTII_const then begin
          TypeDescriptor := TypeDescriptor + 'const';
          NextToken;
        end
        else
        //-- end jgv
          Parsetype(TokenID, TypeName, TypeDescriptor, CheckSemiColon);

        if b then TypeDescriptor := '';
      end;
    CSTII_Interface: // interfaces (  objectname = Interface ... end)
      begin
        TypeDescriptor := ''; // suppresses the default register action
//    Writeln('Interfaces are not supported. At position :'+inttostr(TokenPos));
        ParseInterfaceDef(TypeName);
      end;
    CSTII_class: // classes (  objectname = class ... end)
      begin
        TypeDescriptor := ''; // suppresses the default register action
        ParseClassDef(TypeName);
      end;
  else RaiseError('Expecting valid type, but found ''' + GetTokenName(TokenID) + '''', TokenRow, TokenCol);
  end;
end; {ParseType}
(*----------------------------------------------------------------------------*)
procedure TUnitParser.ParseTypes;
var
  TypeName            : string;
  TypeDescriptor, tmp : string;
  CheckSemiColon      : boolean;
  Len, Index          : integer;
begin {ParseTypes}
  Match(CSTII_type);
  repeat
    // get the type name
    Match(CSTI_Identifier);
    TypeName := PrevOrgToken;

    if IfMatch(CSTI_Less) then // generic
    begin
      // not supported, skip
      while not IfMatch(CSTII_end) do NextToken;
      Match(CSTI_SemiColon);
      Continue;
    end;

    Match(CSTI_equal);
    
    // build the type discriptor
    TypeDescriptor := '';
    ParseType(TokenID, TypeName, TypeDescriptor, CheckSemiColon);
    if CheckSemiColon then
      Match(CSTI_SemiColon);
    if (TypeDescriptor <> '') then
    begin
      TypeDescriptor := trim(TypeDescriptor);
      // break up the TypeDescriptor to make it fit with in 80 characters per line
      tmp := '  CL.AddTypeS(''' + TypeName + ''', ''';
      Len := Length(tmp) + length(TypeDescriptor) + 3;
      if Len <= 80 then
        fCurrentDTProc.Add(tmp + TypeDescriptor + ''');')
      else
      begin
        Len := 79 - Length(tmp);
        fCurrentDTProc.Add(tmp);
        if Len > 0 then
        begin
          tmp := copy(TypeDescriptor, 1, Len);
          Delete(TypeDescriptor, 1, Len);
          Index := fCurrentDTProc.count - 1;
          fCurrentDTProc[Index] := fCurrentDTProc[Index] + tmp + '''';
        end
        else
        begin
          fCurrentDTProc.Add('   +''' + copy(TypeDescriptor, 1, 74) + '''');
          Delete(TypeDescriptor, 1, 74);
        end;
        while TypeDescriptor <> '' do
        begin
          fCurrentDTProc.Add('   +''' + copy(TypeDescriptor, 1, 74) + '''');
          Delete(TypeDescriptor, 1, 74);
        end;
        Index := fCurrentDTProc.count - 1;
        fCurrentDTProc[Index] := fCurrentDTProc[Index] + ');';
      end;
    end;
  until (TokenID <> CSTI_Identifier);
end; {ParseTypes}



procedure TUnitParser.ParserError(Parser: TObject;
  Kind: TPSParserErrorKind);
var
  S: string;
begin
  Writeln('Error parsing file');
  case Kind of
    iCommentError: S := 'Comment';
    iStringError: S := 'String';
    iCharError: S := 'Char';
    iSyntaxError: S := 'Syntax';
  end;
  Writeln(S + ' Error, Position :' + Inttostr(TPSPascalParser(Parser).Row) + ':' + IntToStr(TPSPascalParser(Parser).Col));
end;

end.
