Skip to content

Commit e0c16a8

Browse files
committed
fixing Names, Protect and Unprotect
1 parent ab7921c commit e0c16a8

File tree

3 files changed

+79
-37
lines changed

3 files changed

+79
-37
lines changed

mathics/builtin/attributes.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,17 +201,52 @@ def apply(self, symbols, evaluation):
201201
class Unprotect(Builtin):
202202
"""
203203
<dl>
204-
<dt>'Unprotect'[$s1$, $s2$, ...]
205-
<dd>removes the attribute 'Protected' for the symbols $si$.
204+
<dt>'Unprotect'[$s1$, $s2$, ...]
205+
<dd>removes the attribute 'Protected' for the symbols $si$.
206+
207+
<dt>'Unprotect'[$str$]
208+
<dd>unprotects symbols whose names textually match $str$.
206209
</dl>
207210
"""
208211

209212
attributes = ('HoldAll',)
210-
211-
rules = {
212-
'Unprotect[symbols__]': 'ClearAttributes[{symbols}, Protected]',
213+
messages = {
214+
'ssym': "`1` is not a symbol or a string.",
213215
}
214216

217+
def apply(self, symbols, evaluation):
218+
"Unprotect[symbols___]"
219+
protected = Symbol("System`Protected")
220+
items = []
221+
if isinstance(symbols ,Symbol):
222+
symbols = [symbols]
223+
elif isinstance(symbols, Expression):
224+
symbols = symbols.get_leaves()
225+
elif isinstance(symbols ,String):
226+
symbols = [symbols]
227+
else:
228+
symbols = symbols.get_sequence()
229+
230+
for symbol in symbols:
231+
if isinstance(symbol, Symbol):
232+
items.append(symbol)
233+
else:
234+
pattern = symbol.get_string_value()
235+
if not pattern or pattern=="":
236+
evaluation.message('Unprotect', 'ssym', symbol)
237+
continue
238+
239+
if pattern[0] == "`":
240+
pattern = evaluation.definitions.get_current_context() + pattern[1:]
241+
names = evaluation.definitions.get_matching_names(pattern)
242+
for defn in names:
243+
symbol = Symbol(defn)
244+
if not 'System`Locked' in evaluation.definitions.get_attributes(defn):
245+
items.append(symbol)
246+
247+
Expression("ClearAttributes", Expression("List", *items), protected).evaluate(evaluation)
248+
return Symbol('Null')
249+
215250

216251
class Protected(Predefined):
217252
"""

mathics/builtin/system.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
import os
1010
import platform
1111
import sys
12+
import re
1213

1314
from mathics.core.expression import Expression, Integer, String, Symbol, strip_context
1415
from mathics.builtin.base import Builtin, Predefined
1516
from mathics import version_string
17+
from mathics.builtin.strings import StringExpression, to_regex
1618

1719

1820
class Aborted(Predefined):
@@ -194,8 +196,12 @@ class Names(Builtin):
194196

195197
def apply(self, pattern, evaluation):
196198
"Names[pattern_]"
199+
headname = pattern.get_head_name()
200+
if headname == "System`StringExpression":
201+
pattern = re.compile(to_regex(pattern, evaluation))
202+
else:
203+
pattern = pattern.get_string_value()
197204

198-
pattern = pattern.get_string_value()
199205
if pattern is None:
200206
return
201207

mathics/core/definitions.py

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
)
2222
from mathics.core.characters import letters, letterlikes
2323

24-
24+
type_compiled_pattern = type(re.compile("a.a"))
2525
names_wildcards = "@*"
2626
base_names_pattern = r"((?![0-9])([0-9${0}{1}{2}])+)".format(
2727
letters, letterlikes, names_wildcards
@@ -313,37 +313,38 @@ def get_matching_names(self, pattern) -> typing.List[str]:
313313
which aren't uppercase letters. In the context pattern, both
314314
'*' and '@' match context marks.
315315
"""
316-
317-
if re.match(full_names_pattern, pattern) is None:
318-
# The pattern contained characters which weren't allowed
319-
# in symbols and aren't valid wildcards. Hence, the
320-
# pattern can't match any symbols.
321-
return []
322-
323-
# If we get here, there aren't any regexp metacharacters in
324-
# the pattern.
325-
326-
if "`" in pattern:
327-
ctx_pattern, short_pattern = pattern.rsplit("`", 1)
328-
ctx_pattern = (
329-
(ctx_pattern + "`")
330-
.replace("@", "[^A-Z`]+")
331-
.replace("*", ".*")
332-
.replace("$", r"\$")
333-
)
316+
if isinstance(pattern, type_compiled_pattern):
317+
regex = pattern
334318
else:
335-
short_pattern = pattern
336-
# start with a group matching the accessible contexts
337-
ctx_pattern = "(?:%s)" % "|".join(
338-
re.escape(c) for c in self.get_accessible_contexts()
339-
)
340-
341-
short_pattern = (
342-
short_pattern.replace("@", "[^A-Z]+")
343-
.replace("*", "[^`]*")
344-
.replace("$", r"\$")
345-
)
346-
regex = re.compile("^" + ctx_pattern + short_pattern + "$")
319+
if re.match(full_names_pattern, pattern) is None:
320+
# The pattern contained characters which weren't allowed
321+
# in symbols and aren't valid wildcards. Hence, the
322+
# pattern can't match any symbols.
323+
return []
324+
325+
# If we get here, there aren't any regexp metacharacters in
326+
# the pattern.
327+
328+
if '`' in pattern:
329+
ctx_pattern, short_pattern = pattern.rsplit('`', 1)
330+
if ctx_pattern == "":
331+
ctx_pattern="System`"
332+
else:
333+
ctx_pattern = ((ctx_pattern + '`')
334+
.replace('@', '[^A-Z`]+')
335+
.replace('*', '.*')
336+
.replace('$', r'\$'))
337+
else:
338+
short_pattern = pattern
339+
# start with a group matching the accessible contexts
340+
ctx_pattern = "(?:%s)" % "|".join(
341+
re.escape(c) for c in self.get_accessible_contexts())
342+
343+
short_pattern = (short_pattern
344+
.replace('@', '[^A-Z]+')
345+
.replace('*', '[^`]*')
346+
.replace('$', r'\$'))
347+
regex = re.compile('^' + ctx_pattern + short_pattern + '$')
347348

348349
return [name for name in self.get_names() if regex.match(name)]
349350

0 commit comments

Comments
 (0)