Skip to content

Commit 1bd2750

Browse files
authored
Merge pull request #1029 from mathics/combinatorica-again
Combinatorica again
2 parents 4616d29 + 3a9bbb6 commit 1bd2750

7 files changed

Lines changed: 3589 additions & 9 deletions

File tree

mathics/builtin/control.py

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
from mathics.builtin.base import Builtin, BinaryOperator
1111
from mathics.core.expression import Expression, Symbol, from_python
1212
from mathics.core.evaluation import (
13-
AbortInterrupt, BreakInterrupt, ContinueInterrupt, ReturnInterrupt)
13+
AbortInterrupt, BreakInterrupt, ContinueInterrupt, ReturnInterrupt,
14+
WLThrowInterrupt)
1415
from mathics.builtin.lists import _IterationFunction
1516
from mathics.builtin.patterns import match
1617

@@ -721,3 +722,88 @@ def apply(self, evaluation):
721722
'Continue[]'
722723

723724
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)

mathics/builtin/files.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4793,11 +4793,11 @@ def apply(self, context, evaluation):
47934793
evaluation.message('Needs', 'ctx', Expression(
47944794
'Needs', context), 1, '`')
47954795
return
4796-
4797-
# TODO
4798-
# if Expression('MemberQ', context, Symbol('$Packages')).is_true():
4799-
# # Already loaded
4800-
# return Symbol('Null')
4796+
test_loaded = Expression('MemberQ', Symbol('$Packages'), context)
4797+
test_loaded = test_loaded.evaluate(evaluation)
4798+
if test_loaded.is_true():
4799+
# Already loaded
4800+
return Symbol('Null')
48014801

48024802
result = Expression('Get', context).evaluate(evaluation)
48034803

mathics/builtin/scoping.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from mathics.builtin.base import Builtin, Predefined
66
from mathics.core.expression import (Expression, String, Symbol, Integer,
77
fully_qualified_symbol_name)
8-
8+
from mathics.core.rules import Rule
99

1010
def get_scoping_vars(var_list, msg_symbol='', evaluation=None):
1111
def message(tag, *args):
@@ -55,6 +55,54 @@ def dynamic_scoping(func, vars, evaluation):
5555
return result
5656

5757

58+
class With(Builtin):
59+
"""
60+
<dl>
61+
62+
<dt>'With[{$x$=$x0$, $y$=$y0$, ...}, $expr$]'
63+
<dd>specifies that all occurrences of the symbols $x$, $y$, ... in $expr$ should be replaced by $x0$, $y0$, ...
64+
</dl>
65+
66+
>> n = 10
67+
= 10
68+
>> With[{n = 5}, n ^ 2]
69+
= 25
70+
>> n
71+
= 10
72+
>> With[{x=y}, Hold[x]]
73+
= Hold[y]
74+
>> Table[With[{i=j}, Hold[i]],{j,1,4}]
75+
= {Hold[1], Hold[2], Hold[3], Hold[4]}
76+
>> x=5; With[{x=x}, Hold[x]]
77+
= Hold[5]
78+
>> {Block[{x = 3}, Hold[x]], With[{x = 3}, Hold[x]]}
79+
= {Hold[x], Hold[3]}
80+
>> x=.; ReleaseHold /@ %
81+
= {x, 3}
82+
>> With[{e = y}, Function[{x,y}, e*x*y]]
83+
= Function[{x$, y$}, y x$ y$]
84+
85+
"""
86+
87+
attributes = ('HoldAll',)
88+
89+
messages = {
90+
'lvsym': ("Local variable specification contains `1`, "
91+
"which is not a symbol or an assignment to a symbol."),
92+
'dup': ("Duplicate local variable `1` found in local variable "
93+
"specification."),
94+
'lvlist': "Local variable specification `1` is not a List.",
95+
}
96+
97+
def apply(self, vars, expr, evaluation):
98+
'With[vars_, expr_]'
99+
100+
vars = dict(get_scoping_vars(vars, 'With', evaluation))
101+
result = expr.replace_vars(vars)
102+
result.evaluate(evaluation)
103+
return result
104+
105+
58106
class Block(Builtin):
59107
"""
60108
<dl>
@@ -570,12 +618,15 @@ class BeginPackage(Builtin):
570618

571619
rules = {
572620
'BeginPackage[context_String]': '''
573-
Unprotect[System`Private`$ContextPathStack];
621+
Unprotect[System`Private`$ContextPathStack, System`$Packages];
574622
Begin[context];
575623
System`Private`$ContextPathStack =
576624
Append[System`Private`$ContextPathStack, $ContextPath];
577625
$ContextPath = {context, "System`"};
578-
Protect[System`Private`$ContextPathStack];
626+
$Packages = If[MemberQ[System`$Packages,$Context],
627+
None,
628+
System`$Packages=Join[{$Context}, System`$Packages]];
629+
Protect[System`Private`$ContextPathStack, System`$Packages];
579630
context
580631
''',
581632
}

mathics/core/evaluation.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ class BreakInterrupt(EvaluationInterrupt):
5050
class ContinueInterrupt(EvaluationInterrupt):
5151
pass
5252

53+
class WLThrowInterrupt(EvaluationInterrupt):
54+
def __init__(self, value, tag=None):
55+
self.tag = tag
56+
self.value = value
57+
58+
59+
5360

5461
def _thread_target(request, queue) -> None:
5562
try:
@@ -331,6 +338,18 @@ def evaluate():
331338
self.exc_result = Expression("Overflow")
332339
else:
333340
raise
341+
except WLThrowInterrupt as ti:
342+
if ti.tag:
343+
self.exc_result = Expression("Hold",
344+
Expression("Throw",
345+
ti.value,
346+
ti.tag))
347+
else:
348+
self.exc_result = Expression("Hold",
349+
Expression("Throw",
350+
ti.value
351+
))
352+
self.message("Throw", "nocatch", self.exc_result)
334353
except OverflowError:
335354
self.message("General", "ovfl")
336355
self.exc_result = Expression("Overflow")

0 commit comments

Comments
 (0)