我正在实现一个解释器,我的解释器将支持的一个功能就像Delphi的Format.事实上,我正在使用我的函数SysUtils.Format.但是,我在构建函数的第二个参数时遇到了问题array of TVarRec.
假设我有以下代码.现在,我只假设解释代码需要访问哪些Delphi变量(iVar1和iVar2),但我仍然不知道如何将它们放入Format需要(arFormatArgs)的结构中.
type TFormatArgs = array of TVarRec;
procedure RunInterpretedFormatFunction;
var
iMyAge: integer;
iMyIQ: integer;
sCode: string;
sText: string;
begin
iMyAge := 5;
iMyIQ := -5;
sCode := 'Format(''My age is %d and my IQ is %d'', [iMyAge, iMyIQ])';
sText := FormatThis(sCode, iMyAge, iMyIQ);
end;
function FormatThis(sFormatCode: string; iVar1: integer; iVar2: integer): string;
var
sFormatString: string;
arFormatArgs: TFormatArgs;
begin
sFormatString := GetFormatString(sFormatCode); // I can implement this function
arFormatArgs := ConstructFormatArgs(iVar1, iVar2); // NEED HELP HERE!
result := SysUtils.Format(sFormatString, arFormatArgs);
end;
Run Code Online (Sandbox Code Playgroud)
如何ConstructFormatArgs在Delphi(而不是Assembly)中实现我的功能?
const数组使您可以自由地添加字符串,整数,浮点数等,并将这些格式化为字符串.您可以添加的项目数量没有限制.
Delphi处理这个问题的方式是const数组实际上是TVarRec的数组.
TVarRec是以下类型的记录:
TVarRec = record
case Byte of
vtInteger: (VInteger: Integer; VType: Byte);
vtBoolean: (VBoolean: Boolean);
vtChar: (VChar: Char);
vtExtended: (VExtended: PExtended);
vtString: (VString: PShortString);
vtPointer: (VPointer: Pointer);
vtPChar: (VPChar: PChar);
vtObject: (VObject: TObject);
vtClass: (VClass: TClass);
vtWideChar: (VWideChar: WideChar);
vtPWideChar: (VPWideChar: PWideChar);
vtAnsiString: (VAnsiString: Pointer);
vtCurrency: (VCurrency: PCurrency);
vtVariant: (VVariant: PVariant);
Run Code Online (Sandbox Code Playgroud)
其中的值的类型由值TVarRec确定VType.
这使您可以灵活地将您希望的类型添加到const数组中,就像在Format()函数中一样:
格式('%s是一个字符串,%d是一个整数',['string',10]);
在您自己的过程中使用const数组没什么大不了的.看看这个例子:
procedure VarArraySample( AVarArray : array of const );
var
i : integer;
begin
for i := 0 to High(AVarArray) do
do_something;
end;
Run Code Online (Sandbox Code Playgroud)
函数High()返回数组的最后一个索引.
您还可以转换TVarRec的内容.这个例子来自Delphi的在线帮助,并进行了一些修改.该函数将TVarRec转换为字符串:
function VarRecToStr( AVarRec : TVarRec ) : string;
const
Bool : array[Boolean] of string = ('False', 'True');
begin
case AVarRec.VType of
vtInteger: Result := IntToStr(AVarRec.VInteger);
vtBoolean: Result := Bool[AVarRec.VBoolean];
vtChar: Result := AVarRec.VChar;
vtExtended: Result := FloatToStr(AVarRec.VExtended^);
vtString: Result := AVarRec.VString^;
vtPChar: Result := AVarRec.VPChar;
vtObject: Result := AVarRec.VObject.ClassName;
vtClass: Result := AVarRec.VClass.ClassName;
vtAnsiString: Result := string(AVarRec.VAnsiString);
vtCurrency: Result := CurrToStr(AVarRec.VCurrency^);
vtVariant: Result := string(AVarRec.VVariant^);
else
result := '';
end;
end;
Run Code Online (Sandbox Code Playgroud)
您可以将上面的两个函数组合到一个函数中,该函数将const数组中的所有元素转换为一个字符串:
function VarArrayToStr( AVarArray : array of const ) : string;
var
i : integer;
begin
result := '';
for i := 0 to High(AVarArray) do
result := result + VarRecToStr( AVarArray[i] );
end;
Run Code Online (Sandbox Code Playgroud)
您现在可以创建自己的Format()函数.Format()函数扫描%,并用const数组中的值替换%something,具体取决于格式说明符和精度说明符.
如您所知,array of const与array of TVarRec. 要构造一个,首先声明诸如数组,然后设置每个元素的值,就像设置任何其他数组一样。
TVarRec是一个变体记录,这意味着它可以保存许多不同类型的值。它有一个字段,VType,表示它所持有的值的类型。在其他字段中,一次只有一个具有有效值。设置VType字段,然后设置对应的值字段,如VInteger或VString。
请注意,某些字段实际上是指针,例如VVariant和VInt64。当您分配这些指针值时,您需要确保它们指向的任何内容在需要时都保持可访问和有效Format。
其他字段是其真实值类型的无类型版本。这些包括VAnsiString和VInterface。当您分配给这些字段时,请注意它们不会像普通AnsiString或IUnknown变量那样维护通常的引用计数,因此再次注意这些变量的生命周期。
编译器通常是唯一生成此类数组的东西,因此几乎没有可用的参考代码来了解它们是如何构建的。相反,您可以查看使用 const 数组的其他代码。例如,几年前我为 JCL实现了一个 Unicode 感知Format函数。它使用有限状态机一次解析一个字符的格式字符串。每次解析完一个参数字符串时,它都会从输入数组中获取相应的参数,并根据字符串和参数类型对其进行格式化。
它使用最少的汇编程序,只是为了提高效率,而不是因为它确实是必要的。作为参考,所有汇编程序都在注释中附有等效的 Delphi 代码。
在https://groups.google.com/forum/#!topic/borland.public.delphi.objectpascal/-xb6O0qX2zc找到此代码
procedure test(numArgs:integer; MyFormattingString:string);
var
v:array of tvarrec;
i:integer;
begin
setlength(v, numArgs);
for i:=1 to numArgs do
begin
v[i-1].vtype:=vtpchar;
v[i-1].vtpchar:=strnew(pchar(myDataSet.FieldByName(inttostr(i)).asstring));
end;
memo1.lines.add(Format(MyFormattingString,v);
for i:=1 to numArgs do strdispose(v[i-1].vtpchar);
end;
Run Code Online (Sandbox Code Playgroud)
没有回答我必须处理的所有问题,但我想我现在知道如何构建 TVarRec 数组。