interactive 1.0.0 interactive: ^1.0.0 copied to clipboard
REPL (interactive shelL) for Dart, supporting 3rd party packages, hot reload, and full grammar
dart_interactive #
🚀 Features #
- Use any third-party package freely
- Auto hot-reload code anywhere, preserving state
- Supports full grammar in REPL
In short: A full-featured REPL (interactive shell). For more details, please see section below.
📚 Demo #
TODO
🎼 Getting started #
Install (just standard procedure of installing global dart packages):
dart pub global activate interactive
Use (just a normal binary):
interactive
And play with it :)
Feature details #
Expressions #
>>> a = 'Hello'; b = ' world!';
>>> '$a, $b'
Hello, world!
Statements #
>>> print(a)
Hello
(All methods, not only print
)
Functions #
Define and redefine
>>> String f() => 'old';
>>> f()
old
>>> String f() => 'new';
>>> f()
new
Use local and global variables
>>> a = 10;
>>> int f() { int b = 20; a++; b++; return a+b; }
>>> f()
32
>>> f()
33
Classes #
Define and redefine, preserving states
>>> class C { int a = 10; int f() => a * 2; }
>>> c = C(); print(c.f());
20
>>> class C { int a = 1000; int f() => a * 3; }
>>> c.f()
30
Remark: This follows the Dart hot reload semantics.
Extends and implements
>>> class A { int f() => 10; } class B extends A { int f() => 20; }
>>> A().f() + B().f()
30
>>> class B implements A { int f() => 30; }
>>> A().f() + B().f()
40
Use local variables, fields, and global variables
>>> a = 10;
>>> class C { int b = 20; int f() { int c = 30; a++; b++; c++; return a+b+c; } }
>>> c = C(); print(c.f()); print(c.f());
63
65
Add libraries as dependency #
Use !dart pub add package_name
, just like what is done in Python (Jupyter/IPython).
>>> join('directory', 'file.txt')
(...error, since have not added that dependency...)
>>> !dart pub add path
Resolving dependencies...
+ path 1.8.2
Changed 1 dependency!
>>> join('directory', 'file.txt')
(...error, since have imported it...)
>>> import 'package:path/path.dart';
>>> join('directory', 'file.txt')
directory/file.txt
Imports #
Built-in package
>>> Random().nextInt(100)
(some error outputs here, because it is not imported)
>>> import "dart:math";
>>> Random().nextInt(100)
9
Third party package
Note: If it has not been added to dependency, please follow instructions above and use !dart pub add path
to add it.
>>> join('directory', 'file.txt')
(...error, since have imported it...)
>>> import 'package:path/path.dart';
>>> join('directory', 'file.txt')
directory/file.txt
Multiple in one go #
>>> int g() => 42; class C { int a = 10; int f() => a * 2; }
>>> C().f() + g()
62
Multi line if not ended #
(The ...
, instead of >>>
, appears in the two lines, because the package detects it is not finished.)
>>> class C {
... int a = 10;
... }
>>>
Run commands #
Use prefix !
.
>>> !whoami
tom
>>> !date
2022-10-22 ...outputs...
Implementation #
General:
- Create a blank package and an isolate as execution workspace
- Extract imports/classes/functions/etc using analyzer, with replacing when it has the same name, and synthesize a dart file - thus supports rich Dart feature
- Trigger Dart's hot-reload after the dart file is updated
- Use analyzer to distinguish expressions/statements/compilation-units and do corresponding transformation
- The only thing to let Dart VM service to evaluate is
generatedMethod()
, and do not evaluate anything more - Adding dependencies is as simple as running standard shell command
As for "global" variables:
- Indeed implemented by a field variable
- Statements: Make it inside
extension on dynamic { Object? generatedMethod() { ...the statements... } }
to access it seamlessly - Functions: Convert functions to extension methods on dynamic to access it seamlessly
- Classes: Synthesize getters/setters in classes, and delegate to the field variables, whenever there is a potential access to global variable to access it seamlessly
TODO more implementation discussions if people are interested (above is so brief)
✨ Contributors #
Thanks goes to these wonderful people (emoji key):
fzyzcjy 💻 📖 🤔 |
Vyacheslav Egorov 🤔 |
Andreas Kirsch 🤔 |
Maksim Lin 🤔 |
More specifically, thanks for all these contributions:
- @mraleph (Dart team): Pointing out Dart exposes hot reload and expression evaluation.
- @BlackHC: Prior proof of concept and article on the problem of creating a REPL.
- @maks: Prior prototype as an update-to-Dart-2 of @BlackHC's prototype.