|
10 | 10 | from mathics.builtin.base import Builtin, BinaryOperator |
11 | 11 | from mathics.core.expression import Expression, Symbol, from_python |
12 | 12 | from mathics.core.evaluation import ( |
13 | | - AbortInterrupt, BreakInterrupt, ContinueInterrupt, ReturnInterrupt) |
| 13 | + AbortInterrupt, BreakInterrupt, ContinueInterrupt, ReturnInterrupt, |
| 14 | + WLThrowInterrupt) |
14 | 15 | from mathics.builtin.lists import _IterationFunction |
15 | 16 | from mathics.builtin.patterns import match |
16 | 17 |
|
@@ -721,3 +722,88 @@ def apply(self, evaluation): |
721 | 722 | 'Continue[]' |
722 | 723 |
|
723 | 724 | raise ContinueInterrupt |
| 725 | + |
| 726 | + |
| 727 | +class Catch(Builtin): |
| 728 | + """ |
| 729 | + <dl> |
| 730 | + <dt>'Catch[`expr`]' |
| 731 | + <dd> returns the argument of the first Throw generated in the evaluation of expr. |
| 732 | +
|
| 733 | + <dt>'Catch[`expr`, `form`]' |
| 734 | + <dd> returns value from the first Throw[`value`,`tag`] for which form matches `tag`. |
| 735 | +
|
| 736 | + <dt>'Catch[`expr`,`form`, `f`]' |
| 737 | + <dd> returns the argument of the first `Throw` generated in the evaluation of `expr`. |
| 738 | + </dl> |
| 739 | +
|
| 740 | + Exit to the enclosing Catch as soon as Throw is evaluated: |
| 741 | + << Catch[r; s; Throw[t]; u; v] |
| 742 | + = t |
| 743 | +
|
| 744 | + Define a function that can "throw an exception": |
| 745 | + << f[x_] := If[x > 12, Throw[overflow], x!] |
| 746 | + = ... |
| 747 | + The result of Catch is just what is thrown by Throw: |
| 748 | + << Catch[f[1] + f[15]] |
| 749 | + = overflow |
| 750 | + << Catch[f[1]+f[4]] |
| 751 | + = 24 |
| 752 | +
|
| 753 | + """ |
| 754 | + attributes = ("HoldAll",) |
| 755 | + |
| 756 | + def apply1(self, expr, evaluation): |
| 757 | + 'Catch[expr_]' |
| 758 | + try: |
| 759 | + ret = expr.evaluate(evaluation) |
| 760 | + except WLThrowInterrupt as e: |
| 761 | + return e.value |
| 762 | + return ret |
| 763 | + |
| 764 | + def apply3(self, expr, form, f, evaluation): |
| 765 | + "Catch[expr_, form_, f__:Identity]" |
| 766 | + try: |
| 767 | + ret = expr.evaluate(evaluation) |
| 768 | + except WLThrowInterrupt as e: |
| 769 | + # TODO: check that form match tag. |
| 770 | + # otherwise, re-raise the exception |
| 771 | + match = Expression("MatchQ", e.tag, form).evaluate(evaluation) |
| 772 | + if match.is_true(): |
| 773 | + return Expression(f, e.value) |
| 774 | + else: |
| 775 | + # A plain raise hide, this path and preserves the traceback |
| 776 | + # of the call that was originally given. |
| 777 | + raise |
| 778 | + return ret |
| 779 | + |
| 780 | + |
| 781 | +class Throw(Builtin): |
| 782 | + """ |
| 783 | + <dl> |
| 784 | + <dt>'Throw[`value`]' |
| 785 | + <dd> stops evaluation and returns `value` as the value of the nearest enclosing Catch. |
| 786 | +
|
| 787 | + <dt>'Catch[`value`, `tag`]' |
| 788 | + <dd> is caught only by `Catch[expr,form]`, where tag matches form. |
| 789 | +
|
| 790 | + </dl> |
| 791 | +
|
| 792 | + Using Throw can affect the structure of what is returned by a function: |
| 793 | +
|
| 794 | + << NestList[#^2 + 1 &, 1, 7] |
| 795 | + = ... |
| 796 | + << Catch[NestList[If[# > 1000, Throw[#], #^2 + 1] &, 1, 7]] |
| 797 | + = 458330 |
| 798 | + << Throw[1] |
| 799 | + = Null |
| 800 | + """ |
| 801 | + messages = {'nocatch': 'Uncaught `1` returned to top level.', } |
| 802 | + |
| 803 | + def apply1(self, value, evaluation): |
| 804 | + 'Throw[value_]' |
| 805 | + raise WLThrowInterrupt(value) |
| 806 | + |
| 807 | + def apply2(self, value, tag, evaluation): |
| 808 | + 'Throw[value_, tag_]' |
| 809 | + raise WLThrowInterrupt(value, tag) |
0 commit comments