changeset 17249:923ce8b42db2

improve try-catch-statement to save exception to a variable (bug #33217) * oct-parse.in.yy (except_command): Handle exception identifiers. * oct-parse.in.yy, parse.h (octave_base_parser::make_try_command): New arg, ID. * pt-except.cc, pt-except.h (tree_try_catch_command::expr_id): New data member. (tree_command::tree_command): Initialize it. (tree_command::~tree_command): Delete it. (tree_command::identifier): New function. * pt-check.cc (tree_checker::visit_try_catch_command): Check for valid expr_id. * pt-pr-code.cc (tree_print_code::visit_try_catch_command): Print expr_id. * pt-eval.cc (tree_evaluator::visit_try_catch_command): Assign message and identifier to variable. * try.tst: New test. * NEWS: Note change.
author Stefan Mahr <dac922@gmx.de>
date Tue, 13 Aug 2013 19:35:53 +0200
parents 0b2a0acd0315
children afd235a206a2
files NEWS libinterp/parse-tree/oct-parse.in.yy libinterp/parse-tree/parse.h libinterp/parse-tree/pt-check.cc libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-except.cc libinterp/parse-tree/pt-except.h libinterp/parse-tree/pt-pr-code.cc test/try.tst
diffstat 9 files changed, 121 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Tue Aug 13 19:31:59 2013 -0400
+++ b/NEWS	Tue Aug 13 19:35:53 2013 +0200
@@ -100,6 +100,29 @@
 
     will be consistent with other binary operators.
 
+ ** Octave now has limited support for named exceptions
+
+    The following syntax is now accepted:
+
+      try
+        statements
+      catch exception-id
+        statements
+      end
+
+    The exception-id is a structure with the fields "message" and
+    "identifier".  For example
+
+      try
+        error ("Octave:error-id", "error message");
+      catch myerr
+        printf ("identifier: %s\n", myerr.identifier);
+        printf ("message:    %s\n", myerr.message);
+      end_try_catch
+
+    When classdef-style classes are added to Octave, the exception-id
+    will become an MException object.
+
  ** Warning IDs renamed:
 
     Octave:array-as-scalar => Octave:array-to-scalar
--- a/libinterp/parse-tree/oct-parse.in.yy	Tue Aug 13 19:31:59 2013 -0400
+++ b/libinterp/parse-tree/oct-parse.in.yy	Tue Aug 13 19:35:53 2013 +0200
@@ -240,7 +240,7 @@
 %type <tree_expression_type> matrix cell
 %type <tree_expression_type> primary_expr oper_expr
 %type <tree_expression_type> simple_expr colon_expr assign_expr expression
-%type <tree_identifier_type> identifier fcn_name magic_tilde
+%type <tree_identifier_type> identifier fcn_name magic_tilde opt_identifier
 %type <tree_identifier_type> superclass_identifier meta_identifier
 %type <octave_user_function_type> function1 function2 classdef1
 %type <tree_index_expression_type> word_list_cmd
@@ -975,19 +975,30 @@
                     if (! ($$ = parser.make_unwind_command ($1, $4, $8, $9, $2, $6)))
                       ABORT_PARSE;
                   }
-                | TRY stash_comment opt_sep opt_list CATCH
+                | TRY stash_comment opt_sep opt_list CATCH list END
+                  {
+                    if (! ($$ = parser.make_try_command ($1, $4, $6, 0, $7, $2, 0)))
+                      ABORT_PARSE;
+                  }
+                | TRY stash_comment opt_sep opt_list CATCH opt_identifier
                   stash_comment opt_sep opt_list END
                   {
-                    if (! ($$ = parser.make_try_command ($1, $4, $8, $9, $2, $6)))
+                    if (! ($$ = parser.make_try_command ($1, $4, $9, $6, $10, $2, $7)))
                       ABORT_PARSE;
                   }
                 | TRY stash_comment opt_sep opt_list END
                   {
-                    if (! ($$ = parser.make_try_command ($1, $4, 0, $5, $2, 0)))
+                    if (! ($$ = parser.make_try_command ($1, $4, 0, 0, $5, $2, 0)))
                       ABORT_PARSE;
                   }
                 ;
 
