DWScript执行两次时如何保留变量值?

9

我所工作的应用程序允许将脚本片段嵌入文档中。例如:

SomeText
<* PrintLn("This line is generated by a script"); *>
Some other text
<* PrintLn("This line is generated by a script, too"); *>
Some more lines

结果

SomeText
This line is generated by a script
Some other text
This line is generated by a script, too
Some more lines

我正在使用DWScript。首先,第一个脚本片段被编译并执行。然后是RecompiledInContext的下一个片段,以此类推。在片段中声明的函数/变量等将在所有后续片段中可用。 但是,变量值在片段之间丢失。例如:

SomeText
<* var x: Integer = 5; *>
Some other text
<* PrintLn(x); *>
Some more lines

生成文档后:
SomeText
Some other text
0  <-- I would like this to be 5
Some more lines

这是一个展示问题的样例应用程序:
program POC.Variable;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  dwsExprs,
  dwsComp,
  dwsCompiler; 

var
  FDelphiWebScript: TDelphiWebScript;
  FProgram: IdwsProgram;
  FExecutionResult: IdwsProgramExecution;

begin
  FDelphiWebScript := TDelphiWebScript.Create(nil);
  try
    FProgram := FDelphiWebScript.Compile('var x: Integer = 2;');
    FProgram.Execute;

    FDelphiWebScript.RecompileInContext(FProgram, 'PrintLn(x);');

    FExecutionResult := FProgram.Execute;
    // The next line fails, Result[1] is '0'
    Assert(FExecutionResult.Result.ToString[1] = '2');
  finally
    FDelphiWebScript.Free;
  end
end.

有没有一种方法可以在多次执行之间“传递”或“保留”变量值?
以下是安德鲁答案的更新代码,但该代码不起作用:
begin
  FDelphiWebScript := TDelphiWebScript.Create(nil);
  try
    FProgram := FDelphiWebScript.Compile('PrintLn("Hello");');

    FExecution:= FProgram.BeginNewExecution();

    FDelphiWebScript.RecompileInContext(FProgram, 'var x: Integer;');
    FExecution.RunProgram(0);
    WriteLn('Compile Result:');
    WriteLn(FExecution.Result.ToString);

    FDelphiWebScript.RecompileInContext(FProgram, 'x := 2; PrintLn(x);');
    FExecution.RunProgram(0); // <-- Access violation
    WriteLn('Compile Result:');
    WriteLn(FExecution.Result.ToString);

    FExecution.EndProgram();
    ReadLn;
  finally
    FDelphiWebScript.Free;
  end
end;

呃...交换脚本命令会中断执行。看来你让这个功能正常工作的唯一方法就是用调试器深入挖掘库。祝你在DWScript中好运! :) - Andrew
2个回答

5
您可以尝试使用BeginNewExecution/RunProgram/EndProgram 代码块(已在DWScript 2.2上进行测试):
begin
  FDelphiWebScript := TDelphiWebScript.Create(nil);
  try
    FProgram := FDelphiWebScript.Compile('var x: Integer;');

    FExecution:= FProgram.BeginNewExecution();

    FDelphiWebScript.RecompileInContext(FProgram, 'x := 2; PrintLn(x);');
    FExecution.RunProgram(0);
    WriteLn('Compile Result:');
    WriteLn(FExecution.Result.ToString);

    FDelphiWebScript.RecompileInContext(FProgram, 'x := x + 3; PrintLn(x);');
    FExecution.RunProgram(0);
    WriteLn('Recompile Result: ');
    WriteLn(FExecution.Result.ToString);

    FExecution.EndProgram();

    ReadLn;
  finally
    FDelphiWebScript.Free;
  end
end.

这不是可靠的。我刚刚尝试了以下代码,结果出现了AV错误。(必须将其发布为答案,因为它不适合作为评论) - RM.
你用的是哪个版本的Delphi? - Andrew
Delphi XE2 升级 4(包括热修补程序)+ 最新的 DWScript 源代码 - RM.
@RM。最新版的DWScript是2.3(不稳定的)吗? - Andrew

1
问题在于当RecompileInContext()添加新的全局变量时,它们没有分配空间,因为空间分配是由BeginNewExecution执行的,但如果变量已经存在或者新变量是在函数内部添加的,那么它应该可以工作,因为这些变量是局部变量而不是全局变量。
因此,如果您将“更新的代码”更改为以下内容,它将可以工作。
FProgram := DelphiWebScript1.Compile( 'PrintLn("Hello");'
                                     +'var x: Integer;');

FExecution:= FProgram.BeginNewExecution();

FExecution.RunProgram(0);
SynEdit1.Lines.Add('Compile Result:');
SynEdit1.Lines.Add(FExecution.Result.ToString);

DelphiWebScript1.RecompileInContext(FProgram, 'x := 2; PrintLn(x);');
FExecution.RunProgram(0); // <-- Access violation
SynEdit1.Lines.Add('Compile Result:');
SynEdit1.Lines.Add(FExecution.Result.ToString);

FExecution.EndProgram();

编辑:现在已经通过DWScript SVN的r1513修复了此问题。


嗨Eric,最新源代码的表现符合预期。这太棒了!感谢您修复它。 - RM.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接