diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 14972324..11eb26e8 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -133,14 +133,17 @@ __get_cxx_version () auto input_guard = input_redirection(config.allow_stdin); - // Check for magics - for (auto& pre : preamble_manager.preamble) + if (code.find("%undo") != 0) { - if (pre.second.is_match(code)) + // Check for magics + for (auto& pre : preamble_manager.preamble) { - pre.second.apply(code, kernel_res); - cb(kernel_res); - return; + if (pre.second.is_match(code)) + { + pre.second.apply(code, kernel_res); + cb(kernel_res); + return; + } } } @@ -168,7 +171,42 @@ __get_cxx_version () try { StreamRedirectRAII R(err); - compilation_result = Cpp::Process(code.c_str()); + if (code.rfind("%undo", 0) == 0) + { +#ifdef EMSCRIPTEN + throw std::logic_error("Undo is not supported in xeus-cpp-lite"); +#else + int n = 1; // Default value + if (code.length() > 5) + { + try + { + n = std::stoi(code.substr(6)); + } + catch (const std::invalid_argument&) + { + throw std::runtime_error( + "Invalid format for %undo. Expected '%undo n' where n is an integer" + ); + } + catch (const std::out_of_range&) + { + throw std::runtime_error("Number out of range for %undo"); + } + } + compilation_result = static_cast<bool>(Cpp::Undo(n)); +#endif + } + else + { + compilation_result = Cpp::Process(code.c_str()); + } + } + catch (std::logic_error& e) + { + errorlevel = 1; + ename = "Logic Error: "; + evalue = e.what(); } catch (std::exception& e) { diff --git a/test/test_interpreter.cpp b/test/test_interpreter.cpp index 61d5f5d7..01386692 100644 --- a/test/test_interpreter.cpp +++ b/test/test_interpreter.cpp @@ -1076,3 +1076,78 @@ TEST_SUITE("file") { infile.close(); } } + + +TEST_SUITE("undo_and_redefinition") +{ +#if defined(XEUS_CPP_EMSCRIPTEN_WASM_BUILD) + TEST_CASE("RedefinitionAndUndoErrors" + * doctest::should_fail(true) + * doctest::description("TODO: Currently fails for the Emscripten build")) +#else + TEST_CASE("RedefinitionAndUndoErrors") +#endif + { + std::vector<const char*> Args = {/*"-v", "resource-dir", "..."*/}; + xcpp::interpreter interpreter((int) Args.size(), Args.data()); + std::string code = R"( + int x = 5; + )"; + nl::json user_expressions = nl::json::object(); + xeus::execute_request_config config; + config.silent = false; + config.store_history = false; + config.allow_stdin = false; + nl::json header = nl::json::object(); + xeus::xrequest_context::guid_list id = {}; + xeus::xrequest_context context(header, id); + + // Execute first code: define int x = 5 + std::promise<nl::json> promise1; + std::future<nl::json> future1 = promise1.get_future(); + auto callback1 = [&promise1](nl::json result) + { + promise1.set_value(result); + }; + interpreter + .execute_request(std::move(context), std::move(callback1), code, std::move(config), user_expressions); + nl::json execute1 = future1.get(); + REQUIRE(execute1["status"] == "ok"); + + code = R"(%undo)"; + std::promise<nl::json> promise2; + std::future<nl::json> future2 = promise2.get_future(); + auto callback2 = [&promise2](nl::json result) + { + promise2.set_value(result); + }; + interpreter + .execute_request(std::move(context), std::move(callback2), code, std::move(config), user_expressions); + nl::json execute2 = future2.get(); + REQUIRE(execute2["status"] == "ok"); + + code = "int x = 10;"; + std::promise<nl::json> promise3; + std::future<nl::json> future3 = promise3.get_future(); + auto callback3 = [&promise3](nl::json result) + { + promise3.set_value(result); + }; + interpreter + .execute_request(std::move(context), std::move(callback3), code, std::move(config), user_expressions); + nl::json execute3 = future3.get(); + REQUIRE(execute3["status"] == "ok"); + + code = "int x = 20;"; + std::promise<nl::json> promise4; + std::future<nl::json> future4 = promise4.get_future(); + auto callback4 = [&promise4](nl::json result) + { + promise4.set_value(result); + }; + interpreter + .execute_request(std::move(context), std::move(callback4), code, std::move(config), user_expressions); + nl::json execute4 = future4.get(); + REQUIRE(execute4["status"] == "error"); + } +}