+opt_identifier: // empty
+                  { $$ = 0; }
+                | identifier sep
+                  { $$ = $1; }
+                ;
+
 // ===========================================
 // Some 'subroutines' for function definitions
 // ===========================================
@@ -2191,6 +2202,7 @@
 octave_base_parser::make_try_command (token *try_tok,
                                       tree_statement_list *body,
                                       tree_statement_list *cleanup_stmts,
+                                      tree_identifier *id,
                                       token *end_tok,
                                       octave_comment_list *lc,
                                       octave_comment_list *mc)
@@ -2204,7 +2216,7 @@
       int l = try_tok->line ();
       int c = try_tok->column ();
 
-      retval = new tree_try_catch_command (body, cleanup_stmts,
+      retval = new tree_try_catch_command (body, cleanup_stmts, id,
                                            lc, mc, tc, l, c);
     }
 
--- a/libinterp/parse-tree/parse.h	Tue Aug 13 19:31:59 2013 -0400
+++ b/libinterp/parse-tree/parse.h	Tue Aug 13 19:35:53 2013 +0200
@@ -201,7 +201,7 @@
   // Build a try-catch command.
   tree_command *
   make_try_command (token *try_tok, tree_statement_list *body,
-                    tree_statement_list *cleanup, token *end_tok,
+                    tree_statement_list *cleanup, tree_identifier *id, token *end_tok,
                     octave_comment_list *lc, octave_comment_list *mc);
 
   // Build a while command.
--- a/libinterp/parse-tree/pt-check.cc	Tue Aug 13 19:31:59 2013 -0400
+++ b/libinterp/parse-tree/pt-check.cc	Tue Aug 13 19:35:53 2013 +0200
@@ -499,6 +499,15 @@
 {
   tree_statement_list *try_code = cmd.body ();
 
+  tree_identifier *expr_id = cmd.identifier ();
+
+  if (expr_id)
+    {
+      if (! expr_id->lvalue_ok ())
+        gripe ("invalid lvalue used for identifier in try-catch command",
+               cmd.line ());
+    }
+
   if (try_code)
     try_code->accept (*this);
 
--- a/libinterp/parse-tree/pt-eval.cc	Tue Aug 13 19:31:59 2013 -0400
+++ b/libinterp/parse-tree/pt-eval.cc	Tue Aug 13 19:35:53 2013 +0200
@@ -921,6 +921,27 @@
 
           buffer_error_messages--;
 
+          tree_identifier *expr_id = cmd.identifier ();
+          octave_lvalue ult;
+
+          if (expr_id)
+            {
+
+              octave_scalar_map err;
+
+              ult = expr_id->lvalue ();
+
+              if (error_state)
+                return;
+
+              err.assign ("message", last_error_message ());
+              err.assign ("identifier", last_error_id ());
+
+              if (! error_state)
+                ult.assign (octave_value::op_asn_eq, err);
+
+            }
+
           if (catch_code)
             catch_code->accept (*this);
         }
--- a/libinterp/parse-tree/pt-except.cc	Tue Aug 13 19:31:59 2013 -0400
+++ b/libinterp/parse-tree/pt-except.cc	Tue Aug 13 19:35:53 2013 +0200
@@ -33,6 +33,7 @@
 #include "pt-cmd.h"
 #include "pt-except.h"
 #include "pt-exp.h"
+#include "pt-id.h"
 #include "pt-jump.h"
 #include "pt-stmt.h"
 #include "pt-walk.h"
