From a31003fab964e529152389029ec3126a3802851b Mon Sep 17 00:00:00 2001 From: Richard Doherty Date: Thu, 27 Jan 2022 10:54:05 -0500 Subject: [PATCH] fix(generators): Fix an operator precedence issue in the math_number_property generators to remove extra parentheses (#5685) * Fixed issue where the mathIsPrime function inserted extra parenthesis around certain blocks. * Added tests to generated JS * Updated generated code in Lua * Updated Generated code for tests in Dart * Updated generated code for tests in PHP * Updated generated code for tests in Python * Also changed var to const and let. Co-authored-by: jeremyjacob123 <43049656+jeremyjacob123@users.noreply.github.com> Co-authored-by: LouisCatala <86700310+LouisCatala@users.noreply.github.com> Co-authored-by: jeremyjacob123 <43049656+jeremyjacob123@users.noreply.github.com> --- generators/dart/math.js | 105 +++++++------- generators/javascript/math.js | 59 ++++---- generators/lua/math.js | 107 +++++++------- generators/php/math.js | 96 +++++++------ generators/python/math.js | 111 +++++++-------- tests/generators/golden/generated.dart | 4 + tests/generators/golden/generated.js | 4 + tests/generators/golden/generated.lua | 4 + tests/generators/golden/generated.php | 4 + tests/generators/golden/generated.py | 4 + tests/generators/math.xml | 185 +++++++++++++++++++++---- 11 files changed, 426 insertions(+), 257 deletions(-) diff --git a/generators/dart/math.js b/generators/dart/math.js index 24b7e268e4f..ab2d0cc508d 100644 --- a/generators/dart/math.js +++ b/generators/dart/math.js @@ -164,59 +164,64 @@ Dart['math_constant'] = function(block) { Dart['math_number_property'] = function(block) { // Check if a number is even, odd, prime, whole, positive, or negative // or if it is divisible by certain number. Returns true or false. - const number_to_check = - Dart.valueToCode(block, 'NUMBER_TO_CHECK', Dart.ORDER_MULTIPLICATIVE); - if (!number_to_check) { - return ['false', Dart.ORDER_ATOMIC]; - } - const dropdown_property = block.getFieldValue('PROPERTY'); + const PROPERTIES = { + 'EVEN': [' % 2 == 0', Dart.ORDER_MULTIPLICATIVE, + Dart.ORDER_EQUALITY], + 'ODD': [' % 2 == 1', Dart.ORDER_MULTIPLICATIVE, + Dart.ORDER_EQUALITY], + 'WHOLE': [' % 1 == 0', Dart.ORDER_MULTIPLICATIVE, + Dart.ORDER_EQUALITY], + 'POSITIVE': [' > 0', Dart.ORDER_RELATIONAL, + Dart.ORDER_RELATIONAL], + 'NEGATIVE': [' < 0', Dart.ORDER_RELATIONAL, + Dart.ORDER_RELATIONAL], + 'DIVISIBLE_BY': [null, Dart.ORDER_MULTIPLICATIVE, + Dart.ORDER_EQUALITY], + 'PRIME': [null, Dart.ORDER_NONE, + Dart.ORDER_UNARY_POSTFIX] + }; + const dropdownProperty = block.getFieldValue('PROPERTY'); + const [suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty]; + const numberToCheck = Dart.valueToCode(block, 'NUMBER_TO_CHECK', + inputOrder) || '0'; let code; - if (dropdown_property === 'PRIME') { + if (dropdownProperty === 'PRIME') { // Prime is a special case as it is not a one-liner test. - Dart.definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; - const functionName = Dart.provideFunction_('math_isPrime', [ - 'bool ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '(n) {', - ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' if (n == 2 || n == 3) {', ' return true;', ' }', - ' // False if n is null, negative, is 1, or not whole.', - ' // And false if n is divisible by 2 or 3.', - ' if (n == null || n <= 1 || n % 1 != 0 || n % 2 == 0 ||' + - ' n % 3 == 0) {', - ' return false;', ' }', - ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {', - ' if (n % (x - 1) == 0 || n % (x + 1) == 0) {', ' return false;', - ' }', ' }', ' return true;', '}' - ]); - code = functionName + '(' + number_to_check + ')'; - return [code, Dart.ORDER_UNARY_POSTFIX]; - } - switch (dropdown_property) { - case 'EVEN': - code = number_to_check + ' % 2 == 0'; - break; - case 'ODD': - code = number_to_check + ' % 2 == 1'; - break; - case 'WHOLE': - code = number_to_check + ' % 1 == 0'; - break; - case 'POSITIVE': - code = number_to_check + ' > 0'; - break; - case 'NEGATIVE': - code = number_to_check + ' < 0'; - break; - case 'DIVISIBLE_BY': - const divisor = - Dart.valueToCode(block, 'DIVISOR', Dart.ORDER_MULTIPLICATIVE); - if (!divisor) { - return ['false', Dart.ORDER_ATOMIC]; - } - code = number_to_check + ' % ' + divisor + ' == 0'; - break; + Dart.definitions_['import_dart_math'] = + 'import \'dart:math\' as Math;'; + const functionName = Dart.provideFunction_( + 'math_isPrime', + ['bool ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '(n) {', + ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', + ' if (n == 2 || n == 3) {', + ' return true;', + ' }', + ' // False if n is null, negative, is 1, or not whole.', + ' // And false if n is divisible by 2 or 3.', + ' if (n == null || n <= 1 || n % 1 != 0 || n % 2 == 0 ||' + + ' n % 3 == 0) {', + ' return false;', + ' }', + ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', + ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {', + ' if (n % (x - 1) == 0 || n % (x + 1) == 0) {', + ' return false;', + ' }', + ' }', + ' return true;', + '}']); + code = functionName + '(' + numberToCheck + ')'; + } else if (dropdownProperty === 'DIVISIBLE_BY') { + const divisor = Dart.valueToCode(block, 'DIVISOR', + Dart.ORDER_MULTIPLICATIVE) || '0'; + if (divisor === '0') { + return ['false', Dart.ORDER_ATOMIC]; + } + code = numberToCheck + ' % ' + divisor + ' == 0'; + } else { + code = numberToCheck + suffix; } - return [code, Dart.ORDER_EQUALITY]; + return [code, outputOrder]; }; Dart['math_change'] = function(block) { diff --git a/generators/javascript/math.js b/generators/javascript/math.js index 103d36a2594..55ee3de8230 100644 --- a/generators/javascript/math.js +++ b/generators/javascript/math.js @@ -149,11 +149,28 @@ JavaScript['math_constant'] = function(block) { JavaScript['math_number_property'] = function(block) { // Check if a number is even, odd, prime, whole, positive, or negative // or if it is divisible by certain number. Returns true or false. - const number_to_check = JavaScript.valueToCode(block, 'NUMBER_TO_CHECK', - JavaScript.ORDER_MODULUS) || '0'; - const dropdown_property = block.getFieldValue('PROPERTY'); + const PROPERTIES = { + 'EVEN': [' % 2 === 0', JavaScript.ORDER_MODULUS, + JavaScript.ORDER_EQUALITY], + 'ODD': [' % 2 === 1', JavaScript.ORDER_MODULUS, + JavaScript.ORDER_EQUALITY], + 'WHOLE': [' % 1 === 0', JavaScript.ORDER_MODULUS, + JavaScript.ORDER_EQUALITY], + 'POSITIVE': [' > 0', JavaScript.ORDER_RELATIONAL, + JavaScript.ORDER_RELATIONAL], + 'NEGATIVE': [' < 0', JavaScript.ORDER_RELATIONAL, + JavaScript.ORDER_RELATIONAL], + 'DIVISIBLE_BY': [null, JavaScript.ORDER_MODULUS, + JavaScript.ORDER_EQUALITY], + 'PRIME': [null, JavaScript.ORDER_NONE, + JavaScript.ORDER_FUNCTION_CALL] + }; + const dropdownProperty = block.getFieldValue('PROPERTY'); + const [suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty]; + const numberToCheck = JavaScript.valueToCode(block, 'NUMBER_TO_CHECK', + inputOrder) || '0'; let code; - if (dropdown_property === 'PRIME') { + if (dropdownProperty === 'PRIME') { // Prime is a special case as it is not a one-liner test. const functionName = JavaScript.provideFunction_( 'mathIsPrime', @@ -176,33 +193,15 @@ JavaScript['math_number_property'] = function(block) { ' }', ' return true;', '}']); - code = functionName + '(' + number_to_check + ')'; - return [code, JavaScript.ORDER_FUNCTION_CALL]; - } - switch (dropdown_property) { - case 'EVEN': - code = number_to_check + ' % 2 === 0'; - break; - case 'ODD': - code = number_to_check + ' % 2 === 1'; - break; - case 'WHOLE': - code = number_to_check + ' % 1 === 0'; - break; - case 'POSITIVE': - code = number_to_check + ' > 0'; - break; - case 'NEGATIVE': - code = number_to_check + ' < 0'; - break; - case 'DIVISIBLE_BY': { - const divisor = JavaScript.valueToCode(block, 'DIVISOR', - JavaScript.ORDER_MODULUS) || '0'; - code = number_to_check + ' % ' + divisor + ' === 0'; - break; - } + code = functionName + '(' + numberToCheck + ')'; + } else if (dropdownProperty === 'DIVISIBLE_BY') { + const divisor = JavaScript.valueToCode(block, 'DIVISOR', + JavaScript.ORDER_MODULUS) || '0'; + code = numberToCheck + ' % ' + divisor + ' === 0'; + } else { + code = numberToCheck + suffix; } - return [code, JavaScript.ORDER_EQUALITY]; + return [code, outputOrder]; }; JavaScript['math_change'] = function(block) { diff --git a/generators/lua/math.js b/generators/lua/math.js index 7fdb50aa874..c5127483068 100644 --- a/generators/lua/math.js +++ b/generators/lua/math.js @@ -126,61 +126,66 @@ Lua['math_constant'] = function(block) { Lua['math_number_property'] = function(block) { // Check if a number is even, odd, prime, whole, positive, or negative // or if it is divisible by certain number. Returns true or false. - const number_to_check = - Lua.valueToCode(block, 'NUMBER_TO_CHECK', Lua.ORDER_MULTIPLICATIVE) || - '0'; - const dropdown_property = block.getFieldValue('PROPERTY'); + const PROPERTIES = { + 'EVEN': [' % 2 == 0', Lua.ORDER_MULTIPLICATIVE, + Lua.ORDER_RELATIONAL], + 'ODD': [' % 2 == 1', Lua.ORDER_MULTIPLICATIVE, + Lua.ORDER_RELATIONAL], + 'WHOLE': [' % 1 == 0', Lua.ORDER_MULTIPLICATIVE, + Lua.ORDER_RELATIONAL], + 'POSITIVE': [' > 0', Lua.ORDER_RELATIONAL, + Lua.ORDER_RELATIONAL], + 'NEGATIVE': [' < 0', Lua.ORDER_RELATIONAL, + Lua.ORDER_RELATIONAL], + 'DIVISIBLE_BY': [null, Lua.ORDER_MULTIPLICATIVE, + Lua.ORDER_RELATIONAL], + 'PRIME': [null, Lua.ORDER_NONE, + Lua.ORDER_HIGH] + }; + const dropdownProperty = block.getFieldValue('PROPERTY'); + const [suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty]; + const numberToCheck = Lua.valueToCode(block, 'NUMBER_TO_CHECK', + inputOrder) || '0'; let code; - if (dropdown_property === 'PRIME') { + if (dropdownProperty === 'PRIME') { // Prime is a special case as it is not a one-liner test. - const functionName = Lua.provideFunction_('math_isPrime', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(n)', - ' -- https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' if n == 2 or n == 3 then', ' return true', ' end', - ' -- False if n is NaN, negative, is 1, or not whole.', - ' -- And false if n is divisible by 2 or 3.', - ' if not(n > 1) or n % 1 ~= 0 or n % 2 == 0 or n % 3 == 0 then', - ' return false', ' end', - ' -- Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for x = 6, math.sqrt(n) + 1.5, 6 do', - ' if n % (x - 1) == 0 or n % (x + 1) == 0 then', ' return false', - ' end', ' end', ' return true', 'end' - ]); - code = functionName + '(' + number_to_check + ')'; - return [code, Lua.ORDER_HIGH]; - } - switch (dropdown_property) { - case 'EVEN': - code = number_to_check + ' % 2 == 0'; - break; - case 'ODD': - code = number_to_check + ' % 2 == 1'; - break; - case 'WHOLE': - code = number_to_check + ' % 1 == 0'; - break; - case 'POSITIVE': - code = number_to_check + ' > 0'; - break; - case 'NEGATIVE': - code = number_to_check + ' < 0'; - break; - case 'DIVISIBLE_BY': { - const divisor = - Lua.valueToCode(block, 'DIVISOR', Lua.ORDER_MULTIPLICATIVE); - // If 'divisor' is some code that evals to 0, Lua will produce a nan. - // Let's produce nil if we can determine this at compile-time. - if (!divisor || divisor === '0') { - return ['nil', Lua.ORDER_ATOMIC]; - } - // The normal trick to implement ?: with and/or doesn't work here: - // divisor == 0 and nil or number_to_check % divisor == 0 - // because nil is false, so allow a runtime failure. :-( - code = number_to_check + ' % ' + divisor + ' == 0'; - break; + const functionName = Lua.provideFunction_( + 'math_isPrime', + ['function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(n)', + ' -- https://en.wikipedia.org/wiki/Primality_test#Naive_methods', + ' if n == 2 or n == 3 then', + ' return true', + ' end', + ' -- False if n is NaN, negative, is 1, or not whole.', + ' -- And false if n is divisible by 2 or 3.', + ' if not(n > 1) or n % 1 ~= 0 or n % 2 == 0 or n % 3 == 0 then', + ' return false', + ' end', + ' -- Check all the numbers of form 6k +/- 1, up to sqrt(n).', + ' for x = 6, math.sqrt(n) + 1.5, 6 do', + ' if n % (x - 1) == 0 or n % (x + 1) == 0 then', + ' return false', + ' end', + ' end', + ' return true', + 'end']); + code = functionName + '(' + numberToCheck + ')'; + } else if (dropdownProperty === 'DIVISIBLE_BY') { + const divisor = Lua.valueToCode(block, 'DIVISOR', + Lua.ORDER_MULTIPLICATIVE) || '0'; + // If 'divisor' is some code that evals to 0, Lua will produce a nan. + // Let's produce nil if we can determine this at compile-time. + if (divisor === '0') { + return ['nil', Lua.ORDER_ATOMIC]; } + // The normal trick to implement ?: with and/or doesn't work here: + // divisor == 0 and nil or number_to_check % divisor == 0 + // because nil is false, so allow a runtime failure. :-( + code = numberToCheck + ' % ' + divisor + ' == 0'; + } else { + code = numberToCheck + suffix; } - return [code, Lua.ORDER_RELATIONAL]; + return [code, outputOrder]; }; Lua['math_change'] = function(block) { diff --git a/generators/php/math.js b/generators/php/math.js index d77f47806bd..1c1e3e621ac 100644 --- a/generators/php/math.js +++ b/generators/php/math.js @@ -142,53 +142,63 @@ PHP['math_constant'] = function(block) { PHP['math_number_property'] = function(block) { // Check if a number is even, odd, prime, whole, positive, or negative // or if it is divisible by certain number. Returns true or false. - const number_to_check = - PHP.valueToCode(block, 'NUMBER_TO_CHECK', PHP.ORDER_MODULUS) || '0'; - const dropdown_property = block.getFieldValue('PROPERTY'); + const PROPERTIES = { + 'EVEN': ['', ' % 2 == 0', PHP.ORDER_MODULUS, + PHP.ORDER_EQUALITY], + 'ODD': ['', ' % 2 == 1', PHP.ORDER_MODULUS, + PHP.ORDER_EQUALITY], + 'WHOLE': ['is_int(', ')', PHP.ORDER_NONE, + PHP.ORDER_FUNCTION_CALL], + 'POSITIVE': ['', ' > 0', PHP.ORDER_RELATIONAL, + PHP.ORDER_RELATIONAL], + 'NEGATIVE': ['', ' < 0', PHP.ORDER_RELATIONAL, + PHP.ORDER_RELATIONAL], + 'DIVISIBLE_BY': [null, null, PHP.ORDER_MODULUS, + PHP.ORDER_EQUALITY], + 'PRIME': [null, null, PHP.ORDER_NONE, + PHP.ORDER_FUNCTION_CALL] + }; + const dropdownProperty = block.getFieldValue('PROPERTY'); + const [prefix, suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty]; + const numberToCheck = PHP.valueToCode(block, 'NUMBER_TO_CHECK', + inputOrder) || '0'; let code; - if (dropdown_property === 'PRIME') { + if (dropdownProperty === 'PRIME') { // Prime is a special case as it is not a one-liner test. - const functionName = PHP.provideFunction_('math_isPrime', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($n) {', - ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' if ($n == 2 || $n == 3) {', ' return true;', ' }', - ' // False if n is NaN, negative, is 1, or not whole.', - ' // And false if n is divisible by 2 or 3.', - ' if (!is_numeric($n) || $n <= 1 || $n % 1 != 0 || $n % 2 == 0 ||' + - ' $n % 3 == 0) {', - ' return false;', ' }', - ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for ($x = 6; $x <= sqrt($n) + 1; $x += 6) {', - ' if ($n % ($x - 1) == 0 || $n % ($x + 1) == 0) {', - ' return false;', ' }', ' }', ' return true;', '}' - ]); - code = functionName + '(' + number_to_check + ')'; - return [code, PHP.ORDER_FUNCTION_CALL]; - } - switch (dropdown_property) { - case 'EVEN': - code = number_to_check + ' % 2 == 0'; - break; - case 'ODD': - code = number_to_check + ' % 2 == 1'; - break; - case 'WHOLE': - code = 'is_int(' + number_to_check + ')'; - break; - case 'POSITIVE': - code = number_to_check + ' > 0'; - break; - case 'NEGATIVE': - code = number_to_check + ' < 0'; - break; - case 'DIVISIBLE_BY': { - const divisor = - PHP.valueToCode(block, 'DIVISOR', PHP.ORDER_MODULUS) || '0'; - code = number_to_check + ' % ' + divisor + ' == 0'; - break; + const functionName = PHP.provideFunction_( + 'math_isPrime', + ['function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($n) {', + ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', + ' if ($n == 2 || $n == 3) {', + ' return true;', + ' }', + ' // False if n is NaN, negative, is 1, or not whole.', + ' // And false if n is divisible by 2 or 3.', + ' if (!is_numeric($n) || $n <= 1 || $n % 1 != 0 || $n % 2 == 0 ||' + + ' $n % 3 == 0) {', + ' return false;', + ' }', + ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', + ' for ($x = 6; $x <= sqrt($n) + 1; $x += 6) {', + ' if ($n % ($x - 1) == 0 || $n % ($x + 1) == 0) {', + ' return false;', + ' }', + ' }', + ' return true;', + '}']); + code = functionName + '(' + numberToCheck + ')'; + } else if (dropdownProperty === 'DIVISIBLE_BY') { + const divisor = PHP.valueToCode(block, 'DIVISOR', + PHP.ORDER_MODULUS) || '0'; + if (divisor === '0') { + return ['false', PHP.ORDER_ATOMIC]; + } + code = numberToCheck + ' % ' + divisor + ' == 0'; + } else { + code = prefix + numberToCheck + suffix; } - return [code, PHP.ORDER_EQUALITY]; + return [code, outputOrder]; }; PHP['math_change'] = function(block) { diff --git a/generators/python/math.js b/generators/python/math.js index 9f4fdc983f0..93d4b8355d3 100644 --- a/generators/python/math.js +++ b/generators/python/math.js @@ -152,65 +152,68 @@ Python['math_constant'] = function(block) { }; Python['math_number_property'] = function(block) { - // Check if a number is even, odd, prime, whole, positive, or negative - // or if it is divisible by certain number. Returns true or false. - const number_to_check = - Python.valueToCode( - block, 'NUMBER_TO_CHECK', Python.ORDER_MULTIPLICATIVE) || - '0'; - const dropdown_property = block.getFieldValue('PROPERTY'); + // Check if a number is even, odd, prime, whole, positive, or negative + // or if it is divisible by certain number. Returns true or false. + const PROPERTIES = { + 'EVEN': [' % 2 == 0', Python.ORDER_MULTIPLICATIVE, + Python.ORDER_RELATIONAL], + 'ODD': [' % 2 == 1', Python.ORDER_MULTIPLICATIVE, + Python.ORDER_RELATIONAL], + 'WHOLE': [' % 1 == 0', Python.ORDER_MULTIPLICATIVE, + Python.ORDER_RELATIONAL], + 'POSITIVE': [' > 0', Python.ORDER_RELATIONAL, + Python.ORDER_RELATIONAL], + 'NEGATIVE': [' < 0', Python.ORDER_RELATIONAL, + Python.ORDER_RELATIONAL], + 'DIVISIBLE_BY': [null, Python.ORDER_MULTIPLICATIVE, + Python.ORDER_RELATIONAL], + 'PRIME': [null, Python.ORDER_NONE, + Python.ORDER_FUNCTION_CALL] + } + const dropdownProperty = block.getFieldValue('PROPERTY'); + const [suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty]; + const numberToCheck = Python.valueToCode(block, 'NUMBER_TO_CHECK', + inputOrder) || '0'; let code; - if (dropdown_property === 'PRIME') { + if (dropdownProperty === 'PRIME') { + // Prime is a special case as it is not a one-liner test. Python.definitions_['import_math'] = 'import math'; Python.definitions_['from_numbers_import_Number'] = 'from numbers import Number'; - const functionName = Python.provideFunction_('math_isPrime', [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(n):', - ' # https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' # If n is not a number but a string, try parsing it.', - ' if not isinstance(n, Number):', ' try:', ' n = float(n)', - ' except:', ' return False', - ' if n == 2 or n == 3:', ' return True', - ' # False if n is negative, is 1, or not whole,' + - ' or if n is divisible by 2 or 3.', - ' if n <= 1 or n % 1 != 0 or n % 2 == 0 or n % 3 == 0:', - ' return False', - ' # Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for x in range(6, int(math.sqrt(n)) + 2, 6):', - ' if n % (x - 1) == 0 or n % (x + 1) == 0:', ' return False', - ' return True' - ]); - code = functionName + '(' + number_to_check + ')'; - return [code, Python.ORDER_FUNCTION_CALL]; - } - switch (dropdown_property) { - case 'EVEN': - code = number_to_check + ' % 2 == 0'; - break; - case 'ODD': - code = number_to_check + ' % 2 == 1'; - break; - case 'WHOLE': - code = number_to_check + ' % 1 == 0'; - break; - case 'POSITIVE': - code = number_to_check + ' > 0'; - break; - case 'NEGATIVE': - code = number_to_check + ' < 0'; - break; - case 'DIVISIBLE_BY': { - const divisor = - Python.valueToCode(block, 'DIVISOR', Python.ORDER_MULTIPLICATIVE); - // If 'divisor' is some code that evals to 0, Python will raise an error. - if (!divisor || divisor === '0') { - return ['False', Python.ORDER_ATOMIC]; - } - code = number_to_check + ' % ' + divisor + ' == 0'; - break; + const functionName = Python.provideFunction_( + 'math_isPrime', + ['def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(n):', + ' # https://en.wikipedia.org/wiki/Primality_test#Naive_methods', + ' # If n is not a number but a string, try parsing it.', + ' if not isinstance(n, Number):', + ' try:', + ' n = float(n)', + ' except:', + ' return False', + ' if n == 2 or n == 3:', + ' return True', + ' # False if n is negative, is 1, or not whole,' + + ' or if n is divisible by 2 or 3.', + ' if n <= 1 or n % 1 != 0 or n % 2 == 0 or n % 3 == 0:', + ' return False', + ' # Check all the numbers of form 6k +/- 1, up to sqrt(n).', + ' for x in range(6, int(math.sqrt(n)) + 2, 6):', + ' if n % (x - 1) == 0 or n % (x + 1) == 0:', + ' return False', + ' return True']); + code = functionName + '(' + numberToCheck + ')'; + } else if (dropdownProperty === 'DIVISIBLE_BY') { + const divisor = Python.valueToCode(block, 'DIVISOR', + Python.ORDER_MULTIPLICATIVE) || '0'; + // If 'divisor' is some code that evals to 0, Python will raise an error. + if (divisor === '0') { + return ['False', Python.ORDER_ATOMIC]; } - } - return [code, Python.ORDER_RELATIONAL]; + code = numberToCheck + ' % ' + divisor + ' == 0'; + } else { + code = numberToCheck + suffix; + }; + return [code, outputOrder]; }; Python['math_change'] = function(block) { diff --git a/tests/generators/golden/generated.dart b/tests/generators/golden/generated.dart index 2c45db3b451..c067942e11b 100644 --- a/tests/generators/golden/generated.dart +++ b/tests/generators/golden/generated.dart @@ -440,12 +440,16 @@ void test_number_properties() { unittest_assertequals(42 % 2 == 0, true, 'even'); unittest_assertequals(42.1 % 2 == 1, false, 'odd'); unittest_assertequals(math_isPrime(5), true, 'prime 5'); + unittest_assertequals(math_isPrime(5 + 2), true, 'prime 5 + 2 (extra parentheses)'); unittest_assertequals(math_isPrime(25), false, 'prime 25'); unittest_assertequals(math_isPrime(-31.1), false, 'prime negative'); unittest_assertequals(Math.pi % 1 == 0, false, 'whole'); unittest_assertequals(double.infinity > 0, true, 'positive'); + unittest_assertequals(5 + 2 > 0, true, '5 + 2 is positive (extra parentheses)'); unittest_assertequals(-42 < 0, true, 'negative'); + unittest_assertequals(3 + 2 < 0, false, '3 + 2 is negative (extra parentheses)'); unittest_assertequals(42 % 2 == 0, true, 'divisible'); + unittest_assertequals(!false, true, 'divisible by 0'); } /// Tests the "round" block. diff --git a/tests/generators/golden/generated.js b/tests/generators/golden/generated.js index 01e9bd311a6..019d51d191c 100644 --- a/tests/generators/golden/generated.js +++ b/tests/generators/golden/generated.js @@ -449,12 +449,16 @@ function test_number_properties() { assertEquals(42 % 2 === 0, true, 'even'); assertEquals(42.1 % 2 === 1, false, 'odd'); assertEquals(mathIsPrime(5), true, 'prime 5'); + assertEquals(mathIsPrime(5 + 2), true, 'prime 5 + 2 (extra parentheses)'); assertEquals(mathIsPrime(25), false, 'prime 25'); assertEquals(mathIsPrime(-31.1), false, 'prime negative'); assertEquals(Math.PI % 1 === 0, false, 'whole'); assertEquals(Infinity > 0, true, 'positive'); + assertEquals(5 + 2 > 0, true, '5 + 2 is positive (extra parentheses)'); assertEquals(-42 < 0, true, 'negative'); + assertEquals(3 + 2 < 0, false, '3 + 2 is negative (extra parentheses)'); assertEquals(42 % 2 === 0, true, 'divisible'); + assertEquals(!(42 % 0 === 0), true, 'divisible by 0'); } // Tests the "round" block. diff --git a/tests/generators/golden/generated.lua b/tests/generators/golden/generated.lua index 2724c1d3bd3..ad71d3aca8b 100644 --- a/tests/generators/golden/generated.lua +++ b/tests/generators/golden/generated.lua @@ -450,12 +450,16 @@ function test_number_properties() assertEquals(42 % 2 == 0, true, 'even') assertEquals(42.1 % 2 == 1, false, 'odd') assertEquals(math_isPrime(5), true, 'prime 5') + assertEquals(math_isPrime(5 + 2), true, 'prime 5 + 2 (extra parentheses)') assertEquals(math_isPrime(25), false, 'prime 25') assertEquals(math_isPrime(-31.1), false, 'prime negative') assertEquals(math.pi % 1 == 0, false, 'whole') assertEquals(math.huge > 0, true, 'positive') + assertEquals(5 + 2 > 0, true, '5 + 2 is positive (extra parentheses)') assertEquals(-42 < 0, true, 'negative') + assertEquals(3 + 2 < 0, false, '3 + 2 is negative (extra parentheses)') assertEquals(42 % 2 == 0, true, 'divisible') + assertEquals(not nil, true, 'divisible by 0') end diff --git a/tests/generators/golden/generated.php b/tests/generators/golden/generated.php index 9d6436125ea..728273d5287 100644 --- a/tests/generators/golden/generated.php +++ b/tests/generators/golden/generated.php @@ -444,12 +444,16 @@ function test_number_properties() { assertEquals(42 % 2 == 0, true, 'even'); assertEquals(42.1 % 2 == 1, false, 'odd'); assertEquals(math_isPrime(5), true, 'prime 5'); + assertEquals(math_isPrime(5 + 2), true, 'prime 5 + 2 (extra parentheses)'); assertEquals(math_isPrime(25), false, 'prime 25'); assertEquals(math_isPrime(-31.1), false, 'prime negative'); assertEquals(is_int(M_PI), false, 'whole'); assertEquals(INF > 0, true, 'positive'); + assertEquals(5 + 2 > 0, true, '5 + 2 is positive (extra parentheses)'); assertEquals(-42 < 0, true, 'negative'); + assertEquals(3 + 2 < 0, false, '3 + 2 is negative (extra parentheses)'); assertEquals(42 % 2 == 0, true, 'divisible'); + assertEquals(!false, true, 'divisible by 0'); } // Tests the "round" block. diff --git a/tests/generators/golden/generated.py b/tests/generators/golden/generated.py index 42764d3685b..12185286e2c 100644 --- a/tests/generators/golden/generated.py +++ b/tests/generators/golden/generated.py @@ -398,12 +398,16 @@ def test_number_properties(): assertEquals(42 % 2 == 0, True, 'even') assertEquals(42.1 % 2 == 1, False, 'odd') assertEquals(math_isPrime(5), True, 'prime 5') + assertEquals(math_isPrime(5 + 2), True, 'prime 5 + 2 (extra parentheses)') assertEquals(math_isPrime(25), False, 'prime 25') assertEquals(math_isPrime(-31.1), False, 'prime negative') assertEquals(math.pi % 1 == 0, False, 'whole') assertEquals(float('inf') > 0, True, 'positive') + assertEquals(5 + 2 > 0, True, '5 + 2 is positive (extra parentheses)') assertEquals(-42 < 0, True, 'negative') + assertEquals(3 + 2 < 0, False, '3 + 2 is negative (extra parentheses)') assertEquals(42 % 2 == 0, True, 'divisible') + assertEquals(not False, True, 'divisible by 0') # Tests the "round" block. def test_round(): diff --git a/tests/generators/math.xml b/tests/generators/math.xml index ff079d2e1d6..682d143acb8 100644 --- a/tests/generators/math.xml +++ b/tests/generators/math.xml @@ -1,4 +1,8 @@ + + varToChange + rand + Math @@ -992,10 +996,10 @@ - FALSE + TRUE - prime 25 + prime 5 + 2 (extra parentheses) @@ -1003,8 +1007,18 @@ PRIME - - 25 + + ADD + + + 5 + + + + + 2 + + @@ -1014,7 +1028,7 @@ FALSE - prime negative + prime 25 @@ -1023,7 +1037,7 @@ PRIME - -31.1 + 25 @@ -1033,35 +1047,35 @@ FALSE - whole + prime negative - WHOLE + PRIME - - PI + + -31.1 - TRUE + FALSE - positive + whole - POSITIVE + WHOLE - INFINITY + PI @@ -1071,16 +1085,16 @@ TRUE - negative + positive - NEGATIVE + POSITIVE - - -42 + + INFINITY @@ -1090,25 +1104,138 @@ TRUE - divisible + 5 + 2 is positive (extra parentheses) - - DIVISIBLE_BY + + POSITIVE - - 42 + + ADD + + + 5 + + + + + 2 + + - - - 2 + + + + + TRUE + + + negative + + + + NEGATIVE + + + -42 + + + + + + + FALSE + + + 3 + 2 is negative (extra parentheses) + + + + + + NEGATIVE + + + ADD + + + 3 + + + + + 2 + + + + + + + + + TRUE + + + divisible + + + + + + DIVISIBLE_BY + + + 42 + + + + + 2 + + + + + + + TRUE + + + divisible by 0 + + + + + + + + DIVISIBLE_BY + + + 42 + + + + + 0 + + + + + + + + + + + + - + @@ -1128,7 +1255,7 @@ - + test round Tests the "round" block. @@ -1204,7 +1331,7 @@ - + test change Tests the "change" block.