XPTemplateParser.pas
上传用户:yjb1804
上传日期:2021-01-30
资源大小:3105k
文件大小:13k
- unit XPTemplateParser;
- {
- $Source: /cvsroot/dunit/dunit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject/XPTemplateParser.pas,v $
- $Revision: 1.2 $
- $Date: 2004/05/03 15:07:16 $
- Last amended by $Author: pvspain $
- $State: Exp $
- XPTemplateParser:
- DUnitWizard Name Template parser
- A Parser expression must parse to a literal string.
- Parser logic as a context-free grammar:
- Whitespace is significant *within* an Expression, except where noted.
- <Expression> ::= <Token> | <Token><Expression>
- <Token> ::= <Literal> | <Variable> | <Method>
- <Literal> ::= [valid absolute file spec characters * ]+
- <Variable> ::= '$'<VarName>
- <Method> ::= '$'<MethodName>'('<Expression>')' **
- * excluding: '$()' but including whitespace
- ** whitespace is allowed but ignored between <Expression> and surrounding
- parentheses
- For DUnitWizard:
- <VarName> ::= 'CURRENTUNIT' | 'CURRENTPROJECT' | 'PROJECTGROUP'
- <MethodName> ::= 'FILEPATH' | 'FILENAME' | 'FILESTEM' | 'FILEEXT' | 'ENVVAR'
- Copyright (c) 2003 by The Excellent Programming Company Pty Ltd
- (Australia) (ABN 27 005 394 918). All rights reserved.
- Contact Paul Spain via email: paul@xpro.com.au
- This unit is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- This unit is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this unit; if not, the license can be viewed at:
- http://www.gnu.org/copyleft/lesser.html
- or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- Boston, MA 02111-1307 USA
- }
- interface
- type
- TXPTemplateMethod = function(const Input: string;
- out Output: string): boolean of object;
- TXPTemplateMethodMap = record
- Name: string;
- Value: TXPTemplateMethod;
- end;
- TXPTemplateVariableMap = record
- Name, Value: string
- end;
- IXPTemplateParser = interface
- ['{E2819E9C-883D-4AE5-B15D-1B2C439371F9}']
- // Any leading or trailing whitespace in <Input> is ignored.
- // Parse() succeeds for empty or whitespace-only arguments, with an
- // empty <Output> on return
- function Parse(const Input: string; out Output: string): boolean;
- function GetErrorIndex(out idx: integer): boolean;
- procedure SetMethods(const Methods: array of TXPTemplateMethodMap);
- procedure SetVariables(const Variables: array of TXPTemplateVariableMap);
- end;
- //////////////////////////////////////////////////////////////////////////////
- // Unit entry point
- //////////////////////////////////////////////////////////////////////////////
- function CreateXPTemplateParser: IXPTemplateParser;
- implementation
- uses
- SysUtils, // UpperCase(), Trim()
- XPDUnitCommon; // XPDUnitMacroPrefix
- const
- CVSID: string = '$Header: /cvsroot/dunit/dunit/Contrib/DUnitWizard/Source/DelphiExperts/DUnitProject/XPTemplateParser.pas,v 1.2 2004/05/03 15:07:16 pvspain Exp $';
- MetaId = XPDUnitMacroPrefix;
- FunctionArgOpen = '(';
- FunctionArgClose = ')';
- //////////////////////////////////////////////////////////////////////////////
- // TParser declarations
- //////////////////////////////////////////////////////////////////////////////
- type
- TMethodMaps = array of TXPTemplateMethodMap;
- TVariableMaps = array of TXPTemplateVariableMap;
- TParser = class (TInterfacedObject, IXPTemplateParser)
- private
- // Diagnostic parameters
- FSuccess: boolean; // initialised to false
- FErrorIndex: integer; // initialised to 0
- FNesting: integer;
- FMethods: TMethodMaps;
- FVariables: TVariableMaps;
- // Utility methods
- function IsMethod(const Input: string; const idx: integer;
- out Method: TXPTemplateMethodMap): boolean;
- function IsVariable(const Input: string; const idx: integer;
- out Variable: TXPTemplateVariableMap): boolean;
- // context-free grammar implementation
- function Expression(const Input: string; var idx: integer;
- out Output: string): boolean;
- function Token(const Input: string; var idx: integer;
- out Output: string): boolean;
- function Literal(const Input: string; var idx: integer;
- out Output: string): boolean;
- function Variable(const AVariable: TXPTemplateVariableMap;
- const Input: string; var idx: integer; out Output: string): boolean;
- function Method(const AMethod: TXPTemplateMethodMap;
- const Input: string; var idx: integer; out Output: string): boolean;
- protected
- // IXPTemplateParser implementation
- function Parse(const Input: string; out Output: string): boolean;
- function GetErrorIndex(out idx: integer): boolean;
- procedure SetMethods(const Methods: array of TXPTemplateMethodMap);
- procedure SetVariables(const Variables: array of TXPTemplateVariableMap);
- public
- destructor Destroy; override;
- end;
- //////////////////////////////////////////////////////////////////////////////
- // TParser implementation
- //////////////////////////////////////////////////////////////////////////////
- destructor TParser.Destroy;
- begin
- FMethods := nil;
- FVariables := nil;
- inherited;
- end;
- function TParser.IsMethod(const Input: string; const idx: integer;
- out Method: TXPTemplateMethodMap): boolean;
- var
- jdx: integer;
- NameLength: integer;
- MatchLength: integer;
- SearchDomain: string;
- begin
- // Check for overrun and current input char
- Result := (idx <= System.Length(Input)) and (Input[idx] = MetaId);
- if Result then
- begin
- Result := false;
- MatchLength := 0;
- // Limit search to unparsed section of Input (uppercased)
- SearchDomain := SysUtils.UpperCase(
- System.Copy(Input, idx + 1, System.Length(Input)));
- // Iterate over FMethods looking for longest match
- for jdx := 0 to System.High(FMethods) do
- begin
- NameLength := System.Length(FMethods[jdx].Name);
- // Must be longer than current max
- if (NameLength > MatchLength)
- // ...and function must have an argument
- and (NameLength < System.Length(SearchDomain))
- // ...and function name matches from start of search domain
- and (System.Pos(FMethods[jdx].Name, SearchDomain) = 1)
- // ...and is followed by opening parenthesis
- and (SearchDomain[NameLength + 1] = FunctionArgOpen) then
- begin
- // raise the bar
- MatchLength := NameLength;
- // set return parameters
- Result := true;
- Method := FMethods[jdx];
- end;
- end;
- end;
- end;
- function TParser.IsVariable(const Input: string; const idx: integer;
- out Variable: TXPTemplateVariableMap): boolean;
- var
- jdx: integer;
- NameLength: integer;
- MatchLength: integer;
- SearchDomain: string;
- begin
- // Check for overrun and current input char
- Result := (idx <= System.Length(Input)) and (Input[idx] = MetaId);
- if Result then
- begin
- Result := false;
- MatchLength := 0;
- // Limit search to unparsed section of Input (uppercased)
- SearchDomain := SysUtils.UpperCase(
- System.Copy(Input, idx + 1, System.Length(Input)));
- // Iterate over FVariables looking for longest match
- for jdx := 0 to System.High(FVariables) do
- begin
- NameLength := System.Length(FVariables[jdx].Name);
- // Must be longer than current max
- if (NameLength > MatchLength)
- // ...and variable must not be longer than search domain
- and (NameLength <= System.Length(SearchDomain))
- // ...and function name matches from start of search domain
- and (System.Pos(FVariables[jdx].Name, SearchDomain) = 1) then
- begin
- // raise the bar
- MatchLength := NameLength;
- // set return parameters
- Result := true;
- Variable := FVariables[jdx];
- end;
- end;
- end;
- end;
- function TParser.Parse(const Input: string;
- out Output: string): boolean;
- var
- TrimmedInput: string;
- begin
- FNesting := 0;
- // Remove leading and trailing whitespace
- TrimmedInput := SysUtils.Trim(Input);
- if System.Length(TrimmedInput) > 0 then
- begin
- FErrorIndex := 1;
- FSuccess := Expression(TrimmedInput, FErrorIndex, Output)
- and (FErrorIndex > System.Length(TrimmedInput));
- end
- else
- begin
- FSuccess := true;
- // Point to first character beyond Input
- FErrorIndex := System.Length(Input) + 1;
- Output := '';
- end;
- Result := FSuccess;
- end;
- procedure TParser.SetMethods(const Methods: array of TXPTemplateMethodMap);
- var
- idx: integer;
- begin
- System.SetLength(FMethods, System.Length(Methods));
- for idx := System.High(Methods) downto 0 do
- begin
- FMethods[idx].Name := SysUtils.UpperCase(Methods[idx].Name);
- FMethods[idx].Value := Methods[idx].Value;
- end;
- end;
- procedure TParser.SetVariables(
- const Variables: array of TXPTemplateVariableMap);
- var
- idx: integer;
- begin
- System.SetLength(FVariables, System.Length(Variables));
- for idx := System.High(Variables) downto 0 do
- begin
- FVariables[idx].Name := SysUtils.UpperCase(Variables[idx].Name);
- FVariables[idx].Value := Variables[idx].Value;
- end;
- end;
- function TParser.GetErrorIndex(out idx: integer): boolean;
- begin
- // Return parse stop point
- idx := FErrorIndex;
- // Return true if last Parse failed - successful call on *this* function
- Result := not FSuccess;
- end;
- function TParser.Expression(const Input: string; var idx: integer;
- out Output: string): boolean;
- var
- AToken: string;
- begin
- System.SetLength(Output, 0);
- repeat
- Result := Token(Input, idx, AToken);
- if Result then
- Output := Output + AToken;
- until
- (not Result) or (idx > System.Length(Input));
- // Check for end of nested expression:
- // last Token failed and next char is ')' and FNesting > 0
- if not ( Result or (idx > System.Length(Input)) or (FNesting = 0)
- or (Input[idx] <> FunctionArgClose) ) then
- begin
- Result := true;
- System.Dec(FNesting);
- end
- // Check for missing closing parenthes(is/es)
- else if Result and (idx > System.Length(Input)) then
- Result := (FNesting = 0);
- end;
- function TParser.Token(const Input: string; var idx: integer;
- out Output: string): boolean;
- var
- AMethod: TXPTemplateMethodMap;
- AVariable: TXPTemplateVariableMap;
- begin
- // We must always evaluate longest possible match. Try Method first to cover
- // situation of same-named Method and Variable, wherein Method would result
- // in a longer match than Variable.
- if IsMethod(Input, idx, AMethod) then
- Result := Method(AMethod, Input, idx, Output)
- else if IsVariable(Input, idx, AVariable) then
- Result := Variable(AVariable, Input, idx, Output)
- else
- Result := Literal(Input, idx, Output);
- end;
- function TParser.Literal(const Input: string; var idx: integer;
- out Output: string): boolean;
- // Bail on Win32 filename illegals except ":" or "$()"
- // Win32 illegals reference:
- // http://linux-ntfs.sourceforge.net/ntfs/concepts/filename_namespace.html
- const
- Illegals = [ '"','*','/','<','>','?','|',
- MetaId,FunctionArgOpen,FunctionArgClose ];
- begin
- System.SetLength(Output, 0);
- while (idx <= System.Length(Input)) and not (Input[idx] in Illegals) do
- begin
- Output := Output + Input[idx];
- System.Inc(idx);
- end;
- // Success if we have some output and we've either:
- // run out of input, or
- // encountered a variable or function or function closure
- Result := (System.Length(Output) > 0)
- and ( (idx > System.Length(Input))
- or (Input[idx] in [MetaId, FunctionArgClose]) );
- end;
- function TParser.Method(const AMethod: TXPTemplateMethodMap;
- const Input: string; var idx: integer; out Output: string): boolean;
- var
- MethodArg: string;
- begin
- Result := false;
- if System.Assigned(AMethod.Value) then
- begin
- // Move index up to start of method argument
- System.Inc(idx, System.Length(AMethod.Name) + 2);
- // Entering a nested expression
- System.Inc(FNesting);
- // evaluate method argument
- if Expression(Input, idx, MethodArg)
- // ...and haven't exhausted input
- and (idx <= System.Length(Input))
- // ...and next char is closing parenthesis
- and (Input[idx] = FunctionArgClose)
- // ...and we call method successfully
- and AMethod.Value(SysUtils.Trim(MethodArg), Output) then
- begin
- Result := true;
- // Move beyond closing parenthesis and bail
- System.Inc(idx);
- end;
- end;
- end;
- function TParser.Variable(const AVariable: TXPTemplateVariableMap;
- const Input: string; var idx: integer; out Output: string): boolean;
- begin
- Result := true;
- Output := AVariable.Value;
- // Move index beyond $variable
- System.Inc(idx, System.Length(AVariable.Name) + 1)
- end;
- //////////////////////////////////////////////////////////////////////////////
- // Unit entry point
- //////////////////////////////////////////////////////////////////////////////
- function CreateXPTemplateParser: IXPTemplateParser;
- begin
- Result := TParser.Create;
- end;
- end.