Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding new functions (Check, Interrupt, Unique) to Mathics #696

Merged
merged 6 commits into from
Aug 29, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ before_install:
- if [[ "$ALL_MODULES" == "true" ]]; then
sudo apt-get update -qq &&
sudo apt-get install -qq python-enchant &&
pip install numpy ipywidgets ipykernel IPython nltk langid pycountry pyenchant lxml matplotlib networkx &&
pip install numpy ipywidgets ipykernel requests IPython==5.0.0 nltk langid pycountry pyenchant lxml matplotlib networkx &&
python travis.py &&
if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then
pip install pattern;
Expand Down
15 changes: 15 additions & 0 deletions mathics/builtin/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,22 @@ def apply(self, evaluation):

raise AbortInterrupt

class Interrupt(Builtin):
"""
<dl>
<dt>'Interrupt[]'
<dd>Interrupt an evaluation and returns '$Aborted'.
</dl>
>> Print["a"]; Interrupt[]; Print["b"]
| a
= $Aborted
"""

def apply(self, evaluation):
'Interrupt[]'

raise AbortInterrupt

class Return(Builtin):
'''
<dl>
Expand Down
123 changes: 123 additions & 0 deletions mathics/builtin/inout.py
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,129 @@ def check_message(expr):
return True
return False

class Check(Builtin):
"""
<dl>
<dt>'Check[$expr$, $failexpr$]'
<dd>evaluates $expr$, and returns the result, unless messages were generated, in which case it evaluates and $failexpr$ will be returned.
<dt>'Check[$expr$, $failexpr$, {s1::t1,s2::t2,…}]'
<dd>checks only for the specified messages.
</dl>

Return err when a message is generated:
>> Check[1/0, err]
: Infinite expression 1 / 0 encountered.
= err

#> Check[1^0, err]
= 1

Check only for specific messages:
>> Check[Sin[0^0], err, Sin::argx]
: Indeterminate expression 0 ^ 0 encountered.
= Indeterminate

>> Check[1/0, err, Power::infy]
: Infinite expression 1 / 0 encountered.
= err

#> Check[1 + 2]
: Check called with 1 argument; 2 or more arguments are expected.
= Check[1 + 2]

#> Check[1 + 2, err, 3 + 1]
: Message name 3 + 1 is not of the form symbol::name or symbol::name::language.
= Check[1 + 2, err, 3 + 1]

#> Check[1 + 2, err, hello]
: Message name hello is not of the form symbol::name or symbol::name::language.
= Check[1 + 2, err, hello]

#> Check[1/0, err, Compile::cpbool]
: Infinite expression 1 / 0 encountered.
= ComplexInfinity

#> Check[{0^0, 1/0}, err]
: Indeterminate expression 0 ^ 0 encountered.
: Infinite expression 1 / 0 encountered.
= err

#> Check[0^0/0, err, Power::indet]
: Indeterminate expression 0 ^ 0 encountered.
: Infinite expression 1 / 0 encountered.
= err

#> Check[{0^0, 3/0}, err, Power::indet]
: Indeterminate expression 0 ^ 0 encountered.
: Infinite expression 1 / 0 encountered.
= err

#> Check[1 + 2, err, {a::b, 2 + 5}]
: Message name 2 + 5 is not of the form symbol::name or symbol::name::language.
= Check[1 + 2, err, {a::b, 2 + 5}]

#> Off[Power::infy]
#> Check[1 / 0, err]
= ComplexInfinity

#> On[Power::infy]
#> Check[1 / 0, err]
: Infinite expression 1 / 0 encountered.
= err
"""

attributes = ('HoldAll',)

messages = {
'argmu': 'Check called with 1 argument; 2 or more arguments are expected.',
'name': 'Message name `1` is not of the form symbol::name or symbol::name::language.',
}

def apply_1_argument(self, expr, evaluation):
'Check[expr_]'
return evaluation.message('Check', 'argmu')

def apply(self, expr, failexpr, params, evaluation):
'Check[expr_, failexpr_, params___]'

#Todo: To implement the third form of this function , we need to implement the function $MessageGroups first
#<dt>'Check[$expr$, $failexpr$, "name"]'
#<dd>checks only for messages in the named message group.

def get_msg_list(exprs):
messages = []
for expr in exprs:
if expr.has_form('List', None):
messages.extend(get_msg_list(expr.leaves))
elif check_message(expr):
messages.append(expr)
else:
raise Exception(expr)
return messages

check_messages = set(evaluation.get_quiet_messages())
display_fail_expr = False

params = params.get_sequence()
if len(params) == 0:
result = expr.evaluate(evaluation)
if(len(evaluation.out)):
display_fail_expr = True
else:
try:
msgs = get_msg_list(params)
for x in msgs:
check_messages.add(x)
except Exception as inst :
evaluation.message('Check', 'name', inst.args[0])
return
result = expr.evaluate(evaluation)
for out_msg in evaluation.out:
pattern = Expression('MessageName', Symbol(out_msg.symbol), String(out_msg.tag))
if pattern in check_messages:
display_fail_expr = True
break
return failexpr if display_fail_expr is True else result

class Quiet(Builtin):
"""
Expand Down
136 changes: 136 additions & 0 deletions mathics/builtin/scoping.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,142 @@ def apply(self, vars, expr, evaluation):
result = new_expr.evaluate(evaluation)
return result

class Unique(Predefined):
"""
<dl>
<dt>'Unique[]'
<dd>generates a new symbol and gives a name of the form '$number'.
<dt>'Unique[x]'
<dd>generates a new symbol and gives a name of the form 'x$number'.
<dt>'Unique[{x, y, ...}]'
<dd>generates a list of new symbols.
<dt>'Unique["xxx"]'
<dd>generates a new symbol and gives a name of the form 'xxxnumber'.
</dl>

Create a unique symbol with no particular name:
>> Unique[]
= $1

>> Unique[sym]
= sym$1

Create a unique symbol whose name begins with x:
>> Unique["x"]
= x2

#> Unique[]
= $3

#> Unique[{}]
= {}

#> Unique[{x, x}]
= {x$2, x$3}

Each use of Unique[symbol] increments $ModuleNumber:
>> {$ModuleNumber, Unique[x], $ModuleNumber}
= {4, x$4, 5}

Unique[symbol] creates symbols in the same way Module does:
>> {Module[{x}, x], Unique[x]}
= {x$5, x$6}

Unique with more arguments
>> Unique[{x, "s"}, Flat ^ Listable ^ Orderless]
: Flat ^ Listable ^ Orderless is not a known attribute.
= Unique[{x, s}, Flat ^ Listable ^ Orderless]

Unique call without symbol argument
>> Unique[x + y]
: x + y is not a symbol or a valid symbol name.
= Unique[x + y]

#> Unique[1]
: 1 is not a symbol or a valid symbol name.
= Unique[1]

#> Unique[{m, "s", n}, {Flat, Listable, Orderless}]
= {m$7, s4, n$8}

#> Attributes[{m$7, s4, n$8}]
= {{Flat, Listable, Orderless}, {Flat, Listable, Orderless}, {Flat, Listable, Orderless}}

#> Unique[{x, "s", 1}, {Flat ^ Listable ^ Orderless}]
: 1 is not a symbol or a valid symbol name.
= Unique[{x, s, 1}, {Flat ^ Listable ^ Orderless}]

#> Unique[{"s"}, Flat]
= {s5}

#> Attributes[s5]
= {Flat}
"""

seq_number = 1

messages = {
'usym': '`1` is not a symbol or a valid symbol name.',
'argrx': 'Unique called with `1` arguments; 0 or 1 argument are expected.',
'attnf': '`1` is not a known attribute.',
}

attributes = ('Protected',)

rules = {
'Unique[x_Symbol]': 'Module[{x}, x]',
}

def apply(self, evaluation):
'Unique[]'

new_name = '$%d' % (self.seq_number)
self.seq_number += 1
return Symbol(new_name)
GarkGarcia marked this conversation as resolved.
Show resolved Hide resolved

def apply_symbol(self, vars, attributes, evaluation):
'Unique[vars_, attributes___]'

from mathics.core.parser import is_symbol_name
from mathics.builtin.attributes import get_symbol_list

attributes = attributes.get_sequence()
if len(attributes) > 1:
return evaluation.message('Unique', 'argrx', Integer(len(attributes) + 1))

# Check valid symbol variables
symbols = vars.leaves if vars.has_form('List', None) else [vars]
for symbol in symbols:
if not isinstance(symbol, Symbol):
text = symbol.get_string_value()
if text is None or not is_symbol_name(text):
return evaluation.message('Unique', 'usym', symbol)

# Check valid attributes
attrs = []
if len(attributes) > 0:
attrs = get_symbol_list(attributes[0], lambda item: evaluation.message('Unique', 'attnf', item))
if attrs is None:
return None

# Generate list new symbols
list = []
for symbol in symbols:
if isinstance(symbol, Symbol):
list.append(Module(Expression('List', symbol), symbol).evaluate(evaluation))
else:
new_name = '%s%d' % (symbol.get_string_value(), self.seq_number)
self.seq_number += 1
list.append(Symbol(new_name))

for symbol in list:
for att in attrs:
evaluation.definitions.set_attribute(symbol.get_name(), att)

if vars.has_form('List', None):
return Expression('List', *list)
else:
return list[0]

class Context(Builtin):
r"""
Expand Down