-
Notifications
You must be signed in to change notification settings - Fork 291
/
xinterpreter.cpp
350 lines (304 loc) · 12 KB
/
xinterpreter.cpp
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/***************************************************************************
* Copyright (c) 2016, Johan Mabille, Loic Gouarin and Sylvain Corlay *
* *
* Distributed under the terms of the BSD 3-Clause License. *
* *
* The full license is in the file LICENSE, distributed with this software. *
****************************************************************************/
#include <algorithm>
#include <memory>
#include <regex>
#include <sstream>
#include <vector>
#include "xcpp_config.hpp"
#include "xbuffer.hpp"
#include "xinterpreter.hpp"
#include "xinspect.hpp"
#include "xmagics.hpp"
#include "xmagics/execution.hpp"
#include "xmagics/os.hpp"
#include "xmime_internal.hpp"
#include "xparser.hpp"
#include "xsystem.hpp"
using namespace std::placeholders;
namespace xcpp
{
void interpreter::configure_impl()
{
// Process #include "xeus/xinterpreter.hpp" in a separate block.
cling::Interpreter::CompilationResult compilation_result;
m_processor.process("#include \"xeus/xinterpreter.hpp\"", compilation_result, nullptr, true);
// Expose interpreter instance to cling
std::string block = "xeus::register_interpreter(static_cast<xeus::xinterpreter*>((void*)" + std::to_string(intptr_t(this)) + "));";
m_processor.process(block.c_str(), compilation_result, nullptr, true);
}
interpreter::interpreter(int argc, const char* const* argv)
: m_cling(argc, argv, LLVM_DIR), m_processor(m_cling, cling::errs()),
m_version(get_stdopt(argc, argv)), // Extract C++ language standard version from command-line option
xmagics(),
p_cout_strbuf(nullptr), p_cerr_strbuf(nullptr),
m_cout_buffer(std::bind(&interpreter::publish_stdout, this, _1)),
m_cerr_buffer(std::bind(&interpreter::publish_stderr, this, _1))
{
redirect_output();
init_preamble();
init_magic();
}
interpreter::~interpreter()
{
restore_output();
}
xeus::xjson interpreter::execute_request_impl(int execution_counter,
const std::string& code,
bool silent,
bool /*store_history*/,
const xeus::xjson_node* /*user_expressions*/,
bool /*allow_stdin*/)
{
xeus::xjson kernel_res;
// Check for magics
for (auto& pre : preamble_manager.preamble)
{
if (pre.second.is_match(code))
{
pre.second.apply(code, kernel_res);
return kernel_res;
}
}
// Split code from includes
auto blocks = split_from_includes(code.c_str());
auto errorlevel = 0;
auto indent = 0;
std::string ename;
std::string evalue;
cling::Value output;
cling::Interpreter::CompilationResult compilation_result;
// If silent is set to true, temporarily dismiss all std::cerr and
// std::cout outpus resulting from `m_processor.process`.
auto cout_strbuf = std::cout.rdbuf();
auto cerr_strbuf = std::cerr.rdbuf();
if (silent)
{
auto null = xnull();
std::cout.rdbuf(&null);
std::cerr.rdbuf(&null);
}
for (const auto& block : blocks)
{
// Attempt normal evaluation
try
{
indent = m_processor.process(block, compilation_result, &output, true);
}
// Catch all errors
catch (cling::InterpreterException& e)
{
errorlevel = 1;
ename = "Interpreter Exception";
if (!e.diagnose())
{
evalue = e.what();
}
}
catch (std::exception& e)
{
errorlevel = 1;
ename = "Standard Exception";
evalue = e.what();
}
catch (...)
{
errorlevel = 1;
ename = "Error";
}
if (compilation_result != cling::Interpreter::kSuccess)
{
errorlevel = 1;
ename = "Interpreter Error";
}
// If an error was encountered, don't attempt further execution
if (errorlevel)
{
m_processor.cancelContinuation();
break;
}
}
// Flush streams
std::cout << std::flush;
std::cerr << std::flush;
// Reset non-silent output buffers
if (silent)
{
std::cout.rdbuf(cout_strbuf);
std::cerr.rdbuf(cerr_strbuf);
}
// Depending of error level, publish execution result or execution
// error, and compose execute_reply message.
if (errorlevel)
{
// Classic Notebook does not make use of the "evalue" or "ename"
// fields, and only displays the traceback.
//
// JupyterLab displays the "{ename}: {evalue}" if the traceback is
// empty.
std::vector<std::string> traceback({ename + ": " + evalue});
if (!silent)
{
publish_execution_error(ename, evalue, traceback);
}
// Compose execute_reply message.
kernel_res["status"] = "error";
kernel_res["ename"] = ename;
kernel_res["evalue"] = evalue;
kernel_res["traceback"] = traceback;
}
else
{
// Publish a mime bundle for the last return value if
// the semicolon was omitted.
if (!silent && output.hasValue() && trim(blocks.back()).back() != ';')
{
xeus::xjson pub_data = mime_repr(output);
publish_execution_result(execution_counter, std::move(pub_data), xeus::xjson::object());
}
// Compose execute_reply message.
kernel_res["status"] = "ok";
kernel_res["payload"] = xeus::xjson::array();
kernel_res["user_expressions"] = xeus::xjson::object();
}
return kernel_res;
}
xeus::xjson interpreter::complete_request_impl(const std::string& code,
int cursor_pos)
{
std::vector<std::string> result;
cling::Interpreter::CompilationResult compilation_result;
xeus::xjson kernel_res;
// split the input to have only the word in the back of the cursor
std::string delims = " \t\n`!@#$^&*()=+[{]}\\|;:\'\",<>?.";
std::size_t _cursor_pos = cursor_pos;
auto text = split_line(code, delims, _cursor_pos);
std::string to_complete = text.back().c_str();
compilation_result = m_cling.codeComplete(code.c_str(), _cursor_pos, result);
// change the print result
for (auto& r : result)
{
// remove the definition at the beginning (for example [#int#])
r = std::regex_replace(r, std::regex("\\[\\#.*\\#\\]"), "");
// remove the variable name in <#type name#>
r = std::regex_replace(r, std::regex("(\\ |\\*)+(\\w+)(\\#\\>)"), "$1$3");
// remove unnecessary space at the end of <#type #>
r = std::regex_replace(r, std::regex("\\ *(\\#\\>)"), "$1");
// remove <# #> to keep only the type
r = std::regex_replace(r, std::regex("\\<\\#([^#>]*)\\#\\>"), "$1");
}
kernel_res["matches"] = result;
kernel_res["cursor_start"] = cursor_pos - to_complete.length();
kernel_res["cursor_end"] = cursor_pos;
kernel_res["metadata"] = xeus::xjson::object();
kernel_res["status"] = "ok";
return kernel_res;
}
xeus::xjson interpreter::inspect_request_impl(const std::string& code,
int cursor_pos,
int /*detail_level*/)
{
xeus::xjson kernel_res;
auto dummy = code.substr(0, cursor_pos);
// FIX: same pattern as in inspect function (keep only one)
std::string exp = R"(\w*(?:\:{2}|\<.*\>|\(.*\)|\[.*\])?)";
std::regex re_method{"(" + exp + R"(\.?)*$)"};
std::smatch magic;
if (std::regex_search(dummy, magic, re_method))
{
inspect(magic[0], kernel_res, m_processor);
}
return kernel_res;
}
xeus::xjson interpreter::history_request_impl(const xeus::xhistory_arguments& /*args*/)
{
return xeus::xjson::object();
}
xeus::xjson interpreter::is_complete_request_impl(const std::string& /*code*/)
{
// TODO: use indentation returned from processing the code to determine
// if the code is complete.
xeus::xjson kernel_res;
kernel_res["status"] = "complete";
kernel_res["indent"] = "";
return kernel_res;
}
xeus::xjson interpreter::kernel_info_request_impl()
{
xeus::xjson result;
result["implementation"] = "xeus-cling";
result["implementation_version"] = XCPP_VERSION;
/* The jupyter-console banner for xeus-cling is the following:
__ _____ _ _ ___ ___ _ ___ _ _ ___
\ \/ / __| | | / __|___ / __| | |_ _| \| |/ __|
> <| _|| |_| \__ \___| (__| |__ | || .` | (_ |
/_/\_\___|\___/|___/ \___|____|___|_|\_|\___|
Jupyter Kernel for the Cling C++ interpreter
*/
result["banner"] = " __ _____ _ _ ___ ___ _ ___ _ _ ___ \n"
" \\ \\/ / __| | | / __|___ / __| | |_ _| \\| |/ __|\n"
" > <| _|| |_| \\__ \\___| (__| |__ | || .` | (_ |\n"
" /_/\\_\\___|\\___/|___/ \\___|____|___|_|\\_|\\___|\n"
"\n"
" Jupyter Kernel for the Cling C++ interpreter ";
result["language_info"]["name"] = "c++";
result["language_info"]["version"] = m_version;
result["language_info"]["mimetype"] = "text/x-c++src";
result["language_info"]["codemirror_mode"] = "text/x-c++src";
result["language_info"]["file_extension"] = ".cpp";
return result;
}
void interpreter::input_reply_impl(const std::string& /*value*/)
{
}
void interpreter::redirect_output()
{
p_cout_strbuf = std::cout.rdbuf();
p_cerr_strbuf = std::cerr.rdbuf();
std::cout.rdbuf(&m_cout_buffer);
std::cerr.rdbuf(&m_cerr_buffer);
}
void interpreter::restore_output()
{
std::cout.rdbuf(p_cout_strbuf);
std::cerr.rdbuf(p_cerr_strbuf);
}
void interpreter::publish_stdout(const std::string& s)
{
publish_stream("stdout", s);
}
void interpreter::publish_stderr(const std::string& s)
{
publish_stream("stderr", s);
}
void interpreter::init_preamble()
{
preamble_manager.register_preamble("introspection", new xintrospection(m_processor));
preamble_manager.register_preamble("magics", new xmagics_manager());
preamble_manager.register_preamble("shell", new xsystem());
}
void interpreter::init_magic()
{
preamble_manager["magics"].get_cast<xmagics_manager>().register_magic("file", writefile());
preamble_manager["magics"].get_cast<xmagics_manager>().register_magic("timeit", timeit(&m_processor));
}
std::string interpreter::get_stdopt(int argc, const char* const* argv)
{
std::string res = "-std=c++11";
for (int i = 0; i < argc; ++i)
{
std::string tmp(argv[i]);
if (tmp.find("-std=c++") != std::string::npos)
{
res = tmp;
break;
}
}
return res;
}
}