summaryrefslogtreecommitdiff
path: root/blockly/generators/lua/math.js
diff options
context:
space:
mode:
Diffstat (limited to 'blockly/generators/lua/math.js')
-rw-r--r--blockly/generators/lua/math.js425
1 files changed, 425 insertions, 0 deletions
diff --git a/blockly/generators/lua/math.js b/blockly/generators/lua/math.js
new file mode 100644
index 0000000..c104bfc
--- /dev/null
+++ b/blockly/generators/lua/math.js
@@ -0,0 +1,425 @@
+/**
+ * @license
+ * Visual Blocks Language
+ *
+ * Copyright 2016 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @fileoverview Generating Lua for math blocks.
+ * @author rodrigoq@google.com (Rodrigo Queiro)
+ */
+'use strict';
+
+goog.provide('Blockly.Lua.math');
+
+goog.require('Blockly.Lua');
+
+
+Blockly.Lua['math_number'] = function(block) {
+ // Numeric value.
+ var code = parseFloat(block.getFieldValue('NUM'));
+ var order = code < 0 ? Blockly.Lua.ORDER_UNARY :
+ Blockly.Lua.ORDER_ATOMIC;
+ return [code, order];
+};
+
+Blockly.Lua['math_arithmetic'] = function(block) {
+ // Basic arithmetic operators, and power.
+ var OPERATORS = {
+ ADD: [' + ', Blockly.Lua.ORDER_ADDITIVE],
+ MINUS: [' - ', Blockly.Lua.ORDER_ADDITIVE],
+ MULTIPLY: [' * ', Blockly.Lua.ORDER_MULTIPLICATIVE],
+ DIVIDE: [' / ', Blockly.Lua.ORDER_MULTIPLICATIVE],
+ POWER: [' ^ ', Blockly.Lua.ORDER_EXPONENTIATION]
+ };
+ var tuple = OPERATORS[block.getFieldValue('OP')];
+ var operator = tuple[0];
+ var order = tuple[1];
+ var argument0 = Blockly.Lua.valueToCode(block, 'A', order) || '0';
+ var argument1 = Blockly.Lua.valueToCode(block, 'B', order) || '0';
+ var code = argument0 + operator + argument1;
+ return [code, order];
+};
+
+Blockly.Lua['math_single'] = function(block) {
+ // Math operators with single operand.
+ var operator = block.getFieldValue('OP');
+ var code;
+ var arg;
+ if (operator == 'NEG') {
+ // Negation is a special case given its different operator precedence.
+ arg = Blockly.Lua.valueToCode(block, 'NUM',
+ Blockly.Lua.ORDER_UNARY) || '0';
+ return ['-' + arg, Blockly.Lua.ORDER_UNARY];
+ }
+ if (operator == 'SIN' || operator == 'COS' || operator == 'TAN') {
+ arg = Blockly.Lua.valueToCode(block, 'NUM',
+ Blockly.Lua.ORDER_MULTIPLICATIVE) || '0';
+ } else {
+ arg = Blockly.Lua.valueToCode(block, 'NUM',
+ Blockly.Lua.ORDER_NONE) || '0';
+ }
+ switch (operator) {
+ case 'ABS':
+ code = 'math.abs(' + arg + ')';
+ break;
+ case 'ROOT':
+ code = 'math.sqrt(' + arg + ')';
+ break;
+ case 'LN':
+ code = 'math.log(' + arg + ')';
+ break;
+ case 'LOG10':
+ code = 'math.log10(' + arg + ')';
+ break;
+ case 'EXP':
+ code = 'math.exp(' + arg + ')';
+ break;
+ case 'POW10':
+ code = 'math.pow(10,' + arg + ')';
+ break;
+ case 'ROUND':
+ // This rounds up. Blockly does not specify rounding direction.
+ code = 'math.floor(' + arg + ' + .5)';
+ break;
+ case 'ROUNDUP':
+ code = 'math.ceil(' + arg + ')';
+ break;
+ case 'ROUNDDOWN':
+ code = 'math.floor(' + arg + ')';
+ break;
+ case 'SIN':
+ code = 'math.sin(math.rad(' + arg + '))';
+ break;
+ case 'COS':
+ code = 'math.cos(math.rad(' + arg + '))';
+ break;
+ case 'TAN':
+ code = 'math.tan(math.rad(' + arg + '))';
+ break;
+ case 'ASIN':
+ code = 'math.deg(math.asin(' + arg + '))';
+ break;
+ case 'ACOS':
+ code = 'math.deg(math.acos(' + arg + '))';
+ break;
+ case 'ATAN':
+ code = 'math.deg(math.atan(' + arg + '))';
+ break;
+ default:
+ throw 'Unknown math operator: ' + operator;
+ }
+ return [code, Blockly.Lua.ORDER_HIGH];
+};
+
+Blockly.Lua['math_constant'] = function(block) {
+ // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY.
+ var CONSTANTS = {
+ PI: ['math.pi', Blockly.Lua.ORDER_HIGH],
+ E: ['math.exp(1)', Blockly.Lua.ORDER_HIGH],
+ GOLDEN_RATIO: ['(1 + math.sqrt(5)) / 2', Blockly.Lua.ORDER_MULTIPLICATIVE],
+ SQRT2: ['math.sqrt(2)', Blockly.Lua.ORDER_HIGH],
+ SQRT1_2: ['math.sqrt(1 / 2)', Blockly.Lua.ORDER_HIGH],
+ INFINITY: ['math.huge', Blockly.Lua.ORDER_HIGH]
+ };
+ return CONSTANTS[block.getFieldValue('CONSTANT')];
+};
+
+Blockly.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.
+ var number_to_check = Blockly.Lua.valueToCode(block, 'NUMBER_TO_CHECK',
+ Blockly.Lua.ORDER_MULTIPLICATIVE) || '0';
+ var dropdown_property = block.getFieldValue('PROPERTY');
+ var code;
+ if (dropdown_property == 'PRIME') {
+ // Prime is a special case as it is not a one-liner test.
+ var functionName = Blockly.Lua.provideFunction_(
+ 'math_isPrime',
+ ['function ' + Blockly.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, Blockly.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':
+ var divisor = Blockly.Lua.valueToCode(block, 'DIVISOR',
+ Blockly.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', Blockly.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;
+ }
+ return [code, Blockly.Lua.ORDER_RELATIONAL];
+};
+
+Blockly.Lua['math_change'] = function(block) {
+ // Add to a variable in place.
+ var argument0 = Blockly.Lua.valueToCode(block, 'DELTA',
+ Blockly.Lua.ORDER_ADDITIVE) || '0';
+ var varName = Blockly.Lua.variableDB_.getName(
+ block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE);
+ return varName + ' = ' + varName + ' + ' + argument0 + '\n';
+};
+
+// Rounding functions have a single operand.
+Blockly.Lua['math_round'] = Blockly.Lua['math_single'];
+// Trigonometry functions have a single operand.
+Blockly.Lua['math_trig'] = Blockly.Lua['math_single'];
+
+Blockly.Lua['math_on_list'] = function(block) {
+ // Math functions for lists.
+ var func = block.getFieldValue('OP');
+ var list = Blockly.Lua.valueToCode(block, 'LIST',
+ Blockly.Lua.ORDER_NONE) || '{}';
+ var functionName;
+
+ // Functions needed in more than one case.
+ function provideSum() {
+ return Blockly.Lua.provideFunction_(
+ 'math_sum',
+ ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
+ ' local result = 0',
+ ' for _, v in ipairs(t) do',
+ ' result = result + v',
+ ' end',
+ ' return result',
+ 'end']);
+ }
+
+ switch (func) {
+ case 'SUM':
+ functionName = provideSum();
+ break;
+
+ case 'MIN':
+ // Returns 0 for the empty list.
+ functionName = Blockly.Lua.provideFunction_(
+ 'math_min',
+ ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
+ ' if #t == 0 then',
+ ' return 0',
+ ' end',
+ ' local result = math.huge',
+ ' for _, v in ipairs(t) do',
+ ' if v < result then',
+ ' result = v',
+ ' end',
+ ' end',
+ ' return result',
+ 'end']);
+ break;
+
+ case 'AVERAGE':
+ // Returns 0 for the empty list.
+ functionName = Blockly.Lua.provideFunction_(
+ 'math_average',
+ ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
+ ' if #t == 0 then',
+ ' return 0',
+ ' end',
+ ' return ' + provideSum() + '(t) / #t',
+ 'end']);
+ break;
+
+ case 'MAX':
+ // Returns 0 for the empty list.
+ functionName = Blockly.Lua.provideFunction_(
+ 'math_max',
+ ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
+ ' if #t == 0 then',
+ ' return 0',
+ ' end',
+ ' local result = -math.huge',
+ ' for _, v in ipairs(t) do',
+ ' if v > result then',
+ ' result = v',
+ ' end',
+ ' end',
+ ' return result',
+ 'end']);
+ break;
+
+ case 'MEDIAN':
+ functionName = Blockly.Lua.provideFunction_(
+ 'math_median',
+ // This operation excludes non-numbers.
+ ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
+ ' -- Source: http://lua-users.org/wiki/SimpleStats',
+ ' if #t == 0 then',
+ ' return 0',
+ ' end',
+ ' local temp={}',
+ ' for _, v in ipairs(t) do',
+ ' if type(v) == "number" then',
+ ' table.insert(temp, v)',
+ ' end',
+ ' end',
+ ' table.sort(temp)',
+ ' if #temp % 2 == 0 then',
+ ' return (temp[#temp/2] + temp[(#temp/2)+1]) / 2',
+ ' else',
+ ' return temp[math.ceil(#temp/2)]',
+ ' end',
+ 'end']);
+ break;
+
+ case 'MODE':
+ functionName = Blockly.Lua.provideFunction_(
+ 'math_modes',
+ // As a list of numbers can contain more than one mode,
+ // the returned result is provided as an array.
+ // The Lua version includes non-numbers.
+ ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
+ ' -- Source: http://lua-users.org/wiki/SimpleStats',
+ ' local counts={}',
+ ' for _, v in ipairs(t) do',
+ ' if counts[v] == nil then',
+ ' counts[v] = 1',
+ ' else',
+ ' counts[v] = counts[v] + 1',
+ ' end',
+ ' end',
+ ' local biggestCount = 0',
+ ' for _, v in pairs(counts) do',
+ ' if v > biggestCount then',
+ ' biggestCount = v',
+ ' end',
+ ' end',
+ ' local temp={}',
+ ' for k, v in pairs(counts) do',
+ ' if v == biggestCount then',
+ ' table.insert(temp, k)',
+ ' end',
+ ' end',
+ ' return temp',
+ 'end']);
+ break;
+
+ case 'STD_DEV':
+ functionName = Blockly.Lua.provideFunction_(
+ 'math_standard_deviation',
+ ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
+ ' local m',
+ ' local vm',
+ ' local total = 0',
+ ' local count = 0',
+ ' local result',
+ ' m = #t == 0 and 0 or ' + provideSum() + '(t) / #t',
+ ' for _, v in ipairs(t) do',
+ " if type(v) == 'number' then",
+ ' vm = v - m',
+ ' total = total + (vm * vm)',
+ ' count = count + 1',
+ ' end',
+ ' end',
+ ' result = math.sqrt(total / (count-1))',
+ ' return result',
+ 'end']);
+ break;
+
+ case 'RANDOM':
+ functionName = Blockly.Lua.provideFunction_(
+ 'math_random_list',
+ ['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
+ ' if #t == 0 then',
+ ' return nil',
+ ' end',
+ ' return t[math.random(#t)]',
+ 'end']);
+ break;
+
+ default:
+ throw 'Unknown operator: ' + func;
+ }
+ return [functionName + '(' + list + ')', Blockly.Lua.ORDER_HIGH];
+};
+
+Blockly.Lua['math_modulo'] = function(block) {
+ // Remainder computation.
+ var argument0 = Blockly.Lua.valueToCode(block, 'DIVIDEND',
+ Blockly.Lua.ORDER_MULTIPLICATIVE) || '0';
+ var argument1 = Blockly.Lua.valueToCode(block, 'DIVISOR',
+ Blockly.Lua.ORDER_MULTIPLICATIVE) || '0';
+ var code = argument0 + ' % ' + argument1;
+ return [code, Blockly.Lua.ORDER_MULTIPLICATIVE];
+};
+
+Blockly.Lua['math_constrain'] = function(block) {
+ // Constrain a number between two limits.
+ var argument0 = Blockly.Lua.valueToCode(block, 'VALUE',
+ Blockly.Lua.ORDER_NONE) || '0';
+ var argument1 = Blockly.Lua.valueToCode(block, 'LOW',
+ Blockly.Lua.ORDER_NONE) || '-math.huge';
+ var argument2 = Blockly.Lua.valueToCode(block, 'HIGH',
+ Blockly.Lua.ORDER_NONE) || 'math.huge';
+ var code = 'math.min(math.max(' + argument0 + ', ' + argument1 + '), ' +
+ argument2 + ')';
+ return [code, Blockly.Lua.ORDER_HIGH];
+};
+
+Blockly.Lua['math_random_int'] = function(block) {
+ // Random integer between [X] and [Y].
+ var argument0 = Blockly.Lua.valueToCode(block, 'FROM',
+ Blockly.Lua.ORDER_NONE) || '0';
+ var argument1 = Blockly.Lua.valueToCode(block, 'TO',
+ Blockly.Lua.ORDER_NONE) || '0';
+ var code = 'math.random(' + argument0 + ', ' + argument1 + ')';
+ return [code, Blockly.Lua.ORDER_HIGH];
+};
+
+Blockly.Lua['math_random_float'] = function(block) {
+ // Random fraction between 0 and 1.
+ return ['math.random()', Blockly.Lua.ORDER_HIGH];
+};