ethanblake4 / dart_eval

Extensible Dart interpreter for Dart with full interop
https://pub.dev/packages/dart_eval
BSD 3-Clause "New" or "Revised" License
321 stars 36 forks source link

Interactively using dart_eval #94

Open bingao opened 1 year ago

bingao commented 1 year ago

Hi. I want to use dart_eval to develop an interactive interpreter, i.e. a user does not send codes to the interpreter at once. The interpreter should remember what (for example functions or variables) has been declared before when compiling and executing new codes from the user.

I wonder if it is possible? If yes, could you please provide some tips about what classes/methods I should use from dart_eval? Thank you.

ethanblake4 commented 1 year ago

Hi! dart_eval isn't designed for this, so it could only be achieved by recompiling the entire source code the user has written so far each time a new statement is entered. That's more practical than it sounds since the dart_eval compiler is extremely fast (<100ms for a full recompile usually), but if you're targeting desktop platforms I'd look at https://github.com/fzyzcjy/dart_interactive instead.

bingao commented 1 year ago

Thank you @ethanblake4 I will look at dart_interactive but I am more interested in a library that can be used inside my app.

The problem I want to solve is some variables can take much time to compute from some functions. Users can reuse those variables later. So it would be better to collect those variables and let, for example, Runtime in dart_eval be aware of them instead of recomputing them. Do you think if it is possible for the current design of dart_eval? Thank you.

ethanblake4 commented 1 year ago

No, not possible currently, though it wouldn't be terribly difficult to add. If you're interested in making a PR to add support for this I can give you some guidance.

bingao commented 1 year ago

Thanks. I am not sure if I can or currently have time to do it. But it is definitely good if you can give me some guidance :-)

ethanblake4 commented 1 year ago

Sure, no problem.

The runtime values of top-level variables in dart_eval are stored in the Runtime.globals array.

The compiler keeps track of where each variable is in this array in CompilerContext.topLevelGlobalIndices. For example, if you had a file main.dart with final myVar = 1, topLevelGlobalIndices might look like:

{
   'package:example/main.dart': {
     'myVar': 0
   }
}

And at runtime, the value of myVar (1) would be stored at index 0 of the Runtime.globals array.

In order to persist the state if you recompile, we'd want to copy over all values in the globals array from one Runtime to the next if they are present in topLevelGlobalIndices. (We can't just copy the entire globals array because other things are stored there.)

In order to do this, you'd basically want to create an array out of all of the number values in topLevelGlobalIndices (so in the above example this would just be [0]). Then you'd need to stick this array inside of the generated Program here, serialize it inside of Program.write(), and deserialize it inside of Runtime._load(). Finally you could add a new function on Runtime called initializeGlobalStateFromPrevious or similar, that takes in the previous Runtime as its argument and copies all the globals array indices that are present in the array.

My intuition suggests that, in your case of linearly adding code to the end of the "file", these indices will be consistent on subsequent recompilations so this should work well. However, if that is not the case this could be a bit more complicated - you'd have to generate a mapping from the previous indices to the current ones by comparing the previous compilation's topLevelglobalIndices to the current one.

Let me know if you have any questions!

bingao commented 1 year ago

Thank you @ethanblake4 I will have a try and see if I could make it work for my requirement.