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

Email服务器

开发平台:

Delphi

  1. unit XPTemplateParser;
  2. {
  3.  $Source: /cvsroot/dunit/dunit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject/XPTemplateParser.pas,v $
  4.  $Revision: 1.2 $
  5.  $Date: 2004/05/03 15:07:16 $
  6.  Last amended by $Author: pvspain $
  7.  $State: Exp $
  8.  XPTemplateParser:
  9.  DUnitWizard Name Template parser
  10.  A Parser expression must parse to a literal string.
  11.  Parser logic as a context-free grammar:
  12.  Whitespace is significant *within* an Expression, except where noted. 
  13.  <Expression> ::= <Token> | <Token><Expression>
  14.  <Token> ::= <Literal> | <Variable> | <Method>
  15.  <Literal> ::= [valid absolute file spec characters * ]+
  16.  <Variable> ::= '$'<VarName>
  17.  <Method> ::= '$'<MethodName>'('<Expression>')' **
  18.  *  excluding: '$()'  but including whitespace
  19.  ** whitespace is allowed but ignored between <Expression> and surrounding
  20.     parentheses
  21.  For DUnitWizard:
  22.  <VarName> ::= 'CURRENTUNIT' | 'CURRENTPROJECT' | 'PROJECTGROUP'
  23.  <MethodName> ::= 'FILEPATH' | 'FILENAME' | 'FILESTEM' | 'FILEEXT' | 'ENVVAR'
  24.  Copyright (c) 2003 by The Excellent Programming Company Pty Ltd
  25.  (Australia) (ABN 27 005 394 918). All rights reserved.
  26.  Contact Paul Spain via email: paul@xpro.com.au
  27.  This unit is free software; you can redistribute it and/or
  28.  modify it under the terms of the GNU Lesser General Public
  29.  License as published by the Free Software Foundation; either
  30.  version 2.1 of the License, or (at your option) any later version.
  31.  This unit is distributed in the hope that it will be useful,
  32.  but WITHOUT ANY WARRANTY; without even the implied warranty of
  33.  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  34.  Lesser General Public License for more details.
  35.  You should have received a copy of the GNU Lesser General Public
  36.  License along with this unit; if not, the license can be viewed at:
  37.  http://www.gnu.org/copyleft/lesser.html
  38.  or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  39.  Boston, MA  02111-1307  USA
  40.  }
  41. interface
  42. type
  43.   TXPTemplateMethod = function(const Input: string;
  44.     out Output: string): boolean of object;
  45.   TXPTemplateMethodMap = record
  46.     Name: string;
  47.     Value: TXPTemplateMethod;
  48.   end;
  49.   TXPTemplateVariableMap = record
  50.     Name, Value: string
  51.   end;
  52.   IXPTemplateParser = interface
  53.     ['{E2819E9C-883D-4AE5-B15D-1B2C439371F9}']
  54.     // Any leading or trailing whitespace in <Input> is ignored.
  55.     // Parse() succeeds for empty or whitespace-only arguments, with an
  56.     // empty <Output> on return 
  57.     function Parse(const Input: string; out Output: string): boolean;
  58.     function GetErrorIndex(out idx: integer): boolean;
  59.     procedure SetMethods(const Methods: array of TXPTemplateMethodMap);
  60.     procedure SetVariables(const Variables: array of TXPTemplateVariableMap);
  61.   end;
  62. //////////////////////////////////////////////////////////////////////////////
  63. //  Unit entry point
  64. //////////////////////////////////////////////////////////////////////////////
  65. function CreateXPTemplateParser: IXPTemplateParser;
  66. implementation
  67. uses
  68.   SysUtils,       // UpperCase(), Trim()
  69.   XPDUnitCommon;  // XPDUnitMacroPrefix
  70. const
  71.   CVSID: string = '$Header: /cvsroot/dunit/dunit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject/XPTemplateParser.pas,v 1.2 2004/05/03 15:07:16 pvspain Exp $';
  72.   MetaId = XPDUnitMacroPrefix;
  73.   FunctionArgOpen = '(';
  74.   FunctionArgClose = ')';
  75. //////////////////////////////////////////////////////////////////////////////
  76. //  TParser declarations
  77. //////////////////////////////////////////////////////////////////////////////
  78. type
  79.   TMethodMaps = array of TXPTemplateMethodMap;
  80.   TVariableMaps = array of TXPTemplateVariableMap;
  81.   TParser = class (TInterfacedObject, IXPTemplateParser)
  82.     private
  83.     // Diagnostic parameters
  84.     FSuccess: boolean;       // initialised to false
  85.     FErrorIndex: integer;    // initialised to 0
  86.     FNesting: integer;
  87.     FMethods: TMethodMaps;
  88.     FVariables: TVariableMaps;
  89.     // Utility methods
  90.     function IsMethod(const Input: string; const idx: integer;
  91.       out Method: TXPTemplateMethodMap): boolean;
  92.     function IsVariable(const Input: string; const idx: integer;
  93.       out Variable: TXPTemplateVariableMap): boolean;
  94.     // context-free grammar implementation
  95.     function Expression(const Input: string; var idx: integer;
  96.       out Output: string): boolean;
  97.     function Token(const Input: string; var idx: integer;
  98.       out Output: string): boolean;
  99.     function Literal(const Input: string; var idx: integer;
  100.       out Output: string): boolean;
  101.     function Variable(const AVariable: TXPTemplateVariableMap;
  102.       const Input: string; var idx: integer; out Output: string): boolean;
  103.     function Method(const AMethod: TXPTemplateMethodMap;
  104.       const Input: string; var idx: integer; out Output: string): boolean;
  105.     protected
  106.     // IXPTemplateParser implementation
  107.     function Parse(const Input: string; out Output: string): boolean;
  108.     function GetErrorIndex(out idx: integer): boolean;
  109.     procedure SetMethods(const Methods: array of TXPTemplateMethodMap);
  110.     procedure SetVariables(const Variables: array of TXPTemplateVariableMap);
  111.     public
  112.     destructor Destroy; override;
  113.   end;
  114. //////////////////////////////////////////////////////////////////////////////
  115. //  TParser implementation
  116. //////////////////////////////////////////////////////////////////////////////
  117. destructor TParser.Destroy;
  118. begin
  119.   FMethods := nil;
  120.   FVariables := nil;
  121.   inherited;
  122. end;
  123. function TParser.IsMethod(const Input: string; const idx: integer;
  124.   out Method: TXPTemplateMethodMap): boolean;
  125. var
  126.   jdx: integer;
  127.   NameLength: integer;
  128.   MatchLength: integer;
  129.   SearchDomain: string;
  130. begin
  131.   // Check for overrun and current input char
  132.   Result := (idx <= System.Length(Input)) and (Input[idx] = MetaId);
  133.   if Result then
  134.   begin
  135.     Result := false;
  136.     MatchLength := 0;
  137.     // Limit search to unparsed section of Input (uppercased)
  138.     SearchDomain := SysUtils.UpperCase(
  139.       System.Copy(Input, idx + 1, System.Length(Input)));
  140.     // Iterate over FMethods looking for longest match
  141.     for jdx := 0 to System.High(FMethods) do
  142.     begin
  143.       NameLength := System.Length(FMethods[jdx].Name);
  144.       // Must be longer than current max
  145.       if (NameLength > MatchLength)
  146.         // ...and function must have an argument
  147.         and (NameLength < System.Length(SearchDomain))
  148.         // ...and function name matches from start of search domain
  149.         and (System.Pos(FMethods[jdx].Name, SearchDomain) = 1)
  150.         // ...and is followed by opening parenthesis
  151.         and (SearchDomain[NameLength + 1] = FunctionArgOpen) then
  152.       begin
  153.         // raise the bar
  154.         MatchLength := NameLength;
  155.         // set return parameters
  156.         Result := true;
  157.         Method := FMethods[jdx];
  158.       end;
  159.     end;
  160.   end;
  161. end;
  162. function TParser.IsVariable(const Input: string; const idx: integer;
  163.   out Variable: TXPTemplateVariableMap): boolean;
  164. var
  165.   jdx: integer;
  166.   NameLength: integer;
  167.   MatchLength: integer;
  168.   SearchDomain: string;
  169. begin
  170.   // Check for overrun and current input char
  171.   Result := (idx <= System.Length(Input)) and (Input[idx] = MetaId);
  172.   if Result then
  173.   begin
  174.     Result := false;
  175.     MatchLength := 0;
  176.     // Limit search to unparsed section of Input (uppercased)
  177.     SearchDomain := SysUtils.UpperCase(
  178.       System.Copy(Input, idx + 1, System.Length(Input)));
  179.     // Iterate over FVariables looking for longest match
  180.     for jdx := 0 to System.High(FVariables) do
  181.     begin
  182.       NameLength := System.Length(FVariables[jdx].Name);
  183.       // Must be longer than current max
  184.       if (NameLength > MatchLength)
  185.         // ...and variable must not be longer than search domain
  186.         and (NameLength <= System.Length(SearchDomain))
  187.         // ...and function name matches from start of search domain
  188.         and (System.Pos(FVariables[jdx].Name, SearchDomain) = 1) then
  189.       begin
  190.         // raise the bar
  191.         MatchLength := NameLength;
  192.         // set return parameters
  193.         Result := true;
  194.         Variable := FVariables[jdx];
  195.       end;
  196.     end;
  197.   end;
  198. end;
  199. function TParser.Parse(const Input: string;
  200.   out Output: string): boolean;
  201. var
  202.   TrimmedInput: string;
  203. begin
  204.   FNesting := 0;
  205.   // Remove leading and trailing whitespace
  206.   TrimmedInput := SysUtils.Trim(Input);
  207.   if System.Length(TrimmedInput) > 0 then
  208.   begin
  209.     FErrorIndex := 1;
  210.     FSuccess := Expression(TrimmedInput, FErrorIndex, Output)
  211.       and (FErrorIndex > System.Length(TrimmedInput));
  212.   end
  213.   else
  214.   begin
  215.     FSuccess := true;
  216.     // Point to first character beyond Input
  217.     FErrorIndex := System.Length(Input) + 1;
  218.     Output := '';
  219.   end;
  220.   Result := FSuccess;
  221. end;
  222. procedure TParser.SetMethods(const Methods: array of TXPTemplateMethodMap);
  223. var
  224.   idx: integer;
  225. begin
  226.   System.SetLength(FMethods, System.Length(Methods));
  227.   for idx := System.High(Methods) downto 0 do
  228.   begin
  229.     FMethods[idx].Name := SysUtils.UpperCase(Methods[idx].Name);
  230.     FMethods[idx].Value := Methods[idx].Value;
  231.   end;
  232. end;
  233. procedure TParser.SetVariables(
  234.   const Variables: array of TXPTemplateVariableMap);
  235. var
  236.   idx: integer;
  237. begin
  238.   System.SetLength(FVariables, System.Length(Variables));
  239.   for idx := System.High(Variables) downto 0 do
  240.   begin
  241.     FVariables[idx].Name := SysUtils.UpperCase(Variables[idx].Name);
  242.     FVariables[idx].Value := Variables[idx].Value;
  243.   end;
  244. end;
  245. function TParser.GetErrorIndex(out idx: integer): boolean;
  246. begin
  247.   // Return parse stop point
  248.   idx := FErrorIndex;
  249.   // Return true if last Parse failed - successful call on *this* function
  250.   Result := not FSuccess;
  251. end;
  252. function TParser.Expression(const Input: string; var idx: integer;
  253.   out Output: string): boolean;
  254. var
  255.   AToken: string;
  256. begin
  257.   System.SetLength(Output, 0);
  258.   repeat
  259.     Result := Token(Input, idx, AToken);
  260.     if Result then
  261.       Output := Output + AToken;
  262.    until
  263.      (not Result) or (idx > System.Length(Input));
  264.    // Check for end of nested expression:
  265.    // last Token failed and next char is ')' and FNesting > 0
  266.    if not ( Result or (idx > System.Length(Input)) or (FNesting = 0)
  267.           or (Input[idx] <> FunctionArgClose) ) then
  268.    begin
  269.      Result := true;
  270.      System.Dec(FNesting);
  271.    end
  272.    // Check for missing closing parenthes(is/es)
  273.    else if Result and (idx > System.Length(Input)) then
  274.      Result := (FNesting = 0);
  275. end;
  276. function TParser.Token(const Input: string; var idx: integer;
  277.   out Output: string): boolean;
  278. var
  279.   AMethod: TXPTemplateMethodMap;
  280.   AVariable: TXPTemplateVariableMap;
  281. begin
  282.   // We must always evaluate longest possible match. Try Method first to cover
  283.   // situation of same-named Method and Variable, wherein Method would result
  284.   // in a longer match than Variable.
  285.   if IsMethod(Input, idx, AMethod) then
  286.     Result := Method(AMethod, Input, idx, Output)
  287.   else if IsVariable(Input, idx, AVariable) then
  288.     Result := Variable(AVariable, Input, idx, Output)
  289.   else
  290.     Result := Literal(Input, idx, Output);
  291. end;
  292. function TParser.Literal(const Input: string; var idx: integer;
  293.   out Output: string): boolean;
  294.   // Bail on Win32 filename illegals except ":" or "$()"
  295.   // Win32 illegals reference:
  296.   // http://linux-ntfs.sourceforge.net/ntfs/concepts/filename_namespace.html
  297. const
  298.   Illegals = [ '"','*','/','<','>','?','|',
  299.     MetaId,FunctionArgOpen,FunctionArgClose ];
  300. begin
  301.   System.SetLength(Output, 0);
  302.   while (idx <= System.Length(Input)) and not (Input[idx] in Illegals) do
  303.   begin
  304.     Output := Output + Input[idx];
  305.     System.Inc(idx);
  306.   end;
  307.   // Success if we have some output and we've either:
  308.   // run out of input, or
  309.   // encountered a variable or function or function closure
  310.   Result := (System.Length(Output) > 0)
  311.     and ( (idx > System.Length(Input))
  312.         or (Input[idx] in [MetaId, FunctionArgClose]) );
  313. end;
  314. function TParser.Method(const AMethod: TXPTemplateMethodMap;
  315.   const Input: string; var idx: integer; out Output: string): boolean;
  316. var
  317.   MethodArg: string;
  318. begin
  319.   Result := false;
  320.   if System.Assigned(AMethod.Value) then
  321.   begin
  322.     // Move index up to start of method argument
  323.     System.Inc(idx, System.Length(AMethod.Name) + 2);
  324.     // Entering a nested expression
  325.     System.Inc(FNesting);
  326.     // evaluate method argument
  327.     if Expression(Input, idx, MethodArg)
  328.       // ...and haven't exhausted input
  329.       and (idx <= System.Length(Input))
  330.       // ...and next char is closing parenthesis
  331.       and (Input[idx] = FunctionArgClose)
  332.       // ...and we call method successfully
  333.       and AMethod.Value(SysUtils.Trim(MethodArg), Output) then
  334.     begin
  335.       Result := true;
  336.       // Move beyond closing parenthesis and bail
  337.       System.Inc(idx);
  338.     end;
  339.   end;
  340. end;
  341. function TParser.Variable(const AVariable: TXPTemplateVariableMap;
  342.   const Input: string; var idx: integer; out Output: string): boolean;
  343. begin
  344.   Result := true;
  345.   Output := AVariable.Value;
  346.   // Move index beyond $variable
  347.   System.Inc(idx, System.Length(AVariable.Name) + 1)
  348. end;
  349. //////////////////////////////////////////////////////////////////////////////
  350. //  Unit entry point
  351. //////////////////////////////////////////////////////////////////////////////
  352. function CreateXPTemplateParser: IXPTemplateParser;
  353. begin
  354.   Result := TParser.Create;
  355. end;
  356. end.