(* Title: Install a pretty printer for the exn type Author: David Matthews Copyright David Matthews 2009, 2016 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *) local open PolyML (* Print exception packet. Run-time system exceptions have to be processed specially because the IDs don't have printer functions. *) fun exnPrint depth _ exn = let val (exnId, exnName, exnArg, _) = RunCall.unsafeCast exn (* This parenthesis code is used in various places and probably should be centralised. *) fun parenthesise(s as PrettyBlock(_, _, _, [ _ ])) = s | parenthesise(s as PrettyBlock(_, _, _, (PrettyString("(")::_ ))) = s | parenthesise(s as PrettyBlock(_, _, _, (PrettyString("{")::_ ))) = s | parenthesise(s as PrettyBlock(_, _, _, (PrettyString("[")::_ ))) = s | parenthesise(s as PrettyBlock _) = PrettyBlock(3, true, [], [ PrettyString "(", s, PrettyString ")" ]) | parenthesise s = s (* String or Break *) fun nullaryException s = PrettyString s and parameterException(s, param) = PrettyBlock(1, false, [], [ PrettyString s, PrettyBreak(1, 1), parenthesise param ]) (* Use prettyRepresentation because this correctly quotes the string. *) fun stringException(s, arg: string) = parameterException(s, PolyML.prettyRepresentation(arg, depth-1)) in if RunCall.isShort exnId then case exn of RunCall.Conversion s => stringException(exnName, s) | Fail s => stringException(exnName, s) | RunCall.Foreign s => stringException(exnName, s) | RunCall.Thread s => stringException(exnName, s) | RunCall.XWindows s => stringException(exnName, s) | OS.SysErr param => parameterException("SysErr", if depth <= 1 then PrettyString "..." else PolyML.prettyRepresentation(param, depth-1)) | _ => (* Anything else is nullary. *) nullaryException exnName else ( (* Exceptions generated within ML contain a printer function. *) case !exnId of NONE => nullaryException exnName | SOME printFn => parameterException(exnName, printFn(exnArg, depth-1)) ) end in val () = addPrettyPrinter exnPrint end; (* Print a ref. Because refs can form circular structures we include a check for a loop here. *) local open PolyML (* If we have an expression as the argument we parenthesise it unless it is a simple string, a tuple, a record or a list. *) fun parenthesise(s as PrettyBlock(_, _, _, [ _ ])) = s | parenthesise(s as PrettyBlock(_, _, _, (PrettyString("(")::_ ))) = s | parenthesise(s as PrettyBlock(_, _, _, (PrettyString("{")::_ ))) = s | parenthesise(s as PrettyBlock(_, _, _, (PrettyString("[")::_ ))) = s | parenthesise(s as PrettyBlock _) = PrettyBlock(3, true, [], [ PrettyString "(", s, PrettyString ")" ]) | parenthesise s = s (* String or Break *) val printLimit: word ref list Universal.tag = Universal.tag() fun print_ref depth doArg (r as ref x) = if depth <= 0 then PrettyString "..." else let (* We keep a list in thread-local storage of refs we're currently printing. This is thread-local to avoid interference between different threads. *) val currentRefs = case Thread.Thread.getLocal printLimit of NONE => [] | SOME limit => limit val thisRef: word ref = RunCall.unsafeCast r in if List.exists(fn x => x = thisRef) currentRefs then PrettyString "..." (* We've already seen this ref. *) else ( (* Add this to the list. *) Thread.Thread.setLocal (printLimit, thisRef :: currentRefs); (* Print it and reset the list*) (PrettyBlock(3, false, [], [ PrettyString "ref", PrettyBreak(1, 0), parenthesise(doArg(x, depth-1)) ])) before (Thread.Thread.setLocal (printLimit, currentRefs)) ) handle exn => ( (* Reset the list if there's been an exception. *) Thread.Thread.setLocal (printLimit, currentRefs); raise exn ) end in val () = addPrettyPrinter print_ref end;