# HG changeset patch # User Oliver Heimlich # Date 1452878386 28800 # Node ID 5ed379c8decd941eb49eba353320400b991c168b # Parent 9ff2ae6cd5b0d49cbe45c1a2670686e85c4dbf08 Add new function evalc to core. * NEWS: Announce new function. * eval.txi: Add DOCSTRING to manual. * pager.cc (Fdiary): Add seealso link to evalc. * __unimplemented__.m: Remove evalc from list of unimplemented fcns. * oct-parse.in.yy (Fevalc): New function with BIST tests. * oct-parse.in.yy (Feval): Add seealso link to evalc. diff -r 9ff2ae6cd5b0 -r 5ed379c8decd NEWS --- a/NEWS Thu Jan 14 16:56:46 2016 -0500 +++ b/NEWS Fri Jan 15 09:19:46 2016 -0800 @@ -61,6 +61,7 @@ ** Other new functions added in 4.2: deg2rad + evalc hash im2double psi @@ -96,12 +97,11 @@ java_debug read_readline_init_file java_invoke saving_history - ** The global error_state variable in Octave's C++ API has been - deprecated and will be removed in a future version. Now the error - and print_usage functions throw an exception - (octave_execution_exception) after displaying the error message. - This makes the error and print_usage functions in C++ work more like - the corresponding functions in the scripting language. + ** The global error_state variable in Octave's C++ API has been deprecated + and will be removed in a future version. Now the error and print_usage + functions throw an exception (octave_execution_exception) after displaying + the error message. This makes the error and print_usage functions in C++ + work more like the corresponding functions in the scripting language. ** The default error handlers in liboctave have been updated to use exceptions. After displaying an error message they no longer return @@ -109,12 +109,11 @@ customized through the global variables "current_liboctave_error_handler" and "current_liboctave_error_with_id_handler". If a programmer has installed their own custom error handling routines when directly linking - with liboctave then these must be updated to throw an exception and - not return to the calling program. + with liboctave then these must be updated to throw an exception and not + return to the calling program. - ** New configure option, --enable-address-sanitizer-flags, to build - Octave with memory allocator checks (similar to those provided by - valgrind) built in. + ** New configure option, --enable-address-sanitizer-flags, to build Octave + with memory allocator checks (similar to those in valgrind) built in. Summary of important user-visible changes for version 4.0: --------------------------------------------------------- diff -r 9ff2ae6cd5b0 -r 5ed379c8decd doc/interpreter/eval.txi --- a/doc/interpreter/eval.txi Thu Jan 14 16:56:46 2016 -0500 +++ b/doc/interpreter/eval.txi Fri Jan 15 09:19:46 2016 -0800 @@ -29,6 +29,11 @@ @DOCSTRING(eval) +The @code{evalc} function additionally captures any console output +produced by the evaluated expression. + +@DOCSTRING(evalc) + @menu * Calling a Function by its Name:: * Evaluation in a Different Context:: diff -r 9ff2ae6cd5b0 -r 5ed379c8decd libinterp/corefcn/pager.cc --- a/libinterp/corefcn/pager.cc Thu Jan 14 16:56:46 2016 -0500 +++ b/libinterp/corefcn/pager.cc Fri Jan 15 09:19:46 2016 -0800 @@ -528,7 +528,7 @@ @end table\n\ \n\ With no arguments, @code{diary} toggles the current diary state.\n\ -@seealso{history}\n\ +@seealso{history, evalc}\n\ @end deftypefn") { int nargin = args.length (); diff -r 9ff2ae6cd5b0 -r 5ed379c8decd libinterp/parse-tree/oct-parse.in.yy --- a/libinterp/parse-tree/oct-parse.in.yy Thu Jan 14 16:56:46 2016 -0500 +++ b/libinterp/parse-tree/oct-parse.in.yy Fri Jan 15 09:19:46 2016 -0800 @@ -3,6 +3,7 @@ Copyright (C) 1993-2015 John W. Eaton Copyright (C) 2009 David Grundberg Copyright (C) 2009-2010 VZLU Prague +Copyright (C) 2016 Oliver Heimlich This file is part of Octave. @@ -5001,7 +5002,7 @@ Consider using try/catch blocks or unwind_protect/unwind_protect_cleanup\n\ blocks instead. These techniques have higher performance and don't introduce\n\ the security considerations that the evaluation of arbitrary code does.\n\ -@seealso{evalin}\n\ +@seealso{evalin, evalc, assignin, feval}\n\ @end deftypefn") { octave_value_list retval; @@ -5216,6 +5217,144 @@ return retval; } +DEFUN (evalc, args, nargout, + "-*- texinfo -*-\n\ +@deftypefn {} {@var{s} =} evalc (@var{try})\n\ +@deftypefnx {} {@var{s} =} evalc (@var{try}, @var{catch})\n\ +Parse and evaluate the string @var{try} as if it were an Octave program,\n\ +while capturing the output into the return variable @var{s}.\n\ +\n\ +If execution fails, evaluate the optional string @var{catch}.\n\ +\n\ +This function behaves like @code{eval}, but any output or warning messages\n\ +which would normally be written to the console are captured and returned in\n\ +the string @var{s}.\n\ +\n\ +The @code{diary} is disabled during the execution of this function. When\n\ +@code{system} is used, any output produced by external programs is @emph{not}\n\ +captured, unless their output is captured by the @code{system} function\n\ +itself.\n\ +\n\ +@example\n\ +@group\n\ +s = evalc (\"t = 42\"), t\n\ + @result{} s = t = 42\n\ +\n\ + @result{} t = 42\n\ +@end group\n\ +@end example\n\ +@seealso{eval, diary}\n\ +@end deftypefn") +{ + int nargin = args.length (); + + if (nargin == 0 || nargin > 2) + print_usage (); + + // redirect stdout/stderr to capturing buffer + std::ostringstream buffer; + + std::ostream& out_stream = octave_stdout; + std::ostream& err_stream = std::cerr; + + out_stream.flush (); + err_stream.flush (); + + std::streambuf* old_out_buf = out_stream.rdbuf (buffer.rdbuf ()); + std::streambuf* old_err_buf = err_stream.rdbuf (buffer.rdbuf ()); + + + // call standard eval function + octave_value_list retval; + int eval_nargout = std::max (0, nargout - 1); + + const octave_execution_exception* eval_exception = 0; + try + { + retval = Feval (args, eval_nargout); + } + catch (const octave_execution_exception& e) + { + // hold back exception from eval until we have restored streams + eval_exception = &e; + } + + // stop capturing buffer and restore stdout/stderr + out_stream.flush (); + err_stream.flush (); + + out_stream.rdbuf (old_out_buf); + err_stream.rdbuf (old_err_buf); + + if (eval_exception) + { + // Print error message again, which was lost because of the stderr buffer + // Note: this keeps error_state and last_error_stack intact + message_with_id ("error", last_error_id ().c_str (), + last_error_message ().c_str ()); + // rethrow original exception from above + throw *eval_exception; + } + + retval.prepend (buffer.str ()); + return retval; +} + +/* + +%!assert (evalc ("1"), "ans = 1\n") +%!assert (evalc ("1;"), "") + +%!test +%! [s, y] = evalc ("1"); +%! assert (s, ""); +%! assert (y, 1); + +%!test +%! [s, y] = evalc ("1;"); +%! assert (s, ""); +%! assert (y, 1); + +%!test +%! assert (evalc ("y = 2"), "y = 2\n"); +%! assert (y, 2); + +%!test +%! assert (evalc ("y = 3;"), ""); +%! assert (y, 3); + +%!test +%! [s, a, b] = evalc ("deal (1, 2)"); +%! assert (s, ""); +%! assert (a, 1); +%! assert (b, 2); + +%!function [a, b] = __f_evalc () +%! printf ("foo"); +%! fprintf (stdout, "bar"); +%! disp (pi) +%! a = 1; +%! b = 2; +%!endfunction +%!test +%! [s, a, b] = evalc ("__f_evalc ()"); +%! assert (s, "foobar 3.1416\n"); +%! assert (a, 1); +%! assert (b, 2); + +%!error (evalc ("error ('foo')")) +%!error (evalc ("error ('foo')", "error ('bar')")) + +%!test +%! warning ("off", "quiet", "local"); +%! assert (evalc ("warning ('foo')"), "warning: foo\n"); + +%!test +%! warning ("off", "quiet", "local"); +%! assert (evalc ("error ('foo')", "warning ('bar')"), "warning: bar\n"); + +*/ + DEFUN (__parser_debug_flag__, args, nargout, "-*- texinfo -*-\n\ @deftypefn {} {@var{val} =} __parser_debug_flag__ ()\n\ diff -r 9ff2ae6cd5b0 -r 5ed379c8decd scripts/help/__unimplemented__.m --- a/scripts/help/__unimplemented__.m Thu Jan 14 16:56:46 2016 -0500 +++ b/scripts/help/__unimplemented__.m Fri Jan 15 09:19:46 2016 -0800 @@ -636,7 +636,6 @@ "echodemo", "empty", "enumeration", - "evalc", "events", "export2wsdlg", "figurepalette",