I am trying to create a Read–eval–print loop (REPL) with DWScript and I'm not sure this is possible.
Based on the name, I assumed that RecompileInContext would work fine in that context but I am experiencing some limitations:
- A buggy line is forever included in the program: future run will always fail due to that line
- I didn't find a way to output the value of a variable simply by typing it. For example when typing
var test = "content";thentest,contentshould be displayed. As far as I can tell, usingprintorprintlncan't work as they will be executed at each run
So my question is: Is it possible to create a REPL using DWScript ?
Here is what I've got so far:
program DwsRepl;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
dwsComp,
dwsCompiler,
dwsExprs;
var
oCompiler: TDelphiWebScript;
oProgram: IdwsProgram;
oExecution: IdwsProgramExecution;
sInput: string;
begin
try
oCompiler := TDelphiWebScript.Create(nil);
try
oProgram := oCompiler.Compile('', 'ReplScript');
oExecution := oProgram.BeginNewExecution;
try
while True do
begin
Write('> ');
// Read user input
Readln(sInput);
// Exit if needed
if sInput = '' then Break;
// Compile
oCompiler.RecompileInContext(oProgram, sInput);
if not oProgram.Msgs.HasErrors then
begin
oExecution.RunProgram(0);
// Output
if not oExecution.Msgs.HasErrors then
Writeln(oExecution.Result.ToString)
else
Writeln('EXECUTION ERROR: ' + oExecution.Msgs.AsInfo);
end
else
Writeln('COMPILE ERROR: ' + oProgram.Msgs.AsInfo);
end;
finally
oExecution.EndProgram;
end;
finally
oCompiler.Free();
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
You can use TdwsCompiler.Evaluate to compile a snippet to an IdwsEvaluateExpr, it is then possible to evaluate the contained expression to a string with yourEvaluateExpr.Expression.EvalAsString
The above is the typical mechanism to use for debug evaluations, expression watches or conditional breakpoints (though not always evaluated as string, an expression can return an object, an array, etc. or can hold a statement that returns nothing).
RecompileInContext will keep the declarations, but throw away the "main" code, so bugs in the main code will not affect future runs, but if you declare a faulty type for a variable, or a faulty function implementation, it will stay there in the script context.
However the message list is not cleared automatically, you have to clear it yourself (originally RecompileInContext was intended to handle small script snippets in a larger source, so keeping as many errors as possible in the message list was the desired behavior, so that a correct last script would not "erase" the errors of an incorrect first script)