3

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"; then test, content should be displayed. As far as I can tell, using print or println can'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.
jonjbar
  • 3,896
  • 1
  • 25
  • 46

1 Answers1

3

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)

Eric Grange
  • 5,931
  • 1
  • 39
  • 61
  • Thank you Eric. I've tried TdwsCompiler.Evaluate but it seems limited to trivial expression and execution: I haven't been able to evaluate a variable assignment for example "var test = 2". The idea would be to interact with an existing script and use it for live programming. A simple example would be the Laravel Tinker command: http://laravel-recipes.com/recipes/280/interacting-with-your-application – jonjbar Feb 15 '16 at 14:59