@@ -43,6 +44,7 @@
 
 tree_try_catch_command::~tree_try_catch_command (void)
 {
+  delete expr_id;
   delete try_code;
   delete catch_code;
   delete lead_comm;
@@ -57,6 +59,7 @@
   return new
     tree_try_catch_command (try_code ? try_code->dup (scope, context) : 0,
                             catch_code ? catch_code->dup (scope, context) : 0,
+                            expr_id ? expr_id->dup (scope, context) : 0,
                             lead_comm ? lead_comm->dup () : 0,
                             mid_comm ? mid_comm->dup () : 0,
                             trail_comm ? trail_comm->dup () : 0,
--- a/libinterp/parse-tree/pt-except.h	Tue Aug 13 19:31:59 2013 -0400
+++ b/libinterp/parse-tree/pt-except.h	Tue Aug 13 19:35:53 2013 +0200
@@ -29,6 +29,7 @@
 
 #include "comment-list.h"
 #include "pt-cmd.h"
+#include "pt-id.h"
 #include "symtab.h"
 
 // Simple exception handling.
@@ -39,19 +40,22 @@
 public:
 
   tree_try_catch_command (int l = -1, int c = -1)
-    : tree_command (l, c), try_code (0), catch_code (0), lead_comm (0),
+    : tree_command (l, c), try_code (0), catch_code (0), expr_id (0), lead_comm (0),
       mid_comm (0), trail_comm (0) { }
 
   tree_try_catch_command (tree_statement_list *tc, tree_statement_list *cc,
+                          tree_identifier *id,
                           octave_comment_list *cl = 0,
                           octave_comment_list *cm = 0,
                           octave_comment_list *ct = 0,
                           int l = -1, int c = -1)
-    : tree_command (l, c), try_code (tc), catch_code (cc),
+    : tree_command (l, c), try_code (tc), catch_code (cc), expr_id (id),
       lead_comm (cl), mid_comm (cm), trail_comm (ct) { }
 
   ~tree_try_catch_command (void);
 
+  tree_identifier *identifier (void) { return expr_id; }
+
   tree_statement_list *body (void) { return try_code; }
 
   tree_statement_list *cleanup (void) { return catch_code; }
@@ -75,6 +79,9 @@
   // The code to execute if an error occurs in the first block.
   tree_statement_list *catch_code;
 
+  // Identifier to modify.
+  tree_identifier *expr_id;
+
   // Comment preceding TRY token.
   octave_comment_list *lead_comm;
 
--- a/libinterp/parse-tree/pt-pr-code.cc	Tue Aug 13 19:31:59 2013 -0400
+++ b/libinterp/parse-tree/pt-pr-code.cc	Tue Aug 13 19:35:53 2013 +0200
@@ -993,6 +993,7 @@
   newline ();
 
   tree_statement_list *try_code = cmd.body ();
+  tree_identifier *expr_id = cmd.identifier ();
 
   if (try_code)
     {
@@ -1009,6 +1010,12 @@
 
   os << "catch";
 
+  if (expr_id)
+    {
+      os << " ";
+      expr_id->accept (*this);
+    }
+
   newline ();
 
   tree_statement_list *catch_code = cmd.cleanup ();
--- a/test/try.tst	Tue Aug 13 19:31:59 2013 -0400
+++ b/test/try.tst	Tue Aug 13 19:35:53 2013 +0200
@@ -127,3 +127,34 @@
 %!   assert (lasterr()(1:22), "rethrow: 'a' undefined");
 %! end_try_catch
 
+%!test
+%! clear myerr;
+%! try
+%!   error ("user-defined error");
+%! catch myerr
+%!   assert (myerr.message, "user-defined error");
+%! end_try_catch
+
+%!test
+%! try
+%!   clear a;
+%!   error ("user-defined error");
+%! catch a=1;
+%!   assert (lasterr, "user-defined error");
+%!   assert (a, 1);
+%! end_try_catch
+
+%!test
+%! clear myerr1
+%! clear myerr2
+%! try
+%!   try
+%!     clear a;
+%!     a;
+%!   catch myerr1
+%!     error (myerr1);
+%!   end_try_catch
+%! catch myerr2
+%!   assert (myerr1.message, myerr2.message);
+%!   assert (myerr1.identifier, myerr2.identifier);
+%! end_try_catch