RomanYankovsky / DelphiAST

Abstract syntax tree builder for Delphi
Mozilla Public License 2.0
275 stars 117 forks source link

Typecast File(...) causes Fatal Parser Error #183

Open RomanYankovsky opened 8 years ago

RomanYankovsky commented 8 years ago

Example:

var
  SettingFile: File of TSettingRecord; 
  settings: TSettingRecord;
begin
  AssignFile( File(SettingFile), filename);
  Read(SettingFile, settings);
  CloseFile(settingFile);
JBontes commented 7 years ago

Here's the fix:

It involves the somewhat dirty hack of forcing the TokenID of the lexer.

  TmwBasePasLex = class(TObject)
...
  public
...
    property ForceTokenID: TptTokenKind write FTokenId;
  end;

  const
    ReservedWords = [ptAnd, ptEnd, ptInterface, ptrecord, ptvar,ptarray,ptexcept,ptis,ptrepeat,ptwhile,ptas,ptexports,
                     ptlabel,ptresourcestring, ptwith,ptasm,ptfile,ptlibrary,ptset,ptxor,ptbegin,ptfinalization,
                     ptmod,ptshl,ptcase,ptfinally,ptnil,ptshr,ptclass,ptfor,ptnot,ptstring,ptconst,ptfunction,ptobject,
                     ptthen,ptconstructor,ptgoto,ptof,ptthreadvar,ptdestructor,ptif,ptor,ptto,ptdispinterface,
                     ptimplementation,ptpacked,pttry,ptdiv,ptin,ptprocedure,pttype,ptdo,ptinherited,ptprogram,
                     ptunit,ptdownto,ptinitialization,ptproperty,ptuntil,ptelse,ptinline,ptraise,ptuses];

procedure TmwSimplePasPar.Factor;
begin
  case TokenID of
    ptAsciiChar, ptStringConst:
      begin
        CharString;
      end;
    ptAddressOp, ptDoubleAddressOp, ptIdentifier, ptInherited, ptPointerSymbol:
      begin
        Designator;
      end;
    ptRoundOpen:
      begin
        RoundOpen;
        ExpressionList;
        RoundClose;
      end;
    ptIntegerConst, ptFloat:
      begin
        Number;
      end;
    ptNil:
      begin
        NilToken;
      end;
    ptMinus:
      begin
        UnaryMinus;
        Factor;
      end;
    ptNot:
      begin
        NotOp;
        Factor;
      end;
    ptPlus:
      begin
        NextToken;
        Factor;
      end;
    ptSquareOpen:
      begin
        SetConstructor;
      end;
    ptString:
      begin
        StringStatement;
      end;
    ptFunction, ptProcedure:
      AnonymousMethod;
    else if (TokenID in ReservedWords) then begin
      InitAhead;
      AheadParse.NextToken;
      if (AheadParse.TokenId = ptAssign) then begin
        Lexer.ForceTokenID:= ptIdentifier;
        Factor;
        Exit;
      end;
      //Typecast to file: issue #183
      if (TokenID = ptFile) and (AheadParse.TokenId = ptRoundOpen) then begin
        Lexer.ForceTokenId:= ptIdentifier;
        Factor;
        Exit;
      end;
    end;
  end;

  while TokenID = ptSquareOpen do
    IndexOp;

  while TokenID = ptPointerSymbol do
    PointerSymbol;

  if TokenID = ptRoundOpen then
    Factor;

  while TokenID = ptPoint do
  begin
    DotOp;
    Factor;
  end;
end;

If you don't want to expose that kind of power everywhere, you can use an interposer like so:

  TmwBasePasLex = class(TObject)
...
  protected
...
    property ForceTokenID: TptTokenKind write FTokenId;
  end;

unit SimpleParser;
interface
type
   TmwBasePasLex = class(SimpleParser.Lexer.TmwBasePasLex)
   public
      property ForceTokenID;  //only expose ForceTokenID in the simple parser.
   end;