diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index e715d457..aa2b478e 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -28,6 +28,7 @@ #include "xinput.hpp" #include "xinspect.hpp" #include "xmagics/os.hpp" +#include "xmime_internal.hpp" #include "xparser.hpp" #include "xsystem.hpp" @@ -128,7 +129,7 @@ __get_cxx_version () void interpreter::execute_request_impl( send_reply_callback cb, - int /*execution_count*/, + int execution_counter, const std::string& code, xeus::execute_request_config config, nl::json /*user_expressions*/ @@ -136,9 +137,6 @@ __get_cxx_version () { nl::json kernel_res; - - auto input_guard = input_redirection(config.allow_stdin); - // Check for magics for (auto& pre : preamble_manager.preamble) { @@ -150,10 +148,15 @@ __get_cxx_version () } } + // Split code from includes + auto blocks = split_from_includes(code); + auto errorlevel = 0; + std::string ename; std::string evalue; - bool compilation_result = false; + intptr_t output_value = 0; + bool hadError = false; // If silent is set to true, temporarily dismiss all std::cerr and // std::cout outputs resulting from `process_code`. @@ -170,30 +173,36 @@ __get_cxx_version () std::string err; - // Attempt normal evaluation - try - { - StreamRedirectRAII R(err); - compilation_result = Cpp::Process(code.c_str()); - } - catch (std::exception& e) - { - errorlevel = 1; - ename = "Standard Exception: "; - evalue = e.what(); - } - catch (...) - { - errorlevel = 1; - ename = "Error: "; - } + // Scope guard performing the temporary redirection of input requests. + auto input_guard = input_redirection(config.allow_stdin); - if (compilation_result) + for (const auto& block : blocks) { - errorlevel = 1; - ename = "Error: "; - evalue = "Compilation error! " + err; - std::cerr << err; + // Attempt normal evaluation + try + { + StreamRedirectRAII R(err); + output_value = Cpp::Evaluate(block.c_str(), &hadError); + } + catch (std::exception& e) + { + errorlevel = 1; + ename = "Standard Exception: "; + evalue = e.what(); + } + catch (...) + { + errorlevel = 1; + ename = "Error: "; + } + + if (hadError) + { + errorlevel = 1; + ename = "Error: "; + evalue = "Compilation error! " + err; + std::cerr << err; + } } // Flush streams @@ -233,15 +242,13 @@ __get_cxx_version () } else { - /* - // Publish a mime bundle for the last return value if - // the semicolon was omitted. - if (!config.silent && output.hasValue() && trim(code).back() != ';') - { - nl::json pub_data = mime_repr(output); - publish_execution_result(execution_counter, std::move(pub_data), nl::json::object()); - } - */ + // Publish a mime bundle for the last return value if + // the semicolon was omitted. + if (!config.silent && output_value != 0 && trim(blocks.back()).back() != ';') + { + nl::json pub_data = mime_repr(output_value); + publish_execution_result(execution_counter, std::move(pub_data), nl::json::object()); + } // Compose execute_reply message. kernel_res["status"] = "ok"; kernel_res["payload"] = nl::json::array(); diff --git a/src/xmime_internal.hpp b/src/xmime_internal.hpp new file mode 100644 index 00000000..d7b827d0 --- /dev/null +++ b/src/xmime_internal.hpp @@ -0,0 +1,67 @@ +/************************************************************************************ + * Copyright (c) 2016, Johan Mabille, Loic Gouarin, Sylvain Corlay, Wolf Vollprecht * + * Copyright (c) 2016, QuantStack * + * * + * Distributed under the terms of the BSD 3-Clause License. * + * * + * The full license is in the file LICENSE, distributed with this software. * + ************************************************************************************/ + +#ifndef XCPP_MIME_INTERNAL_HPP +#define XCPP_MIME_INTERNAL_HPP + +#include +#include +#include + +#include "clang/Interpreter/CppInterOp.h" // from CppInterOp package + +#include + +namespace nl = nlohmann; + +namespace xcpp +{ + inline nl::json mime_repr(intptr_t V) + { + // Return a JSON mime bundle representing the specified value. + void* value_ptr = reinterpret_cast(V); + + // Include "xcpp/xmime.hpp" only on the first time a variable is displayed. + static bool xmime_included = false; + + if (!xmime_included) + { + Cpp::Declare("#include \"xcpp/xmime.hpp\""); + xmime_included = true; + } + + intptr_t mimeReprV; + bool hadError = false; + { + std::ostringstream code; + code << "using xcpp::mime_bundle_repr;"; + code << "mime_bundle_repr("; + // code << "*(" << getTypeAsString(V); + code << &value_ptr; + code << "));"; + + std::string codeString = code.str(); + + mimeReprV = Cpp::Evaluate(codeString.c_str(), &hadError); + } + + void* mimeReprV_ptr = reinterpret_cast(V); + + if (!hadError && mimeReprV_ptr) + { + return *(nl::json*) mimeReprV_ptr; + } + else + { + return nl::json::object(); + } + } +} + +#endif diff --git a/src/xparser.cpp b/src/xparser.cpp index fb81e619..08237547 100644 --- a/src/xparser.cpp +++ b/src/xparser.cpp @@ -54,4 +54,80 @@ namespace xcpp return result; } + + std::vector get_lines(const std::string& input) + { + std::vector lines; + std::regex re("\\n"); + + std::copy( + std::sregex_token_iterator(input.begin(), input.end(), re, -1), + std::sregex_token_iterator(), + std::back_inserter(lines) + ); + return lines; + } + + std::vector split_from_includes(const std::string& input) + { + // this function split the input into part where we have only #include. + + // split input into lines + std::vector lines = get_lines(input); + + // check if each line contains #include and concatenate the result in the good part of the result + std::regex incl_re("\\#include.*"); + std::regex magic_re("^\\%\\w+"); + std::vector result; + result.push_back(""); + std::size_t current = 0; // 0 include, 1 other + std::size_t rindex = 0; // current index of result vector + for (std::size_t i = 0; i < lines.size(); ++i) + { + if (!lines[i].empty()) + { + if (std::regex_search(lines[i], magic_re)) + { + result.push_back(lines[i] + "\n"); + result.push_back(""); + rindex += 2; + } + else + { + if (std::regex_match(lines[i], incl_re)) + { + // if we have #include in this line + // but the current item of result vector contains + // other things + if (current != 0) + { + current = 0; + result.push_back(""); + rindex++; + } + } + else + { + // if we don't have #include in this line + // but the current item of result vector contains + // the include parts + if (current != 1) + { + current = 1; + result.push_back(""); + rindex++; + } + } + // if we have multiple lines, we add a semicolon at the end of the lines that not contain + // #include keyword (except for the last line) + result[rindex] += lines[i]; + if (i != lines.size() - 1) + { + result[rindex] += "\n"; + } + } + } + } + return result; + } } diff --git a/src/xparser.hpp b/src/xparser.hpp index 592abee3..0faac86b 100644 --- a/src/xparser.hpp +++ b/src/xparser.hpp @@ -22,5 +22,11 @@ namespace xcpp XEUS_CPP_API std::vector split_line(const std::string& input, const std::string& delims, std::size_t cursor_pos); + + XEUS_CPP_API + std::vector get_lines(const std::string& input); + + XEUS_CPP_API + std::vector split_from_includes(const std::string& input); } #endif