diff --git a/generators/javascript/javascript_generator.ts b/generators/javascript/javascript_generator.ts index 547b7daf7d1..8d3b6158d9e 100644 --- a/generators/javascript/javascript_generator.ts +++ b/generators/javascript/javascript_generator.ts @@ -66,10 +66,8 @@ export enum Order { * JavaScript code generator class. */ export class JavascriptGenerator extends CodeGenerator { - /** - * List of outer-inner pairings that do NOT require parentheses. - */ - ORDER_OVERRIDES: number[][] = [ + /** List of outer-inner pairings that do NOT require parentheses. */ + ORDER_OVERRIDES: [Order, Order][] = [ // (foo()).bar -> foo().bar // (foo())[0] -> foo()[0] [Order.FUNCTION_CALL, Order.MEMBER], diff --git a/generators/javascript/lists.ts b/generators/javascript/lists.ts index 6283f1d274b..e3170cc9733 100644 --- a/generators/javascript/lists.ts +++ b/generators/javascript/lists.ts @@ -303,6 +303,7 @@ export function lists_getSublist( block: Block, generator: JavascriptGenerator, ): [string, Order] { + // Get sublist. // Dictionary of WHEREn field choices and their CamelCase equivalents. const wherePascalCase = { 'FIRST': 'First', @@ -311,7 +312,6 @@ export function lists_getSublist( 'FROM_END': 'FromEnd', }; type WhereOption = keyof typeof wherePascalCase; - // Get sublist. const list = generator.valueToCode(block, 'LIST', Order.MEMBER) || '[]'; const where1 = block.getFieldValue('WHERE1') as WhereOption; const where2 = block.getFieldValue('WHERE2') as WhereOption; diff --git a/generators/php.js b/generators/php.ts similarity index 65% rename from generators/php.js rename to generators/php.ts index f61daccf7cb..18631ad9a37 100644 --- a/generators/php.js +++ b/generators/php.ts @@ -5,9 +5,9 @@ */ /** - * @fileoverview Complete helper functions for generating PHP for - * blocks. This is the entrypoint for php_compressed.js. - * @suppress {extraRequire} + * @file Instantiate a PhpGenerator and populate it with the complete + * set of block generator functions for PHP. This is the entrypoint + * for php_compressed.js. */ // Former goog.module ID: Blockly.PHP.all @@ -32,8 +32,17 @@ export * from './php/php_generator.js'; export const phpGenerator = new PhpGenerator(); // Install per-block-type generator functions: -Object.assign( - phpGenerator.forBlock, - colour, lists, logic, loops, math, procedures, - text, variables, variablesDynamic -); +const generators: typeof phpGenerator.forBlock = { + ...colour, + ...lists, + ...logic, + ...loops, + ...math, + ...procedures, + ...text, + ...variables, + ...variablesDynamic, +}; +for (const name in generators) { + phpGenerator.forBlock[name] = generators[name]; +} diff --git a/generators/php/colour.js b/generators/php/colour.ts similarity index 69% rename from generators/php/colour.js rename to generators/php/colour.ts index e15cc385c38..eefb7cba774 100644 --- a/generators/php/colour.js +++ b/generators/php/colour.ts @@ -5,37 +5,52 @@ */ /** - * @fileoverview Generating PHP for colour blocks. + * @file Generating PHP for colour blocks. */ // Former goog.module ID: Blockly.PHP.colour +import type {Block} from '../../core/block.js'; import {Order} from './php_generator.js'; +import type {PhpGenerator} from './php_generator.js'; - -export function colour_picker(block, generator) { +export function colour_picker( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Colour picker. const code = generator.quote_(block.getFieldValue('COLOUR')); return [code, Order.ATOMIC]; -}; +} -export function colour_random(block, generator) { +export function colour_random( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Generate a random colour. - const functionName = generator.provideFunction_('colour_random', ` + const functionName = generator.provideFunction_( + 'colour_random', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}() { return '#' . str_pad(dechex(mt_rand(0, 0xFFFFFF)), 6, '0', STR_PAD_LEFT); } -`); +`, + ); const code = functionName + '()'; return [code, Order.FUNCTION_CALL]; -}; +} -export function colour_rgb(block, generator) { +export function colour_rgb( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Compose a colour from RGB components expressed as percentages. const red = generator.valueToCode(block, 'RED', Order.NONE) || 0; const green = generator.valueToCode(block, 'GREEN', Order.NONE) || 0; const blue = generator.valueToCode(block, 'BLUE', Order.NONE) || 0; - const functionName = generator.provideFunction_('colour_rgb', ` + const functionName = generator.provideFunction_( + 'colour_rgb', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($r, $g, $b) { $r = round(max(min($r, 100), 0) * 2.55); $g = round(max(min($g, 100), 0) * 2.55); @@ -46,19 +61,23 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}($r, $g, $b) { $hex .= str_pad(dechex($b), 2, '0', STR_PAD_LEFT); return $hex; } -`); +`, + ); const code = functionName + '(' + red + ', ' + green + ', ' + blue + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function colour_blend(block, generator) { +export function colour_blend( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Blend two colours together. - const c1 = - generator.valueToCode(block, 'COLOUR1', Order.NONE) || "'#000000'"; - const c2 = - generator.valueToCode(block, 'COLOUR2', Order.NONE) || "'#000000'"; + const c1 = generator.valueToCode(block, 'COLOUR1', Order.NONE) || "'#000000'"; + const c2 = generator.valueToCode(block, 'COLOUR2', Order.NONE) || "'#000000'"; const ratio = generator.valueToCode(block, 'RATIO', Order.NONE) || 0.5; - const functionName = generator.provideFunction_('colour_blend', ` + const functionName = generator.provideFunction_( + 'colour_blend', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($c1, $c2, $ratio) { $ratio = max(min($ratio, 1), 0); $r1 = hexdec(substr($c1, 1, 2)); @@ -76,7 +95,8 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}($c1, $c2, $ratio) { $hex .= str_pad(dechex($b), 2, '0', STR_PAD_LEFT); return $hex; } -`); +`, + ); const code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')'; return [code, Order.FUNCTION_CALL]; -}; +} diff --git a/generators/php/lists.js b/generators/php/lists.ts similarity index 68% rename from generators/php/lists.js rename to generators/php/lists.ts index d2d5a084200..7ca2639b7f2 100644 --- a/generators/php/lists.js +++ b/generators/php/lists.ts @@ -5,7 +5,7 @@ */ /** - * @fileoverview Generating PHP for list blocks. + * @file Generating PHP for list blocks. */ /** @@ -22,27 +22,42 @@ // Former goog.module ID: Blockly.generator.lists import * as stringUtils from '../../core/utils/string.js'; +import type {Block} from '../../core/block.js'; +import type {CreateWithBlock} from '../../blocks/lists.js'; import {NameType} from '../../core/names.js'; import {Order} from './php_generator.js'; +import type {PhpGenerator} from './php_generator.js'; -export function lists_create_empty(block, generator) { +export function lists_create_empty( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Create an empty list. return ['array()', Order.FUNCTION_CALL]; -}; +} -export function lists_create_with(block, generator) { +export function lists_create_with( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Create a list with any number of elements of any type. - let code = new Array(block.itemCount_); - for (let i = 0; i < block.itemCount_; i++) { - code[i] = generator.valueToCode(block, 'ADD' + i, Order.NONE) || 'null'; + const createWithBlock = block as CreateWithBlock; + const elements = new Array(createWithBlock.itemCount_); + for (let i = 0; i < createWithBlock.itemCount_; i++) { + elements[i] = generator.valueToCode(block, 'ADD' + i, Order.NONE) || 'null'; } - code = 'array(' + code.join(', ') + ')'; + const code = 'array(' + elements.join(', ') + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function lists_repeat(block, generator) { +export function lists_repeat( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Create a list with one element repeated. - const functionName = generator.provideFunction_('lists_repeat', ` + const functionName = generator.provideFunction_( + 'lists_repeat', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($value, $count) { $array = array(); for ($index = 0; $index < $count; $index++) { @@ -50,16 +65,22 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}($value, $count) { } return $array; } -`); +`, + ); const element = generator.valueToCode(block, 'ITEM', Order.NONE) || 'null'; const repeatCount = generator.valueToCode(block, 'NUM', Order.NONE) || '0'; const code = functionName + '(' + element + ', ' + repeatCount + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function lists_length(block, generator) { +export function lists_length( + block: Block, + generator: PhpGenerator, +): [string, Order] { // String or array length. - const functionName = generator.provideFunction_('length', ` + const functionName = generator.provideFunction_( + 'length', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($value) { if (is_string($value)) { return strlen($value); @@ -67,24 +88,29 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}($value) { return count($value); } } -`); +`, + ); const list = generator.valueToCode(block, 'VALUE', Order.NONE) || "''"; return [functionName + '(' + list + ')', Order.FUNCTION_CALL]; -}; +} -export function lists_isEmpty(block, generator) { +export function lists_isEmpty( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Is the string null or array empty? const argument0 = - generator.valueToCode(block, 'VALUE', Order.FUNCTION_CALL) - || 'array()'; + generator.valueToCode(block, 'VALUE', Order.FUNCTION_CALL) || 'array()'; return ['empty(' + argument0 + ')', Order.FUNCTION_CALL]; -}; +} -export function lists_indexOf(block, generator) { +export function lists_indexOf( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Find an item in the list. const argument0 = generator.valueToCode(block, 'FIND', Order.NONE) || "''"; - const argument1 = - generator.valueToCode(block, 'VALUE', Order.MEMBER) || '[]'; + const argument1 = generator.valueToCode(block, 'VALUE', Order.MEMBER) || '[]'; let errorIndex = ' -1'; let indexAdjustment = ''; if (block.workspace.options.oneBasedIndex) { @@ -94,17 +120,22 @@ export function lists_indexOf(block, generator) { let functionName; if (block.getFieldValue('END') === 'FIRST') { // indexOf - functionName = generator.provideFunction_('indexOf', ` + functionName = generator.provideFunction_( + 'indexOf', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($haystack, $needle) { for ($index = 0; $index < count($haystack); $index++) { if ($haystack[$index] == $needle) return $index${indexAdjustment}; } return ${errorIndex}; } -`); +`, + ); } else { // lastIndexOf - functionName = generator.provideFunction_('lastIndexOf', ` + functionName = generator.provideFunction_( + 'lastIndexOf', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($haystack, $needle) { $last = ${errorIndex}; for ($index = 0; $index < count($haystack); $index++) { @@ -112,14 +143,18 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}($haystack, $needle) { } return $last; } -`); +`, + ); } const code = functionName + '(' + argument1 + ', ' + argument0 + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function lists_getIndex(block, generator) { +export function lists_getIndex( + block: Block, + generator: PhpGenerator, +): [string, Order] | string { // Get element at index. const mode = block.getFieldValue('MODE') || 'GET'; const where = block.getFieldValue('WHERE') || 'FROM_START'; @@ -127,34 +162,34 @@ export function lists_getIndex(block, generator) { case 'FIRST': if (mode === 'GET') { const list = - generator.valueToCode(block, 'VALUE', Order.MEMBER) || 'array()'; + generator.valueToCode(block, 'VALUE', Order.MEMBER) || 'array()'; const code = list + '[0]'; return [code, Order.MEMBER]; } else if (mode === 'GET_REMOVE') { const list = - generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; + generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; const code = 'array_shift(' + list + ')'; return [code, Order.FUNCTION_CALL]; } else if (mode === 'REMOVE') { const list = - generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; + generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; return 'array_shift(' + list + ');\n'; } break; case 'LAST': if (mode === 'GET') { const list = - generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; + generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; const code = 'end(' + list + ')'; return [code, Order.FUNCTION_CALL]; } else if (mode === 'GET_REMOVE') { const list = - generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; + generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; const code = 'array_pop(' + list + ')'; return [code, Order.FUNCTION_CALL]; } else if (mode === 'REMOVE') { const list = - generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; + generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; return 'array_pop(' + list + ');\n'; } break; @@ -162,17 +197,17 @@ export function lists_getIndex(block, generator) { const at = generator.getAdjusted(block, 'AT'); if (mode === 'GET') { const list = - generator.valueToCode(block, 'VALUE', Order.MEMBER) || 'array()'; + generator.valueToCode(block, 'VALUE', Order.MEMBER) || 'array()'; const code = list + '[' + at + ']'; return [code, Order.MEMBER]; } else if (mode === 'GET_REMOVE') { const list = - generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; + generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; const code = 'array_splice(' + list + ', ' + at + ', 1)[0]'; return [code, Order.FUNCTION_CALL]; } else if (mode === 'REMOVE') { const list = - generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; + generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; return 'array_splice(' + list + ', ' + at + ', 1);\n'; } break; @@ -180,17 +215,22 @@ export function lists_getIndex(block, generator) { case 'FROM_END': if (mode === 'GET') { const list = - generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; + generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; const at = generator.getAdjusted(block, 'AT', 1, true); const code = 'array_slice(' + list + ', ' + at + ', 1)[0]'; return [code, Order.FUNCTION_CALL]; } else if (mode === 'GET_REMOVE' || mode === 'REMOVE') { const list = - generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; - const at = - generator.getAdjusted(block, 'AT', 1, false, Order.SUBTRACTION); - const code = 'array_splice(' + list + ', count(' + list + ') - ' + at + - ', 1)[0]'; + generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; + const at = generator.getAdjusted( + block, + 'AT', + 1, + false, + Order.SUBTRACTION, + ); + const code = + 'array_splice(' + list + ', count(' + list + ') - ' + at + ', 1)[0]'; if (mode === 'GET_REMOVE') { return [code, Order.FUNCTION_CALL]; } else if (mode === 'REMOVE') { @@ -200,58 +240,65 @@ export function lists_getIndex(block, generator) { break; case 'RANDOM': { const list = - generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; + generator.valueToCode(block, 'VALUE', Order.NONE) || 'array()'; if (mode === 'GET') { - const functionName = - generator.provideFunction_('lists_get_random_item', ` + const functionName = generator.provideFunction_( + 'lists_get_random_item', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($list) { return $list[rand(0,count($list)-1)]; } -`); +`, + ); const code = functionName + '(' + list + ')'; return [code, Order.FUNCTION_CALL]; } else if (mode === 'GET_REMOVE') { - const functionName = - generator.provideFunction_('lists_get_remove_random_item', ` + const functionName = generator.provideFunction_( + 'lists_get_remove_random_item', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(&$list) { $x = rand(0,count($list)-1); unset($list[$x]); return array_values($list); } -`); +`, + ); const code = functionName + '(' + list + ')'; return [code, Order.FUNCTION_CALL]; } else if (mode === 'REMOVE') { - const functionName = - generator.provideFunction_('lists_remove_random_item', ` + const functionName = generator.provideFunction_( + 'lists_remove_random_item', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(&$list) { unset($list[rand(0,count($list)-1)]); } -`); +`, + ); return functionName + '(' + list + ');\n'; } break; } } throw Error('Unhandled combination (lists_getIndex).'); -}; +} -export function lists_setIndex(block, generator) { +export function lists_setIndex(block: Block, generator: PhpGenerator) { // Set element at index. // Note: Until February 2013 this block did not have MODE or WHERE inputs. const mode = block.getFieldValue('MODE') || 'GET'; const where = block.getFieldValue('WHERE') || 'FROM_START'; - const value = - generator.valueToCode(block, 'TO', Order.ASSIGNMENT) || 'null'; + const value = generator.valueToCode(block, 'TO', Order.ASSIGNMENT) || 'null'; // Cache non-trivial values to variables to prevent repeated look-ups. // Closure, which accesses and modifies 'list'. - let cachedList; + let cachedList: string; function cacheList() { if (cachedList.match(/^\$\w+$/)) { return ''; } - const listVar = - generator.nameDB_.getDistinctName('tmp_list', NameType.VARIABLE); + const listVar = generator.nameDB_!.getDistinctName( + 'tmp_list', + NameType.VARIABLE, + ); const code = listVar + ' = &' + cachedList + ';\n'; cachedList = listVar; return code; @@ -260,24 +307,26 @@ export function lists_setIndex(block, generator) { case 'FIRST': if (mode === 'SET') { const list = - generator.valueToCode(block, 'LIST', Order.MEMBER) || 'array()'; + generator.valueToCode(block, 'LIST', Order.MEMBER) || 'array()'; return list + '[0] = ' + value + ';\n'; } else if (mode === 'INSERT') { const list = - generator.valueToCode(block, 'LIST', Order.NONE) || 'array()'; + generator.valueToCode(block, 'LIST', Order.NONE) || 'array()'; return 'array_unshift(' + list + ', ' + value + ');\n'; } break; case 'LAST': { const list = - generator.valueToCode(block, 'LIST', Order.NONE) || 'array()'; + generator.valueToCode(block, 'LIST', Order.NONE) || 'array()'; if (mode === 'SET') { - const functionName = - generator.provideFunction_('lists_set_last_item', ` + const functionName = generator.provideFunction_( + 'lists_set_last_item', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(&$list, $value) { $list[count($list) - 1] = $value; } -`); +`, + ); return functionName + '(' + list + ', ' + value + ');\n'; } else if (mode === 'INSERT') { return 'array_push(' + list + ', ' + value + ');\n'; @@ -288,45 +337,51 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(&$list, $value) { const at = generator.getAdjusted(block, 'AT'); if (mode === 'SET') { const list = - generator.valueToCode(block, 'LIST', Order.MEMBER) || 'array()'; + generator.valueToCode(block, 'LIST', Order.MEMBER) || 'array()'; return list + '[' + at + '] = ' + value + ';\n'; } else if (mode === 'INSERT') { const list = - generator.valueToCode(block, 'LIST', Order.NONE) || 'array()'; + generator.valueToCode(block, 'LIST', Order.NONE) || 'array()'; return 'array_splice(' + list + ', ' + at + ', 0, ' + value + ');\n'; } break; } case 'FROM_END': { const list = - generator.valueToCode(block, 'LIST', Order.NONE) || 'array()'; + generator.valueToCode(block, 'LIST', Order.NONE) || 'array()'; const at = generator.getAdjusted(block, 'AT', 1); if (mode === 'SET') { - const functionName = - generator.provideFunction_('lists_set_from_end', ` + const functionName = generator.provideFunction_( + 'lists_set_from_end', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(&$list, $at, $value) { $list[count($list) - $at] = $value; } -`); +`, + ); return functionName + '(' + list + ', ' + at + ', ' + value + ');\n'; } else if (mode === 'INSERT') { - const functionName = - generator.provideFunction_('lists_insert_from_end', ` + const functionName = generator.provideFunction_( + 'lists_insert_from_end', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(&$list, $at, $value) { return array_splice($list, count($list) - $at, 0, $value); } -`); +`, + ); return functionName + '(' + list + ', ' + at + ', ' + value + ');\n'; } break; } case 'RANDOM': cachedList = - generator.valueToCode(block, 'LIST', Order.REFERENCE) || 'array()'; + generator.valueToCode(block, 'LIST', Order.REFERENCE) || 'array()'; let code = cacheList(); const list = cachedList; - const xVar = - generator.nameDB_.getDistinctName('tmp_x', NameType.VARIABLE); + const xVar = generator.nameDB_!.getDistinctName( + 'tmp_x', + NameType.VARIABLE, + ); code += xVar + ' = rand(0, count(' + list + ')-1);\n'; if (mode === 'SET') { code += list + '[' + xVar + '] = ' + value + ';\n'; @@ -338,9 +393,12 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(&$list, $at, $value) { break; } throw Error('Unhandled combination (lists_setIndex).'); -}; +} -export function lists_getSublist(block, generator) { +export function lists_getSublist( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Get sublist. const list = generator.valueToCode(block, 'LIST', Order.NONE) || 'array()'; const where1 = block.getFieldValue('WHERE1'); @@ -349,8 +407,9 @@ export function lists_getSublist(block, generator) { if (where1 === 'FIRST' && where2 === 'LAST') { code = list; } else if ( - list.match(/^\$\w+$/) || - (where1 !== 'FROM_END' && where2 === 'FROM_START')) { + list.match(/^\$\w+$/) || + (where1 !== 'FROM_END' && where2 === 'FROM_START') + ) { // If the list is a simple value or doesn't require a call for length, don't // generate a helper function. let at1; @@ -359,8 +418,7 @@ export function lists_getSublist(block, generator) { at1 = generator.getAdjusted(block, 'AT1'); break; case 'FROM_END': - at1 = - generator.getAdjusted(block, 'AT1', 1, false, Order.SUBTRACTION); + at1 = generator.getAdjusted(block, 'AT1', 1, false, Order.SUBTRACTION); at1 = 'count(' + list + ') - ' + at1; break; case 'FIRST': @@ -373,11 +431,12 @@ export function lists_getSublist(block, generator) { let length; switch (where2) { case 'FROM_START': - at2 = - generator.getAdjusted(block, 'AT2', 0, false, Order.SUBTRACTION); + at2 = generator.getAdjusted(block, 'AT2', 0, false, Order.SUBTRACTION); length = at2 + ' - '; - if (stringUtils.isNumber(String(at1)) || - String(at1).match(/^\(.+\)$/)) { + if ( + stringUtils.isNumber(String(at1)) || + String(at1).match(/^\(.+\)$/) + ) { length += at1; } else { length += '(' + at1 + ')'; @@ -385,11 +444,12 @@ export function lists_getSublist(block, generator) { length += ' + 1'; break; case 'FROM_END': - at2 = - generator.getAdjusted(block, 'AT2', 0, false, Order.SUBTRACTION); + at2 = generator.getAdjusted(block, 'AT2', 0, false, Order.SUBTRACTION); length = 'count(' + list + ') - ' + at2 + ' - '; - if (stringUtils.isNumber(String(at1)) || - String(at1).match(/^\(.+\)$/)) { + if ( + stringUtils.isNumber(String(at1)) || + String(at1).match(/^\(.+\)$/) + ) { length += at1; } else { length += '(' + at1 + ')'; @@ -397,8 +457,10 @@ export function lists_getSublist(block, generator) { break; case 'LAST': length = 'count(' + list + ') - '; - if (stringUtils.isNumber(String(at1)) || - String(at1).match(/^\(.+\)$/)) { + if ( + stringUtils.isNumber(String(at1)) || + String(at1).match(/^\(.+\)$/) + ) { length += at1; } else { length += '(' + at1 + ')'; @@ -411,8 +473,9 @@ export function lists_getSublist(block, generator) { } else { const at1 = generator.getAdjusted(block, 'AT1'); const at2 = generator.getAdjusted(block, 'AT2'); - const functionName = - generator.provideFunction_('lists_get_sublist', ` + const functionName = generator.provideFunction_( + 'lists_get_sublist', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($list, $where1, $at1, $where2, $at2) { if ($where1 == 'FROM_END') { $at1 = count($list) - 1 - $at1; @@ -433,20 +496,37 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}($list, $where1, $at1, $where2, } return array_slice($list, $at1, $length); } -`); - code = functionName + '(' + list + ', \'' + where1 + '\', ' + at1 + ', \'' + - where2 + '\', ' + at2 + ')'; +`, + ); + code = + functionName + + '(' + + list + + ", '" + + where1 + + "', " + + at1 + + ", '" + + where2 + + "', " + + at2 + + ')'; } return [code, Order.FUNCTION_CALL]; -}; +} -export function lists_sort(block, generator) { +export function lists_sort( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Block for sorting a list. const listCode = - generator.valueToCode(block, 'LIST', Order.NONE) || 'array()'; + generator.valueToCode(block, 'LIST', Order.NONE) || 'array()'; const direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; const type = block.getFieldValue('TYPE'); - const functionName = generator.provideFunction_('lists_sort', ` + const functionName = generator.provideFunction_( + 'lists_sort', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($list, $type, $direction) { $sortCmpFuncs = array( 'NUMERIC' => 'strnatcasecmp', @@ -461,17 +541,20 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}($list, $type, $direction) { } return $list2; } -`); +`, + ); const sortCode = - functionName + '(' + listCode + ', "' + type + '", ' + direction + ')'; + functionName + '(' + listCode + ', "' + type + '", ' + direction + ')'; return [sortCode, Order.FUNCTION_CALL]; -}; +} -export function lists_split(block, generator) { +export function lists_split( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Block for splitting text into a list, or joining a list into text. let value_input = generator.valueToCode(block, 'INPUT', Order.NONE); - const value_delim = - generator.valueToCode(block, 'DELIM', Order.NONE) || "''"; + const value_delim = generator.valueToCode(block, 'DELIM', Order.NONE) || "''"; const mode = block.getFieldValue('MODE'); let functionName; if (mode === 'SPLIT') { @@ -489,11 +572,14 @@ export function lists_split(block, generator) { } const code = functionName + '(' + value_delim + ', ' + value_input + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function lists_reverse(block, generator) { +export function lists_reverse( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Block for reversing a list. const list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; const code = 'array_reverse(' + list + ')'; return [code, Order.FUNCTION_CALL]; -}; +} diff --git a/generators/php/logic.js b/generators/php/logic.ts similarity index 51% rename from generators/php/logic.js rename to generators/php/logic.ts index 0968c1ea40f..8aaa5c89213 100644 --- a/generators/php/logic.js +++ b/generators/php/logic.ts @@ -5,35 +5,43 @@ */ /** - * @fileoverview Generating PHP for logic blocks. + * @file Generating PHP for logic blocks. */ // Former goog.module ID: Blockly.PHP.logic +import type {Block} from '../../core/block.js'; import {Order} from './php_generator.js'; +import type {PhpGenerator} from './php_generator.js'; - -export function controls_if(block, generator) { +export function controls_if(block: Block, generator: PhpGenerator) { // If/elseif/else condition. let n = 0; - let code = '', branchCode, conditionCode; + let code = '', + branchCode, + conditionCode; if (generator.STATEMENT_PREFIX) { // Automatic prefix insertion is switched off for this block. Add manually. code += generator.injectId(generator.STATEMENT_PREFIX, block); } do { conditionCode = - generator.valueToCode(block, 'IF' + n, Order.NONE) || 'false'; + generator.valueToCode(block, 'IF' + n, Order.NONE) || 'false'; branchCode = generator.statementToCode(block, 'DO' + n); if (generator.STATEMENT_SUFFIX) { branchCode = - generator.prefixLines( - generator.injectId(generator.STATEMENT_SUFFIX, block), - generator.INDENT) + - branchCode; + generator.prefixLines( + generator.injectId(generator.STATEMENT_SUFFIX, block), + generator.INDENT, + ) + branchCode; } - code += (n > 0 ? ' else ' : '') + 'if (' + conditionCode + ') {\n' + - branchCode + '}'; + code += + (n > 0 ? ' else ' : '') + + 'if (' + + conditionCode + + ') {\n' + + branchCode + + '}'; n++; } while (block.getInput('IF' + n)); @@ -41,36 +49,48 @@ export function controls_if(block, generator) { branchCode = generator.statementToCode(block, 'ELSE'); if (generator.STATEMENT_SUFFIX) { branchCode = - generator.prefixLines( - generator.injectId(generator.STATEMENT_SUFFIX, block), - generator.INDENT) + - branchCode; + generator.prefixLines( + generator.injectId(generator.STATEMENT_SUFFIX, block), + generator.INDENT, + ) + branchCode; } code += ' else {\n' + branchCode + '}'; } return code + '\n'; -}; +} export const controls_ifelse = controls_if; -export function logic_compare(block, generator) { +export function logic_compare( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Comparison operator. - const OPERATORS = - {'EQ': '==', 'NEQ': '!=', 'LT': '<', 'LTE': '<=', 'GT': '>', 'GTE': '>='}; - const operator = OPERATORS[block.getFieldValue('OP')]; - const order = (operator === '==' || operator === '!=') ? Order.EQUALITY : - Order.RELATIONAL; + const OPERATORS = { + 'EQ': '==', + 'NEQ': '!=', + 'LT': '<', + 'LTE': '<=', + 'GT': '>', + 'GTE': '>=', + }; + type OperatorOption = keyof typeof OPERATORS; + const operator = OPERATORS[block.getFieldValue('OP') as OperatorOption]; + const order = + operator === '==' || operator === '!=' ? Order.EQUALITY : Order.RELATIONAL; const argument0 = generator.valueToCode(block, 'A', order) || '0'; const argument1 = generator.valueToCode(block, 'B', order) || '0'; const code = argument0 + ' ' + operator + ' ' + argument1; return [code, order]; -}; +} -export function logic_operation(block, generator) { +export function logic_operation( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Operations 'and', 'or'. - const operator = (block.getFieldValue('OP') === 'AND') ? '&&' : '||'; - const order = - (operator === '&&') ? Order.LOGICAL_AND : Order.LOGICAL_OR; + const operator = block.getFieldValue('OP') === 'AND' ? '&&' : '||'; + const order = operator === '&&' ? Order.LOGICAL_AND : Order.LOGICAL_OR; let argument0 = generator.valueToCode(block, 'A', order); let argument1 = generator.valueToCode(block, 'B', order); if (!argument0 && !argument1) { @@ -79,7 +99,7 @@ export function logic_operation(block, generator) { argument1 = 'false'; } else { // Single missing arguments have no effect on the return value. - const defaultArgument = (operator === '&&') ? 'true' : 'false'; + const defaultArgument = operator === '&&' ? 'true' : 'false'; if (!argument0) { argument0 = defaultArgument; } @@ -89,35 +109,47 @@ export function logic_operation(block, generator) { } const code = argument0 + ' ' + operator + ' ' + argument1; return [code, order]; -}; +} -export function logic_negate(block, generator) { +export function logic_negate( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Negation. const order = Order.LOGICAL_NOT; const argument0 = generator.valueToCode(block, 'BOOL', order) || 'true'; const code = '!' + argument0; return [code, order]; -}; +} -export function logic_boolean(block, generator) { +export function logic_boolean( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Boolean values true and false. - const code = (block.getFieldValue('BOOL') === 'TRUE') ? 'true' : 'false'; + const code = block.getFieldValue('BOOL') === 'TRUE' ? 'true' : 'false'; return [code, Order.ATOMIC]; -}; +} -export function logic_null(block, generator) { +export function logic_null( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Null data type. return ['null', Order.ATOMIC]; -}; +} -export function logic_ternary(block, generator) { +export function logic_ternary( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Ternary operator. const value_if = - generator.valueToCode(block, 'IF', Order.CONDITIONAL) || 'false'; + generator.valueToCode(block, 'IF', Order.CONDITIONAL) || 'false'; const value_then = - generator.valueToCode(block, 'THEN', Order.CONDITIONAL) || 'null'; + generator.valueToCode(block, 'THEN', Order.CONDITIONAL) || 'null'; const value_else = - generator.valueToCode(block, 'ELSE', Order.CONDITIONAL) || 'null'; + generator.valueToCode(block, 'ELSE', Order.CONDITIONAL) || 'null'; const code = value_if + ' ? ' + value_then + ' : ' + value_else; return [code, Order.CONDITIONAL]; -}; +} diff --git a/generators/php/loops.js b/generators/php/loops.ts similarity index 57% rename from generators/php/loops.js rename to generators/php/loops.ts index 981a4e219cf..ae87dbb294b 100644 --- a/generators/php/loops.js +++ b/generators/php/loops.ts @@ -5,17 +5,19 @@ */ /** - * @fileoverview Generating PHP for loop blocks. + * @file Generating PHP for loop blocks. */ // Former goog.module ID: Blockly.PHP.loops import * as stringUtils from '../../core/utils/string.js'; +import type {Block} from '../../core/block.js'; +import type {ControlFlowInLoopBlock} from '../../blocks/loops.js'; import {NameType} from '../../core/names.js'; import {Order} from './php_generator.js'; +import type {PhpGenerator} from './php_generator.js'; - -export function controls_repeat_ext(block, generator) { +export function controls_repeat_ext(block: Block, generator: PhpGenerator) { // Repeat n times. let repeats; if (block.getField('TIMES')) { @@ -28,55 +30,80 @@ export function controls_repeat_ext(block, generator) { let branch = generator.statementToCode(block, 'DO'); branch = generator.addLoopTrap(branch, block); let code = ''; - const loopVar = - generator.nameDB_.getDistinctName('count', NameType.VARIABLE); + const loopVar = generator.nameDB_!.getDistinctName( + 'count', + NameType.VARIABLE, + ); let endVar = repeats; if (!repeats.match(/^\w+$/) && !stringUtils.isNumber(repeats)) { - endVar = - generator.nameDB_.getDistinctName('repeat_end', NameType.VARIABLE); + endVar = generator.nameDB_!.getDistinctName( + 'repeat_end', + NameType.VARIABLE, + ); code += endVar + ' = ' + repeats + ';\n'; } - code += 'for (' + loopVar + ' = 0; ' + loopVar + ' < ' + endVar + '; ' + - loopVar + '++) {\n' + branch + '}\n'; + code += + 'for (' + + loopVar + + ' = 0; ' + + loopVar + + ' < ' + + endVar + + '; ' + + loopVar + + '++) {\n' + + branch + + '}\n'; return code; -}; +} export const controls_repeat = controls_repeat_ext; -export function controls_whileUntil(block, generator) { +export function controls_whileUntil(block: Block, generator: PhpGenerator) { // Do while/until loop. const until = block.getFieldValue('MODE') === 'UNTIL'; let argument0 = - generator.valueToCode( - block, 'BOOL', until ? Order.LOGICAL_NOT : Order.NONE) || - 'false'; + generator.valueToCode( + block, + 'BOOL', + until ? Order.LOGICAL_NOT : Order.NONE, + ) || 'false'; let branch = generator.statementToCode(block, 'DO'); branch = generator.addLoopTrap(branch, block); if (until) { argument0 = '!' + argument0; } return 'while (' + argument0 + ') {\n' + branch + '}\n'; -}; +} -export function controls_for(block, generator) { +export function controls_for(block: Block, generator: PhpGenerator) { // For loop. - const variable0 = - generator.getVariableName(block.getFieldValue('VAR')); + const variable0 = generator.getVariableName(block.getFieldValue('VAR')); const argument0 = - generator.valueToCode(block, 'FROM', Order.ASSIGNMENT) || '0'; - const argument1 = - generator.valueToCode(block, 'TO', Order.ASSIGNMENT) || '0'; - const increment = - generator.valueToCode(block, 'BY', Order.ASSIGNMENT) || '1'; + generator.valueToCode(block, 'FROM', Order.ASSIGNMENT) || '0'; + const argument1 = generator.valueToCode(block, 'TO', Order.ASSIGNMENT) || '0'; + const increment = generator.valueToCode(block, 'BY', Order.ASSIGNMENT) || '1'; let branch = generator.statementToCode(block, 'DO'); branch = generator.addLoopTrap(branch, block); let code; - if (stringUtils.isNumber(argument0) && stringUtils.isNumber(argument1) && - stringUtils.isNumber(increment)) { + if ( + stringUtils.isNumber(argument0) && + stringUtils.isNumber(argument1) && + stringUtils.isNumber(increment) + ) { // All arguments are simple numbers. const up = Number(argument0) <= Number(argument1); - code = 'for (' + variable0 + ' = ' + argument0 + '; ' + variable0 + - (up ? ' <= ' : ' >= ') + argument1 + '; ' + variable0; + code = + 'for (' + + variable0 + + ' = ' + + argument0 + + '; ' + + variable0 + + (up ? ' <= ' : ' >= ') + + argument1 + + '; ' + + variable0; const step = Math.abs(Number(increment)); if (step === 1) { code += up ? '++' : '--'; @@ -89,55 +116,78 @@ export function controls_for(block, generator) { // Cache non-trivial values to variables to prevent repeated look-ups. let startVar = argument0; if (!argument0.match(/^\w+$/) && !stringUtils.isNumber(argument0)) { - startVar = - generator.nameDB_.getDistinctName( - variable0 + '_start', NameType.VARIABLE); + startVar = generator.nameDB_!.getDistinctName( + variable0 + '_start', + NameType.VARIABLE, + ); code += startVar + ' = ' + argument0 + ';\n'; } let endVar = argument1; if (!argument1.match(/^\w+$/) && !stringUtils.isNumber(argument1)) { - endVar = - generator.nameDB_.getDistinctName( - variable0 + '_end', NameType.VARIABLE); + endVar = generator.nameDB_!.getDistinctName( + variable0 + '_end', + NameType.VARIABLE, + ); code += endVar + ' = ' + argument1 + ';\n'; } // Determine loop direction at start, in case one of the bounds // changes during loop execution. - const incVar = - generator.nameDB_.getDistinctName( - variable0 + '_inc', NameType.VARIABLE); + const incVar = generator.nameDB_!.getDistinctName( + variable0 + '_inc', + NameType.VARIABLE, + ); code += incVar + ' = '; if (stringUtils.isNumber(increment)) { - code += Math.abs(increment) + ';\n'; + code += Math.abs(Number(increment)) + ';\n'; } else { code += 'abs(' + increment + ');\n'; } code += 'if (' + startVar + ' > ' + endVar + ') {\n'; code += generator.INDENT + incVar + ' = -' + incVar + ';\n'; code += '}\n'; - code += 'for (' + variable0 + ' = ' + startVar + '; ' + incVar + - ' >= 0 ? ' + variable0 + ' <= ' + endVar + ' : ' + variable0 + - ' >= ' + endVar + '; ' + variable0 + ' += ' + incVar + ') {\n' + - branch + '}\n'; + code += + 'for (' + + variable0 + + ' = ' + + startVar + + '; ' + + incVar + + ' >= 0 ? ' + + variable0 + + ' <= ' + + endVar + + ' : ' + + variable0 + + ' >= ' + + endVar + + '; ' + + variable0 + + ' += ' + + incVar + + ') {\n' + + branch + + '}\n'; } return code; -}; +} -export function controls_forEach(block, generator) { +export function controls_forEach(block: Block, generator: PhpGenerator) { // For each loop. - const variable0 = - generator.getVariableName(block.getFieldValue('VAR')); + const variable0 = generator.getVariableName(block.getFieldValue('VAR')); const argument0 = - generator.valueToCode(block, 'LIST', Order.ASSIGNMENT) || '[]'; + generator.valueToCode(block, 'LIST', Order.ASSIGNMENT) || '[]'; let branch = generator.statementToCode(block, 'DO'); branch = generator.addLoopTrap(branch, block); let code = ''; code += - 'foreach (' + argument0 + ' as ' + variable0 + ') {\n' + branch + '}\n'; + 'foreach (' + argument0 + ' as ' + variable0 + ') {\n' + branch + '}\n'; return code; -}; +} -export function controls_flow_statements(block, generator) { +export function controls_flow_statements( + block: Block, + generator: PhpGenerator, +) { // Flow statements: continue, break. let xfix = ''; if (generator.STATEMENT_PREFIX) { @@ -150,7 +200,7 @@ export function controls_flow_statements(block, generator) { xfix += generator.injectId(generator.STATEMENT_SUFFIX, block); } if (generator.STATEMENT_PREFIX) { - const loop = block.getSurroundLoop(); + const loop = (block as ControlFlowInLoopBlock).getSurroundLoop(); if (loop && !loop.suppressPrefixSuffix) { // Inject loop's statement prefix here since the regular one at the end // of the loop will not get executed if 'continue' is triggered. @@ -165,4 +215,4 @@ export function controls_flow_statements(block, generator) { return xfix + 'continue;\n'; } throw Error('Unknown flow statement.'); -}; +} diff --git a/generators/php/math.js b/generators/php/math.ts similarity index 70% rename from generators/php/math.js rename to generators/php/math.ts index f1985949ea8..6218bf4d114 100644 --- a/generators/php/math.js +++ b/generators/php/math.ts @@ -5,45 +5,55 @@ */ /** - * @fileoverview Generating PHP for math blocks. + * @file Generating PHP for math blocks. */ // Former goog.module ID: Blockly.PHP.math +import type {Block} from '../../core/block.js'; import {Order} from './php_generator.js'; +import type {PhpGenerator} from './php_generator.js'; - -export function math_number(block, generator) { +export function math_number( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Numeric value. - let code = Number(block.getFieldValue('NUM')); - const order = code >= 0 ? Order.ATOMIC : Order.UNARY_NEGATION; - if (code === Infinity) { - code = 'INF'; - } else if (code === -Infinity) { - code = '-INF'; + let number = Number(block.getFieldValue('NUM')); + if (number === Infinity) { + return ['INF', Order.ATOMIC]; + } else if (number === -Infinity) { + return ['-INF', Order.UNARY_NEGATION]; } - return [code, order]; -}; + return [String(number), number >= 0 ? Order.ATOMIC : Order.UNARY_NEGATION]; +} -export function math_arithmetic(block, generator) { +export function math_arithmetic( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Basic arithmetic operators, and power. - const OPERATORS = { + const OPERATORS: Record = { 'ADD': [' + ', Order.ADDITION], 'MINUS': [' - ', Order.SUBTRACTION], 'MULTIPLY': [' * ', Order.MULTIPLICATION], 'DIVIDE': [' / ', Order.DIVISION], 'POWER': [' ** ', Order.POWER], }; - const tuple = OPERATORS[block.getFieldValue('OP')]; + type OperatorOption = keyof typeof OPERATORS; + const tuple = OPERATORS[block.getFieldValue('OP') as OperatorOption]; const operator = tuple[0]; const order = tuple[1]; const argument0 = generator.valueToCode(block, 'A', order) || '0'; const argument1 = generator.valueToCode(block, 'B', order) || '0'; const code = argument0 + operator + argument1; return [code, order]; -}; +} -export function math_single(block, generator) { +export function math_single( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Math operators with single operand. const operator = block.getFieldValue('OP'); let code; @@ -122,11 +132,14 @@ export function math_single(block, generator) { throw Error('Unknown math operator: ' + operator); } return [code, Order.DIVISION]; -}; +} -export function math_constant(block, generator) { +export function math_constant( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY. - const CONSTANTS = { + const CONSTANTS: Record = { 'PI': ['M_PI', Order.ATOMIC], 'E': ['M_E', Order.ATOMIC], 'GOLDEN_RATIO': ['(1 + sqrt(5)) / 2', Order.DIVISION], @@ -134,13 +147,20 @@ export function math_constant(block, generator) { 'SQRT1_2': ['M_SQRT1_2', Order.ATOMIC], 'INFINITY': ['INF', Order.ATOMIC], }; - return CONSTANTS[block.getFieldValue('CONSTANT')]; -}; + type ConstantOption = keyof typeof CONSTANTS; + return CONSTANTS[block.getFieldValue('CONSTANT') as ConstantOption]; +} -export function math_number_property(block, generator) { +export function math_number_property( + block: Block, + generator: PhpGenerator, +): [string, Order] { // 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 = { + const PROPERTIES: Record< + string, + [string, string, Order, Order] | [null, null, Order, Order] + > = { 'EVEN': ['', ' % 2 == 0', Order.MODULUS, Order.EQUALITY], 'ODD': ['', ' % 2 == 1', Order.MODULUS, Order.EQUALITY], 'WHOLE': ['is_int(', ')', Order.NONE, Order.FUNCTION_CALL], @@ -149,15 +169,18 @@ export function math_number_property(block, generator) { 'DIVISIBLE_BY': [null, null, Order.MODULUS, Order.EQUALITY], 'PRIME': [null, null, Order.NONE, Order.FUNCTION_CALL], }; - const dropdownProperty = block.getFieldValue('PROPERTY'); + type PropertyOption = keyof typeof PROPERTIES; + const dropdownProperty = block.getFieldValue('PROPERTY') as PropertyOption; const [prefix, suffix, inputOrder, outputOrder] = - PROPERTIES[dropdownProperty]; - const numberToCheck = generator.valueToCode(block, 'NUMBER_TO_CHECK', - inputOrder) || '0'; + PROPERTIES[dropdownProperty]; + const numberToCheck = + generator.valueToCode(block, 'NUMBER_TO_CHECK', inputOrder) || '0'; let code; if (dropdownProperty === 'PRIME') { // Prime is a special case as it is not a one-liner test. - const functionName = generator.provideFunction_('math_isPrime', ` + const functionName = generator.provideFunction_( + 'math_isPrime', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($n) { // https://en.wikipedia.org/wiki/Primality_test#Naive_methods if ($n == 2 || $n == 3) { @@ -176,37 +199,39 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}($n) { } return true; } -`); +`, + ); code = functionName + '(' + numberToCheck + ')'; } else if (dropdownProperty === 'DIVISIBLE_BY') { - const divisor = generator.valueToCode(block, 'DIVISOR', - Order.MODULUS) || '0'; + const divisor = + generator.valueToCode(block, 'DIVISOR', Order.MODULUS) || '0'; if (divisor === '0') { return ['false', Order.ATOMIC]; - } code = numberToCheck + ' % ' + divisor + ' == 0'; } else { code = prefix + numberToCheck + suffix; } return [code, outputOrder]; -}; +} -export function math_change(block, generator) { +export function math_change(block: Block, generator: PhpGenerator) { // Add to a variable in place. const argument0 = - generator.valueToCode(block, 'DELTA', Order.ADDITION) || '0'; - const varName = - generator.getVariableName(block.getFieldValue('VAR')); + generator.valueToCode(block, 'DELTA', Order.ADDITION) || '0'; + const varName = generator.getVariableName(block.getFieldValue('VAR')); return varName + ' += ' + argument0 + ';\n'; -}; +} // Rounding functions have a single operand. export const math_round = math_single; // Trigonometry functions have a single operand. export const math_trig = math_single; -export function math_on_list(block, generator) { +export function math_on_list( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Math functions for lists. const func = block.getFieldValue('OP'); let list; @@ -214,40 +239,43 @@ export function math_on_list(block, generator) { switch (func) { case 'SUM': list = - generator.valueToCode(block, 'LIST', Order.FUNCTION_CALL) - || 'array()'; + generator.valueToCode(block, 'LIST', Order.FUNCTION_CALL) || 'array()'; code = 'array_sum(' + list + ')'; break; case 'MIN': list = - generator.valueToCode(block, 'LIST', Order.FUNCTION_CALL) - || 'array()'; + generator.valueToCode(block, 'LIST', Order.FUNCTION_CALL) || 'array()'; code = 'min(' + list + ')'; break; case 'MAX': list = - generator.valueToCode(block, 'LIST', Order.FUNCTION_CALL) - || 'array()'; + generator.valueToCode(block, 'LIST', Order.FUNCTION_CALL) || 'array()'; code = 'max(' + list + ')'; break; case 'AVERAGE': { - const functionName = generator.provideFunction_('math_mean', ` + const functionName = generator.provideFunction_( + 'math_mean', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($myList) { return array_sum($myList) / count($myList); } -`); +`, + ); list = generator.valueToCode(block, 'LIST', Order.NONE) || 'array()'; code = functionName + '(' + list + ')'; break; } case 'MEDIAN': { - const functionName = generator.provideFunction_('math_median', ` + const functionName = generator.provideFunction_( + 'math_median', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($arr) { sort($arr,SORT_NUMERIC); return (count($arr) % 2) ? $arr[floor(count($arr) / 2)] : ($arr[floor(count($arr) / 2)] + $arr[floor(count($arr) / 2) - 1]) / 2; } -`); +`, + ); list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; @@ -256,7 +284,9 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}($arr) { // As a list of numbers can contain more than one mode, // the returned result is provided as an array. // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1]. - const functionName = generator.provideFunction_('math_modes', ` + const functionName = generator.provideFunction_( + 'math_modes', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($values) { if (empty($values)) return array(); $counts = array_count_values($values); @@ -264,14 +294,16 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}($values) { $modes = array_keys($counts, current($counts), true); return $modes; } -`); +`, + ); list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; } case 'STD_DEV': { - const functionName = - generator.provideFunction_('math_standard_deviation', ` + const functionName = generator.provideFunction_( + 'math_standard_deviation', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($numbers) { $n = count($numbers); if (!$n) return null; @@ -279,18 +311,22 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}($numbers) { foreach($numbers as $key => $num) $devs[$key] = pow($num - $mean, 2); return sqrt(array_sum($devs) / (count($devs) - 1)); } -`); +`, + ); list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; } case 'RANDOM': { - const functionName = generator.provideFunction_('math_random_list', ` + const functionName = generator.provideFunction_( + 'math_random_list', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($list) { $x = rand(0, count($list)-1); return $list[$x]; } -`); +`, + ); list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; @@ -299,56 +335,74 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}($list) { throw Error('Unknown operator: ' + func); } return [code, Order.FUNCTION_CALL]; -}; +} -export function math_modulo(block, generator) { +export function math_modulo( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Remainder computation. const argument0 = - generator.valueToCode(block, 'DIVIDEND', Order.MODULUS) || '0'; + generator.valueToCode(block, 'DIVIDEND', Order.MODULUS) || '0'; const argument1 = - generator.valueToCode(block, 'DIVISOR', Order.MODULUS) || '0'; + generator.valueToCode(block, 'DIVISOR', Order.MODULUS) || '0'; const code = argument0 + ' % ' + argument1; return [code, Order.MODULUS]; -}; +} -export function math_constrain(block, generator) { +export function math_constrain( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Constrain a number between two limits. const argument0 = generator.valueToCode(block, 'VALUE', Order.NONE) || '0'; const argument1 = generator.valueToCode(block, 'LOW', Order.NONE) || '0'; const argument2 = - generator.valueToCode(block, 'HIGH', Order.NONE) || 'Infinity'; + generator.valueToCode(block, 'HIGH', Order.NONE) || 'Infinity'; const code = - 'min(max(' + argument0 + ', ' + argument1 + '), ' + argument2 + ')'; + 'min(max(' + argument0 + ', ' + argument1 + '), ' + argument2 + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function math_random_int(block, generator) { +export function math_random_int( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Random integer between [X] and [Y]. const argument0 = generator.valueToCode(block, 'FROM', Order.NONE) || '0'; const argument1 = generator.valueToCode(block, 'TO', Order.NONE) || '0'; - const functionName = generator.provideFunction_('math_random_int', ` + const functionName = generator.provideFunction_( + 'math_random_int', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($a, $b) { if ($a > $b) { return rand($b, $a); } return rand($a, $b); } -`); +`, + ); const code = functionName + '(' + argument0 + ', ' + argument1 + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function math_random_float(block, generator) { +export function math_random_float( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Random fraction between 0 and 1. return ['(float)rand()/(float)getrandmax()', Order.FUNCTION_CALL]; -}; +} -export function math_atan2(block, generator) { +export function math_atan2( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Arctangent of point (X, Y) in degrees from -180 to 180. const argument0 = generator.valueToCode(block, 'X', Order.NONE) || '0'; const argument1 = generator.valueToCode(block, 'Y', Order.NONE) || '0'; return [ 'atan2(' + argument1 + ', ' + argument0 + ') / pi() * 180', - Order.DIVISION + Order.DIVISION, ]; -}; +} diff --git a/generators/php/php_generator.js b/generators/php/php_generator.js deleted file mode 100644 index dfe6f854359..00000000000 --- a/generators/php/php_generator.js +++ /dev/null @@ -1,307 +0,0 @@ -/** - * @license - * Copyright 2015 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Helper functions for generating PHP for blocks. - * @suppress {checkTypes|globalThis} - */ - -// Former goog.module ID: Blockly.PHP - -import * as stringUtils from '../../core/utils/string.js'; -// import type {Block} from '../../core/block.js'; -import {CodeGenerator} from '../../core/generator.js'; -import {Names} from '../../core/names.js'; -// import type {Workspace} from '../../core/workspace.js'; -import {inputTypes} from '../../core/inputs/input_types.js'; - - -/** - * Order of operation ENUMs. - * http://php.net/manual/en/language.operators.precedence.php - * @enum {number} - */ -export const Order = { - ATOMIC: 0, // 0 "" ... - CLONE: 1, // clone - NEW: 1, // new - MEMBER: 2.1, // [] - FUNCTION_CALL: 2.2, // () - POWER: 3, // ** - INCREMENT: 4, // ++ - DECREMENT: 4, // -- - BITWISE_NOT: 4, // ~ - CAST: 4, // (int) (float) (string) (array) ... - SUPPRESS_ERROR: 4, // @ - INSTANCEOF: 5, // instanceof - LOGICAL_NOT: 6, // ! - UNARY_PLUS: 7.1, // + - UNARY_NEGATION: 7.2, // - - MULTIPLICATION: 8.1, // * - DIVISION: 8.2, // / - MODULUS: 8.3, // % - ADDITION: 9.1, // + - SUBTRACTION: 9.2, // - - STRING_CONCAT: 9.3, // . - BITWISE_SHIFT: 10, // << >> - RELATIONAL: 11, // < <= > >= - EQUALITY: 12, // == != === !== <> <=> - REFERENCE: 13, // & - BITWISE_AND: 13, // & - BITWISE_XOR: 14, // ^ - BITWISE_OR: 15, // | - LOGICAL_AND: 16, // && - LOGICAL_OR: 17, // || - IF_NULL: 18, // ?? - CONDITIONAL: 19, // ?: - ASSIGNMENT: 20, // = += -= *= /= %= <<= >>= ... - LOGICAL_AND_WEAK: 21, // and - LOGICAL_XOR: 22, // xor - LOGICAL_OR_WEAK: 23, // or - NONE: 99, // (...) -}; - -export class PhpGenerator extends CodeGenerator { - /** - * List of outer-inner pairings that do NOT require parentheses. - * @type {!Array>} - */ - ORDER_OVERRIDES = [ - // (foo()).bar() -> foo().bar() - // (foo())[0] -> foo()[0] - [Order.MEMBER, Order.FUNCTION_CALL], - // (foo[0])[1] -> foo[0][1] - // (foo.bar).baz -> foo.bar.baz - [Order.MEMBER, Order.MEMBER], - // !(!foo) -> !!foo - [Order.LOGICAL_NOT, Order.LOGICAL_NOT], - // a * (b * c) -> a * b * c - [Order.MULTIPLICATION, Order.MULTIPLICATION], - // a + (b + c) -> a + b + c - [Order.ADDITION, Order.ADDITION], - // a && (b && c) -> a && b && c - [Order.LOGICAL_AND, Order.LOGICAL_AND], - // a || (b || c) -> a || b || c - [Order.LOGICAL_OR, Order.LOGICAL_OR] - ]; - - constructor(name) { - super(name ?? 'PHP'); - this.isInitialized = false; - - // Copy Order values onto instance for backwards compatibility - // while ensuring they are not part of the publically-advertised - // API. - // - // TODO(#7085): deprecate these in due course. (Could initially - // replace data properties with get accessors that call - // deprecate.warn().) - for (const key in Order) { - this['ORDER_' + key] = Order[key]; - } - - // List of illegal variable names. This is not intended to be a - // security feature. Blockly is 100% client-side, so bypassing - // this list is trivial. This is intended to prevent users from - // accidentally clobbering a built-in object or function. - this.addReservedWords( - // http://php.net/manual/en/reserved.keywords.php - '__halt_compiler,abstract,and,array,as,break,callable,case,catch,class,' + - 'clone,const,continue,declare,default,die,do,echo,else,elseif,empty,' + - 'enddeclare,endfor,endforeach,endif,endswitch,endwhile,eval,exit,' + - 'extends,final,for,foreach,function,global,goto,if,implements,include,' + - 'include_once,instanceof,insteadof,interface,isset,list,namespace,new,' + - 'or,print,private,protected,public,require,require_once,return,static,' + - 'switch,throw,trait,try,unset,use,var,while,xor,' + - // http://php.net/manual/en/reserved.constants.php - 'PHP_VERSION,PHP_MAJOR_VERSION,PHP_MINOR_VERSION,PHP_RELEASE_VERSION,' + - 'PHP_VERSION_ID,PHP_EXTRA_VERSION,PHP_ZTS,PHP_DEBUG,PHP_MAXPATHLEN,' + - 'PHP_OS,PHP_SAPI,PHP_EOL,PHP_INT_MAX,PHP_INT_SIZE,DEFAULT_INCLUDE_PATH,' + - 'PEAR_INSTALL_DIR,PEAR_EXTENSION_DIR,PHP_EXTENSION_DIR,PHP_PREFIX,' + - 'PHP_BINDIR,PHP_BINARY,PHP_MANDIR,PHP_LIBDIR,PHP_DATADIR,' + - 'PHP_SYSCONFDIR,PHP_LOCALSTATEDIR,PHP_CONFIG_FILE_PATH,' + - 'PHP_CONFIG_FILE_SCAN_DIR,PHP_SHLIB_SUFFIX,E_ERROR,E_WARNING,E_PARSE,' + - 'E_NOTICE,E_CORE_ERROR,E_CORE_WARNING,E_COMPILE_ERROR,' + - 'E_COMPILE_WARNING,E_USER_ERROR,E_USER_WARNING,E_USER_NOTICE,' + - 'E_DEPRECATED,E_USER_DEPRECATED,E_ALL,E_STRICT,' + - '__COMPILER_HALT_OFFSET__,TRUE,FALSE,NULL,__CLASS__,__DIR__,__FILE__,' + - '__FUNCTION__,__LINE__,__METHOD__,__NAMESPACE__,__TRAIT__' - ); - } - - /** - * Initialise the database of variable names. - * @param {!Workspace} workspace Workspace to generate code from. - */ - init(workspace) { - super.init(workspace); - - if (!this.nameDB_) { - this.nameDB_ = new Names(this.RESERVED_WORDS_, '$'); - } else { - this.nameDB_.reset(); - } - - this.nameDB_.setVariableMap(workspace.getVariableMap()); - this.nameDB_.populateVariables(workspace); - this.nameDB_.populateProcedures(workspace); - - this.isInitialized = true; - }; - - /** - * Prepend the generated code with the variable definitions. - * @param {string} code Generated code. - * @return {string} Completed code. - */ - finish(code) { - // Convert the definitions dictionary into a list. - const definitions = Object.values(this.definitions_); - // Call Blockly.CodeGenerator's finish. - code = super.finish(code); - this.isInitialized = false; - - this.nameDB_.reset(); - return definitions.join('\n\n') + '\n\n\n' + code; - }; - - /** - * Naked values are top-level blocks with outputs that aren't plugged into - * anything. A trailing semicolon is needed to make this legal. - * @param {string} line Line of generated code. - * @return {string} Legal line of code. - */ - scrubNakedValue(line) { - return line + ';\n'; - }; - - /** - * Encode a string as a properly escaped PHP string, complete with - * quotes. - * @param {string} string Text to encode. - * @return {string} PHP string. - */ - quote_(string) { - string = string.replace(/\\/g, '\\\\') - .replace(/\n/g, '\\\n') - .replace(/'/g, '\\\''); - return '\'' + string + '\''; - }; - - /** - * Encode a string as a properly escaped multiline PHP string, complete with - * quotes. - * @param {string} string Text to encode. - * @return {string} PHP string. - */ - multiline_quote_(string) { - const lines = string.split(/\n/g).map(this.quote_); - // Join with the following, plus a newline: - // . "\n" . - // Newline escaping only works in double-quoted strings. - return lines.join(' . \"\\n\" .\n'); - }; - - /** - * Common tasks for generating PHP from blocks. - * Handles comments for the specified block and any connected value blocks. - * Calls any statements following this block. - * @param {!Block} block The current block. - * @param {string} code The PHP code created for this block. - * @param {boolean=} opt_thisOnly True to generate code for only this - * statement. - * @return {string} PHP code with comments and subsequent blocks added. - * @protected - */ - scrub_(block, code, opt_thisOnly) { - let commentCode = ''; - // Only collect comments for blocks that aren't inline. - if (!block.outputConnection || !block.outputConnection.targetConnection) { - // Collect comment for this block. - let comment = block.getCommentText(); - if (comment) { - comment = stringUtils.wrap(comment, this.COMMENT_WRAP - 3); - commentCode += this.prefixLines(comment, '// ') + '\n'; - } - // Collect comments for all value arguments. - // Don't collect comments for nested statements. - for (let i = 0; i < block.inputList.length; i++) { - if (block.inputList[i].type === inputTypes.VALUE) { - const childBlock = block.inputList[i].connection.targetBlock(); - if (childBlock) { - comment = this.allNestedComments(childBlock); - if (comment) { - commentCode += this.prefixLines(comment, '// '); - } - } - } - } - } - const nextBlock = - block.nextConnection && block.nextConnection.targetBlock(); - const nextCode = opt_thisOnly ? '' : this.blockToCode(nextBlock); - return commentCode + code + nextCode; - }; - - /** - * Gets a property and adjusts the value while taking into account indexing. - * @param {!Block} block The block. - * @param {string} atId The property ID of the element to get. - * @param {number=} opt_delta Value to add. - * @param {boolean=} opt_negate Whether to negate the value. - * @param {number=} opt_order The highest order acting on this value. - * @return {string|number} - */ - getAdjusted(block, atId, opt_delta, opt_negate, opt_order) { - let delta = opt_delta || 0; - let order = opt_order || this.ORDER_NONE; - if (block.workspace.options.oneBasedIndex) { - delta--; - } - let defaultAtIndex = block.workspace.options.oneBasedIndex ? '1' : '0'; - let outerOrder = order; - let innerOrder; - if (delta > 0) { - outerOrder = this.ORDER_ADDITION; - innerOrder = this.ORDER_ADDITION; - } else if (delta < 0) { - outerOrder = this.ORDER_SUBTRACTION; - innerOrder = this.ORDER_SUBTRACTION; - } else if (opt_negate) { - outerOrder = this.ORDER_UNARY_NEGATION; - innerOrder = this.ORDER_UNARY_NEGATION; - } - let at = this.valueToCode(block, atId, outerOrder) || defaultAtIndex; - - if (stringUtils.isNumber(at)) { - // If the index is a naked number, adjust it right now. - at = Number(at) + delta; - if (opt_negate) { - at = -at; - } - } else { - // If the index is dynamic, adjust it in code. - if (delta > 0) { - at = at + ' + ' + delta; - } else if (delta < 0) { - at = at + ' - ' + -delta; - } - if (opt_negate) { - if (delta) { - at = '-(' + at + ')'; - } else { - at = '-' + at; - } - } - innerOrder = Math.floor(innerOrder); - order = Math.floor(order); - if (innerOrder && order >= innerOrder) { - at = '(' + at + ')'; - } - } - return at; - }; -} diff --git a/generators/php/php_generator.ts b/generators/php/php_generator.ts new file mode 100644 index 00000000000..ecdfb76fd01 --- /dev/null +++ b/generators/php/php_generator.ts @@ -0,0 +1,320 @@ +/** + * @license + * Copyright 2015 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file PHP code generator class, including helper methods for + * generating PHP for blocks. + */ + +// Former goog.module ID: Blockly.PHP + +import * as stringUtils from '../../core/utils/string.js'; +import type {Block} from '../../core/block.js'; +import {CodeGenerator} from '../../core/generator.js'; +import {Names} from '../../core/names.js'; +import type {Workspace} from '../../core/workspace.js'; +import {inputTypes} from '../../core/inputs/input_types.js'; + +/** + * Order of operation ENUMs. + * http://php.net/manual/en/language.operators.precedence.php + */ +// prettier-ignore +export enum Order { + ATOMIC = 0, // 0 "" ... + CLONE = 1, // clone + NEW = 1, // new + MEMBER = 2.1, // [] + FUNCTION_CALL = 2.2, // () + POWER = 3, // ** + INCREMENT = 4, // ++ + DECREMENT = 4, // -- + BITWISE_NOT = 4, // ~ + CAST = 4, // (int) (float) (string) (array) ... + SUPPRESS_ERROR = 4, // @ + INSTANCEOF = 5, // instanceof + LOGICAL_NOT = 6, // ! + UNARY_PLUS = 7.1, // + + UNARY_NEGATION = 7.2, // - + MULTIPLICATION = 8.1, // * + DIVISION = 8.2, // / + MODULUS = 8.3, // % + ADDITION = 9.1, // + + SUBTRACTION = 9.2, // - + STRING_CONCAT = 9.3, // . + BITWISE_SHIFT = 10, // << >> + RELATIONAL = 11, // < <= > >= + EQUALITY = 12, // == != === !== <> <=> + REFERENCE = 13, // & + BITWISE_AND = 13, // & + BITWISE_XOR = 14, // ^ + BITWISE_OR = 15, // | + LOGICAL_AND = 16, // && + LOGICAL_OR = 17, // || + IF_NULL = 18, // ?? + CONDITIONAL = 19, // ?: + ASSIGNMENT = 20, // = += -= *= /= %= <<= >>= ... + LOGICAL_AND_WEAK = 21, // and + LOGICAL_XOR = 22, // xor + LOGICAL_OR_WEAK = 23, // or + NONE = 99, // (...) +} + +export class PhpGenerator extends CodeGenerator { + /** List of outer-inner pairings that do NOT require parentheses. */ + ORDER_OVERRIDES: [Order, Order][] = [ + // (foo()).bar() -> foo().bar() + // (foo())[0] -> foo()[0] + [Order.MEMBER, Order.FUNCTION_CALL], + // (foo[0])[1] -> foo[0][1] + // (foo.bar).baz -> foo.bar.baz + [Order.MEMBER, Order.MEMBER], + // !(!foo) -> !!foo + [Order.LOGICAL_NOT, Order.LOGICAL_NOT], + // a * (b * c) -> a * b * c + [Order.MULTIPLICATION, Order.MULTIPLICATION], + // a + (b + c) -> a + b + c + [Order.ADDITION, Order.ADDITION], + // a && (b && c) -> a && b && c + [Order.LOGICAL_AND, Order.LOGICAL_AND], + // a || (b || c) -> a || b || c + [Order.LOGICAL_OR, Order.LOGICAL_OR], + ]; + + /** @param name Name of the language the generator is for. */ + constructor(name = 'PHP') { + super(name); + this.isInitialized = false; + + // Copy Order values onto instance for backwards compatibility + // while ensuring they are not part of the publically-advertised + // API. + // + // TODO(#7085): deprecate these in due course. (Could initially + // replace data properties with get accessors that call + // deprecate.warn().) + for (const key in Order) { + // Must assign Order[key] to a temporary to get the type guard to work; + // see https://github.com/microsoft/TypeScript/issues/10530. + const value = Order[key]; + // Skip reverse-lookup entries in the enum. Due to + // https://github.com/microsoft/TypeScript/issues/55713 this (as + // of TypeScript 5.5.2) actually narrows the type of value to + // never - but that still allows the following assignment to + // succeed. + if (typeof value === 'string') continue; + (this as unknown as Record)['ORDER_' + key] = value; + } + + // List of illegal variable names. This is not intended to be a + // security feature. Blockly is 100% client-side, so bypassing + // this list is trivial. This is intended to prevent users from + // accidentally clobbering a built-in object or function. + this.addReservedWords( + // http://php.net/manual/en/reserved.keywords.php + '__halt_compiler,abstract,and,array,as,break,callable,case,catch,class,' + + 'clone,const,continue,declare,default,die,do,echo,else,elseif,empty,' + + 'enddeclare,endfor,endforeach,endif,endswitch,endwhile,eval,exit,' + + 'extends,final,for,foreach,function,global,goto,if,implements,include,' + + 'include_once,instanceof,insteadof,interface,isset,list,namespace,new,' + + 'or,print,private,protected,public,require,require_once,return,static,' + + 'switch,throw,trait,try,unset,use,var,while,xor,' + + // http://php.net/manual/en/reserved.constants.php + 'PHP_VERSION,PHP_MAJOR_VERSION,PHP_MINOR_VERSION,PHP_RELEASE_VERSION,' + + 'PHP_VERSION_ID,PHP_EXTRA_VERSION,PHP_ZTS,PHP_DEBUG,PHP_MAXPATHLEN,' + + 'PHP_OS,PHP_SAPI,PHP_EOL,PHP_INT_MAX,PHP_INT_SIZE,DEFAULT_INCLUDE_PATH,' + + 'PEAR_INSTALL_DIR,PEAR_EXTENSION_DIR,PHP_EXTENSION_DIR,PHP_PREFIX,' + + 'PHP_BINDIR,PHP_BINARY,PHP_MANDIR,PHP_LIBDIR,PHP_DATADIR,' + + 'PHP_SYSCONFDIR,PHP_LOCALSTATEDIR,PHP_CONFIG_FILE_PATH,' + + 'PHP_CONFIG_FILE_SCAN_DIR,PHP_SHLIB_SUFFIX,E_ERROR,E_WARNING,E_PARSE,' + + 'E_NOTICE,E_CORE_ERROR,E_CORE_WARNING,E_COMPILE_ERROR,' + + 'E_COMPILE_WARNING,E_USER_ERROR,E_USER_WARNING,E_USER_NOTICE,' + + 'E_DEPRECATED,E_USER_DEPRECATED,E_ALL,E_STRICT,' + + '__COMPILER_HALT_OFFSET__,TRUE,FALSE,NULL,__CLASS__,__DIR__,__FILE__,' + + '__FUNCTION__,__LINE__,__METHOD__,__NAMESPACE__,__TRAIT__', + ); + } + + /** + * Initialise the database of variable names. + * + * @param workspace Workspace to generate code from. + */ + init(workspace: Workspace) { + super.init(workspace); + + if (!this.nameDB_) { + this.nameDB_ = new Names(this.RESERVED_WORDS_, '$'); + } else { + this.nameDB_.reset(); + } + + this.nameDB_.setVariableMap(workspace.getVariableMap()); + this.nameDB_.populateVariables(workspace); + this.nameDB_.populateProcedures(workspace); + + this.isInitialized = true; + } + + /** + * Prepend the generated code with the variable definitions. + * + * @param code Generated code. + * @returns Completed code. + */ + finish(code: string): string { + // Convert the definitions dictionary into a list. + const definitions = Object.values(this.definitions_); + // Call Blockly.CodeGenerator's finish. + code = super.finish(code); + this.isInitialized = false; + + this.nameDB_!.reset(); + return definitions.join('\n\n') + '\n\n\n' + code; + } + + /** + * Naked values are top-level blocks with outputs that aren't plugged into + * anything. + * + * @param line Line of generated code. + * @returns Legal line of code. + */ + scrubNakedValue(line: string): string { + return line + ';\n'; + } + + /** + * Encode a string as a properly escaped PHP string, complete with + * quotes. + * + * @param string Text to encode. + * @returns PHP string. + */ + quote_(string: string): string { + string = string + .replace(/\\/g, '\\\\') + .replace(/\n/g, '\\\n') + .replace(/'/g, "\\'"); + return "'" + string + "'"; + } + + /** + * Encode a string as a properly escaped multiline PHP string, complete with + * quotes. + * @param string Text to encode. + * @returns PHP string. + */ + multiline_quote_(string: string): string { + const lines = string.split(/\n/g).map(this.quote_); + // Join with the following, plus a newline: + // . "\n" . + // Newline escaping only works in double-quoted strings. + return lines.join(' . "\\n" .\n'); + } + + /** + * Common tasks for generating PHP from blocks. + * Handles comments for the specified block and any connected value blocks. + * Calls any statements following this block. + * + * @param block The current block. + * @param code The PHP code created for this block. + * @param thisOnly True to generate code for only this statement. + * @returns PHP code with comments and subsequent blocks added. + */ + scrub_(block: Block, code: string, thisOnly = false): string { + let commentCode = ''; + // Only collect comments for blocks that aren't inline. + if (!block.outputConnection || !block.outputConnection.targetConnection) { + // Collect comment for this block. + let comment = block.getCommentText(); + if (comment) { + comment = stringUtils.wrap(comment, this.COMMENT_WRAP - 3); + commentCode += this.prefixLines(comment, '// ') + '\n'; + } + // Collect comments for all value arguments. + // Don't collect comments for nested statements. + for (let i = 0; i < block.inputList.length; i++) { + if (block.inputList[i].type === inputTypes.VALUE) { + const childBlock = block.inputList[i].connection!.targetBlock(); + if (childBlock) { + comment = this.allNestedComments(childBlock); + if (comment) { + commentCode += this.prefixLines(comment, '// '); + } + } + } + } + } + const nextBlock = + block.nextConnection && block.nextConnection.targetBlock(); + const nextCode = thisOnly ? '' : this.blockToCode(nextBlock); + return commentCode + code + nextCode; + } + + /** + * Generate code representing the specified value input, adjusted to take into + * account indexing (zero- or one-based) and optionally by a specified delta + * and/or by negation. + * + * @param block The block. + * @param atId The ID of the input block to get (and adjust) the value of. + * @param delta Value to add. + * @param negate Whether to negate the value. + * @param order The highest order acting on this value. + * @returns The adjusted value or code that evaluates to it. + */ + getAdjusted( + block: Block, + atId: string, + delta = 0, + negate = false, + order = Order.NONE, + ): string { + if (block.workspace.options.oneBasedIndex) { + delta--; + } + let defaultAtIndex = block.workspace.options.oneBasedIndex ? '1' : '0'; + + let orderForInput = order; + if (delta > 0) { + orderForInput = Order.ADDITION; + } else if (delta < 0) { + orderForInput = Order.SUBTRACTION; + } else if (negate) { + orderForInput = Order.UNARY_NEGATION; + } + + let at = this.valueToCode(block, atId, orderForInput) || defaultAtIndex; + + // Easy case: no adjustments. + if (delta === 0 && !negate) { + return at; + } + // If the index is a naked number, adjust it right now. + if (stringUtils.isNumber(at)) { + at = String(Number(at) + delta); + if (negate) { + at = String(-Number(at)); + } + return at; + } + // If the index is dynamic, adjust it in code. + if (delta > 0) { + at = `${at} + ${delta}`; + } else if (delta < 0) { + at = `${at} - ${-delta}`; + } + if (negate) { + at = delta ? `-(${at})` : `-${at}`; + } + if (Math.floor(order) >= Math.floor(orderForInput)) { + at = `(${at})`; + } + return at; + } +} diff --git a/generators/php/procedures.js b/generators/php/procedures.ts similarity index 65% rename from generators/php/procedures.js rename to generators/php/procedures.ts index f261ec919fe..5d44eb21bae 100644 --- a/generators/php/procedures.js +++ b/generators/php/procedures.ts @@ -5,17 +5,19 @@ */ /** - * @fileoverview Generating PHP for procedure blocks. + * @file Generating PHP for procedure blocks. */ // Former goog.module ID: Blockly.PHP.procedures import * as Variables from '../../core/variables.js'; +import type {Block} from '../../core/block.js'; +import type {IfReturnBlock} from '../../blocks/procedures.js'; import {NameType} from '../../core/names.js'; import {Order} from './php_generator.js'; +import type {PhpGenerator} from './php_generator.js'; - -export function procedures_defreturn(block, generator) { +export function procedures_defreturn(block: Block, generator: PhpGenerator) { // Define a procedure with a return value. // First, add a 'global' statement for every variable that is not shadowed by // a local parameter. @@ -33,15 +35,14 @@ export function procedures_defreturn(block, generator) { const devVarList = Variables.allDeveloperVariables(workspace); for (let i = 0; i < devVarList.length; i++) { globals.push( - generator.nameDB_.getName( - devVarList[i], NameType.DEVELOPER_VARIABLE)); + generator.nameDB_!.getName(devVarList[i], NameType.DEVELOPER_VARIABLE), + ); } - const globalStr = - globals.length ? - generator.INDENT + 'global ' + globals.join(', ') + ';\n' : ''; + const globalStr = globals.length + ? generator.INDENT + 'global ' + globals.join(', ') + ';\n' + : ''; - const funcName = - generator.getProcedureName(block.getFieldValue('NAME')); + const funcName = generator.getProcedureName(block.getFieldValue('NAME')); let xfix1 = ''; if (generator.STATEMENT_PREFIX) { xfix1 += generator.injectId(generator.STATEMENT_PREFIX, block); @@ -55,8 +56,9 @@ export function procedures_defreturn(block, generator) { let loopTrap = ''; if (generator.INFINITE_LOOP_TRAP) { loopTrap = generator.prefixLines( - generator.injectId(generator.INFINITE_LOOP_TRAP, block), - generator.INDENT); + generator.injectId(generator.INFINITE_LOOP_TRAP, block), + generator.INDENT, + ); } const branch = generator.statementToCode(block, 'STACK'); let returnValue = generator.valueToCode(block, 'RETURN', Order.NONE) || ''; @@ -73,22 +75,37 @@ export function procedures_defreturn(block, generator) { for (let i = 0; i < variables.length; i++) { args[i] = generator.getVariableName(variables[i]); } - let code = 'function ' + funcName + '(' + args.join(', ') + ') {\n' + - globalStr + xfix1 + loopTrap + branch + xfix2 + returnValue + '}'; + let code = + 'function ' + + funcName + + '(' + + args.join(', ') + + ') {\n' + + globalStr + + xfix1 + + loopTrap + + branch + + xfix2 + + returnValue + + '}'; code = generator.scrub_(block, code); // Add % so as not to collide with helper functions in definitions list. - generator.definitions_['%' + funcName] = code; + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['%' + funcName] = code; return null; -}; +} // Defining a procedure without a return value uses the same generator as // a procedure with a return value. export const procedures_defnoreturn = procedures_defreturn; -export function procedures_callreturn(block, generator) { +export function procedures_callreturn( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Call a procedure with a return value. - const funcName = - generator.getProcedureName(block.getFieldValue('NAME')); + const funcName = generator.getProcedureName(block.getFieldValue('NAME')); const args = []; const variables = block.getVars(); for (let i = 0; i < variables.length; i++) { @@ -96,30 +113,33 @@ export function procedures_callreturn(block, generator) { } const code = funcName + '(' + args.join(', ') + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function procedures_callnoreturn(block, generator) { +export function procedures_callnoreturn(block: Block, generator: PhpGenerator) { // Call a procedure with no return value. // Generated code is for a function call as a statement is the same as a // function call as a value, with the addition of line ending. - const tuple = generator.forBlock['procedures_callreturn'](block, generator); + const tuple = generator.forBlock['procedures_callreturn']( + block, + generator, + ) as [string, Order]; return tuple[0] + ';\n'; -}; +} -export function procedures_ifreturn(block, generator) { +export function procedures_ifreturn(block: Block, generator: PhpGenerator) { // Conditionally return value from a procedure. const condition = - generator.valueToCode(block, 'CONDITION', Order.NONE) || 'false'; + generator.valueToCode(block, 'CONDITION', Order.NONE) || 'false'; let code = 'if (' + condition + ') {\n'; if (generator.STATEMENT_SUFFIX) { // Inject any statement suffix here since the regular one at the end // will not get executed if the return is triggered. - code += - generator.prefixLines( - generator.injectId(generator.STATEMENT_SUFFIX, block), - generator.INDENT); + code += generator.prefixLines( + generator.injectId(generator.STATEMENT_SUFFIX, block), + generator.INDENT, + ); } - if (block.hasReturnValue_) { + if ((block as IfReturnBlock).hasReturnValue_) { const value = generator.valueToCode(block, 'VALUE', Order.NONE) || 'null'; code += generator.INDENT + 'return ' + value + ';\n'; } else { @@ -127,4 +147,4 @@ export function procedures_ifreturn(block, generator) { } code += '}\n'; return code; -}; +} diff --git a/generators/php/text.js b/generators/php/text.ts similarity index 63% rename from generators/php/text.js rename to generators/php/text.ts index b5941315be2..f2fa69faec3 100644 --- a/generators/php/text.js +++ b/generators/php/text.ts @@ -5,87 +5,104 @@ */ /** - * @fileoverview Generating PHP for text blocks. + * @file Generating PHP for text blocks. */ // Former goog.module ID: Blockly.PHP.texts +import type {Block} from '../../core/block.js'; +import type {JoinMutatorBlock} from '../../blocks/text.js'; import {Order} from './php_generator.js'; +import type {PhpGenerator} from './php_generator.js'; - -export function text(block, generator) { +export function text(block: Block, generator: PhpGenerator): [string, Order] { // Text value. const code = generator.quote_(block.getFieldValue('TEXT')); return [code, Order.ATOMIC]; -}; +} -export function text_multiline(block, generator) { +export function text_multiline( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Text value. const code = generator.multiline_quote_(block.getFieldValue('TEXT')); - const order = - code.indexOf('.') !== -1 ? Order.STRING_CONCAT : Order.ATOMIC; + const order = code.indexOf('.') !== -1 ? Order.STRING_CONCAT : Order.ATOMIC; return [code, order]; -}; +} -export function text_join(block, generator) { +export function text_join( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Create a string made up of any number of elements of any type. - if (block.itemCount_ === 0) { + const joinBlock = block as JoinMutatorBlock; + if (joinBlock.itemCount_ === 0) { return ["''", Order.ATOMIC]; - } else if (block.itemCount_ === 1) { + } else if (joinBlock.itemCount_ === 1) { const element = generator.valueToCode(block, 'ADD0', Order.NONE) || "''"; const code = element; return [code, Order.NONE]; - } else if (block.itemCount_ === 2) { + } else if (joinBlock.itemCount_ === 2) { const element0 = - generator.valueToCode(block, 'ADD0', Order.STRING_CONCAT) || "''"; + generator.valueToCode(block, 'ADD0', Order.STRING_CONCAT) || "''"; const element1 = - generator.valueToCode(block, 'ADD1', Order.STRING_CONCAT) || "''"; + generator.valueToCode(block, 'ADD1', Order.STRING_CONCAT) || "''"; const code = element0 + ' . ' + element1; return [code, Order.STRING_CONCAT]; } else { - const elements = new Array(block.itemCount_); - for (let i = 0; i < block.itemCount_; i++) { - elements[i] = - generator.valueToCode(block, 'ADD' + i, Order.NONE) || "''"; + const elements = new Array(joinBlock.itemCount_); + for (let i = 0; i < joinBlock.itemCount_; i++) { + elements[i] = generator.valueToCode(block, 'ADD' + i, Order.NONE) || "''"; } - const code = 'implode(\'\', array(' + elements.join(',') + '))'; + const code = "implode('', array(" + elements.join(',') + '))'; return [code, Order.FUNCTION_CALL]; } -}; +} -export function text_append(block, generator) { +export function text_append(block: Block, generator: PhpGenerator) { // Append to a variable in place. - const varName = - generator.getVariableName(block.getFieldValue('VAR')); - const value = - generator.valueToCode(block, 'TEXT', Order.ASSIGNMENT) || "''"; + const varName = generator.getVariableName(block.getFieldValue('VAR')); + const value = generator.valueToCode(block, 'TEXT', Order.ASSIGNMENT) || "''"; return varName + ' .= ' + value + ';\n'; -}; +} -export function text_length(block, generator) { +export function text_length( + block: Block, + generator: PhpGenerator, +): [string, Order] { // String or array length. - const functionName = generator.provideFunction_('length', ` + const functionName = generator.provideFunction_( + 'length', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($value) { if (is_string($value)) { return strlen($value); } return count($value); } -`); +`, + ); const text = generator.valueToCode(block, 'VALUE', Order.NONE) || "''"; return [functionName + '(' + text + ')', Order.FUNCTION_CALL]; -}; +} -export function text_isEmpty(block, generator) { +export function text_isEmpty( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Is the string null or array empty? const text = generator.valueToCode(block, 'VALUE', Order.NONE) || "''"; return ['empty(' + text + ')', Order.FUNCTION_CALL]; -}; +} -export function text_indexOf(block, generator) { +export function text_indexOf( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Search the text for a substring. const operator = - block.getFieldValue('END') === 'FIRST' ? 'strpos' : 'strrpos'; + block.getFieldValue('END') === 'FIRST' ? 'strpos' : 'strrpos'; const substring = generator.valueToCode(block, 'FIND', Order.NONE) || "''"; const text = generator.valueToCode(block, 'VALUE', Order.NONE) || "''"; let errorIndex = ' -1'; @@ -95,22 +112,27 @@ export function text_indexOf(block, generator) { indexAdjustment = ' + 1'; } const functionName = generator.provideFunction_( - block.getFieldValue('END') === 'FIRST' ? 'text_indexOf' : - 'text_lastIndexOf', - ` + block.getFieldValue('END') === 'FIRST' + ? 'text_indexOf' + : 'text_lastIndexOf', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($text, $search) { $pos = ${operator}($text, $search); return $pos === false ? ${errorIndex} : $pos${indexAdjustment}; } -`); +`, + ); const code = functionName + '(' + text + ', ' + substring + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function text_charAt(block, generator) { +export function text_charAt( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Get letter at index. const where = block.getFieldValue('WHERE') || 'FROM_START'; - const textOrder = (where === 'RANDOM') ? Order.NONE : Order.NONE; + const textOrder = where === 'RANDOM' ? Order.NONE : Order.NONE; const text = generator.valueToCode(block, 'VALUE', textOrder) || "''"; switch (where) { case 'FIRST': { @@ -132,19 +154,25 @@ export function text_charAt(block, generator) { return [code, Order.FUNCTION_CALL]; } case 'RANDOM': { - const functionName = generator.provideFunction_('text_random_letter', ` + const functionName = generator.provideFunction_( + 'text_random_letter', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($text) { return $text[rand(0, strlen($text) - 1)]; } -`); +`, + ); const code = functionName + '(' + text + ')'; return [code, Order.FUNCTION_CALL]; } } throw Error('Unhandled option (text_charAt).'); -}; +} -export function text_getSubstring(block, generator) { +export function text_getSubstring( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Get substring. const where1 = block.getFieldValue('WHERE1'); const where2 = block.getFieldValue('WHERE2'); @@ -155,7 +183,9 @@ export function text_getSubstring(block, generator) { } else { const at1 = generator.getAdjusted(block, 'AT1'); const at2 = generator.getAdjusted(block, 'AT2'); - const functionName = generator.provideFunction_('text_get_substring', ` + const functionName = generator.provideFunction_( + 'text_get_substring', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}($text, $where1, $at1, $where2, $at2) { if ($where1 == 'FROM_END') { $at1 = strlen($text) - 1 - $at1; @@ -176,14 +206,29 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}($text, $where1, $at1, $where2, } return substr($text, $at1, $length); } -`); - const code = functionName + '(' + text + ', \'' + where1 + '\', ' + at1 + - ', \'' + where2 + '\', ' + at2 + ')'; +`, + ); + const code = + functionName + + '(' + + text + + ", '" + + where1 + + "', " + + at1 + + ", '" + + where2 + + "', " + + at2 + + ')'; return [code, Order.FUNCTION_CALL]; } -}; +} -export function text_changeCase(block, generator) { +export function text_changeCase( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Change capitalization. const text = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; let code; @@ -194,24 +239,31 @@ export function text_changeCase(block, generator) { } else if (block.getFieldValue('CASE') === 'TITLECASE') { code = 'ucwords(strtolower(' + text + '))'; } - return [code, Order.FUNCTION_CALL]; -}; + return [code as string, Order.FUNCTION_CALL]; +} -export function text_trim(block, generator) { +export function text_trim( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Trim spaces. const OPERATORS = {'LEFT': 'ltrim', 'RIGHT': 'rtrim', 'BOTH': 'trim'}; - const operator = OPERATORS[block.getFieldValue('MODE')]; + type OperatorOption = keyof typeof OPERATORS; + const operator = OPERATORS[block.getFieldValue('MODE') as OperatorOption]; const text = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; return [operator + '(' + text + ')', Order.FUNCTION_CALL]; -}; +} -export function text_print(block, generator) { +export function text_print(block: Block, generator: PhpGenerator) { // Print statement. const msg = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; return 'print(' + msg + ');\n'; -}; +} -export function text_prompt_ext(block, generator) { +export function text_prompt_ext( + block: Block, + generator: PhpGenerator, +): [string, Order] { // Prompt function. let msg; if (block.getField('TEXT')) { @@ -227,29 +279,47 @@ export function text_prompt_ext(block, generator) { code = 'floatval(' + code + ')'; } return [code, Order.FUNCTION_CALL]; -}; +} export const text_prompt = text_prompt_ext; -export function text_count(block, generator) { +export function text_count( + block: Block, + generator: PhpGenerator, +): [string, Order] { const text = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; const sub = generator.valueToCode(block, 'SUB', Order.NONE) || "''"; - const code = 'strlen(' + sub + ') === 0' + - ' ? strlen(' + text + ') + 1' + - ' : substr_count(' + text + ', ' + sub + ')'; + const code = + 'strlen(' + + sub + + ') === 0' + + ' ? strlen(' + + text + + ') + 1' + + ' : substr_count(' + + text + + ', ' + + sub + + ')'; return [code, Order.CONDITIONAL]; -}; +} -export function text_replace(block, generator) { +export function text_replace( + block: Block, + generator: PhpGenerator, +): [string, Order] { const text = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; const from = generator.valueToCode(block, 'FROM', Order.NONE) || "''"; const to = generator.valueToCode(block, 'TO', Order.NONE) || "''"; const code = 'str_replace(' + from + ', ' + to + ', ' + text + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function text_reverse(block, generator) { +export function text_reverse( + block: Block, + generator: PhpGenerator, +): [string, Order] { const text = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; const code = 'strrev(' + text + ')'; return [code, Order.FUNCTION_CALL]; -}; +} diff --git a/generators/php/variables.js b/generators/php/variables.js deleted file mode 100644 index 50429fa9b3b..00000000000 --- a/generators/php/variables.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @license - * Copyright 2015 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Generating PHP for variable blocks. - */ - -// Former goog.module ID: Blockly.PHP.variables - -import {Order} from './php_generator.js'; - - -export function variables_get(block, generator) { - // Variable getter. - const code = - generator.getVariableName(block.getFieldValue('VAR')); - return [code, Order.ATOMIC]; -}; - -export function variables_set(block, generator) { - // Variable setter. - const argument0 = - generator.valueToCode(block, 'VALUE', Order.ASSIGNMENT) || '0'; - const varName = - generator.getVariableName(block.getFieldValue('VAR')); - return varName + ' = ' + argument0 + ';\n'; -}; diff --git a/generators/php/variables.ts b/generators/php/variables.ts new file mode 100644 index 00000000000..4aacc293b6d --- /dev/null +++ b/generators/php/variables.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright 2015 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file Generating PHP for variable blocks. + */ + +// Former goog.module ID: Blockly.PHP.variables + +import type {Block} from '../../core/block.js'; +import {Order} from './php_generator.js'; +import type {PhpGenerator} from './php_generator.js'; + +export function variables_get( + block: Block, + generator: PhpGenerator, +): [string, Order] { + // Variable getter. + const code = generator.getVariableName(block.getFieldValue('VAR')); + return [code, Order.ATOMIC]; +} + +export function variables_set(block: Block, generator: PhpGenerator) { + // Variable setter. + const argument0 = + generator.valueToCode(block, 'VALUE', Order.ASSIGNMENT) || '0'; + const varName = generator.getVariableName(block.getFieldValue('VAR')); + return varName + ' = ' + argument0 + ';\n'; +} diff --git a/generators/php/variables_dynamic.js b/generators/php/variables_dynamic.ts similarity index 83% rename from generators/php/variables_dynamic.js rename to generators/php/variables_dynamic.ts index 1f4a3c10095..5ab5ec6cab5 100644 --- a/generators/php/variables_dynamic.js +++ b/generators/php/variables_dynamic.ts @@ -5,12 +5,11 @@ */ /** - * @fileoverview Generating PHP for dynamic variable blocks. + * @file Generating PHP for dynamic variable blocks. */ // Former goog.module ID: Blockly.PHP.variablesDynamic - // generator is dynamically typed. export { variables_get as variables_get_dynamic, diff --git a/generators/python/python_generator.ts b/generators/python/python_generator.ts index 5e5daf9f2e8..f4bef167761 100644 --- a/generators/python/python_generator.ts +++ b/generators/python/python_generator.ts @@ -52,10 +52,8 @@ export enum Order { * PythonScript code generator class. */ export class PythonGenerator extends CodeGenerator { - /** - * List of outer-inner pairings that do NOT require parentheses. - */ - ORDER_OVERRIDES: number[][] = [ + /** List of outer-inner pairings that do NOT require parentheses. */ + ORDER_OVERRIDES: [Order, Order][] = [ // (foo()).bar -> foo().bar // (foo())[0] -> foo()[0] [Order.FUNCTION_CALL, Order.MEMBER],