From 4be173dc9663d3f3b314cda5ed49282433869efd Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Fri, 20 Oct 2023 17:22:02 +0100 Subject: [PATCH] chore(generator): Format One block protected with // prettier-ignore to preserve careful comment formatting. Where there are repeated concatenations prettier has made a pretty mess of things, but the correct fix is probably to use template literals instead (rather than just locally disabling prettier). This has been added to the to-do list in #7600. --- .prettierignore | 5 +- generators/javascript.ts | 11 +- generators/javascript/colour.ts | 65 +++-- generators/javascript/javascript_generator.ts | 39 ++- generators/javascript/lists.ts | 266 ++++++++++------- generators/javascript/logic.ts | 116 ++++---- generators/javascript/loops.ts | 170 +++++++---- generators/javascript/math.ts | 249 +++++++++------- generators/javascript/procedures.ts | 81 +++--- generators/javascript/text.ts | 268 ++++++++++-------- generators/javascript/variables.ts | 14 +- generators/javascript/variables_dynamic.ts | 1 - 12 files changed, 775 insertions(+), 510 deletions(-) diff --git a/.prettierignore b/.prettierignore index 64dd749f1b0..9d52f19fe6a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -18,7 +18,6 @@ # Demos, scripts, misc /node_modules/* -/generators/* /demos/* /appengine/* /externs/* @@ -27,5 +26,5 @@ CHANGELOG.md PULL_REQUEST_TEMPLATE.md -# Don't bother formatting js blocks since we're getting rid of them -/blocks/*.js +# Don't bother formatting JavaScript files we're about to migrate: +/generators/**/*.js diff --git a/generators/javascript.ts b/generators/javascript.ts index e1d1a7481c9..079c2f793a4 100644 --- a/generators/javascript.ts +++ b/generators/javascript.ts @@ -33,8 +33,15 @@ export const javascriptGenerator = new JavascriptGenerator(); // Install per-block-type generator functions: const generators: typeof javascriptGenerator.forBlock = { - ...colour, ...lists, ...logic, ...loops, ...math, - ...procedures, ...text, ...variables, ...variablesDynamic + ...colour, + ...lists, + ...logic, + ...loops, + ...math, + ...procedures, + ...text, + ...variables, + ...variablesDynamic, }; for (const name in generators) { javascriptGenerator.forBlock[name] = generators[name]; diff --git a/generators/javascript/colour.ts b/generators/javascript/colour.ts index e459f564725..3e7089cd769 100644 --- a/generators/javascript/colour.ts +++ b/generators/javascript/colour.ts @@ -14,33 +14,44 @@ import type {Block} from '../../core/block.js'; import type {JavascriptGenerator} from './javascript_generator.js'; import {Order} from './javascript_generator.js'; - -export function colour_picker(block: Block, generator: JavascriptGenerator): [string, Order] { +export function colour_picker( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Colour picker. const code = generator.quote_(block.getFieldValue('COLOUR')); return [code, Order.ATOMIC]; -}; +} -export function colour_random(block: Block, generator: JavascriptGenerator): [string, Order] { +export function colour_random( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Generate a random colour. - const functionName = generator.provideFunction_('colourRandom', ` + const functionName = generator.provideFunction_( + 'colourRandom', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}() { var num = Math.floor(Math.random() * Math.pow(2, 24)); return '#' + ('00000' + num.toString(16)).substr(-6); } -`); +`, + ); const code = functionName + '()'; return [code, Order.FUNCTION_CALL]; -}; +} -export function colour_rgb(block: Block, generator: JavascriptGenerator): [string, Order] { +export function colour_rgb( + block: Block, + generator: JavascriptGenerator, +): [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_('colourRgb', ` + const green = generator.valueToCode(block, 'GREEN', Order.NONE) || 0; + const blue = generator.valueToCode(block, 'BLUE', Order.NONE) || 0; + const functionName = generator.provideFunction_( + 'colourRgb', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(r, g, b) { r = Math.max(Math.min(Number(r), 100), 0) * 2.55; g = Math.max(Math.min(Number(g), 100), 0) * 2.55; @@ -50,20 +61,23 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(r, g, b) { b = ('0' + (Math.round(b) || 0).toString(16)).slice(-2); return '#' + r + g + b; } -`); +`, + ); const code = functionName + '(' + red + ', ' + green + ', ' + blue + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function colour_blend(block: Block, generator: JavascriptGenerator): [string, Order] { +export function colour_blend( + block: Block, + generator: JavascriptGenerator, +): [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 ratio = - generator.valueToCode(block, 'RATIO', Order.NONE) || 0.5; - const functionName = generator.provideFunction_('colourBlend', ` + 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_( + 'colourBlend', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(c1, c2, ratio) { ratio = Math.max(Math.min(Number(ratio), 1), 0); var r1 = parseInt(c1.substring(1, 3), 16); @@ -80,7 +94,8 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(c1, c2, ratio) { b = ('0' + (b || 0).toString(16)).slice(-2); return '#' + r + g + b; } -`); +`, + ); const code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')'; return [code, Order.FUNCTION_CALL]; -}; +} diff --git a/generators/javascript/javascript_generator.ts b/generators/javascript/javascript_generator.ts index 370558424ce..7d9d9d03e41 100644 --- a/generators/javascript/javascript_generator.ts +++ b/generators/javascript/javascript_generator.ts @@ -19,11 +19,11 @@ import {Names, NameType} from '../../core/names.js'; import type {Workspace} from '../../core/workspace.js'; import {inputTypes} from '../../core/inputs/input_types.js'; - /** * Order of operation ENUMs. * https://developer.mozilla.org/en/JavaScript/Reference/Operators/Operator_Precedence */ +// prettier-ignore export enum Order { ATOMIC = 0, // 0 "" ... NEW = 1.1, // new @@ -60,7 +60,7 @@ export enum Order { YIELD = 17, // yield COMMA = 18, // , NONE = 99, // (...) -}; +} /** * JavaScript code generator class. @@ -93,7 +93,7 @@ export class JavascriptGenerator extends CodeGenerator { // a && (b && c) -> a && b && c [Order.LOGICAL_AND, Order.LOGICAL_AND], // a || (b || c) -> a || b || c - [Order.LOGICAL_OR, Order.LOGICAL_OR] + [Order.LOGICAL_OR, Order.LOGICAL_OR], ]; /** @param name Name of language generator is for. */ @@ -126,8 +126,8 @@ export class JavascriptGenerator extends CodeGenerator { // this list is trivial. This is intended to prevent users from // accidentally clobbering a built-in object or function. this.addReservedWords( - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords - 'break,case,catch,class,const,continue,debugger,default,delete,do,' + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords + 'break,case,catch,class,const,continue,debugger,default,delete,do,' + 'else,export,extends,finally,for,function,if,import,in,instanceof,' + 'new,return,super,switch,this,throw,try,typeof,var,void,' + 'while,with,yield,' + @@ -139,7 +139,7 @@ export class JavascriptGenerator extends CodeGenerator { 'arguments,' + // Everything in the current environment (835 items in Chrome, // 104 in Node). - Object.getOwnPropertyNames(globalThis).join(',') + Object.getOwnPropertyNames(globalThis).join(','), ); } @@ -166,14 +166,16 @@ export class JavascriptGenerator extends CodeGenerator { const devVarList = Variables.allDeveloperVariables(workspace); for (let i = 0; i < devVarList.length; i++) { defvars.push( - this.nameDB_.getName(devVarList[i], NameType.DEVELOPER_VARIABLE)); + this.nameDB_.getName(devVarList[i], NameType.DEVELOPER_VARIABLE), + ); } // Add user variables, but only ones that are being used. const variables = Variables.allUsedVarModels(workspace); for (let i = 0; i < variables.length; i++) { defvars.push( - this.nameDB_.getName(variables[i].getId(), NameType.VARIABLE)); + this.nameDB_.getName(variables[i].getId(), NameType.VARIABLE), + ); } // Declare all of the variables. @@ -221,10 +223,11 @@ export class JavascriptGenerator extends CodeGenerator { quote_(string: string): string { // Can't use goog.string.quote since Google's style guide recommends // JS string literals use single quotes. - string = string.replace(/\\/g, '\\\\') - .replace(/\n/g, '\\\n') - .replace(/'/g, '\\\''); - return '\'' + string + '\''; + string = string + .replace(/\\/g, '\\\\') + .replace(/\n/g, '\\\n') + .replace(/'/g, "\\'"); + return "'" + string + "'"; } /** @@ -237,7 +240,7 @@ export class JavascriptGenerator extends CodeGenerator { // Can't use goog.string.quote since Google's style guide recommends // JS string literals use single quotes. const lines = string.split(/\n/g).map(this.quote_); - return lines.join(' + \'\\n\' +\n'); + return lines.join(" + '\\n' +\n"); } /** @@ -276,7 +279,7 @@ export class JavascriptGenerator extends CodeGenerator { } } const nextBlock = - block.nextConnection && block.nextConnection.targetBlock(); + block.nextConnection && block.nextConnection.targetBlock(); const nextCode = thisOnly ? '' : this.blockToCode(nextBlock); return commentCode + code + nextCode; } @@ -293,7 +296,13 @@ export class JavascriptGenerator extends CodeGenerator { * @param order The highest order acting on this value. * @returns The adjusted value. */ - getAdjusted(block: Block, atId: string, delta = 0, negate = false, order = Order.NONE): string|number { + getAdjusted( + block: Block, + atId: string, + delta = 0, + negate = false, + order = Order.NONE, + ): string | number { if (block.workspace.options.oneBasedIndex) { delta--; } diff --git a/generators/javascript/lists.ts b/generators/javascript/lists.ts index 8a513ced6ff..6283f1d274b 100644 --- a/generators/javascript/lists.ts +++ b/generators/javascript/lists.ts @@ -17,28 +17,36 @@ import type {JavascriptGenerator} from './javascript_generator.js'; import {NameType} from '../../core/names.js'; import {Order} from './javascript_generator.js'; - -export function lists_create_empty(block: Block, generator: JavascriptGenerator): [string, Order] { +export function lists_create_empty( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Create an empty list. return ['[]', Order.ATOMIC]; -}; +} -export function lists_create_with(block: Block, generator: JavascriptGenerator): [string, Order] { +export function lists_create_with( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { const createWithBlock = block as CreateWithBlock; // Create a list with any number of elements of any type. const elements = new Array(createWithBlock.itemCount_); for (let i = 0; i < createWithBlock.itemCount_; i++) { - elements[i] = - generator.valueToCode(block, 'ADD' + i, Order.NONE) || - 'null'; + elements[i] = generator.valueToCode(block, 'ADD' + i, Order.NONE) || 'null'; } const code = '[' + elements.join(', ') + ']'; return [code, Order.ATOMIC]; -}; +} -export function lists_repeat(block: Block, generator: JavascriptGenerator): [string, Order] { +export function lists_repeat( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Create a list with one element repeated. - const functionName = generator.provideFunction_('listsRepeat', ` + const functionName = generator.provideFunction_( + 'listsRepeat', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(value, n) { var array = []; for (var i = 0; i < n; i++) { @@ -46,56 +54,61 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(value, n) { } return array; } -`); - const element = - generator.valueToCode(block, 'ITEM', Order.NONE) || 'null'; - const repeatCount = - generator.valueToCode(block, 'NUM', Order.NONE) || '0'; +`, + ); + 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: Block, generator: JavascriptGenerator): [string, Order] { +export function lists_length( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // String or array length. - const list = - generator.valueToCode(block, 'VALUE', Order.MEMBER) || '[]'; + const list = generator.valueToCode(block, 'VALUE', Order.MEMBER) || '[]'; return [list + '.length', Order.MEMBER]; -}; +} -export function lists_isEmpty(block: Block, generator: JavascriptGenerator): [string, Order] { +export function lists_isEmpty( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Is the string null or array empty? - const list = - generator.valueToCode(block, 'VALUE', Order.MEMBER) || '[]'; + const list = generator.valueToCode(block, 'VALUE', Order.MEMBER) || '[]'; return ['!' + list + '.length', Order.LOGICAL_NOT]; -}; +} -export function lists_indexOf(block: Block, generator: JavascriptGenerator): [string, Order] { +export function lists_indexOf( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Find an item in the list. const operator = - block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; - const item = - generator.valueToCode(block, 'FIND', Order.NONE) || "''"; - const list = - generator.valueToCode(block, 'VALUE', Order.MEMBER) || '[]'; + block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; + const item = generator.valueToCode(block, 'FIND', Order.NONE) || "''"; + const list = generator.valueToCode(block, 'VALUE', Order.MEMBER) || '[]'; const code = list + '.' + operator + '(' + item + ')'; if (block.workspace.options.oneBasedIndex) { return [code + ' + 1', Order.ADDITION]; } return [code, Order.FUNCTION_CALL]; -}; +} -export function lists_getIndex(block: Block, generator: JavascriptGenerator): [string, Order] | string { +export function lists_getIndex( + block: Block, + generator: JavascriptGenerator, +): [string, Order] | string { // Get element at index. // Note: Until January 2013 this block did not have MODE or WHERE inputs. const mode = block.getFieldValue('MODE') || 'GET'; const where = block.getFieldValue('WHERE') || 'FROM_START'; - const listOrder = - (where === 'RANDOM') ? Order.NONE : Order.MEMBER; - const list = - generator.valueToCode(block, 'VALUE', listOrder) || '[]'; + const listOrder = where === 'RANDOM' ? Order.NONE : Order.MEMBER; + const list = generator.valueToCode(block, 'VALUE', listOrder) || '[]'; switch (where) { - case ('FIRST'): + case 'FIRST': if (mode === 'GET') { const code = list + '[0]'; return [code, Order.MEMBER]; @@ -106,7 +119,7 @@ export function lists_getIndex(block: Block, generator: JavascriptGenerator): [s return list + '.shift();\n'; } break; - case ('LAST'): + case 'LAST': if (mode === 'GET') { const code = list + '.slice(-1)[0]'; return [code, Order.MEMBER]; @@ -117,7 +130,7 @@ export function lists_getIndex(block: Block, generator: JavascriptGenerator): [s return list + '.pop();\n'; } break; - case ('FROM_START'): { + case 'FROM_START': { const at = generator.getAdjusted(block, 'AT'); if (mode === 'GET') { const code = list + '[' + at + ']'; @@ -130,7 +143,7 @@ export function lists_getIndex(block: Block, generator: JavascriptGenerator): [s } break; } - case ('FROM_END'): { + case 'FROM_END': { const at = generator.getAdjusted(block, 'AT', 1, true); if (mode === 'GET') { const code = list + '.slice(' + at + ')[0]'; @@ -143,9 +156,10 @@ export function lists_getIndex(block: Block, generator: JavascriptGenerator): [s } break; } - case ('RANDOM'): { - const functionName = - generator.provideFunction_('listsGetRandomItem', ` + case 'RANDOM': { + const functionName = generator.provideFunction_( + 'listsGetRandomItem', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(list, remove) { var x = Math.floor(Math.random() * list.length); if (remove) { @@ -154,7 +168,8 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(list, remove) { return list[x]; } } -`); +`, + ); const code = functionName + '(' + list + ', ' + (mode !== 'GET') + ')'; if (mode === 'GET' || mode === 'GET_REMOVE') { return [code, Order.FUNCTION_CALL]; @@ -165,40 +180,38 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(list, remove) { } } throw Error('Unhandled combination (lists_getIndex).'); -}; +} export function lists_setIndex(block: Block, generator: JavascriptGenerator) { // Set element at index. // Note: Until February 2013 this block did not have MODE or WHERE inputs. - let list = - generator.valueToCode(block, 'LIST', Order.MEMBER) || '[]'; + let list = generator.valueToCode(block, 'LIST', Order.MEMBER) || '[]'; 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'. function cacheList() { if (list.match(/^\w+$/)) { return ''; } - const listVar = - generator.nameDB_!.getDistinctName( - 'tmpList', NameType.VARIABLE)!; + const listVar = generator.nameDB_!.getDistinctName( + 'tmpList', + NameType.VARIABLE, + )!; const code = 'var ' + listVar + ' = ' + list + ';\n'; list = listVar; return code; } switch (where) { - case ('FIRST'): + case 'FIRST': if (mode === 'SET') { return list + '[0] = ' + value + ';\n'; } else if (mode === 'INSERT') { return list + '.unshift(' + value + ');\n'; } break; - case ('LAST'): + case 'LAST': if (mode === 'SET') { let code = cacheList(); code += list + '[' + list + '.length - 1] = ' + value + ';\n'; @@ -207,7 +220,7 @@ export function lists_setIndex(block: Block, generator: JavascriptGenerator) { return list + '.push(' + value + ');\n'; } break; - case ('FROM_START'): { + case 'FROM_START': { const at = generator.getAdjusted(block, 'AT'); if (mode === 'SET') { return list + '[' + at + '] = ' + value + ';\n'; @@ -216,27 +229,40 @@ export function lists_setIndex(block: Block, generator: JavascriptGenerator) { } break; } - case ('FROM_END'): { + case 'FROM_END': { const at = generator.getAdjusted( - block, 'AT', 1, false, Order.SUBTRACTION); + block, + 'AT', + 1, + false, + Order.SUBTRACTION, + ); let code = cacheList(); if (mode === 'SET') { code += list + '[' + list + '.length - ' + at + '] = ' + value + ';\n'; return code; } else if (mode === 'INSERT') { - code += list + '.splice(' + list + '.length - ' + at + ', 0, ' + value + - ');\n'; + code += + list + + '.splice(' + + list + + '.length - ' + + at + + ', 0, ' + + value + + ');\n'; return code; } break; } - case ('RANDOM'): { + case 'RANDOM': { let code = cacheList(); - const xVar = - generator.nameDB_!.getDistinctName( - 'tmpX', NameType.VARIABLE); - code += 'var ' + xVar + ' = Math.floor(Math.random() * ' + list + - '.length);\n'; + const xVar = generator.nameDB_!.getDistinctName( + 'tmpX', + NameType.VARIABLE, + ); + code += + 'var ' + xVar + ' = Math.floor(Math.random() * ' + list + '.length);\n'; if (mode === 'SET') { code += list + '[' + xVar + '] = ' + value + ';\n'; return code; @@ -248,7 +274,7 @@ export function lists_setIndex(block: Block, generator: JavascriptGenerator) { } } throw Error('Unhandled combination (lists_setIndex).'); -}; +} /** * Returns an expression calculating the index into a list. @@ -257,7 +283,11 @@ export function lists_setIndex(block: Block, generator: JavascriptGenerator) { * @param opt_at The optional offset when indexing from start/end. * @returns Index expression. */ -const getSubstringIndex = function(listName: string, where: string, opt_at?: string): string | undefined { +const getSubstringIndex = function ( + listName: string, + where: string, + opt_at?: string, +): string | undefined { if (where === 'FIRST') { return '0'; } else if (where === 'FROM_END') { @@ -269,7 +299,10 @@ const getSubstringIndex = function(listName: string, where: string, opt_at?: str } }; -export function lists_getSublist(block: Block, generator: JavascriptGenerator): [string, Order] { +export function lists_getSublist( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Dictionary of WHEREn field choices and their CamelCase equivalents. const wherePascalCase = { 'FIRST': 'First', @@ -279,16 +312,16 @@ export function lists_getSublist(block: Block, generator: JavascriptGenerator): }; type WhereOption = keyof typeof wherePascalCase; // Get sublist. - const list = - generator.valueToCode(block, 'LIST', Order.MEMBER) || '[]'; + const list = generator.valueToCode(block, 'LIST', Order.MEMBER) || '[]'; const where1 = block.getFieldValue('WHERE1') as WhereOption; const where2 = block.getFieldValue('WHERE2') as WhereOption; let code; if (where1 === 'FIRST' && where2 === 'LAST') { code = list + '.slice(0)'; } 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 variable or doesn't require a call for length, don't // generate a helper function. let at1; @@ -297,8 +330,7 @@ export function lists_getSublist(block: Block, generator: JavascriptGenerator): 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 = list + '.length - ' + at1; break; case 'FIRST': @@ -313,8 +345,7 @@ export function lists_getSublist(block: Block, generator: JavascriptGenerator): at2 = generator.getAdjusted(block, 'AT2', 1); break; case 'FROM_END': - at2 = generator.getAdjusted( - block, 'AT2', 0, false, Order.SUBTRACTION); + at2 = generator.getAdjusted(block, 'AT2', 0, false, Order.SUBTRACTION); at2 = list + '.length - ' + at2; break; case 'LAST': @@ -330,36 +361,46 @@ export function lists_getSublist(block: Block, generator: JavascriptGenerator): // The value for 'FROM_END' and'FROM_START' depends on `at` so // we add it as a parameter. const at1Param = - (where1 === 'FROM_END' || where1 === 'FROM_START') ? ', at1' : ''; + where1 === 'FROM_END' || where1 === 'FROM_START' ? ', at1' : ''; const at2Param = - (where2 === 'FROM_END' || where2 === 'FROM_START') ? ', at2' : ''; + where2 === 'FROM_END' || where2 === 'FROM_START' ? ', at2' : ''; const functionName = generator.provideFunction_( - 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], ` -function ${generator.FUNCTION_NAME_PLACEHOLDER_}(sequence${at1Param}${at2Param}) { + 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], + ` +function ${ + generator.FUNCTION_NAME_PLACEHOLDER_ + }(sequence${at1Param}${at2Param}) { var start = ${getSubstringIndex('sequence', where1, 'at1')}; var end = ${getSubstringIndex('sequence', where2, 'at2')} + 1; return sequence.slice(start, end); } -`); - code = functionName + '(' + list + - // The value for 'FROM_END' and 'FROM_START' depends on `at` so we - // pass it. - ((where1 === 'FROM_END' || where1 === 'FROM_START') ? ', ' + at1 : '') + - ((where2 === 'FROM_END' || where2 === 'FROM_START') ? ', ' + at2 : '') + - ')'; +`, + ); + code = + functionName + + '(' + + list + + // The value for 'FROM_END' and 'FROM_START' depends on `at` so we + // pass it. + (where1 === 'FROM_END' || where1 === 'FROM_START' ? ', ' + at1 : '') + + (where2 === 'FROM_END' || where2 === 'FROM_START' ? ', ' + at2 : '') + + ')'; } return [code, Order.FUNCTION_CALL]; -}; +} -export function lists_sort(block: Block, generator: JavascriptGenerator): [string, Order] { +export function lists_sort( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Block for sorting a list. const list = - generator.valueToCode(block, 'LIST', Order.FUNCTION_CALL) || - '[]'; + generator.valueToCode(block, 'LIST', Order.FUNCTION_CALL) || '[]'; const direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; const type = block.getFieldValue('TYPE'); - const getCompareFunctionName = - generator.provideFunction_('listsGetSortCompare', ` + const getCompareFunctionName = generator.provideFunction_( + 'listsGetSortCompare', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(type, direction) { var compareFuncs = { 'NUMERIC': function(a, b) { @@ -372,19 +413,28 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(type, direction) { var compare = compareFuncs[type]; return function(a, b) { return compare(a, b) * direction; }; } - `); + `, + ); return [ - list + '.slice().sort(' + getCompareFunctionName + '("' + type + '", ' + - direction + '))', - Order.FUNCTION_CALL + list + + '.slice().sort(' + + getCompareFunctionName + + '("' + + type + + '", ' + + direction + + '))', + Order.FUNCTION_CALL, ]; -}; +} -export function lists_split(block: Block, generator: JavascriptGenerator): [string, Order] { +export function lists_split( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Block for splitting text into a list, or joining a list into text. let input = generator.valueToCode(block, 'INPUT', Order.MEMBER); - const delimiter = - generator.valueToCode(block, 'DELIM', Order.NONE) || "''"; + const delimiter = generator.valueToCode(block, 'DELIM', Order.NONE) || "''"; const mode = block.getFieldValue('MODE'); let functionName; if (mode === 'SPLIT') { @@ -402,13 +452,15 @@ export function lists_split(block: Block, generator: JavascriptGenerator): [stri } const code = input + '.' + functionName + '(' + delimiter + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function lists_reverse(block: Block, generator: JavascriptGenerator): [string, Order] { +export function lists_reverse( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Block for reversing a list. const list = - generator.valueToCode(block, 'LIST', Order.FUNCTION_CALL) || - '[]'; + generator.valueToCode(block, 'LIST', Order.FUNCTION_CALL) || '[]'; const code = list + '.slice().reverse()'; return [code, Order.FUNCTION_CALL]; -}; +} diff --git a/generators/javascript/logic.ts b/generators/javascript/logic.ts index fe90b1da109..36604ea7d12 100644 --- a/generators/javascript/logic.ts +++ b/generators/javascript/logic.ts @@ -14,69 +14,81 @@ import type {Block} from '../../core/block.js'; import type {JavascriptGenerator} from './javascript_generator.js'; import {Order} from './javascript_generator.js'; - export function controls_if(block: Block, generator: JavascriptGenerator) { // If/elseif/else condition. let n = 0; let code = ''; if (generator.STATEMENT_PREFIX) { // Automatic prefix insertion is switched off for this block. Add manually. - code += generator.injectId( - generator.STATEMENT_PREFIX, block); + code += generator.injectId(generator.STATEMENT_PREFIX, block); } do { const conditionCode = - generator.valueToCode(block, 'IF' + n, Order.NONE) || - 'false'; + generator.valueToCode(block, 'IF' + n, Order.NONE) || 'false'; let branchCode = generator.statementToCode(block, 'DO' + n); if (generator.STATEMENT_SUFFIX) { - branchCode = generator.prefixLines( - generator.injectId( - generator.STATEMENT_SUFFIX, block), - generator.INDENT) + - branchCode; + 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)); if (block.getInput('ELSE') || generator.STATEMENT_SUFFIX) { let branchCode = generator.statementToCode(block, 'ELSE'); if (generator.STATEMENT_SUFFIX) { - branchCode = generator.prefixLines( - generator.injectId( - generator.STATEMENT_SUFFIX, block), - generator.INDENT) + - branchCode; + 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: Block, generator: JavascriptGenerator): [string, Order] { +export function logic_compare( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Dictionary of OP comparison operators and their implementations. - const OPERATORS = - {'EQ': '==', 'NEQ': '!=', 'LT': '<', 'LTE': '<=', 'GT': '>', 'GTE': '>='}; + 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 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: Block, generator: JavascriptGenerator): [string, Order] { +export function logic_operation( + block: Block, + generator: JavascriptGenerator, +): [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) { @@ -85,7 +97,7 @@ export function logic_operation(block: Block, generator: JavascriptGenerator): [ 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; } @@ -95,39 +107,47 @@ export function logic_operation(block: Block, generator: JavascriptGenerator): [ } const code = argument0 + ' ' + operator + ' ' + argument1; return [code, order]; -}; +} -export function logic_negate(block: Block, generator: JavascriptGenerator): [string, Order] { +export function logic_negate( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Negation. const order = Order.LOGICAL_NOT; - const argument0 = - generator.valueToCode(block, 'BOOL', order) || 'true'; + const argument0 = generator.valueToCode(block, 'BOOL', order) || 'true'; const code = '!' + argument0; return [code, order]; -}; +} -export function logic_boolean(block: Block, generator: JavascriptGenerator): [string, Order] { +export function logic_boolean( + block: Block, + generator: JavascriptGenerator, +): [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: Block, generator: JavascriptGenerator): [string, Order] { +export function logic_null( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Null data type. return ['null', Order.ATOMIC]; -}; +} -export function logic_ternary(block: Block, generator: JavascriptGenerator): [string, Order] { +export function logic_ternary( + block: Block, + generator: JavascriptGenerator, +): [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/javascript/loops.ts b/generators/javascript/loops.ts index 5eb5c39a748..a5dd8635731 100644 --- a/generators/javascript/loops.ts +++ b/generators/javascript/loops.ts @@ -17,8 +17,10 @@ import type {JavascriptGenerator} from './javascript_generator.js'; import {NameType} from '../../core/names.js'; import {Order} from './javascript_generator.js'; - -export function controls_repeat_ext(block: Block, generator: JavascriptGenerator) { +export function controls_repeat_ext( + block: Block, + generator: JavascriptGenerator, +) { // Repeat n times. let repeats; if (block.getField('TIMES')) { @@ -26,65 +28,88 @@ export function controls_repeat_ext(block: Block, generator: JavascriptGenerator repeats = String(Number(block.getFieldValue('TIMES'))); } else { // External number. - repeats = - generator.valueToCode(block, 'TIMES', Order.ASSIGNMENT) || - '0'; + repeats = generator.valueToCode(block, 'TIMES', Order.ASSIGNMENT) || '0'; } 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 += 'var ' + endVar + ' = ' + repeats + ';\n'; } - code += 'for (var ' + loopVar + ' = 0; ' + loopVar + ' < ' + endVar + '; ' + - loopVar + '++) {\n' + branch + '}\n'; + code += + 'for (var ' + + loopVar + + ' = 0; ' + + loopVar + + ' < ' + + endVar + + '; ' + + loopVar + + '++) {\n' + + branch + + '}\n'; return code; -}; +} export const controls_repeat = controls_repeat_ext; -export function controls_whileUntil(block: Block, generator: JavascriptGenerator) { +export function controls_whileUntil( + block: Block, + generator: JavascriptGenerator, +) { // 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: Block, generator: JavascriptGenerator) { // 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 ? '++' : '--'; @@ -98,19 +123,25 @@ export function controls_for(block: Block, generator: JavascriptGenerator) { let startVar = argument0; if (!argument0.match(/^\w+$/) && !stringUtils.isNumber(argument0)) { startVar = generator.nameDB_!.getDistinctName( - variable0 + '_start', NameType.VARIABLE); + variable0 + '_start', + NameType.VARIABLE, + ); code += 'var ' + startVar + ' = ' + argument0 + ';\n'; } let endVar = argument1; if (!argument1.match(/^\w+$/) && !stringUtils.isNumber(argument1)) { endVar = generator.nameDB_!.getDistinctName( - variable0 + '_end', NameType.VARIABLE); + variable0 + '_end', + NameType.VARIABLE, + ); code += 'var ' + 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); + variable0 + '_inc', + NameType.VARIABLE, + ); code += 'var ' + incVar + ' = '; if (stringUtils.isNumber(increment)) { code += Math.abs(Number(increment)) + ';\n'; @@ -120,21 +151,37 @@ export function controls_for(block: Block, generator: JavascriptGenerator) { 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: Block, generator: JavascriptGenerator) { // 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 = ''; @@ -142,30 +189,42 @@ export function controls_forEach(block: Block, generator: JavascriptGenerator) { let listVar = argument0; if (!argument0.match(/^\w+$/)) { listVar = generator.nameDB_!.getDistinctName( - variable0 + '_list', NameType.VARIABLE); + variable0 + '_list', + NameType.VARIABLE, + ); code += 'var ' + listVar + ' = ' + argument0 + ';\n'; } const indexVar = generator.nameDB_!.getDistinctName( - variable0 + '_index', NameType.VARIABLE); - branch = generator.INDENT + variable0 + ' = ' + listVar + - '[' + indexVar + '];\n' + branch; + variable0 + '_index', + NameType.VARIABLE, + ); + branch = + generator.INDENT + + variable0 + + ' = ' + + listVar + + '[' + + indexVar + + '];\n' + + branch; code += 'for (var ' + indexVar + ' in ' + listVar + ') {\n' + branch + '}\n'; return code; -}; +} -export function controls_flow_statements(block: Block, generator: JavascriptGenerator) { +export function controls_flow_statements( + block: Block, + generator: JavascriptGenerator, +) { // Flow statements: continue, break. let xfix = ''; if (generator.STATEMENT_PREFIX) { // Automatic prefix insertion is switched off for this block. Add manually. - xfix += generator.injectId( - generator.STATEMENT_PREFIX, block); + xfix += generator.injectId(generator.STATEMENT_PREFIX, block); } if (generator.STATEMENT_SUFFIX) { // Inject any statement suffix here since the regular one at the end // will not get executed if the break/continue is triggered. - xfix += generator.injectId( - generator.STATEMENT_SUFFIX, block); + xfix += generator.injectId(generator.STATEMENT_SUFFIX, block); } if (generator.STATEMENT_PREFIX) { const loop = (block as ControlFlowInLoopBlock).getSurroundLoop(); @@ -173,8 +232,7 @@ export function controls_flow_statements(block: Block, generator: JavascriptGene // Inject loop's statement prefix here since the regular one at the end // of the loop will not get executed if 'continue' is triggered. // In the case of 'break', a prefix is needed due to the loop's suffix. - xfix += generator.injectId( - generator.STATEMENT_PREFIX, loop); + xfix += generator.injectId(generator.STATEMENT_PREFIX, loop); } } switch (block.getFieldValue('FLOW')) { @@ -184,4 +242,4 @@ export function controls_flow_statements(block: Block, generator: JavascriptGene return xfix + 'continue;\n'; } throw Error('Unknown flow statement.'); -}; +} diff --git a/generators/javascript/math.ts b/generators/javascript/math.ts index 726fd623862..c05e817cf3e 100644 --- a/generators/javascript/math.ts +++ b/generators/javascript/math.ts @@ -15,23 +15,27 @@ import type {Block} from '../../core/block.js'; import type {JavascriptGenerator} from './javascript_generator.js'; import {Order} from './javascript_generator.js'; - -export function math_number(block: Block, generator: JavascriptGenerator): [string, Order] { +export function math_number( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Numeric value. const number = Number(block.getFieldValue('NUM')); - const order = number >= 0 ? Order.ATOMIC : - Order.UNARY_NEGATION; + const order = number >= 0 ? Order.ATOMIC : Order.UNARY_NEGATION; return [String(number), order]; -}; +} -export function math_arithmetic(block: Block, generator: JavascriptGenerator): [string, Order] { +export function math_arithmetic( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Basic arithmetic operators, and power. const OPERATORS: Record = { 'ADD': [' + ', Order.ADDITION], 'MINUS': [' - ', Order.SUBTRACTION], 'MULTIPLY': [' * ', Order.MULTIPLICATION], 'DIVIDE': [' / ', Order.DIVISION], - 'POWER': [null, Order.NONE], // Handle power separately. + 'POWER': [null, Order.NONE], // Handle power separately. }; type OperatorOption = keyof typeof OPERATORS; const tuple = OPERATORS[block.getFieldValue('OP') as OperatorOption]; @@ -47,17 +51,19 @@ export function math_arithmetic(block: Block, generator: JavascriptGenerator): [ } code = argument0 + operator + argument1; return [code, order]; -}; +} -export function math_single(block: Block, generator: JavascriptGenerator): [string, Order] { +export function math_single( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Math operators with single operand. const operator = block.getFieldValue('OP'); let code; let arg; if (operator === 'NEG') { // Negation is a special case given its different operator precedence. - arg = generator.valueToCode(block, 'NUM', - Order.UNARY_NEGATION) || '0'; + arg = generator.valueToCode(block, 'NUM', Order.UNARY_NEGATION) || '0'; if (arg[0] === '-') { // --3 is not legal in JS. arg = ' ' + arg; @@ -66,11 +72,9 @@ export function math_single(block: Block, generator: JavascriptGenerator): [stri return [code, Order.UNARY_NEGATION]; } if (operator === 'SIN' || operator === 'COS' || operator === 'TAN') { - arg = generator.valueToCode(block, 'NUM', - Order.DIVISION) || '0'; + arg = generator.valueToCode(block, 'NUM', Order.DIVISION) || '0'; } else { - arg = generator.valueToCode(block, 'NUM', - Order.NONE) || '0'; + arg = generator.valueToCode(block, 'NUM', Order.NONE) || '0'; } // First, handle cases which generate values that don't need parentheses // wrapping the code. @@ -131,9 +135,12 @@ export function math_single(block: Block, generator: JavascriptGenerator): [stri throw Error('Unknown math operator: ' + operator); } return [code, Order.DIVISION]; -}; +} -export function math_constant(block: Block, generator: JavascriptGenerator): [string, Order] { +export function math_constant( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY. const CONSTANTS: Record = { 'PI': ['Math.PI', Order.MEMBER], @@ -143,22 +150,22 @@ export function math_constant(block: Block, generator: JavascriptGenerator): [st 'SQRT1_2': ['Math.SQRT1_2', Order.MEMBER], 'INFINITY': ['Infinity', Order.ATOMIC], }; - type ConstantOption = keyof typeof CONSTANTS + type ConstantOption = keyof typeof CONSTANTS; return CONSTANTS[block.getFieldValue('CONSTANT') as ConstantOption]; -}; +} -export function math_number_property(block: Block, generator: JavascriptGenerator): [string, Order] { +export function math_number_property( + block: Block, + generator: JavascriptGenerator, +): [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: Record = { 'EVEN': [' % 2 === 0', Order.MODULUS, Order.EQUALITY], 'ODD': [' % 2 === 1', Order.MODULUS, Order.EQUALITY], - 'WHOLE': [' % 1 === 0', Order.MODULUS, - Order.EQUALITY], - 'POSITIVE': [' > 0', Order.RELATIONAL, - Order.RELATIONAL], - 'NEGATIVE': [' < 0', Order.RELATIONAL, - Order.RELATIONAL], + 'WHOLE': [' % 1 === 0', Order.MODULUS, Order.EQUALITY], + 'POSITIVE': [' > 0', Order.RELATIONAL, Order.RELATIONAL], + 'NEGATIVE': [' < 0', Order.RELATIONAL, Order.RELATIONAL], 'DIVISIBLE_BY': [null, Order.MODULUS, Order.EQUALITY], 'PRIME': [null, Order.NONE, Order.FUNCTION_CALL], }; @@ -166,12 +173,13 @@ export function math_number_property(block: Block, generator: JavascriptGenerato const dropdownProperty = block.getFieldValue('PROPERTY') as PropertyOption; const [suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty]; const numberToCheck = - generator.valueToCode(block, 'NUMBER_TO_CHECK', inputOrder) || - '0'; + 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_('mathIsPrime', ` + const functionName = generator.provideFunction_( + 'mathIsPrime', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(n) { // https://en.wikipedia.org/wiki/Primality_test#Naive_methods if (n == 2 || n == 3) { @@ -190,68 +198,81 @@ 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'; code = numberToCheck + ' % ' + divisor + ' === 0'; } else { code = numberToCheck + suffix; } return [code, outputOrder]; -}; +} export function math_change(block: Block, generator: JavascriptGenerator) { // Add to a variable in place. - const argument0 = generator.valueToCode(block, 'DELTA', - Order.ADDITION) || '0'; + const argument0 = + generator.valueToCode(block, 'DELTA', Order.ADDITION) || '0'; const varName = generator.getVariableName(block.getFieldValue('VAR')); - return varName + ' = (typeof ' + varName + ' === \'number\' ? ' + varName + - ' : 0) + ' + argument0 + ';\n'; -}; + return ( + varName + + ' = (typeof ' + + varName + + " === 'number' ? " + + varName + + ' : 0) + ' + + 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: Block, generator: JavascriptGenerator): [string, Order] { +export function math_on_list( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Math functions for lists. const func = block.getFieldValue('OP'); let list; let code; switch (func) { case 'SUM': - list = generator.valueToCode(block, 'LIST', - Order.MEMBER) || '[]'; + list = generator.valueToCode(block, 'LIST', Order.MEMBER) || '[]'; code = list + '.reduce(function(x, y) {return x + y;}, 0)'; break; case 'MIN': - list = generator.valueToCode(block, 'LIST', - Order.NONE) || '[]'; + list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; code = 'Math.min.apply(null, ' + list + ')'; break; case 'MAX': - list = generator.valueToCode(block, 'LIST', - Order.NONE) || '[]'; + list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; code = 'Math.max.apply(null, ' + list + ')'; break; case 'AVERAGE': { // mathMean([null,null,1,3]) === 2.0. - const functionName = generator.provideFunction_('mathMean', ` + const functionName = generator.provideFunction_( + 'mathMean', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(myList) { return myList.reduce(function(x, y) {return x + y;}, 0) / myList.length; } -`); - list = generator.valueToCode(block, 'LIST', - Order.NONE) || '[]'; +`, + ); + list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; } case 'MEDIAN': { // mathMedian([null,null,1,3]) === 2.0. - const functionName = generator.provideFunction_('mathMedian', ` + const functionName = generator.provideFunction_( + 'mathMedian', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(myList) { var localList = myList.filter(function (x) {return typeof x === 'number';}); if (!localList.length) return null; @@ -262,9 +283,9 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(myList) { return localList[(localList.length - 1) / 2]; } } -`); - list = generator.valueToCode(block, 'LIST', - Order.NONE) || '[]'; +`, + ); + list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; } @@ -272,7 +293,9 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(myList) { // 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_('mathModes', ` + const functionName = generator.provideFunction_( + 'mathModes', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(values) { var modes = []; var counts = []; @@ -301,15 +324,16 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(values) { } return modes; } -`); - list = generator.valueToCode(block, 'LIST', - Order.NONE) || '[]'; +`, + ); + list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; } case 'STD_DEV': { - const functionName = - generator.provideFunction_('mathStandardDeviation', ` + const functionName = generator.provideFunction_( + 'mathStandardDeviation', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(numbers) { var n = numbers.length; if (!n) return null; @@ -321,22 +345,23 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(numbers) { variance = variance / n; return Math.sqrt(variance); } -`); - list = generator.valueToCode(block, 'LIST', - Order.NONE) || '[]'; +`, + ); + list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; } case 'RANDOM': { - const functionName = - generator.provideFunction_('mathRandomList', ` + const functionName = generator.provideFunction_( + 'mathRandomList', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(list) { var x = Math.floor(Math.random() * list.length); return list[x]; } -`); - list = generator.valueToCode(block, 'LIST', - Order.NONE) || '[]'; +`, + ); + list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; code = functionName + '(' + list + ')'; break; } @@ -344,38 +369,51 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(list) { throw Error('Unknown operator: ' + func); } return [code, Order.FUNCTION_CALL]; -}; +} -export function math_modulo(block: Block, generator: JavascriptGenerator): [string, Order] { +export function math_modulo( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Remainder computation. - const argument0 = generator.valueToCode(block, 'DIVIDEND', - Order.MODULUS) || '0'; - const argument1 = generator.valueToCode(block, 'DIVISOR', - Order.MODULUS) || '0'; + const argument0 = + generator.valueToCode(block, 'DIVIDEND', Order.MODULUS) || '0'; + const argument1 = + generator.valueToCode(block, 'DIVISOR', Order.MODULUS) || '0'; const code = argument0 + ' % ' + argument1; return [code, Order.MODULUS]; -}; +} -export function math_constrain(block: Block, generator: JavascriptGenerator): [string, Order] { +export function math_constrain( + block: Block, + generator: JavascriptGenerator, +): [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'; - const code = 'Math.min(Math.max(' + argument0 + ', ' + argument1 + '), ' + - argument2 + ')'; + 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'; + const code = + 'Math.min(Math.max(' + + argument0 + + ', ' + + argument1 + + '), ' + + argument2 + + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function math_random_int(block: Block, generator: JavascriptGenerator): [string, Order] { +export function math_random_int( + block: Block, + generator: JavascriptGenerator, +): [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_('mathRandomInt', ` + const argument0 = generator.valueToCode(block, 'FROM', Order.NONE) || '0'; + const argument1 = generator.valueToCode(block, 'TO', Order.NONE) || '0'; + const functionName = generator.provideFunction_( + 'mathRandomInt', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(a, b) { if (a > b) { // Swap a and b to ensure a is smaller. @@ -385,22 +423,29 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(a, b) { } return Math.floor(Math.random() * (b - a + 1) + a); } -`); +`, + ); const code = functionName + '(' + argument0 + ', ' + argument1 + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function math_random_float(block: Block, generator: JavascriptGenerator): [string, Order] { +export function math_random_float( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Random fraction between 0 and 1. return ['Math.random()', Order.FUNCTION_CALL]; -}; +} -export function math_atan2(block: Block, generator: JavascriptGenerator): [string, Order] { +export function math_atan2( + block: Block, + generator: JavascriptGenerator, +): [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 ['Math.atan2(' + argument1 + ', ' + argument0 + ') / Math.PI * 180', - Order.DIVISION]; -}; + const argument0 = generator.valueToCode(block, 'X', Order.NONE) || '0'; + const argument1 = generator.valueToCode(block, 'Y', Order.NONE) || '0'; + return [ + 'Math.atan2(' + argument1 + ', ' + argument0 + ') / Math.PI * 180', + Order.DIVISION, + ]; +} diff --git a/generators/javascript/procedures.ts b/generators/javascript/procedures.ts index f4e656e371b..f0b5c94f09a 100644 --- a/generators/javascript/procedures.ts +++ b/generators/javascript/procedures.ts @@ -15,18 +15,18 @@ import type {IfReturnBlock} from '../../blocks/procedures.js'; import type {JavascriptGenerator} from './javascript_generator.js'; import {Order} from './javascript_generator.js'; - -export function procedures_defreturn(block: Block, generator: JavascriptGenerator) { +export function procedures_defreturn( + block: Block, + generator: JavascriptGenerator, +) { // Define a procedure with a return value. const funcName = generator.getProcedureName(block.getFieldValue('NAME')); let xfix1 = ''; if (generator.STATEMENT_PREFIX) { - xfix1 += generator.injectId( - generator.STATEMENT_PREFIX, block); + xfix1 += generator.injectId(generator.STATEMENT_PREFIX, block); } if (generator.STATEMENT_SUFFIX) { - xfix1 += generator.injectId( - generator.STATEMENT_SUFFIX, block); + xfix1 += generator.injectId(generator.STATEMENT_SUFFIX, block); } if (xfix1) { xfix1 = generator.prefixLines(xfix1, generator.INDENT); @@ -34,13 +34,12 @@ export function procedures_defreturn(block: Block, generator: JavascriptGenerato 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) || ''; + let returnValue = generator.valueToCode(block, 'RETURN', Order.NONE) || ''; let xfix2 = ''; if (branch && returnValue) { // After executing the function body, revisit this block for the return. @@ -52,65 +51,83 @@ export function procedures_defreturn(block: Block, generator: JavascriptGenerato const args = []; const variables = block.getVars(); for (let i = 0; i < variables.length; i++) { - args[i] = - generator.getVariableName(variables[i]); + args[i] = generator.getVariableName(variables[i]); } - let code = 'function ' + funcName + '(' + args.join(', ') + ') {\n' + xfix1 + - loopTrap + branch + xfix2 + returnValue + '}'; + let code = + 'function ' + + funcName + + '(' + + args.join(', ') + + ') {\n' + + xfix1 + + loopTrap + + branch + + xfix2 + + returnValue + + '}'; code = generator.scrub_(block, code); // Add % so as not to collide with helper functions in definitions list. // 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: Block, generator: JavascriptGenerator): [string, Order] { +export function procedures_callreturn( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Call a procedure with a return value. const funcName = generator.getProcedureName(block.getFieldValue('NAME')); const args = []; const variables = block.getVars(); for (let i = 0; i < variables.length; i++) { - args[i] = generator.valueToCode(block, 'ARG' + i, Order.NONE) || - 'null'; + args[i] = generator.valueToCode(block, 'ARG' + i, Order.NONE) || 'null'; } const code = funcName + '(' + args.join(', ') + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function procedures_callnoreturn(block: Block, generator: JavascriptGenerator) { +export function procedures_callnoreturn( + block: Block, + generator: JavascriptGenerator, +) { // 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) as [string, Order]; + const tuple = generator.forBlock['procedures_callreturn']( + block, + generator, + ) as [string, Order]; return tuple[0] + ';\n'; -}; +} -export function procedures_ifreturn(block: Block, generator: JavascriptGenerator) { +export function procedures_ifreturn( + block: Block, + generator: JavascriptGenerator, +) { // 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); + generator.injectId(generator.STATEMENT_SUFFIX, block), + generator.INDENT, + ); } if ((block as IfReturnBlock).hasReturnValue_) { - const value = - generator.valueToCode(block, 'VALUE', Order.NONE) || 'null'; + const value = generator.valueToCode(block, 'VALUE', Order.NONE) || 'null'; code += generator.INDENT + 'return ' + value + ';\n'; } else { code += generator.INDENT + 'return;\n'; } code += '}\n'; return code; -}; +} diff --git a/generators/javascript/text.ts b/generators/javascript/text.ts index a3bf9bef468..79008cba9a0 100644 --- a/generators/javascript/text.ts +++ b/generators/javascript/text.ts @@ -15,7 +15,6 @@ import type {JoinMutatorBlock} from '../../blocks/text.js'; import type {JavascriptGenerator} from './javascript_generator.js'; import {Order} from './javascript_generator.js'; - /** * Regular expression to detect a single-quoted string literal. */ @@ -28,7 +27,7 @@ const strRegExp = /^\s*'([^']|\\')*'\s*$/; * @returns Array containing code evaluating to a string * and the order of the returned code.[string, number] */ -const forceString = function(value: string): [string, Order] { +const forceString = function (value: string): [string, Order] { if (strRegExp.test(value)) { return [value, Order.ATOMIC]; } @@ -42,7 +41,11 @@ const forceString = function(value: string): [string, Order] { * @param opt_at The optional offset when indexing from start/end. * @returns Index expression. */ -const getSubstringIndex = function(stringName: string, where: string, opt_at?: string): string | undefined { +const getSubstringIndex = function ( + stringName: string, + where: string, + opt_at?: string, +): string | undefined { if (where === 'FIRST') { return '0'; } else if (where === 'FROM_END') { @@ -54,102 +57,112 @@ const getSubstringIndex = function(stringName: string, where: string, opt_at?: s } }; -export function text(block: Block, generator: JavascriptGenerator): [string, Order] { +export function text( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Text value. const code = generator.quote_(block.getFieldValue('TEXT')); return [code, Order.ATOMIC]; -}; +} -export function text_multiline(block: Block, generator: JavascriptGenerator): [string, Order] { +export function text_multiline( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Text value. - const code = - generator.multiline_quote_(block.getFieldValue('TEXT')); - const order = code.indexOf('+') !== -1 ? Order.ADDITION : - Order.ATOMIC; + const code = generator.multiline_quote_(block.getFieldValue('TEXT')); + const order = code.indexOf('+') !== -1 ? Order.ADDITION : Order.ATOMIC; return [code, order]; -}; +} -export function text_join(block: Block, generator: JavascriptGenerator): [string, Order] { +export function text_join( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { const joinBlock = block as JoinMutatorBlock; // Create a string made up of any number of elements of any type. switch (joinBlock.itemCount_) { case 0: return ["''", Order.ATOMIC]; case 1: { - const element = generator.valueToCode(joinBlock, 'ADD0', - Order.NONE) || "''"; + const element = + generator.valueToCode(joinBlock, 'ADD0', Order.NONE) || "''"; const codeAndOrder = forceString(element); return codeAndOrder; } case 2: { - const element0 = generator.valueToCode(joinBlock, 'ADD0', - Order.NONE) || "''"; - const element1 = generator.valueToCode(joinBlock, 'ADD1', - Order.NONE) || "''"; - const code = forceString(element0)[0] + - ' + ' + forceString(element1)[0]; + const element0 = + generator.valueToCode(joinBlock, 'ADD0', Order.NONE) || "''"; + const element1 = + generator.valueToCode(joinBlock, 'ADD1', Order.NONE) || "''"; + const code = forceString(element0)[0] + ' + ' + forceString(element1)[0]; return [code, Order.ADDITION]; } default: { const elements = new Array(joinBlock.itemCount_); for (let i = 0; i < joinBlock.itemCount_; i++) { - elements[i] = generator.valueToCode(joinBlock, 'ADD' + i, - Order.NONE) || "''"; + elements[i] = + generator.valueToCode(joinBlock, 'ADD' + i, Order.NONE) || "''"; } - const code = '[' + elements.join(',') + '].join(\'\')'; + const code = '[' + elements.join(',') + "].join('')"; return [code, Order.FUNCTION_CALL]; } } -}; +} export function text_append(block: Block, generator: JavascriptGenerator) { // Append to a variable in place. const varName = generator.getVariableName(block.getFieldValue('VAR')); - const value = generator.valueToCode(block, 'TEXT', - Order.NONE) || "''"; - const code = varName + ' += ' + - forceString(value)[0] + ';\n'; + const value = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; + const code = varName + ' += ' + forceString(value)[0] + ';\n'; return code; -}; +} -export function text_length(block: Block, generator: JavascriptGenerator): [string, Order] { +export function text_length( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // String or array length. - const text = generator.valueToCode(block, 'VALUE', - Order.MEMBER) || "''"; + const text = generator.valueToCode(block, 'VALUE', Order.MEMBER) || "''"; return [text + '.length', Order.MEMBER]; -}; +} -export function text_isEmpty(block: Block, generator: JavascriptGenerator): [string, Order] { +export function text_isEmpty( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Is the string null or array empty? - const text = generator.valueToCode(block, 'VALUE', - Order.MEMBER) || "''"; + const text = generator.valueToCode(block, 'VALUE', Order.MEMBER) || "''"; return ['!' + text + '.length', Order.LOGICAL_NOT]; -}; +} -export function text_indexOf(block: Block, generator: JavascriptGenerator): [string, Order] { +export function text_indexOf( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Search the text for a substring. - const operator = block.getFieldValue('END') === 'FIRST' ? - 'indexOf' : 'lastIndexOf'; - const substring = generator.valueToCode(block, 'FIND', - Order.NONE) || "''"; - const text = generator.valueToCode(block, 'VALUE', - Order.MEMBER) || "''"; + const operator = + block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; + const substring = generator.valueToCode(block, 'FIND', Order.NONE) || "''"; + const text = generator.valueToCode(block, 'VALUE', Order.MEMBER) || "''"; const code = text + '.' + operator + '(' + substring + ')'; // Adjust index if using one-based indices. if (block.workspace.options.oneBasedIndex) { return [code + ' + 1', Order.ADDITION]; } return [code, Order.FUNCTION_CALL]; -}; +} -export function text_charAt(block: Block, generator: JavascriptGenerator): [string, Order] { +export function text_charAt( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Get letter at index. // Note: Until January 2013 this block did not have the WHERE input. const where = block.getFieldValue('WHERE') || 'FROM_START'; - const textOrder = (where === 'RANDOM') ? Order.NONE : - Order.MEMBER; - const text = - generator.valueToCode(block, 'VALUE', textOrder) || "''"; + const textOrder = where === 'RANDOM' ? Order.NONE : Order.MEMBER; + const text = generator.valueToCode(block, 'VALUE', textOrder) || "''"; switch (where) { case 'FIRST': { const code = text + '.charAt(0)'; @@ -171,21 +184,26 @@ export function text_charAt(block: Block, generator: JavascriptGenerator): [stri return [code, Order.FUNCTION_CALL]; } case 'RANDOM': { - const functionName = - generator.provideFunction_('textRandomLetter', ` + const functionName = generator.provideFunction_( + 'textRandomLetter', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(text) { var x = Math.floor(Math.random() * text.length); return text[x]; } -`); +`, + ); const code = functionName + '(' + text + ')'; return [code, Order.FUNCTION_CALL]; } } throw Error('Unhandled option (text_charAt).'); -}; +} -export function text_getSubstring(block: Block, generator: JavascriptGenerator): [string, Order] { +export function text_getSubstring( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Dictionary of WHEREn field choices and their CamelCase equivalents. */ const wherePascalCase = { 'FIRST': 'First', @@ -197,12 +215,13 @@ export function text_getSubstring(block: Block, generator: JavascriptGenerator): // Get substring. const where1 = block.getFieldValue('WHERE1') as WhereOption; const where2 = block.getFieldValue('WHERE2') as WhereOption; - const requiresLengthCall = (where1 !== 'FROM_END' && where1 !== 'LAST' && - where2 !== 'FROM_END' && where2 !== 'LAST'); - const textOrder = requiresLengthCall ? Order.MEMBER : - Order.NONE; - const text = - generator.valueToCode(block, 'STRING', textOrder) || "''"; + const requiresLengthCall = + where1 !== 'FROM_END' && + where1 !== 'LAST' && + where2 !== 'FROM_END' && + where2 !== 'LAST'; + const textOrder = requiresLengthCall ? Order.MEMBER : Order.NONE; + const text = generator.valueToCode(block, 'STRING', textOrder) || "''"; let code; if (where1 === 'FIRST' && where2 === 'LAST') { code = text; @@ -216,8 +235,7 @@ export function text_getSubstring(block: Block, generator: JavascriptGenerator): 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 = text + '.length - ' + at1; break; case 'FIRST': @@ -232,8 +250,7 @@ export function text_getSubstring(block: Block, generator: JavascriptGenerator): at2 = generator.getAdjusted(block, 'AT2', 1); break; case 'FROM_END': - at2 = generator.getAdjusted(block, 'AT2', 0, false, - Order.SUBTRACTION); + at2 = generator.getAdjusted(block, 'AT2', 0, false, Order.SUBTRACTION); at2 = text + '.length - ' + at2; break; case 'LAST': @@ -249,28 +266,38 @@ export function text_getSubstring(block: Block, generator: JavascriptGenerator): // The value for 'FROM_END' and'FROM_START' depends on `at` so // we add it as a parameter. const at1Param = - (where1 === 'FROM_END' || where1 === 'FROM_START') ? ', at1' : ''; + where1 === 'FROM_END' || where1 === 'FROM_START' ? ', at1' : ''; const at2Param = - (where2 === 'FROM_END' || where2 === 'FROM_START') ? ', at2' : ''; + where2 === 'FROM_END' || where2 === 'FROM_START' ? ', at2' : ''; const functionName = generator.provideFunction_( - 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], ` -function ${generator.FUNCTION_NAME_PLACEHOLDER_}(sequence${at1Param}${at2Param}) { + 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], + ` +function ${ + generator.FUNCTION_NAME_PLACEHOLDER_ + }(sequence${at1Param}${at2Param}) { var start = ${getSubstringIndex('sequence', where1, 'at1')}; var end = ${getSubstringIndex('sequence', where2, 'at2')} + 1; return sequence.slice(start, end); } -`); - code = functionName + '(' + text + - // The value for 'FROM_END' and 'FROM_START' depends on `at` so we - // pass it. - ((where1 === 'FROM_END' || where1 === 'FROM_START') ? ', ' + at1 : '') + - ((where2 === 'FROM_END' || where2 === 'FROM_START') ? ', ' + at2 : '') + - ')'; +`, + ); + code = + functionName + + '(' + + text + + // The value for 'FROM_END' and 'FROM_START' depends on `at` so we + // pass it. + (where1 === 'FROM_END' || where1 === 'FROM_START' ? ', ' + at1 : '') + + (where2 === 'FROM_END' || where2 === 'FROM_START' ? ', ' + at2 : '') + + ')'; } return [code, Order.FUNCTION_CALL]; -}; +} -export function text_changeCase(block: Block, generator: JavascriptGenerator): [string, Order] { +export function text_changeCase( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Change capitalization. const OPERATORS = { 'UPPERCASE': '.toUpperCase()', @@ -280,27 +307,31 @@ export function text_changeCase(block: Block, generator: JavascriptGenerator): [ type OperatorOption = keyof typeof OPERATORS; const operator = OPERATORS[block.getFieldValue('CASE') as OperatorOption]; const textOrder = operator ? Order.MEMBER : Order.NONE; - const text = - generator.valueToCode(block, 'TEXT', textOrder) || "''"; + const text = generator.valueToCode(block, 'TEXT', textOrder) || "''"; let code; if (operator) { // Upper and lower case are functions built into generator. code = text + operator; } else { // Title case is not a native JavaScript function. Define one. - const functionName = - generator.provideFunction_('textToTitleCase', ` + const functionName = generator.provideFunction_( + 'textToTitleCase', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(str) { return str.replace(/\\S+/g, function(txt) {return txt[0].toUpperCase() + txt.substring(1).toLowerCase();}); } -`); +`, + ); code = functionName + '(' + text + ')'; } return [code, Order.FUNCTION_CALL]; -}; +} -export function text_trim(block: Block, generator: JavascriptGenerator): [string, Order] { +export function text_trim( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Trim spaces. const OPERATORS = { 'LEFT': ".replace(/^[\\s\\xa0]+/, '')", @@ -309,19 +340,20 @@ export function text_trim(block: Block, generator: JavascriptGenerator): [string }; type OperatorOption = keyof typeof OPERATORS; const operator = OPERATORS[block.getFieldValue('MODE') as OperatorOption]; - const text = generator.valueToCode(block, 'TEXT', - Order.MEMBER) || "''"; + const text = generator.valueToCode(block, 'TEXT', Order.MEMBER) || "''"; return [text + operator, Order.FUNCTION_CALL]; -}; +} export function text_print(block: Block, generator: JavascriptGenerator) { // Print statement. - const msg = generator.valueToCode(block, 'TEXT', - Order.NONE) || "''"; + const msg = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; return 'window.alert(' + msg + ');\n'; -}; +} -export function text_prompt_ext(block: Block, generator: JavascriptGenerator): [string, Order] { +export function text_prompt_ext( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { // Prompt function. let msg; if (block.getField('TEXT')) { @@ -337,16 +369,19 @@ export function text_prompt_ext(block: Block, generator: JavascriptGenerator): [ code = 'Number(' + code + ')'; } return [code, Order.FUNCTION_CALL]; -}; +} export const text_prompt = text_prompt_ext; -export function text_count(block: Block, generator: JavascriptGenerator): [string, Order] { - const text = generator.valueToCode(block, 'TEXT', - Order.NONE) || "''"; - const sub = generator.valueToCode(block, 'SUB', - Order.NONE) || "''"; - const functionName = generator.provideFunction_('textCount', ` +export function text_count( + block: Block, + generator: JavascriptGenerator, +): [string, Order] { + const text = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; + const sub = generator.valueToCode(block, 'SUB', Order.NONE) || "''"; + const functionName = generator.provideFunction_( + 'textCount', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(haystack, needle) { if (needle.length === 0) { return haystack.length + 1; @@ -354,33 +389,40 @@ function ${generator.FUNCTION_NAME_PLACEHOLDER_}(haystack, needle) { return haystack.split(needle).length - 1; } } -`); +`, + ); const code = functionName + '(' + text + ', ' + sub + ')'; return [code, Order.FUNCTION_CALL]; -}; +} -export function text_replace(block: Block, generator: JavascriptGenerator): [string, Order] { - const text = generator.valueToCode(block, 'TEXT', - Order.NONE) || "''"; - const from = generator.valueToCode(block, 'FROM', - Order.NONE) || "''"; +export function text_replace( + block: Block, + generator: JavascriptGenerator, +): [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) || "''"; // The regex escaping code below is taken from the implementation of // goog.string.regExpEscape. - const functionName = generator.provideFunction_('textReplace', ` + const functionName = generator.provideFunction_( + 'textReplace', + ` function ${generator.FUNCTION_NAME_PLACEHOLDER_}(haystack, needle, replacement) { needle = needle.replace(/([-()\\[\\]{}+?*.$\\^|,:#