xpLex.pas
上传用户:yjb1804
上传日期:2021-01-30
资源大小:3105k
文件大小:15k
源码类别:

Email服务器

开发平台:

Delphi

  1. unit xpLex;
  2. (*
  3.  * The contents of this file are subject to the Mozilla Public
  4.  * License Version 1.1 (the "License"); you may not use this file
  5.  * except in compliance with the License. You may obtain a copy of
  6.  * the License at http://www.mozilla.org/MPL/
  7.  *
  8.  * Software distributed under the License is distributed on an "AS
  9.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10.  * implied. See the License for the specific language governing
  11.  * rights and limitations under the License.
  12.  *
  13.  * This code was inspired to expidite the creation of unit tests 
  14.  * for use the Dunit test frame work.
  15.  * 
  16.  * The Initial Developer of XPGen is Michael A. Johnson.
  17.  * Portions created The Initial Developer is Copyright (C) 2000.
  18.  * Portions created by The DUnit Group are Copyright (C) 2000.
  19.  * All rights reserved.
  20.  *
  21.  * Contributor(s):
  22.  * Michael A. Johnson <majohnson@golden.net>
  23.  * Juanco A馿z <juanco@users.sourceforge.net>
  24.  * Chris Morris <chrismo@users.sourceforge.net>
  25.  * Jeff Moore <JeffMoore@users.sourceforge.net>
  26.  * The DUnit group at SourceForge <http://dunit.sourceforge.net>
  27.  *
  28.  *)
  29. (*
  30. Unit        : xpLex
  31. Description : provides a lexical analyzer with which the xpParser can
  32.               rely on to provide tokens from a source stream. This code
  33.               has a very strong resemblance the TPARSER class found in
  34.               delphi's classes unit.  However, when I was just about done and
  35.               ready to release, I discovered that Tparser cannot deal with text
  36.               inside a comment and would get tripped up trying to parse:
  37.               { this is delphi's fault }.
  38. Programmer  : mike
  39. Date        : 05-Aug-2000
  40. *)
  41. interface
  42. uses
  43.   classes;
  44. const
  45.   toEOF = Char(0);
  46.   toSymbol = Char(1);
  47.   toString = Char(2);
  48.   toBraceComment = char(3);
  49.   toSlashComment = char(4);
  50.   toLegacyComment = char(5);
  51.   toEOL = char(6);
  52.   toNull = char(7);
  53.   lambdaSet: set of char = [toBraceComment, toSlashComment,
  54.     toLegacyComment, toEOL, toNull];
  55. type
  56.   TActionResult = (arLambda, arRecognized, arTransition);
  57.   TLegacyCommentState = (lcsOpenParen, lcsOpenStar, lcsLambda, lcsCloseStar);
  58.   TSlashCommentState = (scsSlash, scsLambda);
  59.   TActionState = class
  60.   protected
  61.     fTokenStr: string;
  62.   public
  63.     constructor create;
  64.     function doAction(input: char): TActionResult; virtual; abstract;
  65.     function StartAccept(input: char): boolean; virtual;
  66.     function TokenStr: string;
  67.     procedure Reset; virtual;
  68.     function TokenType: char; virtual;
  69.   end;
  70.   TDefaultState = class(TActionState)
  71.   public
  72.     function doAction(input: char): TActionResult; override;
  73.     function StartAccept(input: char): boolean; override;
  74.     function TokenType: char; override;
  75.   end;
  76.   
  77.   TNullCharacter = class(TActionState)
  78.   private
  79.     fSourceLine: integer;
  80.   public
  81.     constructor create;
  82.     function doAction(input: char): TActionResult; override;
  83.     function StartAccept(input: char): boolean; override;
  84.     property SourceLine: integer read fSourceLine;
  85.     function TokenType: char; override;
  86.   end;
  87.   TSrcLine = class(TActionState)
  88.   protected
  89.     FSourceLine: Integer;
  90.   public
  91.     constructor create;
  92.     function doAction(input: char): TActionResult; override;
  93.     function StartAccept(input: char): boolean; override;
  94.     property SourceLine: integer read fSourceLine;
  95.     function TokenType: char; override;
  96.   end;
  97.   TIdentToken = class(TActionState)
  98.   public
  99.     constructor Create;
  100.     function doAction(input: char): TActionResult; override;
  101.     function StartAccept(input: char): boolean; override;
  102.     function TokenType: char; override;
  103.   end;
  104.   TConstStringToken = class(TActionState)
  105.   public
  106.     constructor Create;
  107.     function doAction(input: char): TActionResult; override;
  108.     function StartAccept(input: char): boolean; override;
  109.     function TokenType: char; override;
  110.   end;
  111.   TBraceComment = class(TSrcLine)
  112.   public
  113.     constructor Create;
  114.     function doAction(input: char): TActionResult; override;
  115.     function StartAccept(input: char): boolean; override;
  116.     function TokenType: char; override;
  117.   end;
  118.   TLegacyComment = class(TSrcLine)
  119.   protected
  120.     tokenState: TLegacyCommentState;
  121.   public
  122.     constructor Create;
  123.     function doAction(input: char): TActionResult; override;
  124.     function StartAccept(input: char): boolean; override;
  125.     function TokenType: char; override;
  126.   end;
  127.   TSlashComment = class(TSrcLine)
  128.     tokenState: TSlashCommentState;
  129.   public
  130.     constructor Create;
  131.     function doAction(input: char): TActionResult; override;
  132.     function StartAccept(input: char): boolean; override;
  133.     function TokenType: char; override;
  134.   end;
  135.   TLexer = class
  136.   private
  137.     function TokenType: Char;
  138.     {    procedure SkipBlanks;}
  139.   protected
  140.     FStreamSize: Longint;
  141.     FBuffer: PChar;
  142.     FBufPtr: PChar;
  143.     FToken: Char;
  144.     tokenStr: string;
  145.     stateActionList: TList;
  146.     activeState: TActionState;
  147.     {    procedure SkipBlanks;}
  148.     procedure Error(errMsg: string);
  149.     function GetSrcLine: Integer;
  150.     procedure CreateStateActions;
  151.     procedure ReleaseStateActions;
  152.     function FindState(inputChar: char): TActionState;
  153.   public
  154.     constructor Create(Stream: TStream);
  155.     destructor Destroy; override;
  156.     function NextToken: Char;
  157.     function TokenString: string;
  158.     property SourceLine: Integer read GetSrcLine;
  159.     property Token: Char read TokenType;
  160.   end;
  161. implementation
  162. uses
  163.   ListSupport,
  164.   SysUtils;
  165. { TLexer }
  166. constructor TLexer.Create(Stream: TStream);
  167. begin
  168.   CreateStateActions;
  169.   activeState := nil;
  170.   tokenStr := '';
  171.   FStreamSize := Stream.Size+1;
  172.   GetMem(FBuffer, fStreamSize);
  173.   FBufPtr := FBuffer;
  174.   Stream.Read(FBufPtr[0], Stream.Size);
  175.   FBufPtr[Stream.Size] := #0;
  176.   FBufPtr := FBuffer;
  177.   { find a state }
  178.   NextToken;
  179. end;
  180. procedure TLexer.CreateStateActions;
  181. begin
  182.   stateActionList := TList.Create;
  183.   stateActionList.Add(TBraceComment.Create);
  184.   stateActionList.Add(TConstStringToken.Create);
  185.   stateActionList.Add(TIdentToken.Create);
  186.   stateActionList.Add(TLegacyComment.Create);
  187.   stateActionList.Add(TNullCharacter.Create);
  188.   stateActionList.Add(TSlashComment.Create);
  189.   stateActionList.Add(TSrcLine.Create);
  190.   stateActionList.Add(TDefaultState.Create);
  191. end;
  192. destructor TLexer.Destroy;
  193. begin
  194.   ReleaseStateActions;
  195.   if FBuffer <> nil then
  196.     begin
  197.       FreeMem(FBuffer, FStreamSize);
  198.     end;
  199. end;
  200. procedure TLexer.Error(errMsg: string);
  201. begin
  202.   raise exception.create(errMsg);
  203. end;
  204. function TLexer.FindState(inputChar: char): TActionState;
  205. var
  206.   stateIter: integer;
  207.   stateAction: TActionState;
  208. begin
  209.   result := nil;
  210.   for stateIter := 0 to stateActionList.Count - 1 do
  211.     begin
  212.       stateAction := stateActionList[stateIter];
  213.       if stateAction.StartAccept(inputChar) then
  214.         begin
  215.           { reset the internal state }
  216.           stateAction.Reset;
  217.           result := stateAction;
  218.           exit;
  219.         end;
  220.     end;
  221. end;
  222. function TLexer.GetSrcLine: Integer;
  223. var
  224.   stateIter: integer;
  225.   lineRef: TSrcLine;
  226.   stateAction: TActionState;
  227. begin
  228.   result := 0;
  229.   for stateIter := 0 to stateActionList.Count - 1 do
  230.     begin
  231.       stateAction := stateActionList[stateIter];
  232.       if stateAction is TSrcLine then
  233.         begin
  234.           lineRef := TSrcLine(stateAction);
  235.           result := result + lineRef.SourceLine;
  236.         end;
  237.     end;
  238. end;
  239. function TLexer.NextToken: Char;
  240. var
  241.   actionResult: TActionResult;
  242. begin
  243.   repeat
  244.     activeState := FindState(FBufPtr^);
  245.     repeat
  246.       actionResult := activeState.doAction(FBufPtr^);
  247.       case actionResult of
  248.         arLambda,
  249.           arRecognized:
  250.           begin
  251.             Inc(FBufPtr);
  252.           end;
  253.       end;
  254.     until (actionResult <> arLambda);
  255.   until not (activeState.TokenType in lambdaSet);
  256.   result := activeState.TokenType; 
  257. end;
  258. procedure TLexer.ReleaseStateActions;
  259. begin
  260.   ListFreeObjectItems(stateActionList);
  261.   stateActionList.free;
  262. end;
  263. function TLexer.TokenString: string;
  264. begin
  265.   result := activeState.TokenStr;
  266. end;
  267. function TLexer.TokenType: Char;
  268. begin
  269.   result := activeState.TokenType;
  270. end;
  271. { TActionState }
  272. constructor TActionState.create;
  273. begin
  274.   inherited create;
  275. end;
  276. procedure TActionState.Reset;
  277. begin
  278.   ftokenStr := '';
  279. end;
  280. function TActionState.StartAccept(input: char): boolean;
  281. begin
  282.   result := false;
  283. end;
  284. function TActionState.TokenStr: string;
  285. begin
  286.   result := ftokenStr;
  287. end;
  288. function TActionState.TokenType: char;
  289. begin
  290.   result := toNull;
  291. end;
  292. { TSrcLine }
  293. constructor TSrcLine.create;
  294. begin
  295.   inherited Create;
  296.   FSourceLine := 1;
  297. end;
  298. function TSrcLine.doAction(input: char): TActionResult;
  299. begin
  300.   result := arTransition;
  301.   if input = #10 then
  302.     begin
  303.       inc(fSourceLine);
  304.       result := arRecognized
  305.     end;
  306. end;
  307. function TSrcLine.StartAccept(input: char): boolean;
  308. begin
  309.   result := input = #10;
  310. end;
  311. function TSrcLine.TokenType: char;
  312. begin
  313.   result := toEOL;
  314. end;
  315. { TIdentToken }
  316. constructor TIdentToken.Create;
  317. begin
  318.   inherited create;
  319.   Reset;
  320. end;
  321. function TIdentToken.doAction(input: char): TActionResult;
  322. begin
  323.   if ftokenStr = '' then
  324.     begin
  325.       if input in ['A'..'Z', 'a'..'z', '_'] then
  326.         begin
  327.           ftokenStr := ftokenStr + input;
  328.           result := arLambda;
  329.         end
  330.       else
  331.         result := arTransition;
  332.     end
  333.   else
  334.     if input in ['A'..'Z', 'a'..'z', '0'..'9', '_'] then
  335.       begin
  336.         ftokenStr := ftokenStr + input;
  337.         result := arLambda;
  338.       end
  339.     else
  340.       begin
  341.         result := arTransition;
  342.       end;
  343. end;
  344. function TIdentToken.StartAccept(input: char): boolean;
  345. begin
  346.   result := input in ['A'..'Z', 'a'..'z', '_'];
  347. end;
  348. function TIdentToken.TokenType: char;
  349. begin
  350.   result := toSymbol;
  351. end;
  352. { TConstStringToken }
  353. constructor TConstStringToken.Create;
  354. begin
  355. end;
  356. function TConstStringToken.doAction(input: char): TActionResult;
  357. begin
  358.   if ftokenStr = '' then
  359.     begin
  360.       if input in [''''] then
  361.         begin
  362.           ftokenStr := ftokenStr + input;
  363.           result := arLambda;
  364.         end
  365.       else
  366.         result := arTransition;
  367.     end
  368.   else
  369.     begin
  370.       result := arLambda;
  371.       ftokenStr := ftokenStr + input;
  372.       if input in [''''] then
  373.         result := arRecognized;
  374.     end;
  375. end;
  376. function TConstStringToken.StartAccept(input: char): boolean;
  377. begin
  378.   result := input in [''''];
  379. end;
  380. function TConstStringToken.TokenType: char;
  381. begin
  382.   result := toString;
  383. end;
  384. { TBraceComment }
  385. constructor TBraceComment.Create;
  386. begin
  387.   inherited create;
  388. end;
  389. function TBraceComment.doAction(input: char): TActionResult;
  390. begin
  391.   if ftokenStr = '' then
  392.     begin
  393.       if input in ['{'] then
  394.         begin
  395.           ftokenStr := ftokenStr + input;
  396.           result := arLambda;
  397.         end
  398.       else
  399.         begin
  400.           result := arTransition;
  401.         end;
  402.     end
  403.   else
  404.     begin
  405.       ftokenStr := ftokenStr + input;
  406.       result := arLambda;
  407.       if input in ['}'] then
  408.         result := arRecognized;
  409.     end;
  410. end;
  411. function TBraceComment.StartAccept(input: char): boolean;
  412. begin
  413.   result := input in ['{']
  414. end;
  415. function TBraceComment.TokenType: char;
  416. begin
  417.   result := toBraceComment;
  418. end;
  419. { TLegacyComment }
  420. constructor TLegacyComment.Create;
  421. begin
  422.   inherited create;
  423.   reset;
  424. end;
  425. function TLegacyComment.doAction(input: char): TActionResult;
  426. begin
  427.   if ftokenStr = '' then
  428.     begin
  429.       if input in ['('] then
  430.         begin
  431.           ftokenStr := ftokenStr + input;
  432.           result := arLambda;
  433.           tokenState := lcsOpenParen;
  434.         end
  435.       else
  436.         result := arTransition;
  437.     end
  438.   else
  439.     begin
  440.       result := arLambda;
  441.       case tokenState of
  442.         lcsOpenParen:
  443.           begin
  444.             if input in ['*'] then
  445.               begin
  446.                 ftokenStr := ftokenStr + input;
  447.                 tokenState := lcsOpenStar;
  448.               end
  449.             else
  450.               result := arTransition;
  451.           end;
  452.         lcsLambda,
  453.           lcsOpenStar:
  454.           begin
  455.             if input in ['*'] then
  456.               begin
  457.                 ftokenStr := ftokenStr + input;
  458.                 tokenState := lcsCloseStar;
  459.               end
  460.             else
  461.               begin
  462.                 ftokenStr := ftokenStr + input;
  463.                 tokenState := lcslambda;
  464.               end;
  465.           end;
  466.         lcsCloseStar:
  467.           begin
  468.             if input in [')'] then
  469.               begin
  470.                 ftokenStr := ftokenStr + input;
  471.                 result := arRecognized;
  472.               end
  473.             else
  474.               begin
  475.                 ftokenStr := ftokenStr + input;
  476.                 tokenState := lcslambda;
  477.               end;
  478.           end;
  479.       end;
  480.     end;
  481. end;
  482. function TLegacyComment.StartAccept(input: char): boolean;
  483. begin
  484.   result := input in ['('];
  485. end;
  486. function TLegacyComment.TokenType: char;
  487. begin
  488.   result := toLegacyComment;
  489.   if length(ftokenStr) = 1 then
  490.     result := ftokenStr[1];
  491. end;
  492. { TSlashComment }
  493. constructor TSlashComment.Create;
  494. begin
  495.   inherited create;
  496. end;
  497. function TSlashComment.doAction(input: char): TActionResult;
  498. begin
  499.   if ftokenStr = '' then
  500.     begin
  501.       if input in ['/'] then
  502.         begin
  503.           ftokenStr := ftokenStr + input;
  504.           result := arLambda;
  505.           tokenState := scsSlash;
  506.         end
  507.       else
  508.         result := arTransition;
  509.     end
  510.   else
  511.     begin
  512.       result := arLambda;
  513.       case tokenState of
  514.         scsSlash:
  515.           begin
  516.             case input of
  517.               '/':
  518.                 begin
  519.                   tokenState := scsLambda;
  520.                   ftokenStr := ftokenStr + input;
  521.                 end
  522.               else
  523.                 begin
  524.                   result := arTransition;
  525.                 end;
  526.             end;
  527.           end;
  528.         scsLambda:
  529.           begin
  530.             case input of
  531.               #10:
  532.                 begin
  533.                   result := arRecognized;
  534.                 end;
  535.               else
  536.                 ftokenStr := ftokenStr + input;
  537.             end;
  538.           end;
  539.       end;
  540.     end;
  541. end;
  542. function TSlashComment.StartAccept(input: char): boolean;
  543. begin
  544.   result := input in ['/'];
  545. end;
  546. function TSlashComment.TokenType: char;
  547. begin
  548.   result := toSlashComment;
  549.   if length(ftokenStr) = 1 then
  550.     result := ftokenStr[1];  
  551. end;
  552. { TNullCharacter }
  553. constructor TNullCharacter.Create;
  554. begin
  555.   inherited Create;
  556. end;
  557. function TNullCharacter.doAction(input: char): TActionResult;
  558. begin
  559.   case input of
  560.     #0..#9,
  561.       #11..#32: result := arLambda;
  562.     else
  563.       result := arTransition;
  564.   end;
  565. end;
  566. function TNullCharacter.StartAccept(input: char): boolean;
  567. begin
  568.   result := (input in [#0..#9]) or
  569.     (input in [#11..#32]);
  570. end;
  571. function TNullCharacter.TokenType: char;
  572. begin
  573.   result := toNull;
  574. end;
  575. { TDefaultState }
  576. function TDefaultState.doAction(input: char): TActionResult;
  577. begin
  578.   ftokenStr := ftokenStr + input;
  579.   result := arRecognized;
  580. end;
  581. function TDefaultState.StartAccept(input: char): boolean;
  582. begin
  583.   result := true;
  584. end;
  585. function TDefaultState.TokenType: char;
  586. begin
  587.   result := inherited TokenType;
  588.   if ftokenStr <> '' then
  589.     result := ftokenStr[1];
  590. end;
  591. end.