@@ -53,6 +53,7 @@ Shell::Shell(Flags flags)
5353 colour_info = no_colour ? " " : " \033 [36m" ;
5454 colour_success = no_colour ? " " : " \033 [32m" ;
5555 colour_reset = no_colour ? " " : " \033 [0m" ;
56+ colour_bold = no_colour ? " " : " \033 [1m" ;
5657
5758 if (!(flags & Flags::NoReadline)) {
5859 set_histfile ();
@@ -94,8 +95,9 @@ void Shell::interact()
9495
9596 // Print startup info banner
9697 if (!(flags & Flags::NoBanner)) {
97- write_stdout (" Cadabra " CADABRA_VERSION_FULL " (build " CADABRA_VERSION_BUILD
98- " dated " CADABRA_VERSION_DATE " )" );
98+ write_stdout (colour_bold + std::string (" Cadabra " ) + std::string (CADABRA_VERSION_FULL) + colour_reset
99+ + " (build " + std::string (CADABRA_VERSION_BUILD) +
100+ " dated " + std::string (CADABRA_VERSION_DATE) + " )" );
99101 write_stdout (
" Copyright (C) " COPYRIGHT_YEARS
" Kasper Peeters <[email protected] >" );
100102 write_stdout (" Info at https://cadabra.science/" );
101103 write_stdout (" Available under the terms of the GNU GPL v3." );
@@ -126,7 +128,10 @@ void Shell::interact()
126128 using namespace std ::placeholders;
127129 if (!(flags & Flags::NoReadline))
128130 SetCompletionCallback (std::bind (&Shell::set_completion_callback, this , _1, _2));
131+
132+
129133 if (collect.empty ()) {
134+ curline.clear ();
130135 if (get_input (get_ps1 (), curline)) {
131136 PyErr_SetNone (PyExc_KeyboardInterrupt);
132137 handle_error ();
@@ -371,66 +376,53 @@ void Shell::process_ps1(const std::string& line)
371376 {
372377 // Convert cadabra to python
373378 bool display = !(flags & Flags::IgnoreSemicolons);
379+
380+ // In contrast to notebook cell processing, what we do here is
381+ // simpler. We just convert this first line. If it is complete
382+ // python code, we can execute it. If not, we just take this
383+ // line together with future lines until an empty line is
384+ // input, and then we convert & execute the whole block.
385+ // So we don't really do anything with ConvertData nor with
386+ // the result of `convert_line`.
374387 cadabra::ConvertData cv;
375388 std::pair<std::string, std::string> res = cadabra::convert_line (line, cv, display);
376- // FIXME: we need to process res.first
377389 const std::string& output = res.second ;
378390 if (output == " ::empty" ) {
379- // Cadabra continuation line, add to collect
380- collect + = line + " \n " ;
391+ // Cadabra continuation line, add the unprocessed line to collect
392+ collect = line + " \n " ;
381393 return ;
382394 }
383395
384- try {
385- py::object res = evaluate (output);
386- if (!res.is_none ()) {
387- write_stdout (str (res));
388- globals[" _" ] = res;
389- }
390- }
391- catch (py::error_already_set& eval_err) {
392- if (eval_err.matches (PyExc_SyntaxError)) {
393- // Might have valid Python, but needs to be executed not evaluated.
394- try {
395- execute (output);
396- }
397- catch (py::error_already_set& exec_err) {
398- bool need_more = false ;
399- // Check if we need more input. The approach taken is from the Python codeop library:
400- // https://github.com/python/cpython/blob/8c93a63c03ddc789040a6ad50a18af1df7764884/Lib/codeop.py#L19
401- // First compile with a newline appended. If it compiles, then we need more input.
402- std::string output_with_nl = output + " \n " ;
403- auto co_with_nl = py::reinterpret_steal<py::object>(
404- Py_CompileString (output_with_nl.c_str (), " <internal>" , Py_file_input));
405- if (co_with_nl) {
406- need_more = true ;
407- }
408- else {
409- // Recompile with two newlines appended. Compare the error generated by the two compilations:
410- // if they are different then we need more, otherwise it is a genuine error
411- py::error_already_set err1; // Save&clear error from co_with_nl
412- std::string output_with_nlnl = output + " \n\n " ;
413- auto co_with_nlnl = py::reinterpret_steal<py::object>(
414- Py_CompileString (output_with_nlnl.c_str (), " <internal>" , Py_file_input));
415- py::error_already_set err2; // Save&clear error from co_with_nlnl
416- need_more = (repr (err1.type ()) != repr (err2.type ()) || repr (err1.value ()) != repr (err2.value ()));
417- }
418-
419- if (need_more)
420- collect += " \n " ;
421- else
422- handle_error (exec_err);
396+ std::string error;
397+ int status = cadabra::is_python_code_complete (output, error);
398+ switch (status) {
399+ case 0 :
400+ // std::cerr << "seting collect to\n|" << res.first + res.second << "|" << std::endl;
401+ collect = res.first + res.second + " \n " ;
402+ break ;
403+ case 1 : {
404+ std::string tmp = res.first + res.second ;
405+ collect.clear ();
406+ if (tmp.size ()>0 ) {
407+ // std::cerr << "executing ps1\n|" << tmp << "|" << std::endl;
408+ execute (tmp);
423409 }
410+ break ;
424411 }
425- else {
426- handle_error (eval_err);
427- }
412+ case -1 :
413+ collect.clear ();
414+ throw ParseException (error);
415+ case -2 :
416+ collect.clear ();
417+ throw ParseException (error);
418+ default :
419+ throw InternalError (" Code completion check returned invalid response." );
428420 }
429421 }
430422
431423void Shell::process_ps2 (const std::string& line)
432424 {
433- if (!line.empty ()) {
425+ if (!line.empty ()) {
434426 collect += line + " \n " ;
435427 return ;
436428 }
@@ -439,6 +431,7 @@ void Shell::process_ps2(const std::string& line)
439431 std::string error;
440432 std::string code = cadabra::cdb2python_string (collect, display, error);
441433 collect.clear ();
434+ // std::cerr << "executing ps2\n|" << code << "|" << std::endl;
442435 execute (code);
443436 }
444437
@@ -480,6 +473,7 @@ std::string Shell::get_ps2()
480473
481474void Shell::handle_error ()
482475 {
476+ collect.clear ();
483477 if (PyErr_Occurred ()) {
484478 py::error_already_set err;
485479 handle_error (err);
0 commit comments