home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue38 / Parser / newparse.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-07-08  |  16.3 KB  |  624 lines

  1. { *****************************************************
  2.                  NewParse Unit
  3.  
  4.                   Paul Warren
  5.          HomeGrown Software Development
  6.        (c) 1997 Langley British Columbia.
  7.                 (604) 856-6523
  8.          e-mail:  hg_soft@uniserve.com
  9.     Home page: http://users.uniserve.com/~hg_soft
  10.   ***************************************************** }
  11.  
  12. unit Newparse;
  13. { $DEFINE DEBUG}
  14.  
  15. interface
  16.  
  17. uses Classes, Consts, SysUtils, Dialogs;
  18.  
  19. type
  20.   TParserClass = class of TCustomParser;
  21.  
  22.   TCustomParser = class
  23.   private
  24.     { private declarations }
  25.     FStream: TStream;
  26.     FOrigin: Longint;
  27.     FBuffer: PChar;
  28.     FBufPtr: PChar;
  29.     FBufEnd: PChar;
  30.     FSourcePtr: PChar;
  31.     FSourceEnd: PChar;
  32.     FTokenPtr: PChar;
  33.     FStringPtr: PChar;
  34.     FSourceLine: Integer;
  35.     FSaveChar: Char;
  36.     FToken: Char;
  37.     procedure ReadBuffer;
  38.     procedure SkipBlanks;
  39.     {$IFDEF Win32}
  40.     procedure Error(const Ident: string); virtual;
  41.     {$ELSE}
  42.     procedure Error(MessageID: Word); virtual;
  43.     {$ENDIF}
  44.     procedure ErrorStr(const Message: string);
  45.   public
  46.     { public declarations }
  47.     constructor Create(Stream: TStream); virtual;
  48.     destructor Destroy; override;
  49.     function NextToken: Char; virtual;
  50.     function TokenString: string; virtual;
  51.     function SourcePos: Longint;
  52.     property Token: Char read FToken;
  53.     property SourceLine: integer read FSourceLine;
  54.   end;
  55.  
  56.   TCSVParser = class(TCustomParser)
  57.   private
  58.     { private declarations }
  59.   public
  60.     { public declarations }
  61.     function TokenString: string; override;
  62.     function NextToken: char; override;
  63.   end;
  64.   TTextParser = class(TCustomParser)
  65.   private
  66.     { private declarations }
  67.   public
  68.     { public declarations }
  69.     function NextToken: Char; override;  end;
  70.   TPasParser = class(TTextParser)
  71.   private
  72.     { private declarations }
  73.   public
  74.     { public declarations }
  75.     function NextToken: Char; override;  end;
  76. const
  77.   toComment = Char(5);
  78.  
  79. type
  80.   TEnhPasParser = class(TPasParser)
  81.   private
  82.     { private declarations }
  83.   public
  84.     { public declarations }
  85.     function TokenString: string; override;    function NextToken: Char; override;  end;
  86. const
  87.   toOpenTag = Char(6);
  88.   toCloseTag = Char(7);
  89.  
  90. type
  91.   THtmlParser = class(TTextParser)
  92.   private
  93.     { private declarations }
  94.   public
  95.     { public declarations }
  96.     function TokenString: string; override;    function NextToken: Char; override;  end;
  97. var
  98.   Log: TextFile;
  99.  
  100. implementation
  101.  
  102. { TCustomParser }
  103.  
  104. const
  105.   ParseBufSize: integer = 4096;
  106.  
  107. constructor TCustomParser.Create(Stream: TStream);
  108. begin
  109.   FStream := Stream;
  110.   GetMem(FBuffer, ParseBufSize);
  111.   FBuffer[0] := #0;
  112.   FBufPtr := FBuffer;
  113.   FBufEnd := FBuffer + ParseBufSize;
  114.   FSourcePtr := FBuffer;
  115.   FSourceEnd := FBuffer;
  116.   FTokenPtr := FBuffer;
  117.   FSourceLine := 1;
  118.   {$IFDEF DEBUG}
  119.   writeln(log,'');
  120.   writeln(log, 'FBuffer FBufPtr FSrcPtr   FSrcEnd FBufEnd Pos Occured');
  121.   writeln(log,'');
  122.   writeln(log,LongInt(FBuffer), ' ', LongInt(FBufPtr), ' ', LongInt(FSourcePtr),' ', FSourcePtr^, ' ', LongInt(FSourceEnd), ' ', LongInt(FBufEnd), ' ',FStream.Position,' on create');
  123.   {$ENDIF}
  124.   NextToken;
  125. end;
  126.  
  127. destructor TCustomParser.Destroy;
  128. begin
  129.   if FBuffer <> nil then
  130.   begin
  131.     FStream.Seek(Longint(FTokenPtr) - Longint(FSourceEnd), 1);
  132.     FreeMem(FBuffer, ParseBufSize);
  133.   end;
  134. end;
  135.  
  136. procedure TCustomParser.ReadBuffer;
  137. var
  138.   Count: Integer;
  139. begin
  140.   try
  141.     Inc(FOrigin, FSourcePtr - FBuffer);
  142.     FSourceEnd[0] := FSaveChar;
  143.   {$IFDEF DEBUG}
  144.     writeln(log,LongInt(FBuffer), ' ', LongInt(FBufPtr), ' ', LongInt(FSourcePtr),' ', FSourcePtr^,' ', LongInt(FSourceEnd),' ',LongInt(FBufEnd), ' ',FStream.Position, ' before read');
  145.   {$ENDIF}
  146.     Count := FBufPtr - FSourcePtr;
  147.     if Count <> 0 then Move(FSourcePtr[0], FBuffer[0], Count);
  148.     FBufPtr := FBuffer + Count;
  149.     Inc(FBufPtr, FStream.Read(FBufPtr[0], FBufEnd - FBufPtr));
  150.   {$IFDEF DEBUG}
  151.     writeln(log,LongInt(FBuffer), ' ', LongInt(FBufPtr), ' ', LongInt(FSourcePtr),' ', FSourcePtr^, ' ', LongInt(FSourceEnd), ' ', LongInt(FBufEnd), ' ',FStream.Position, ' after read');
  152.   {$ENDIF}
  153.     FSourcePtr := FBuffer;
  154.     FSourceEnd := FBufPtr;
  155.     if FSourceEnd = FBufEnd then
  156.     begin
  157.       FSourceEnd := LineStart(FBuffer, FSourceEnd - 1);
  158.       if FSourceEnd = FBuffer then Error(SLineTooLong);
  159.     end;
  160.     FSaveChar := FSourceEnd[0];
  161.     FSourceEnd[0] := #0;
  162.   except
  163.     on EStreamError do
  164.       MessageDlg('FSourcePtr^ = '+FSourcePtr^,mtError,
  165.         [mbOK],0);
  166.     on EAccessViolation do
  167.       MessageDlg('FSourcePtr^ = '+FSourcePtr^,mtError,
  168.         [mbOK],0);
  169.   end;
  170. end;
  171.  
  172. function TCustomParser.NextToken: Char;
  173. begin
  174.   FToken := FSourcePtr^;
  175.   if FToken <> toEOF then Inc(FSourcePtr);
  176.   Result := FToken;
  177. end;
  178.  
  179. procedure TCustomParser.SkipBlanks;
  180. begin
  181.   while True do
  182.   begin
  183.     case FSourcePtr^ of
  184.       #0:
  185.         begin
  186.           ReadBuffer;
  187.           if FSourcePtr^ = #0 then Exit;
  188.           Continue;
  189.         end;
  190.       #10:
  191.         Inc(FSourceLine);
  192.       #33..#255:
  193.         Exit;
  194.     end;
  195.     Inc(FSourcePtr);
  196.   end;
  197. end;
  198.  
  199. function TCustomParser.TokenString: string;
  200. var
  201.   L: Integer;
  202. begin
  203.   if (FToken = toString) then
  204.     L := FStringPtr - FTokenPtr else
  205.     L := FSourcePtr - FTokenPtr;
  206.   {$IFDEF Win32}
  207.   SetString(Result, FTokenPtr, L);
  208.   {$ELSE}
  209.   if L > 255 then L := 255;
  210.   Result[0] := Char(L);
  211.   {$ENDIF}
  212.   Move(FTokenPtr[0], Result[1], L);
  213. end;
  214.  
  215. {$IFDEF Win32}
  216. procedure TCustomParser.Error(const Ident: string);
  217. begin
  218.   ErrorStr(Ident);
  219. end;
  220.  
  221. procedure TCustomParser.ErrorStr(const Message: string);
  222. begin
  223.   raise EParserError.CreateFmt(SParseError, [Message, FSourceLine]);
  224. end;
  225. {$ELSE}
  226. procedure TCustomParser.Error(MessageID: Word);
  227. begin
  228.   ErrorStr(LoadStr(MessageID));
  229. end;
  230.  
  231. procedure TCustomParser.ErrorStr(const Message: string);
  232. begin
  233.   raise EParserError.Create(FmtLoadStr(SParseError, [Message, FSourceLine]));
  234. end;
  235. {$ENDIF}
  236.  
  237. function TCustomParser.SourcePos: Longint;
  238. begin
  239.   Result := FOrigin + (FTokenPtr - FBuffer);
  240. end;
  241.  
  242. { TCSVParser }
  243. function TCSVParser.TokenString: string;
  244. var
  245.   L: Integer;
  246. begin
  247.   if (FToken = toSymbol) then
  248.     L := FStringPtr - FTokenPtr else
  249.     L := FSourcePtr - FTokenPtr;
  250.   {$IFDEF Win32}
  251.   SetString(Result, FTokenPtr, L);
  252.   {$ELSE}
  253.   if L > 255 then L := 255;
  254.   Result[0] := Char(L);
  255.   {$ENDIF}
  256.   Move(FTokenPtr[0], Result[1], L);
  257. end;
  258.  
  259. function TCSVParser.NextToken: Char;
  260. begin
  261.   SkipBlanks;
  262.   FTokenPtr := FSourcePtr;
  263.   case FSourcePtr^ of
  264.     'A'..'Z', 'a'..'z', '_':
  265.       begin
  266.         Inc(FSourcePtr);
  267.         FStringPtr := FSourcePtr;
  268.         while true do
  269.         begin
  270.           case FSourcePtr^ of
  271.             ',': Break;
  272.             #0: Break;
  273.           end;
  274.           FStringPtr^ := FSourcePtr^;
  275.           Inc(FStringPtr);
  276.           Inc(FSourcePtr);
  277.         end;
  278.         FToken := toSymbol;
  279.         Result := FToken;
  280.       end;
  281.     '-', '0'..'9':
  282.       begin
  283.         Inc(FSourcePtr);
  284.         while FSourcePtr^ in ['0'..'9'] do Inc(FSourcePtr);
  285.         FToken := toInteger;
  286.         Result := FToken;
  287.         while FSourcePtr^ in ['0'..'9', '.', 'e', 'E', '+', '-'] do
  288.         begin
  289.           Inc(FSourcePtr);
  290.           FToken := toFloat;
  291.           Result := FToken;
  292.         end;
  293.       end;
  294.     else Result := inherited NextToken;
  295.   end;
  296. end;
  297.  
  298. { TTextParser }
  299. function TTextParser.NextToken: Char;
  300. begin
  301.   SkipBlanks;
  302.   FTokenPtr := FSourcePtr;
  303.   case FSourcePtr^ of
  304.     'A'..'Z', 'a'..'z', '_':
  305.       begin
  306.         Inc(FSourcePtr);
  307.         while True do
  308.           case FSourcePtr^ of
  309.             'A'..'Z', 'a'..'z', '0'..'9', '_': Inc(FSourcePtr);
  310.             '''': begin  { apostrophies }
  311.                 if (FSourcePtr+1)^ in ['A'..'Z', 'a'..'z', '0'..'9', '_'] then Inc(FSourcePtr)
  312.                 else Break;
  313.               end;
  314.             '-': begin  { hyphenated words }
  315.                 if (FSourcePtr+1)^ in ['A'..'Z', 'a'..'z', '0'..'9', '_'] then Inc(FSourcePtr)
  316.                 else Break;
  317.               end;
  318.             else Break;
  319.           end;
  320.         FToken := toSymbol;
  321.         Result := FToken;
  322.       end;
  323.     '-', '0'..'9':
  324.       begin
  325.         Inc(FSourcePtr);
  326.         while FSourcePtr^ in ['0'..'9'] do Inc(FSourcePtr);
  327.         FToken := toInteger;
  328.         Result := FToken;
  329.         while FSourcePtr^ in ['0'..'9', '.', 'e', 'E', '+', '-'] do
  330.         begin
  331.           Inc(FSourcePtr);
  332.           FToken := toFloat;
  333.           Result := FToken;
  334.         end;
  335.       end;
  336.     else Result := inherited NextToken;
  337.   end;
  338. end;
  339.  
  340. { TPasParser }
  341. function TPasParser.NextToken: Char;
  342. var
  343.   I: integer;
  344. begin
  345.   SkipBlanks;
  346.   FTokenPtr := FSourcePtr;
  347.   case FSourcePtr^ of
  348.     'A'..'Z', 'a'..'z', '_':
  349.       begin
  350.         Inc(FSourcePtr);
  351.         while FSourcePtr^ in ['A'..'Z', 'a'..'z', '0'..'9', '_'] do Inc(FSourcePtr);
  352.         FToken := toSymbol;
  353.         Result := FToken;
  354.       end;
  355.     '#', '''':
  356.       begin
  357.         FStringPtr := FSourcePtr;
  358.         while True do
  359.           case FSourcePtr^ of
  360.             '#':
  361.               begin
  362.                 Inc(FSourcePtr);
  363.                 I := 0;
  364.                 while FSourcePtr^ in ['0'..'9'] do
  365.                 begin
  366.                   I := I * 10 + (Ord(FSourcePtr^) - Ord('0'));
  367.                   Inc(FSourcePtr);
  368.                 end;
  369.                 FStringPtr^ := Chr(I);
  370.                 Inc(FStringPtr);
  371.               end;
  372.             '''':
  373.               begin
  374.                 Inc(FSourcePtr);
  375.                 while True do
  376.                 begin
  377.                   case FSourcePtr^ of
  378.                     #0, #10, #13:
  379.                       Error(SInvalidString);
  380.                     '''':
  381.                       begin
  382.                         Inc(FSourcePtr);
  383.                         if FSourcePtr^ <> '''' then Break;
  384.                       end;
  385.                   end;
  386.                   FStringPtr^ := FSourcePtr^;
  387.                   Inc(FStringPtr);
  388.                   Inc(FSourcePtr);
  389.                 end;
  390.               end;
  391.           else
  392.             Break;
  393.           end;
  394.         FToken := toString;
  395.         Result := FToken;
  396.       end;
  397.     '$':
  398.       begin
  399.         FToken := FSourcePtr^;  { assume NOT an integer }
  400.         Result := FToken;
  401.         Inc(FSourcePtr);
  402.         while true do
  403.         begin
  404.           case FSourcePtr^ of
  405.             '0'..'9', 'A'..'F', 'a'..'f': Inc(FSourcePtr);
  406.             else Break;
  407.           end;
  408.           FToken := toInteger;
  409.           Result := FToken;
  410.         end;
  411.       end;
  412.   (*  '-', '0'..'9':
  413.       begin
  414.         Inc(FSourcePtr);
  415.         while FSourcePtr^ in ['0'..'9'] do Inc(FSourcePtr);
  416.         FToken := toInteger;
  417.         Result := FToken;
  418.         while FSourcePtr^ in ['0'..'9', '.', 'e', 'E', '+', '-'] do
  419.         begin
  420.           Inc(FSourcePtr);
  421.           FToken := toFloat;
  422.           Result := FToken;
  423.         end;
  424.       end;  *)
  425.     else Result := inherited NextToken;
  426.   end;
  427. end;
  428.  
  429. { TEnhPasParser }
  430. function TEnhPasParser.TokenString: string;
  431. var
  432.   L: Integer;
  433. begin
  434.   if (FToken = toString) or (FToken = toComment) then
  435.     L := FStringPtr - FTokenPtr else
  436.     L := FSourcePtr - FTokenPtr;
  437.   {$IFDEF Win32}
  438.   SetString(Result, FTokenPtr, L);
  439.   {$ELSE}
  440.   if L > 255 then L := 255;
  441.   Result[0] := Char(L);
  442.   {$ENDIF}
  443.   Move(FTokenPtr[0], Result[1], L);
  444. end;
  445.  
  446. function TEnhPasParser.NextToken: Char;
  447. begin
  448.   SkipBlanks;
  449.   FTokenPtr := FSourcePtr;
  450.   case FSourcePtr^ of
  451.     '{':
  452.       begin { comment or compiler directive... }
  453.         FStringPtr := FSourcePtr;
  454.         Inc(FSourcePtr);  { check next char... }
  455.         while true do
  456.         begin
  457.           case FSourcePtr^ of
  458.             #0: begin
  459.               ReadBuffer;
  460.               FStringPtr := FSourcePtr;
  461.               if FSourcePtr^ = #0 then Break;
  462.               {$IFDEF DEBUG}
  463.               writeln(Log, 'in comment');
  464.               {$ENDIF}
  465.             end;
  466.             #10: Inc(FSourceLine);
  467.             '}':
  468.               begin
  469.                 Inc(FSourcePtr);
  470.                 Break;      { end comment... }
  471.               end;
  472.           end;
  473.           FStringPtr^ := FSourcePtr^;
  474.           Inc(FStringPtr);
  475.           Inc(FSourcePtr);
  476.         end;
  477.         FToken := toComment;
  478.         Result := FToken;
  479.       end;
  480.     '(', '/':  { possible comment or compiler directive... }
  481.       begin
  482.         FToken := FSourcePtr^; { assume NOT a comment }
  483.         Result := FToken;
  484.         FStringPtr := FSourcePtr;
  485.         Inc(FSourcePtr);  { check next char... }
  486.         case FSourcePtr^ of
  487.           '*':  { is a comment }
  488.             begin
  489.               Inc(FSourcePtr);  { check next char... }
  490.               while True do
  491.               begin
  492.                 case FSourcePtr^ of
  493.                   #0: begin
  494.                     ReadBuffer;
  495.                     FStringPtr := FSourcePtr;
  496.                     if FSourcePtr^ = #0 then Break;
  497.                     {$IFDEF DEBUG}
  498.                     writeln(Log, 'in comment');
  499.                     {$ENDIF}
  500.                   end;
  501.                   #10: Inc(FSourceLine);
  502.                   '*':
  503.                     begin
  504.                       Inc(FSourcePtr);
  505.                       if FSourcePtr^ = ')' then
  506.                       begin
  507.                         Inc(FSourcePtr);
  508.                         Break; { end of comment }
  509.                       end;
  510.                     end;
  511.                 end;
  512.                 FStringPtr^ := FSourcePtr^;
  513.                 Inc(FStringPtr);
  514.                 Inc(FSourcePtr);
  515.               end;
  516.               FToken := toComment;
  517.               Result := FToken;
  518.             end;
  519.           '/':  { is a comment }
  520.             begin
  521.               Inc(FSourcePtr);
  522.               while (FSourcePtr^ <> #13) do  { end of line, hence comment }
  523.               begin
  524.                 FStringPtr^ := FSourcePtr^;
  525.                 Inc(FStringPtr);
  526.                 Inc(FSourcePtr);
  527.               end;
  528.               FToken := toComment;
  529.               Result := FToken;
  530.             end;
  531.         end;
  532.       end;
  533.     else Result := inherited NextToken;
  534.   end;
  535. end;
  536.  
  537. { THtmlParser }
  538. function THtmlParser.TokenString: string;
  539. var
  540.   L: Integer;
  541. begin
  542.   if (FToken = toString) or (FToken = toOpenTag)
  543.     or (FToken = toCloseTag) then
  544.       L := FStringPtr - FTokenPtr else
  545.       L := FSourcePtr - FTokenPtr;
  546.   {$IFDEF Win32}
  547.   SetString(Result, FTokenPtr, L);
  548.   {$ELSE}
  549.   if L > 255 then L := 255;
  550.   Result[0] := Char(L);
  551.   {$ENDIF}
  552.   Move(FTokenPtr[0], Result[1], L);
  553. end;
  554.  
  555. function THtmlParser.NextToken: Char;
  556. begin
  557.   SkipBlanks;
  558.   FTokenPtr := FSourcePtr;
  559.   case FSourcePtr^ of
  560.     '<':   { is a tag }
  561.     begin
  562.       FStringPtr := FSourcePtr;
  563.       Inc(FSourcePtr);
  564.       case FSourcePtr^ of
  565.         '/':  { is an 'close' tag }
  566.           begin
  567.             Inc(FSourcePtr);
  568.             while true do
  569.             begin
  570.               case FSourcePtr^ of
  571.                 #0: begin
  572.                   ReadBuffer;
  573.                   FStringPtr := FSourcePtr;
  574.                   if FSourcePtr^ = #0 then Break;
  575.                 end;
  576.                 '>': begin
  577.                   Inc(FSourcePtr);
  578.                   Break; { end of tag }
  579.                 end;
  580.               end; {case}
  581.               FStringPtr^ := FSourcePtr^;
  582.               Inc(FStringPtr);
  583.               Inc(FSourcePtr);
  584.             end;
  585.             FToken := toCloseTag;
  586.             Result := FToken;
  587.           end;
  588.         else
  589.           begin
  590.             while true do
  591.             begin
  592.               case FSourcePtr^ of
  593.                 #0: begin
  594.                   ReadBuffer;
  595.                   FStringPtr := FSourcePtr;
  596.                   if FSourcePtr^ = #0 then Break;
  597.                 end;
  598.                 '>': begin
  599.                   Inc(FSourcePtr);
  600.                   Break; { end of tag }
  601.                 end;
  602.               end; {case}
  603.               FStringPtr^ := FSourcePtr^;
  604.               Inc(FStringPtr);
  605.               Inc(FSourcePtr);
  606.             end;
  607.             FToken := toOpenTag;
  608.             Result := FToken;
  609.           end;
  610.       end; {case}
  611.     end;
  612.     else Result := inherited NextToken;
  613.   end;
  614. end;
  615.  
  616. {$IFDEF DEBUG}
  617. initialization
  618.   AssignFile(Log, 'debug.log');
  619.   Rewrite(Log);
  620. finalization
  621.   CloseFile(Log);
  622. {$ENDIF}
  623. end.
  624.