-
Notifications
You must be signed in to change notification settings - Fork 1
/
REPLSandbox.rakumod
139 lines (116 loc) · 3.85 KB
/
REPLSandbox.rakumod
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# The REPL sandbox code was taken from Jupyter::Kernel::Sandbox :
# https://github.com/bduggan/p6-jupyter-kernel/blob/master/lib/Jupyter/Kernel/Sandbox.rakumod
use v6.d;
use nqp;
#| Mime type sub (not used now)
sub mime-type($str) is export {
return do given $str {
when /:i ^ '<svg' / {
'image/svg+xml';
}
default { 'text/plain' }
}
}
#| Result class to keep outputs, exception, mime-type, and expression completeness flag
my class Result {
has Str $.output;
has $.output-raw is default(Nil);
has $.exception;
has Bool $.incomplete;
method output-mime-type {
return mime-type($.output // '');
}
}
#| REPL sandbox class
class Text::CodeProcessing::REPLSandbox is export {
has $.save_ctx;
has $.compiler;
has $.repl;
has $.execution-count is rw = 0;
#| The creation of a sandbox object is tweaked
#| to have REPL compiler initialized and
#| have initialization code for storing outputs executed in
#| that REPL compiler.
method TWEAK () {
$!compiler := nqp::getcomp("Raku") || nqp::getcomp('perl6');
$!repl = REPL.new($!compiler, {});
#| The following REPL initialization code:
#| - Sets a list/array for all output: C<$Out>
#| - Defines a function to to obtain the list of saved outputs: C<Out>
#| - Defines a sigill-less variable, C<\_>, to store and retrieve the last result
#| - See the (C<if $store {...}}>) code in the method C<eval>
self.eval(q:to/INIT/);
my $Out = [];
sub Out { $Out };
my \_ = do {
state $last;
Proxy.new( FETCH => method () { $last },
STORE => method ($x) { $last = $x } );
}
INIT
}
#| The main REPL sandbox method
method eval(Str $code, Bool :$no-persist, Int :$store) {
#| Context to be saved
my $*CTXSAVE = $!repl;
#| Variable for $!save_ctx
my $*MAIN_CTX;
my $exception;
my $eval-code = $code;
#| If the named argument C<$store> is larger than 0
#| then the code is wrapped in appropriate storage calls.
if $store {
$eval-code = qq:to/DONE/
my \\_$store = \$(
$code
);
\$Out[$store] := _$store;
_ = _$store;
DONE
}
#| Get the evaluation result
my $output is default(Nil);
my $gist;
try {
$output = $!repl.repl-eval(
$eval-code,
$exception,
:outer_ctx($!save_ctx),
:interactive(1)
);
$gist = $output.gist;
CATCH {
default {
$exception = $_;
}
}
}
#| If the output is "non-result" make it Nil
given $output {
$_ = Nil if .?__hide;
$_ = Nil if $_ ~~ List and .elems and .[*- 1].?__hide;
$_ = Nil if $_ === Any;
}
#| If the gist is "non-result" make it Nil
if $gist === Any or $gist === Nil { $gist = "Nil"}
#| REPL context is saved/stored
if $*MAIN_CTX and !$no-persist {
$!save_ctx := $*MAIN_CTX;
}
#| If there is an exception modify the output
# with $exception {
# $output = ~$_;
# $gist = $output;
# }
#| Set the flag for non-completion
my $incomplete = so $!repl.input-incomplete($output);
#| Make and initialize the result object
my $result = Result.new:
:output($gist),
:output-raw($output),
:$exception,
:$incomplete;
#| Result
$result;
}
}