Skip to content

Commit 66259ff

Browse files
authored
More parameter checks (#1485)
Checks added to 'If', 'While', and 'Throw', "ToExpression". We start to handle 'argb' tag, "between x and y". Some linting and small document corrections were done.
1 parent 9a7376f commit 66259ff

File tree

9 files changed

+158
-57
lines changed

9 files changed

+158
-57
lines changed

mathics/builtin/atomic/strings.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from mathics_scanner.errors import SyntaxError
1515

16-
from mathics.core.atoms import Integer, Integer0, Integer1, String
16+
from mathics.core.atoms import Integer, Integer1, String
1717
from mathics.core.attributes import A_LISTABLE, A_PROTECTED
1818
from mathics.core.builtin import Builtin, Predefined, PrefixOperator
1919
from mathics.core.convert.expression import to_mathics_list
@@ -331,7 +331,7 @@ class CharacterEncodings(Predefined):
331331
https://reference.wolfram.com/language/ref/\$CharacterEncodings.html</url>
332332
333333
<dl>
334-
<dt>'\\$CharacterEncodings'
334+
<dt>'\$CharacterEncodings'
335335
<dd>stores the list of available character encodings.
336336
</dl>
337337
@@ -739,7 +739,7 @@ class ToExpression(Builtin):
739739
https://reference.wolfram.com/language/ref/ToExpression.html</url>
740740
<dl>
741741
<dt>'ToExpression'[$input$]
742-
<dd>interprets a given string as Mathics input.
742+
<dd>interprets a given string as Mathics3 input.
743743
744744
<dt>'ToExpression'[$input$, $form$]
745745
<dd>reads the given input in the specified $form$.
@@ -773,11 +773,11 @@ class ToExpression(Builtin):
773773
"""
774774
attributes = A_LISTABLE | A_PROTECTED
775775

776+
# Set checking that the between one and three arguments are allowed.
777+
eval_error = Builtin.generic_argument_error
778+
expected_args = range(1, 4)
779+
776780
messages = {
777-
"argb": (
778-
"`1` called with `2` arguments; "
779-
"between `3` and `4` arguments are expected."
780-
),
781781
"interpfmt": (
782782
"`1` is not a valid interpretation format. "
783783
"Valid interpretation formats include InputForm "
@@ -844,13 +844,6 @@ def eval(self, seq, evaluation: Evaluation):
844844

845845
return result
846846

847-
def eval_empty(self, evaluation: Evaluation):
848-
"ToExpression[]"
849-
evaluation.message(
850-
"ToExpression", "argb", "ToExpression", Integer0, Integer1, Integer(3)
851-
)
852-
return
853-
854847

855848
class ToString(Builtin):
856849
"""

mathics/builtin/codetables.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"""
66

77
# an (incomplete) list of ISO 639-3 language codes. these codes are used in Extended Open Multilingual Wordnet
8-
# (see e.g. http://compling.hss.ntu.edu.sg/omw/summx.html) and in Tesseract (see e.g.
8+
# (see e.g. http://compiling.hss.ntu.edu.sg/omw/summx.html) and in Tesseract (see e.g.
99
# https://github.com/tesseract-ocr/tessdata). For the complete list, see http://www-01.sil.org/iso639-3/codes.asp
1010

1111
iso639_3 = {

mathics/builtin/numeric.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ class Round(Builtin):
713713
eval_error = Builtin.generic_argument_error
714714
expected_args = (1, 2)
715715

716-
# For now, we handle Rounding Complex numnbers as rules.
716+
# For now, we handle Rounding Complex numbers as rules.
717717
# In the future consider folding this into the code.
718718
rules = {
719719
"Round[expr_Complex]": "Round[Re[expr], 1] + I * Round[Im[expr], 1]",

mathics/builtin/procedural.py

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,13 @@ class Abort(Builtin):
5555
= $Aborted
5656
"""
5757

58+
# Set checking that the no arguments are allowed.
59+
# eval_error = Builtin.generic_argument_error
60+
# expected_args = 0
61+
5862
summary_text = "generate an abort"
5963

60-
def eval(self, evaluation: Evaluation):
64+
def eval(self, evaluation: Evaluation): # pylint: disable=unused-argument
6165
"Abort[]"
6266

6367
raise AbortInterrupt
@@ -78,13 +82,17 @@ class Break(Builtin):
7882
= 11
7983
"""
8084

85+
# Set checking that the no arguments are allowed.
86+
# eval_error = Builtin.generic_argument_error
87+
# expected_args = 0
88+
8189
messages = {
8290
"nofwd": "No enclosing For, While, or Do found for Break[].",
8391
}
8492

8593
summary_text = "exit a 'For', 'While', or 'Do' loop"
8694

87-
def eval(self, evaluation: Evaluation):
95+
def eval(self, evaluation: Evaluation): # pylint: disable=unused-argument
8896
"Break[]"
8997

9098
raise BreakInterrupt
@@ -125,6 +133,10 @@ class Catch(Builtin):
125133

126134
attributes = A_HOLD_ALL | A_PROTECTED
127135

136+
# Set checking that the between one and three arguments are allowed.
137+
eval_error = Builtin.generic_argument_error
138+
expected_args = range(1, 4)
139+
128140
summary_text = "handle an exception raised by a 'Throw'"
129141

130142
def eval_expr(self, expr, evaluation):
@@ -297,7 +309,7 @@ class Do(IterationFunction):
297309
allow_loopcontrol = True
298310
summary_text = "evaluate an expression looping over a variable"
299311

300-
def get_result(self, items):
312+
def get_result(self, _items):
301313
return SymbolNull
302314

303315

@@ -377,15 +389,32 @@ class If(Builtin):
377389
>> If[False, a] //FullForm
378390
= Null
379391
380-
You might use comments (inside '(*' and '*)') to make the branches of 'If'
392+
You might use comments inside '(*' and '*)' to make the branches of 'If'
381393
more readable:
382394
>> If[a, (*then*) b, (*else*) c];
395+
396+
397+
Since one or more arguments to a boolean operation could be symbolic, it is\
398+
possible that an 'If' cannot be evaluated. For example:
399+
400+
>> Clear[a, b]; If [a < b, a, b]
401+
= If[a < b, a, b]
402+
403+
To handle this, 'If' takes an optional fourth parameter:
404+
405+
>> If [a < b, a, b, "I give up"]
406+
= I give up
407+
383408
"""
384409

385-
summary_text = "if-then-else conditional expression"
386410
# This is the WR summary: "test if a condition is true, false, or
387411
# of unknown truth value"
388412
attributes = A_HOLD_REST | A_PROTECTED
413+
414+
# Set checking that the number of arguments required between 2 and 4.
415+
eval_error = Builtin.generic_argument_error
416+
expected_args = range(2, 5)
417+
389418
summary_text = "test if a condition is true, false, or of unknown truth value"
390419

391420
def eval(self, condition, t, evaluation):
@@ -429,9 +458,13 @@ class Interrupt(Builtin):
429458
= $Aborted
430459
"""
431460

461+
# Set checking that the no arguments are allowed.
462+
# eval_error = Builtin.generic_argument_error
463+
# expected_args = 0
464+
432465
summary_text = "interrupt evaluation and return '$Aborted'"
433466

434-
def eval(self, evaluation: Evaluation):
467+
def eval(self, evaluation: Evaluation): # pylint: disable=unused-argument
435468
"Interrupt[]"
436469

437470
raise AbortInterrupt
@@ -460,7 +493,7 @@ class Pause(Builtin):
460493
# Number of timeout polls per second that we perform in looking
461494
# for a timeout.
462495

463-
def eval(self, n, evaluation: Evaluation):
496+
def eval(self, n, evaluation: Evaluation): # pylint: disable=unused-argument
464497
"Pause[n_]"
465498
try:
466499
sleep_time = valid_time_from_expression(n, evaluation)
@@ -504,7 +537,7 @@ class Return(Builtin):
504537

505538
summary_text = "return from a function"
506539

507-
def eval(self, expr, evaluation: Evaluation):
540+
def eval(self, expr, evaluation: Evaluation): # pylint: disable=unused-argument
508541
"Return[expr_]"
509542
raise ReturnInterrupt(expr)
510543

@@ -594,17 +627,23 @@ class Throw(Builtin):
594627
= Hold[Throw[1]]
595628
"""
596629

630+
# Set checking that the number of arguments required is one or two. WMA uses 1..3.
631+
eval_error = Builtin.generic_argument_error
632+
expected_args = (1, 2)
633+
597634
messages = {
598635
"nocatch": "Uncaught `1` returned to top level.",
599636
}
600637

601638
summary_text = "throw an expression to be caught by a surrounding 'Catch'"
602639

603-
def eval(self, value, evaluation: Evaluation):
640+
def eval(self, value, evaluation: Evaluation): # pylint: disable=unused-argument
604641
"Throw[value_]"
605642
raise WLThrowInterrupt(value)
606643

607-
def eval_with_tag(self, value, tag, evaluation: Evaluation):
644+
def eval_with_tag(
645+
self, value, tag, evaluation: Evaluation
646+
): # pylint: disable=unused-argument
608647
"Throw[value_, tag_]"
609648
raise WLThrowInterrupt(value, tag)
610649

@@ -692,11 +731,15 @@ class While(Builtin):
692731
= 3
693732
"""
694733

695-
summary_text = "evaluate an expression while a criterion is true"
696734
attributes = A_HOLD_ALL | A_PROTECTED
735+
# Set checking that the number of arguments required is one.
736+
eval_error = Builtin.generic_argument_error
737+
expected_args = (1, 2)
738+
697739
rules = {
698740
"While[test_]": "While[test, Null]",
699741
}
742+
summary_text = "evaluate an expression while a criterion is true"
700743

701744
def eval(self, test, body, evaluation):
702745
"While[test_, body_]"

mathics/builtin/quantities.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ class QuantityQ(Test):
314314
https://reference.wolfram.com/language/ref/QuantityQ.html</url>
315315
<dl>
316316
<dt>'QuantityQ'[$expr$]
317-
<dd>return 'True' if $expr$ is a valid <url>:'Quantity':/doc/reference-of-built-in-symbols/units-and-quantities
317+
<dd>return 'True' if $expr$ is a valid <url>:Quantity:/doc/reference-of-built-in-symbols/units-and-quantities
318318
/quantity/</url> with valid arguments, and 'False' otherwise.
319319
</dl>
320320

mathics/core/builtin.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def eval_with_options(x, evaluation: Evaluation, options: dict):
204204
defaults: Dict[Optional[int], str] = {}
205205

206206
# Number of arguments expected. -1 is used for arbitrary number.
207-
expected_args: Union[int, Tuple[int, int]] = -1
207+
expected_args: Union[int, Tuple[int, int], range] = -1
208208

209209
formats: Dict[str, Any] = {}
210210
messages: Dict[str, Any] = {}
@@ -473,6 +473,15 @@ def generic_argument_error(self, invalid, evaluation: Evaluation):
473473
Integer(expected_args1),
474474
Integer(expected_args2),
475475
)
476+
elif isinstance(self.expected_args, range):
477+
evaluation.message(
478+
name,
479+
"argb",
480+
Symbol(name),
481+
Integer(got_arg_count),
482+
Integer(self.expected_args.start),
483+
Integer(self.expected_args.stop - 1),
484+
)
476485
else:
477486
if self.expected_args == 1:
478487
evaluation.message(name, "argx", Symbol(name), Integer(got_arg_count))

mathics/core/expression.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1373,7 +1373,7 @@ def rules():
13731373
result._timestamp_cache(evaluation)
13741374
return result, False
13751375
else:
1376-
return result, True
1376+
return result, result is not SymbolAborted
13771377

13781378
# Step 7: If we are here, is because we didn't find any rule that
13791379
# matches the expression.

0 commit comments

Comments
 (0)