Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Exception handling



Here's a suggestion for adding the exception handling functions with
Bart's changes.  It seemed to warrant a separate function directory
rather than "Misc".  A single init-exceptions function in Misc would be
another way of doing it, but I think adding a third function name is an
unnecessary complication.

Index: Doc/Zsh/contrib.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/contrib.yo,v
retrieving revision 1.39
diff -u -r1.39 contrib.yo
--- Doc/Zsh/contrib.yo	13 Jan 2005 15:31:52 -0000	1.39
+++ Doc/Zsh/contrib.yo	14 Feb 2005 14:35:05 -0000
@@ -13,6 +13,7 @@
 menu(Utilities)
 menu(Prompt Themes)
 menu(ZLE Functions)
+menu(Exception Handling)
 menu(MIME Functions)
 menu(Other Functions)
 endmenu()
@@ -345,7 +346,7 @@
 )
 enditem()
 
-texinode(ZLE Functions)(MIME Functions)(Prompt Themes)(User Contributions)
+texinode(ZLE Functions)(Exception Handling)(Prompt Themes)(User Contributions)
 sect(ZLE Functions)
 
 subsect(Widgets)
@@ -1033,7 +1034,93 @@
 )
 enditem()
 
-texinode(MIME Functions)(Other Functions)(ZLE Functions)(User Contributions)
+texinode(Exception Handling)(MIME Functions)(ZLE Functions)(User Contributions)
+sect(Exception Handling)
+
+Two functions are provided to enable zsh to provide exception handling in a
+form that should be familiar from other languages.
+
+startitem()
+findex(throw)
+item(tt(throw) var(exception))(
+The function tt(throw) throws the named var(exception).  The name is
+an arbitrary string and is only used by the tt(throw) and tt(catch)
+functions.  An exception is for the most part treated the same as a
+shell error, i.e. an unhandled exception will cause the shell to abort all
+processing in a function or script and to return to the top level in an
+interative shell.
+)
+item(tt(catch) var(exception-pattern))(
+The function tt(catch) returns status zero if an exception was thrown and
+the pattern var(exception-pattern) matches its name.  Otherwise it
+returns status 1.  var(exception-pattern) is a standard
+shell pattern, respecting the current setting of the tt(EXTENDED_GLOB)
+option.  An alias tt(catch) is also defined to prevent the argument to the
+function from matching filenames, so patterns may be used unquoted.  Note
+that as exceptions are not fundamentally different from other shell errors
+it is possible to catch shell errors by using an empty string as the
+exception name.  The shell variable tt(CAUGHT) is set by tt(catch) to the
+name of the exception caught.  It is possible to rethrow an exception by
+calling the tt(throw) function again once an exception has been caught.
+findex(catch)
+)
+enditem()
+
+The functions are designed to be use together with the tt(always) construct
+described in
+ifzman(zmanref(zshmisc))\
+ifnzman(noderef(Complex Commands)).  This is important as only this
+construct provides the required support for exceptions.  A typical example
+is as follows.
+
+example({
+  # "try" block
+  # ... nested code here calls "throw MyExcept"
+} always {
+  # "always" block
+  if catch MyExcept; then
+    print "Caught exception MyExcept"
+  elif catch ''; then
+    print "Caught a shell error.  Propagating..."
+    throw ''
+  fi
+  # Other exceptions are not handled but may be caught further
+  # up the call stack.
+})
+
+If all exceptions should be caught, the following idiom might be
+preferable.
+
+example({
+  # ... nested code here throws an exception
+} always {
+  if catch *; then
+    case $CAUGHT in
+      LPAR()MyExcept+RPAR()
+      print "Caught my own exception"
+      ;;
+      LPAR()*RPAR()
+      print "Caught some other exception"
+      ;;
+    esac
+  fi
+})
+
+In common with exception handling in other languages, the exception may be
+thrown by code deeply nested inside the `try' block.  However, note that it
+must be thrown inside the current shell, not in a subshell forked for a
+pipline, parenthesised current-shell construct, or some form of
+substitution.
+
+The system internally uses the shell variable tt(EXCEPTION) to record the
+name of the exception between throwing and catching.  One drawback of this
+scheme is that if the exception is not handled the variable tt(EXCEPTION)
+remains set and may be incorrectly recognised as the name of an exception
+if a shell error subsequently occurs.  Adding tt(unset EXCEPTION) at the
+start of the outermost layer of any code that uses exception handling will
+eliminate this problem.
+
+texinode(MIME Functions)(Other Functions)(Exception Handling)(User Contributions)
 sect(MIME Functions)
 
 Three functions are available to provide handling of files recognised by
Index: Functions/Exceptions/catch
===================================================================
RCS file: Functions/Exceptions/catch
diff -N Functions/Exceptions/catch
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Functions/Exceptions/catch	14 Feb 2005 14:35:05 -0000
@@ -0,0 +1,41 @@
+# Catch an exception.  Returns 0 if the exception in question was caught.
+# The first argument gives the exception to catch, which may be a
+# pattern.
+# This must be within an always-block.  A typical set of handlers looks
+# like:
+#   {
+#     # try block; something here throws exceptions
+#   } always {
+#      if catch MyExcept; then
+#         # Handler code goes here.
+#         print Handling exception MyExcept
+#      elif catch *; then
+#         # This is the way to implement a catch-all.
+#         print Handling any other exception
+#      fi
+#   }
+# As with other languages, exceptions do not need to be handled
+# within an always block and may propagate to a handler further up the
+# call chain.
+#
+# It is possible to throw an exception from within the handler by
+# using "throw".
+#
+# The shell variable $CAUGHT is set to the last exception caught,
+# which is useful if the argument to "catch" was a pattern.
+#
+# Use "function" keyword in case catch is already an alias.
+function catch {
+  if [[ $TRY_BLOCK_ERROR -gt 0 && $EXCEPTION = ${~1} ]]; then
+    (( TRY_BLOCK_ERROR = 0 ))
+    CAUGHT="$EXCEPTION"
+    unset EXCEPTION
+    return 0
+  fi
+
+  return 1
+}
+# Never use globbing with "catch".
+alias catch="noglob catch"
+
+catch "$@"
Index: Functions/Exceptions/throw
===================================================================
RCS file: Functions/Exceptions/throw
diff -N Functions/Exceptions/throw
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Functions/Exceptions/throw	14 Feb 2005 14:35:05 -0000
@@ -0,0 +1,29 @@
+# Throw an exception.
+# The first argument is a string giving the exception.  Other arguments
+# are ignored.
+#
+# This is designed to be called somewhere inside a "try-block", i.e.
+# some code of the form:
+#   {
+#     # try-block
+#   } always {
+#     # always-block
+#   }
+# although as normal with exceptions it might be hidden deep inside
+# other code.  Note, however, that it must be code running within the
+# current shell; with shells, unlike other languages, it is quite easy
+# to miss points at which the shell forks.
+#
+# If there is nothing to catch an exception, this behaves like any
+# other shell error, aborting to the command prompt or abandoning a
+# script.
+
+# The following must not be local.
+EXCEPTION="$1"
+if (( TRY_BLOCK_ERROR == 0 )); then
+  # We are throwing an exception from the middle of an always-block.
+  # We can do this by restoring the error status from the try-block.
+  (( TRY_BLOCK_ERROR = 1 ))
+fi
+# Raise an error, but don't show an error message.
+{ ${:?THROW} } 2>/dev/null
Index: Src/zsh.mdd
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/zsh.mdd,v
retrieving revision 1.11
diff -u -r1.11 zsh.mdd
--- Src/zsh.mdd	6 Oct 2003 18:39:58 -0000	1.11
+++ Src/zsh.mdd	14 Feb 2005 14:35:05 -0000
@@ -2,7 +2,7 @@
 link=static
 load=yes
 # load=static should replace use of alwayslink
-functions='Functions/Misc/* Functions/MIME/* Functions/Prompts/*'
+functions='Functions/Exceptions/* Functions/Misc/* Functions/MIME/* Functions/Prompts/*'
 
 nozshdep=1
 alwayslink=1

-- 
Peter Stephenson <pws@xxxxxxx>                  Software Engineer
CSR PLC, Churchill House, Cambridge Business Park, Cowley Road
Cambridge, CB4 0WZ, UK                          Tel: +44 (0)1223 692070


**********************************************************************
This email and any files transmitted with it are confidential and
intended solely for the use of the individual or entity to whom they
are addressed. If you have received this email in error please notify
the system manager.

**********************************************************************



Messages sorted by: Reverse Date, Date, Thread, Author