From 545fe8b9cf7631c2f954a8c4b17361d33089a385 Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Sun, 12 Nov 2023 12:24:47 +0000 Subject: [PATCH 1/8] refactor(generators): Migrate dart_generator.js to TypeScript --- .../{dart_generator.js => dart_generator.ts} | 170 +++++++++--------- 1 file changed, 90 insertions(+), 80 deletions(-) rename generators/dart/{dart_generator.js => dart_generator.ts} (67%) diff --git a/generators/dart/dart_generator.js b/generators/dart/dart_generator.ts similarity index 67% rename from generators/dart/dart_generator.js rename to generators/dart/dart_generator.ts index 412af49ee52..9918344bcda 100644 --- a/generators/dart/dart_generator.js +++ b/generators/dart/dart_generator.ts @@ -5,53 +5,53 @@ */ /** - * @fileoverview Helper functions for generating Dart for blocks. - * @suppress {checkTypes|globalThis} + * @file Dart code generator class, including helper methods for + * generating Dart for blocks. */ // Former goog.module ID: Blockly.Dart import * as Variables from '../../core/variables.js'; import * as stringUtils from '../../core/utils/string.js'; -// import type {Block} from '../../core/block.js'; +import type {Block} from '../../core/block.js'; import {CodeGenerator} from '../../core/generator.js'; import {Names, NameType} from '../../core/names.js'; -// import type {Workspace} from '../../core/workspace.js'; +import type {Workspace} from '../../core/workspace.js'; import {inputTypes} from '../../core/inputs/input_types.js'; /** * Order of operation ENUMs. * https://dart.dev/guides/language/language-tour#operators - * @enum {number} */ -export const Order = { - ATOMIC: 0, // 0 "" ... - UNARY_POSTFIX: 1, // expr++ expr-- () [] . ?. - UNARY_PREFIX: 2, // -expr !expr ~expr ++expr --expr - MULTIPLICATIVE: 3, // * / % ~/ - ADDITIVE: 4, // + - - SHIFT: 5, // << >> - BITWISE_AND: 6, // & - BITWISE_XOR: 7, // ^ - BITWISE_OR: 8, // | - RELATIONAL: 9, // >= > <= < as is is! - EQUALITY: 10, // == != - LOGICAL_AND: 11, // && - LOGICAL_OR: 12, // || - IF_NULL: 13, // ?? - CONDITIONAL: 14, // expr ? expr : expr - CASCADE: 15, // .. - ASSIGNMENT: 16, // = *= /= ~/= %= += -= <<= >>= &= ^= |= - NONE: 99, // (...) -}; +export enum Order { + ATOMIC = 0, // 0 "" ... + UNARY_POSTFIX = 1, // expr++ expr-- () [] . ?. + UNARY_PREFIX = 2, // -expr !expr ~expr ++expr --expr + MULTIPLICATIVE = 3, // * / % ~/ + ADDITIVE = 4, // + - + SHIFT = 5, // << >> + BITWISE_AND = 6, // & + BITWISE_XOR = 7, // ^ + BITWISE_OR = 8, // | + RELATIONAL = 9, // >= > <= < as is is! + EQUALITY = 10, // == != + LOGICAL_AND = 11, // && + LOGICAL_OR = 12, // || + IF_NULL = 13, // ?? + CONDITIONAL = 14, // expr ? expr = expr + CASCADE = 15, // .. + ASSIGNMENT = 16, // = *= /= ~/= %= += -= <<= >>= &= ^= |= + NONE = 99, // (...) +} /** * Dart code generator class. */ export class DartGenerator extends CodeGenerator { - constructor(name) { - super(name ?? 'Dart'); + /** @param name Name of the language the generator is for. */ + constructor(name = 'Dart') { + super(name); this.isInitialized = false; // Copy Order values onto instance for backwards compatibility @@ -62,7 +62,16 @@ export class DartGenerator extends CodeGenerator { // replace data properties with get accessors that call // deprecate.warn().) for (const key in Order) { - this['ORDER_' + key] = Order[key]; + // 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 @@ -91,10 +100,11 @@ export class DartGenerator extends CodeGenerator { /** * Initialise the database of variable names. - * @param {!Workspace} workspace Workspace to generate code from. + * + * @param workspace Workspace to generate code from. */ - init(workspace) { - super.init(); + init(workspace: Workspace) { + super.init(workspace); if (!this.nameDB_) { this.nameDB_ = new Names(this.RESERVED_WORDS_); @@ -131,10 +141,11 @@ export class DartGenerator extends CodeGenerator { /** * Prepend the generated code with import statements and variable definitions. - * @param {string} code Generated code. - * @return {string} Completed code. + * + * @param code Generated code. + * @returns Completed code. */ - finish(code) { + finish(code: string): string { // Indent every line. if (code) { code = this.prefixLines(code, this.INDENT); @@ -156,27 +167,29 @@ export class DartGenerator extends CodeGenerator { code = super.finish(code); this.isInitialized = false; - this.nameDB_.reset(); + this.nameDB_!.reset(); const allDefs = imports.join('\n') + '\n\n' + definitions.join('\n\n'); return allDefs.replace(/\n\n+/g, '\n\n').replace(/\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. + * anything. + * + * @param line Line of generated code. + * @returns Legal line of code. */ - scrubNakedValue(line) { + scrubNakedValue(line: string): string { return line + ';\n'; } /** * Encode a string as a properly escaped Dart string, complete with quotes. - * @param {string} string Text to encode. - * @return {string} Dart string. + * + * @param string Text to encode. + * @returns Dart string. */ - quote_(string) { + quote_(string: string): string { // Can't use goog.string.quote since $ must also be escaped. string = string.replace(/\\/g, '\\\\') .replace(/\n/g, '\\\n') @@ -186,12 +199,13 @@ export class DartGenerator extends CodeGenerator { } /** - * Encode a string as a properly escaped multiline Dart string, complete with - * quotes. - * @param {string} string Text to encode. - * @return {string} Dart string. + * Encode a string as a properly escaped multiline Dart string, complete + * with quotes. + * + * @param string Text to encode. + * @returns Dart string. */ - multiline_quote_(string) { + multiline_quote_(string: string): string { const lines = string.split(/\n/g).map(this.quote_); // Join with the following, plus a newline: // + '\n' + @@ -202,14 +216,13 @@ export class DartGenerator extends CodeGenerator { * Common tasks for generating Dart 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 Dart code created for this block. - * @param {boolean=} opt_thisOnly True to generate code for only this - * statement. - * @return {string} Dart code with comments and subsequent blocks added. - * @protected + * + * @param block The current block. + * @param code The Dart code created for this block. + * @param thisOnly True to generate code for only this statement. + * @returns Dart code with comments and subsequent blocks added. */ - scrub_(block, code, opt_thisOnly) { + 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) { @@ -217,7 +230,7 @@ export class DartGenerator extends CodeGenerator { let comment = block.getCommentText(); if (comment) { comment = stringUtils.wrap(comment, this.COMMENT_WRAP - 3); - if (block.getProcedureDef) { + if ((block as AnyDuringMigration).getProcedureDef) { // Use documentation comment for function comments. commentCode += this.prefixLines(comment + '\n', '/// '); } else { @@ -228,7 +241,7 @@ export class DartGenerator extends CodeGenerator { // 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(); + const childBlock = block.inputList[i].connection!.targetBlock(); if (childBlock) { comment = this.allNestedComments(childBlock); if (comment) { @@ -240,48 +253,45 @@ export class DartGenerator extends CodeGenerator { } const nextBlock = block.nextConnection && block.nextConnection.targetBlock(); - const nextCode = opt_thisOnly ? '' : this.blockToCode(nextBlock); + const nextCode = 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} + * + * @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. */ - getAdjusted(block, atId, opt_delta, opt_negate, opt_order) { - let delta = opt_delta || 0; - let order = opt_order || this.ORDER_NONE; + getAdjusted(block: Block, atId: string, delta = 0, negate = false, order = Order.NONE): string | number { if (block.workspace.options.oneBasedIndex) { delta--; } const defaultAtIndex = block.workspace.options.oneBasedIndex ? '1' : '0'; - /** @type {number} */ - let outerOrder; - let innerOrder; + let outerOrder: Order; + let innerOrder: Order | undefined; if (delta) { - outerOrder = this.ORDER_ADDITIVE; - innerOrder = this.ORDER_ADDITIVE; - } else if (opt_negate) { - outerOrder = this.ORDER_UNARY_PREFIX; - innerOrder = this.ORDER_UNARY_PREFIX; + outerOrder = Order.ADDITIVE; + innerOrder = Order.ADDITIVE; + } else if (negate) { + outerOrder = Order.UNARY_PREFIX; + innerOrder = Order.UNARY_PREFIX; } else { outerOrder = order; } - /** @type {string|number} */ let at = this.valueToCode(block, atId, outerOrder) || defaultAtIndex; if (stringUtils.isNumber(at)) { // If the index is a naked number, adjust it right now. - at = parseInt(at, 10) + delta; - if (opt_negate) { - at = -at; + at = String(Number(at) + delta); + if (negate) { + at = String(-Number(at)); } } else { // If the index is dynamic, adjust it in code. @@ -290,14 +300,14 @@ export class DartGenerator extends CodeGenerator { } else if (delta < 0) { at = at + ' - ' + -delta; } - if (opt_negate) { + if (negate) { if (delta) { at = '-(' + at + ')'; } else { at = '-' + at; } } - innerOrder = Math.floor(innerOrder); + innerOrder = innerOrder === undefined ? NaN : Math.floor(innerOrder); order = Math.floor(order); if (innerOrder && order >= innerOrder) { at = '(' + at + ')'; From b93dd708933a56cb78753c023966fb771c3085c9 Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Sun, 12 Nov 2023 17:44:16 +0000 Subject: [PATCH 2/8] refactor(generators): Simplify getAdjusted Slightly simplify the implementation of getAdjusted, in part to make it more readable. Also improve its JSDoc comment. --- generators/dart/dart_generator.ts | 55 ++++++++++++++----------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/generators/dart/dart_generator.ts b/generators/dart/dart_generator.ts index 9918344bcda..dc46bf2d29f 100644 --- a/generators/dart/dart_generator.ts +++ b/generators/dart/dart_generator.ts @@ -258,7 +258,9 @@ export class DartGenerator extends CodeGenerator { } /** - * Gets a property and adjusts the value while taking into account indexing. + * 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. @@ -273,45 +275,38 @@ export class DartGenerator extends CodeGenerator { } const defaultAtIndex = block.workspace.options.oneBasedIndex ? '1' : '0'; - let outerOrder: Order; - let innerOrder: Order | undefined; + let orderForInput = order; if (delta) { - outerOrder = Order.ADDITIVE; - innerOrder = Order.ADDITIVE; + orderForInput = Order.ADDITIVE; } else if (negate) { - outerOrder = Order.UNARY_PREFIX; - innerOrder = Order.UNARY_PREFIX; - } else { - outerOrder = order; + orderForInput = Order.UNARY_PREFIX; } - let at = this.valueToCode(block, atId, outerOrder) || defaultAtIndex; + 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)) { - // If the index is a naked number, adjust it right now. at = String(Number(at) + delta); if (negate) { at = String(-Number(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 (negate) { - if (delta) { - at = '-(' + at + ')'; - } else { - at = '-' + at; - } - } - innerOrder = innerOrder === undefined ? NaN : Math.floor(innerOrder); - order = Math.floor(order); - if (innerOrder && order >= innerOrder) { - at = '(' + 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; } From 52b30b8ad1c53036e80fe8ad9a7037adb2dca974 Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Sun, 12 Nov 2023 18:12:49 +0000 Subject: [PATCH 3/8] refactor(generators): Migrate generators/dart/* to TypeScript First pass doing very mechanistic migration, not attempting to fix all the resulting type errors. --- generators/dart/{colour.js => colour.ts} | 10 +++--- generators/dart/{lists.js => lists.ts} | 26 +++++++------- generators/dart/{logic.js => logic.ts} | 16 +++++---- generators/dart/{loops.js => loops.ts} | 14 ++++---- generators/dart/{math.js => math.ts} | 26 +++++++------- .../dart/{procedures.js => procedures.ts} | 10 +++--- generators/dart/{text.js => text.ts} | 34 ++++++++++--------- .../dart/{variables.js => variables.ts} | 6 ++-- ...iables_dynamic.js => variables_dynamic.ts} | 0 9 files changed, 79 insertions(+), 63 deletions(-) rename generators/dart/{colour.js => colour.ts} (88%) rename generators/dart/{lists.js => lists.ts} (92%) rename generators/dart/{logic.js => logic.ts} (83%) rename generators/dart/{loops.js => loops.ts} (92%) rename generators/dart/{math.js => math.ts} (92%) rename generators/dart/{procedures.js => procedures.ts} (88%) rename generators/dart/{text.js => text.ts} (87%) rename generators/dart/{variables.js => variables.ts} (70%) rename generators/dart/{variables_dynamic.js => variables_dynamic.ts} (100%) diff --git a/generators/dart/colour.js b/generators/dart/colour.ts similarity index 88% rename from generators/dart/colour.js rename to generators/dart/colour.ts index b5405a34fc2..700a1a1146c 100644 --- a/generators/dart/colour.js +++ b/generators/dart/colour.ts @@ -10,18 +10,20 @@ // Former goog.module ID: Blockly.Dart.colour +import type {Block} from '../../core/block.js'; +import type {DartGenerator} from './dart_generator.js'; import {Order} from './dart_generator.js'; // RESERVED WORDS: 'Math' -export function colour_picker(block, generator) { +export function colour_picker(block: Block, generator: DartGenerator): [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: DartGenerator): [string, Order] { // Generate a random colour. generator.definitions_['import_dart_math'] = "import 'dart:math' as Math;"; @@ -38,7 +40,7 @@ String ${generator.FUNCTION_NAME_PLACEHOLDER_}() { return [code, Order.UNARY_POSTFIX]; }; -export function colour_rgb(block, generator) { +export function colour_rgb(block: Block, generator: DartGenerator): [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; @@ -67,7 +69,7 @@ String ${generator.FUNCTION_NAME_PLACEHOLDER_}(num r, num g, num b) { return [code, Order.UNARY_POSTFIX]; }; -export function colour_blend(block, generator) { +export function colour_blend(block: Block, generator: DartGenerator): [string, Order] { // Blend two colours together. const c1 = generator.valueToCode(block, 'COLOUR1', Order.NONE) || "'#000000'"; diff --git a/generators/dart/lists.js b/generators/dart/lists.ts similarity index 92% rename from generators/dart/lists.js rename to generators/dart/lists.ts index c7a540b69c6..2d0e4a0e293 100644 --- a/generators/dart/lists.js +++ b/generators/dart/lists.ts @@ -10,18 +10,20 @@ // Former goog.module ID: Blockly.Dart.lists +import type {Block} from '../../core/block.js'; +import type {DartGenerator} from './dart_generator.js'; import {NameType} from '../../core/names.js'; import {Order} from './dart_generator.js'; // RESERVED WORDS: 'Math' -export function lists_create_empty(block, generator) { +export function lists_create_empty(block: Block, generator: DartGenerator): [string, Order] { // Create an empty list. return ['[]', Order.ATOMIC]; }; -export function lists_create_with(block, generator) { +export function lists_create_with(block: Block, generator: DartGenerator): [string, Order] { // Create a list with any number of elements of any type. const elements = new Array(block.itemCount_); for (let i = 0; i < block.itemCount_; i++) { @@ -32,7 +34,7 @@ export function lists_create_with(block, generator) { return [code, Order.ATOMIC]; }; -export function lists_repeat(block, generator) { +export function lists_repeat(block: Block, generator: DartGenerator): [string, Order] { // Create a list with one element repeated. const element = generator.valueToCode(block, 'ITEM', Order.NONE) || 'null'; @@ -42,21 +44,21 @@ export function lists_repeat(block, generator) { return [code, Order.UNARY_POSTFIX]; }; -export function lists_length(block, generator) { +export function lists_length(block: Block, generator: DartGenerator): [string, Order] { // String or array length. const list = generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || '[]'; return [list + '.length', Order.UNARY_POSTFIX]; }; -export function lists_isEmpty(block, generator) { +export function lists_isEmpty(block: Block, generator: DartGenerator): [string, Order] { // Is the string null or array empty? const list = generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || '[]'; return [list + '.isEmpty', Order.UNARY_POSTFIX]; }; -export function lists_indexOf(block, generator) { +export function lists_indexOf(block: Block, generator: DartGenerator): [string, Order] { // Find an item in the list. const operator = block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; @@ -70,7 +72,7 @@ export function lists_indexOf(block, generator) { return [code, Order.UNARY_POSTFIX]; }; -export function lists_getIndex(block, generator) { +export function lists_getIndex(block: Block, generator: DartGenerator): [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'; @@ -233,7 +235,7 @@ dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List my_list) { throw Error('Unhandled combination (lists_getIndex).'); }; -export function lists_setIndex(block, generator) { +export function lists_setIndex(block: Block, generator: DartGenerator) { // Set element at index. // Note: Until February 2013 this block did not have MODE or WHERE inputs. const mode = block.getFieldValue('MODE') || 'GET'; @@ -315,7 +317,7 @@ export function lists_setIndex(block, generator) { throw Error('Unhandled combination (lists_setIndex).'); }; -export function lists_getSublist(block, generator) { +export function lists_getSublist(block: Block, generator: DartGenerator): [string, Order] { // Get sublist. const list = generator.valueToCode(block, 'LIST', Order.UNARY_POSTFIX) || '[]'; @@ -389,7 +391,7 @@ List ${generator.FUNCTION_NAME_PLACEHOLDER_}(List list, String where1, num at1, return [code, Order.UNARY_POSTFIX]; }; -export function lists_sort(block, generator) { +export function lists_sort(block: Block, generator: DartGenerator): [string, Order] { // Block for sorting a list. const list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; const direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; @@ -416,7 +418,7 @@ List ${generator.FUNCTION_NAME_PLACEHOLDER_}(List list, String type, int directi ]; }; -export function lists_split(block, generator) { +export function lists_split(block: Block, generator: DartGenerator): [string, Order] { // Block for splitting text into a list, or joining a list into text. let input = generator.valueToCode(block, 'INPUT', Order.UNARY_POSTFIX); const delimiter = @@ -440,7 +442,7 @@ export function lists_split(block, generator) { return [code, Order.UNARY_POSTFIX]; }; -export function lists_reverse(block, generator) { +export function lists_reverse(block: Block, generator: DartGenerator): [string, Order] { // Block for reversing a list. const list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; // XXX What should the operator precedence be for a `new`? diff --git a/generators/dart/logic.js b/generators/dart/logic.ts similarity index 83% rename from generators/dart/logic.js rename to generators/dart/logic.ts index f3cbcb8ae43..fdd925f8882 100644 --- a/generators/dart/logic.js +++ b/generators/dart/logic.ts @@ -10,10 +10,12 @@ // Former goog.module ID: Blockly.Dart.logic +import type {Block} from '../../core/block.js'; +import type {DartGenerator} from './dart_generator.js'; import {Order} from './dart_generator.js'; -export function controls_if(block, generator) { +export function controls_if(block: Block, generator: DartGenerator) { // If/elseif/else condition. let n = 0; let code = '', branchCode, conditionCode; @@ -53,7 +55,7 @@ export function controls_if(block, generator) { export const controls_ifelse = controls_if; -export function logic_compare(block, generator) { +export function logic_compare(block: Block, generator: DartGenerator): [string, Order] { // Comparison operator. const OPERATORS = {'EQ': '==', 'NEQ': '!=', 'LT': '<', 'LTE': '<=', 'GT': '>', 'GTE': '>='}; @@ -67,7 +69,7 @@ export function logic_compare(block, generator) { return [code, order]; }; -export function logic_operation(block, generator) { +export function logic_operation(block: Block, generator: DartGenerator): [string, Order] { // Operations 'and', 'or'. const operator = (block.getFieldValue('OP') === 'AND') ? '&&' : '||'; const order = @@ -92,7 +94,7 @@ export function logic_operation(block, generator) { return [code, order]; }; -export function logic_negate(block, generator) { +export function logic_negate(block: Block, generator: DartGenerator): [string, Order] { // Negation. const order = Order.UNARY_PREFIX; const argument0 = generator.valueToCode(block, 'BOOL', order) || 'true'; @@ -100,18 +102,18 @@ export function logic_negate(block, generator) { return [code, order]; }; -export function logic_boolean(block, generator) { +export function logic_boolean(block: Block, generator: DartGenerator): [string, Order] { // Boolean values true and 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: DartGenerator): [string, Order] { // Null data type. return ['null', Order.ATOMIC]; }; -export function logic_ternary(block, generator) { +export function logic_ternary(block: Block, generator: DartGenerator): [string, Order] { // Ternary operator. const value_if = generator.valueToCode(block, 'IF', Order.CONDITIONAL) || 'false'; diff --git a/generators/dart/loops.js b/generators/dart/loops.ts similarity index 92% rename from generators/dart/loops.js rename to generators/dart/loops.ts index 1ead2d7e86b..18b8b93dcd8 100644 --- a/generators/dart/loops.js +++ b/generators/dart/loops.ts @@ -10,12 +10,14 @@ // Former goog.module ID: Blockly.Dart.loops -import {Order} from './dart_generator.js'; import * as stringUtils from '../../core/utils/string.js'; +import type {Block} from '../../core/block.js'; +import type {DartGenerator} from './dart_generator.js'; import {NameType} from '../../core/names.js'; +import {Order} from './dart_generator.js'; -export function controls_repeat_ext(block, generator) { +export function controls_repeat_ext(block: Block, generator: DartGenerator) { let repeats; // Repeat n times. if (block.getField('TIMES')) { @@ -44,7 +46,7 @@ export function controls_repeat_ext(block, generator) { export const controls_repeat = controls_repeat_ext; -export function controls_whileUntil(block, generator) { +export function controls_whileUntil(block: Block, generator: DartGenerator) { // Do while/until loop. const until = block.getFieldValue('MODE') === 'UNTIL'; let argument0 = @@ -59,7 +61,7 @@ export function controls_whileUntil(block, generator) { return 'while (' + argument0 + ') {\n' + branch + '}\n'; }; -export function controls_for(block, generator) { +export function controls_for(block: Block, generator: DartGenerator) { // For loop. const variable0 = generator.getVariableName(block.getFieldValue('VAR')); @@ -124,7 +126,7 @@ export function controls_for(block, generator) { return code; }; -export function controls_forEach(block, generator) { +export function controls_forEach(block: Block, generator: DartGenerator) { // For each loop. const variable0 = generator.getVariableName(block.getFieldValue('VAR')); @@ -137,7 +139,7 @@ export function controls_forEach(block, generator) { return code; }; -export function controls_flow_statements(block, generator) { +export function controls_flow_statements(block: Block, generator: DartGenerator) { // Flow statements: continue, break. let xfix = ''; if (generator.STATEMENT_PREFIX) { diff --git a/generators/dart/math.js b/generators/dart/math.ts similarity index 92% rename from generators/dart/math.js rename to generators/dart/math.ts index 8541b95a87a..4038b6c02a1 100644 --- a/generators/dart/math.js +++ b/generators/dart/math.ts @@ -10,12 +10,14 @@ // Former goog.module ID: Blockly.Dart.math +import type {Block} from '../../core/block.js'; +import type {DartGenerator} from './dart_generator.js'; import {Order} from './dart_generator.js'; // RESERVED WORDS: 'Math' -export function math_number(block, generator) { +export function math_number(block: Block, generator: DartGenerator): [string, Order] { // Numeric value. let code = Number(block.getFieldValue('NUM')); let order; @@ -33,7 +35,7 @@ export function math_number(block, generator) { return [code, order]; }; -export function math_arithmetic(block, generator) { +export function math_arithmetic(block: Block, generator: DartGenerator): [string, Order] { // Basic arithmetic operators, and power. const OPERATORS = { 'ADD': [' + ', Order.ADDITIVE], @@ -59,7 +61,7 @@ export function math_arithmetic(block, generator) { return [code, order]; }; -export function math_single(block, generator) { +export function math_single(block: Block, generator: DartGenerator): [string, Order] { // Math operators with single operand. const operator = block.getFieldValue('OP'); let code; @@ -144,7 +146,7 @@ export function math_single(block, generator) { return [code, Order.MULTIPLICATIVE]; }; -export function math_constant(block, generator) { +export function math_constant(block: Block, generator: DartGenerator): [string, Order] { // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY. const CONSTANTS = { 'PI': ['Math.pi', Order.UNARY_POSTFIX], @@ -162,7 +164,7 @@ export function math_constant(block, generator) { return CONSTANTS[constant]; }; -export function math_number_property(block, generator) { +export function math_number_property(block: Block, generator: DartGenerator): [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 = { @@ -217,7 +219,7 @@ bool ${generator.FUNCTION_NAME_PLACEHOLDER_}(n) { return [code, outputOrder]; }; -export function math_change(block, generator) { +export function math_change(block: Block, generator: DartGenerator) { // Add to a variable in place. const argument0 = generator.valueToCode(block, 'DELTA', Order.ADDITIVE) || '0'; @@ -232,7 +234,7 @@ 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: DartGenerator): [string, Order] { // Math functions for lists. const func = block.getFieldValue('OP'); const list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; @@ -393,7 +395,7 @@ dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { return [code, Order.UNARY_POSTFIX]; }; -export function math_modulo(block, generator) { +export function math_modulo(block: Block, generator: DartGenerator): [string, Order] { // Remainder computation. const argument0 = generator.valueToCode(block, 'DIVIDEND', Order.MULTIPLICATIVE) || '0'; @@ -403,7 +405,7 @@ export function math_modulo(block, generator) { return [code, Order.MULTIPLICATIVE]; }; -export function math_constrain(block, generator) { +export function math_constrain(block: Block, generator: DartGenerator): [string, Order] { // Constrain a number between two limits. generator.definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; @@ -417,7 +419,7 @@ export function math_constrain(block, generator) { return [code, Order.UNARY_POSTFIX]; }; -export function math_random_int(block, generator) { +export function math_random_int(block: Block, generator: DartGenerator): [string, Order] { // Random integer between [X] and [Y]. generator.definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; @@ -438,14 +440,14 @@ int ${generator.FUNCTION_NAME_PLACEHOLDER_}(num a, num b) { return [code, Order.UNARY_POSTFIX]; }; -export function math_random_float(block, generator) { +export function math_random_float(block: Block, generator: DartGenerator): [string, Order] { // Random fraction between 0 and 1. generator.definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; return ['new Math.Random().nextDouble()', Order.UNARY_POSTFIX]; }; -export function math_atan2(block, generator) { +export function math_atan2(block: Block, generator: DartGenerator): [string, Order] { // Arctangent of point (X, Y) in degrees from -180 to 180. generator.definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; diff --git a/generators/dart/procedures.js b/generators/dart/procedures.ts similarity index 88% rename from generators/dart/procedures.js rename to generators/dart/procedures.ts index e782abb6808..a2144366ccb 100644 --- a/generators/dart/procedures.js +++ b/generators/dart/procedures.ts @@ -10,10 +10,12 @@ // Former goog.module ID: Blockly.Dart.procedures +import type {Block} from '../../core/block.js'; +import type {DartGenerator} from './dart_generator.js'; import {Order} from './dart_generator.js'; -export function procedures_defreturn(block, generator) { +export function procedures_defreturn(block: Block, generator: DartGenerator) { // Define a procedure with a return value. const funcName = generator.getProcedureName(block.getFieldValue('NAME')); @@ -62,7 +64,7 @@ export function procedures_defreturn(block, generator) { // 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: DartGenerator): [string, Order] { // Call a procedure with a return value. const funcName = generator.getProcedureName(block.getFieldValue('NAME')); @@ -75,7 +77,7 @@ export function procedures_callreturn(block, generator) { return [code, Order.UNARY_POSTFIX]; }; -export function procedures_callnoreturn(block, generator) { +export function procedures_callnoreturn(block: Block, generator: DartGenerator) { // 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. @@ -83,7 +85,7 @@ export function procedures_callnoreturn(block, generator) { return tuple[0] + ';\n'; }; -export function procedures_ifreturn(block, generator) { +export function procedures_ifreturn(block: Block, generator: DartGenerator) { // Conditionally return value from a procedure. const condition = generator.valueToCode(block, 'CONDITION', Order.NONE) || 'false'; diff --git a/generators/dart/text.js b/generators/dart/text.ts similarity index 87% rename from generators/dart/text.js rename to generators/dart/text.ts index 69ffbf7df6a..bdca13b30f4 100644 --- a/generators/dart/text.js +++ b/generators/dart/text.ts @@ -10,18 +10,20 @@ // Former goog.module ID: Blockly.Dart.texts +import type {Block} from '../../core/block.js'; +import type {DartGenerator} from './dart_generator.js'; import {Order} from './dart_generator.js'; // RESERVED WORDS: 'Html,Math' -export function text(block, generator) { +export function text(block: Block, generator: DartGenerator): [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: DartGenerator): [string, Order] { // Text value. const code = generator.multiline_quote_(block.getFieldValue('TEXT')); const order = @@ -29,7 +31,7 @@ export function text_multiline(block, generator) { return [code, order]; }; -export function text_join(block, generator) { +export function text_join(block: Block, generator: DartGenerator): [string, Order] { // Create a string made up of any number of elements of any type. switch (block.itemCount_) { case 0: @@ -52,7 +54,7 @@ export function text_join(block, generator) { } }; -export function text_append(block, generator) { +export function text_append(block: Block, generator: DartGenerator) { // Append to a variable in place. const varName = generator.getVariableName(block.getFieldValue('VAR')); @@ -60,21 +62,21 @@ export function text_append(block, generator) { return varName + ' = [' + varName + ', ' + value + '].join();\n'; }; -export function text_length(block, generator) { +export function text_length(block: Block, generator: DartGenerator): [string, Order] { // String or array length. const text = generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || "''"; return [text + '.length', Order.UNARY_POSTFIX]; }; -export function text_isEmpty(block, generator) { +export function text_isEmpty(block: Block, generator: DartGenerator): [string, Order] { // Is the string null or array empty? const text = generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || "''"; return [text + '.isEmpty', Order.UNARY_POSTFIX]; }; -export function text_indexOf(block, generator) { +export function text_indexOf(block: Block, generator: DartGenerator): [string, Order] { // Search the text for a substring. const operator = block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; @@ -89,7 +91,7 @@ export function text_indexOf(block, generator) { return [code, Order.UNARY_POSTFIX]; }; -export function text_charAt(block, generator) { +export function text_charAt(block: Block, generator: DartGenerator): [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'; @@ -138,7 +140,7 @@ String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String text) { throw Error('Unhandled option (text_charAt).'); }; -export function text_getSubstring(block, generator) { +export function text_getSubstring(block: Block, generator: DartGenerator): [string, Order] { // Get substring. const where1 = block.getFieldValue('WHERE1'); const where2 = block.getFieldValue('WHERE2'); @@ -217,7 +219,7 @@ String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String text, String where1, num a return [code, Order.UNARY_POSTFIX]; }; -export function text_changeCase(block, generator) { +export function text_changeCase(block: Block, generator: DartGenerator): [string, Order] { // Change capitalization. const OPERATORS = { 'UPPERCASE': '.toUpperCase()', @@ -254,7 +256,7 @@ String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String str) { return [code, Order.UNARY_POSTFIX]; }; -export function text_trim(block, generator) { +export function text_trim(block: Block, generator: DartGenerator): [string, Order] { // Trim spaces. const OPERATORS = { 'LEFT': '.replaceFirst(new RegExp(r\'^\\s+\'), \'\')', @@ -267,13 +269,13 @@ export function text_trim(block, generator) { return [text + operator, Order.UNARY_POSTFIX]; }; -export function text_print(block, generator) { +export function text_print(block: Block, generator: DartGenerator) { // 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: DartGenerator): [string, Order] { // Prompt function. generator.definitions_['import_dart_html'] = 'import \'dart:html\' as Html;'; @@ -297,7 +299,7 @@ export function text_prompt_ext(block, generator) { export const text_prompt = text_prompt_ext; -export function text_count(block, generator) { +export function text_count(block: Block, generator: DartGenerator): [string, Order] { const text = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; const sub = generator.valueToCode(block, 'SUB', Order.NONE) || "''"; // Substring count is not a native generator function. Define one. @@ -322,7 +324,7 @@ int ${generator.FUNCTION_NAME_PLACEHOLDER_}(String haystack, String needle) { return [code, Order.UNARY_POSTFIX]; }; -export function text_replace(block, generator) { +export function text_replace(block: Block, generator: DartGenerator): [string, Order] { const text = generator.valueToCode(block, 'TEXT', Order.UNARY_POSTFIX) || "''"; const from = generator.valueToCode(block, 'FROM', Order.NONE) || "''"; @@ -331,7 +333,7 @@ export function text_replace(block, generator) { return [code, Order.UNARY_POSTFIX]; }; -export function text_reverse(block, generator) { +export function text_reverse(block: Block, generator: DartGenerator): [string, Order] { // There isn't a sensible way to do this in generator. See: // http://stackoverflow.com/a/21613700/3529104 // Implementing something is possibly better than not implementing anything? diff --git a/generators/dart/variables.js b/generators/dart/variables.ts similarity index 70% rename from generators/dart/variables.js rename to generators/dart/variables.ts index c088437603b..052c53fd5c4 100644 --- a/generators/dart/variables.js +++ b/generators/dart/variables.ts @@ -10,17 +10,19 @@ // Former goog.module ID: Blockly.Dart.variables +import type {Block} from '../../core/block.js'; +import type {DartGenerator} from './dart_generator.js'; import {Order} from './dart_generator.js'; -export function variables_get(block, generator) { +export function variables_get(block: Block, generator: DartGenerator): [string, Order] { // Variable getter. const code = generator.getVariableName(block.getFieldValue('VAR')); return [code, Order.ATOMIC]; }; -export function variables_set(block, generator) { +export function variables_set(block: Block, generator: DartGenerator) { // Variable setter. const argument0 = generator.valueToCode(block, 'VALUE', Order.ASSIGNMENT) || '0'; diff --git a/generators/dart/variables_dynamic.js b/generators/dart/variables_dynamic.ts similarity index 100% rename from generators/dart/variables_dynamic.js rename to generators/dart/variables_dynamic.ts From a4f2fbe2aaca2e776c5e234af3b7263aa78ccdee Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Sun, 12 Nov 2023 19:19:21 +0000 Subject: [PATCH 4/8] fix(generators): Fix type errors in generator functions This consists almost entirely of adding casts, so the code output by tsc should be as similar as possible to the pre-migration .js source files. --- generators/dart/colour.ts | 12 +++-- generators/dart/lists.ts | 28 +++++++---- generators/dart/logic.ts | 3 +- generators/dart/loops.ts | 15 +++--- generators/dart/math.ts | 88 +++++++++++++++++++++++------------ generators/dart/procedures.ts | 9 ++-- generators/dart/text.ts | 26 +++++++---- 7 files changed, 118 insertions(+), 63 deletions(-) diff --git a/generators/dart/colour.ts b/generators/dart/colour.ts index 700a1a1146c..59af9ce2a62 100644 --- a/generators/dart/colour.ts +++ b/generators/dart/colour.ts @@ -25,7 +25,9 @@ export function colour_picker(block: Block, generator: DartGenerator): [string, export function colour_random(block: Block, generator: DartGenerator): [string, Order] { // Generate a random colour. - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = "import 'dart:math' as Math;"; const functionName = generator.provideFunction_('colour_random', ` String ${generator.FUNCTION_NAME_PLACEHOLDER_}() { @@ -46,7 +48,9 @@ export function colour_rgb(block: Block, generator: DartGenerator): [string, Ord const green = generator.valueToCode(block, 'GREEN', Order.NONE) || 0; const blue = generator.valueToCode(block, 'BLUE', Order.NONE) || 0; - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = "import 'dart:math' as Math;"; const functionName = generator.provideFunction_('colour_rgb', ` String ${generator.FUNCTION_NAME_PLACEHOLDER_}(num r, num g, num b) { @@ -78,7 +82,9 @@ export function colour_blend(block: Block, generator: DartGenerator): [string, O const ratio = generator.valueToCode(block, 'RATIO', Order.NONE) || 0.5; - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = "import 'dart:math' as Math;"; const functionName = generator.provideFunction_('colour_blend', ` String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String c1, String c2, num ratio) { diff --git a/generators/dart/lists.ts b/generators/dart/lists.ts index 2d0e4a0e293..e156a06dae7 100644 --- a/generators/dart/lists.ts +++ b/generators/dart/lists.ts @@ -11,6 +11,7 @@ // Former goog.module ID: Blockly.Dart.lists import type {Block} from '../../core/block.js'; +import type {CreateWithBlock} from '../../blocks/lists.js'; import type {DartGenerator} from './dart_generator.js'; import {NameType} from '../../core/names.js'; import {Order} from './dart_generator.js'; @@ -25,8 +26,9 @@ export function lists_create_empty(block: Block, generator: DartGenerator): [str export function lists_create_with(block: Block, generator: DartGenerator): [string, Order] { // Create a list with any number of elements of any type. - const elements = new Array(block.itemCount_); - for (let i = 0; i < block.itemCount_; i++) { + 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'; } @@ -85,7 +87,7 @@ export function lists_getIndex(block: Block, generator: DartGenerator): [string, // Closure, which accesses and modifies 'list'. function cacheList() { const listVar = - generator.nameDB_.getDistinctName('tmp_list', NameType.VARIABLE); + generator.nameDB_!.getDistinctName('tmp_list', NameType.VARIABLE); const code = 'List ' + listVar + ' = ' + list + ';\n'; list = listVar; return code; @@ -97,12 +99,14 @@ export function lists_getIndex(block: Block, generator: DartGenerator): [string, !list.match(/^\w+$/)) { // `list` is an expression, so we may not evaluate it more than once. if (where === 'RANDOM') { - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; // We can use multiple statements. let code = cacheList(); const xVar = - generator.nameDB_.getDistinctName('tmp_x', NameType.VARIABLE); + generator.nameDB_!.getDistinctName('tmp_x', NameType.VARIABLE); code += 'int ' + xVar + ' = new Math.Random().nextInt(' + list + '.length);\n'; code += list + '.removeAt(' + xVar + ');\n'; @@ -198,12 +202,14 @@ dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List my_list, num x) { break; } case 'RANDOM': - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; if (mode === 'REMOVE') { // We can use multiple statements. const xVar = - generator.nameDB_.getDistinctName('tmp_x', NameType.VARIABLE); + generator.nameDB_!.getDistinctName('tmp_x', NameType.VARIABLE); let code = 'int ' + xVar + ' = new Math.Random().nextInt(' + list + '.length);\n'; code += list + '.removeAt(' + xVar + ');\n'; @@ -251,7 +257,7 @@ export function lists_setIndex(block: Block, generator: DartGenerator) { return ''; } const listVar = - generator.nameDB_.getDistinctName('tmp_list', NameType.VARIABLE); + generator.nameDB_!.getDistinctName('tmp_list', NameType.VARIABLE); const code = 'List ' + listVar + ' = ' + list + ';\n'; list = listVar; return code; @@ -297,11 +303,13 @@ export function lists_setIndex(block: Block, generator: DartGenerator) { break; } case 'RANDOM': { - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; let code = cacheList(); const xVar = - generator.nameDB_.getDistinctName('tmp_x', NameType.VARIABLE); + generator.nameDB_!.getDistinctName('tmp_x', NameType.VARIABLE); code += 'int ' + xVar + ' = new Math.Random().nextInt(' + list + '.length);\n'; if (mode === 'SET') { diff --git a/generators/dart/logic.ts b/generators/dart/logic.ts index fdd925f8882..cddbcb0e84e 100644 --- a/generators/dart/logic.ts +++ b/generators/dart/logic.ts @@ -59,7 +59,8 @@ export function logic_compare(block: Block, generator: DartGenerator): [string, // Comparison operator. const OPERATORS = {'EQ': '==', 'NEQ': '!=', 'LT': '<', 'LTE': '<=', 'GT': '>', 'GTE': '>='}; - const operator = OPERATORS[block.getFieldValue('OP')]; + type OperatorOption = keyof typeof OPERATORS; + const operator = OPERATORS[block.getFieldValue('OP') as OperatorOption]; const order = (operator === '==' || operator === '!=') ? Order.EQUALITY : Order.RELATIONAL; diff --git a/generators/dart/loops.ts b/generators/dart/loops.ts index 18b8b93dcd8..adb335d905b 100644 --- a/generators/dart/loops.ts +++ b/generators/dart/loops.ts @@ -12,6 +12,7 @@ import * as stringUtils from '../../core/utils/string.js'; import type {Block} from '../../core/block.js'; +import type {ControlFlowInLoopBlock} from '../../blocks/loops.js'; import type {DartGenerator} from './dart_generator.js'; import {NameType} from '../../core/names.js'; import {Order} from './dart_generator.js'; @@ -32,11 +33,11 @@ export function controls_repeat_ext(block: Block, generator: DartGenerator) { branch = generator.addLoopTrap(branch, block); let code = ''; const loopVar = - generator.nameDB_.getDistinctName('count', NameType.VARIABLE); + generator.nameDB_!.getDistinctName('count', NameType.VARIABLE); let endVar = repeats; if (!repeats.match(/^\w+$/) && !stringUtils.isNumber(repeats)) { endVar = - generator.nameDB_.getDistinctName('repeat_end', NameType.VARIABLE); + generator.nameDB_!.getDistinctName('repeat_end', NameType.VARIABLE); code += 'var ' + endVar + ' = ' + repeats + ';\n'; } code += 'for (int ' + loopVar + ' = 0; ' + loopVar + ' < ' + endVar + '; ' + @@ -93,25 +94,25 @@ export function controls_for(block: Block, generator: DartGenerator) { let startVar = argument0; if (!argument0.match(/^\w+$/) && !stringUtils.isNumber(argument0)) { startVar = - generator.nameDB_.getDistinctName( + generator.nameDB_!.getDistinctName( variable0 + '_start', NameType.VARIABLE); code += 'var ' + startVar + ' = ' + argument0 + ';\n'; } let endVar = argument1; if (!argument1.match(/^\w+$/) && !stringUtils.isNumber(argument1)) { endVar = - generator.nameDB_.getDistinctName( + generator.nameDB_!.getDistinctName( 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( + generator.nameDB_!.getDistinctName( variable0 + '_inc', NameType.VARIABLE); code += 'num ' + incVar + ' = '; if (stringUtils.isNumber(increment)) { - code += Math.abs(increment) + ';\n'; + code += Math.abs(Number(increment)) + ';\n'; } else { code += '(' + increment + ').abs();\n'; } @@ -152,7 +153,7 @@ export function controls_flow_statements(block: Block, generator: DartGenerator) 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. diff --git a/generators/dart/math.ts b/generators/dart/math.ts index 4038b6c02a1..d0dc12307fa 100644 --- a/generators/dart/math.ts +++ b/generators/dart/math.ts @@ -19,32 +19,30 @@ import {Order} from './dart_generator.js'; export function math_number(block: Block, generator: DartGenerator): [string, Order] { // Numeric value. - let code = Number(block.getFieldValue('NUM')); - let order; - if (code === Infinity) { - code = 'double.infinity'; - order = Order.UNARY_POSTFIX; - } else if (code === -Infinity) { - code = '-double.infinity'; - order = Order.UNARY_PREFIX; + const number = Number(block.getFieldValue('NUM')); + if (number === Infinity) { + return ['double.infinity', Order.UNARY_POSTFIX]; + } else if (number === -Infinity) { + return ['-double.infinity', Order.UNARY_PREFIX]; } else { - // -4.abs() returns -4 in generator due to strange order of operation choices. - // -4 is actually an operator and a number. Reflect this in the order. - order = code < 0 ? Order.UNARY_PREFIX : Order.ATOMIC; + // -4.abs() returns -4 in generator due to strange order of + // operation choices. 4 is actually an operator and a number. + // Reflect this in the order. + return [String(number), number < 0 ? Order.UNARY_PREFIX : Order.ATOMIC]; } - return [code, order]; }; export function math_arithmetic(block: Block, generator: DartGenerator): [string, Order] { // Basic arithmetic operators, and power. - const OPERATORS = { + const OPERATORS: Record = { 'ADD': [' + ', Order.ADDITIVE], 'MINUS': [' - ', Order.ADDITIVE], 'MULTIPLY': [' * ', Order.MULTIPLICATIVE], 'DIVIDE': [' / ', Order.MULTIPLICATIVE], 'POWER': [null, Order.NONE], // Handle power separately. }; - 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'; @@ -52,7 +50,9 @@ export function math_arithmetic(block: Block, generator: DartGenerator): [string let code; // Power in generator requires a special case since it has no operator. if (!operator) { - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; code = 'Math.pow(' + argument0 + ', ' + argument1 + ')'; return [code, Order.UNARY_POSTFIX]; @@ -76,7 +76,9 @@ export function math_single(block: Block, generator: DartGenerator): [string, Or code = '-' + arg; return [code, Order.UNARY_PREFIX]; } - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; if (operator === 'ABS' || operator.substring(0, 5) === 'ROUND') { arg = generator.valueToCode(block, 'NUM', Order.UNARY_POSTFIX) || '0'; @@ -148,7 +150,7 @@ export function math_single(block: Block, generator: DartGenerator): [string, Or export function math_constant(block: Block, generator: DartGenerator): [string, Order] { // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY. - const CONSTANTS = { + const CONSTANTS: Record = { 'PI': ['Math.pi', Order.UNARY_POSTFIX], 'E': ['Math.e', Order.UNARY_POSTFIX], 'GOLDEN_RATIO': ['(1 + Math.sqrt(5)) / 2', Order.MULTIPLICATIVE], @@ -156,9 +158,12 @@ export function math_constant(block: Block, generator: DartGenerator): [string, 'SQRT1_2': ['Math.sqrt1_2', Order.UNARY_POSTFIX], 'INFINITY': ['double.infinity', Order.ATOMIC], }; - const constant = block.getFieldValue('CONSTANT'); + type ConstantOption = keyof typeof CONSTANTS; + const constant = block.getFieldValue('CONSTANT') as ConstantOption; if (constant !== 'INFINITY') { - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; } return CONSTANTS[constant]; @@ -167,7 +172,7 @@ export function math_constant(block: Block, generator: DartGenerator): [string, export function math_number_property(block: Block, generator: DartGenerator): [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 = { 'EVEN': [' % 2 == 0', Order.MULTIPLICATIVE, Order.EQUALITY], 'ODD': [' % 2 == 1', Order.MULTIPLICATIVE, Order.EQUALITY], 'WHOLE': [' % 1 == 0', Order.MULTIPLICATIVE, Order.EQUALITY], @@ -176,14 +181,17 @@ export function math_number_property(block: Block, generator: DartGenerator): [s 'DIVISIBLE_BY': [null, Order.MULTIPLICATIVE, Order.EQUALITY], 'PRIME': [null, Order.NONE, Order.UNARY_POSTFIX], }; - const dropdownProperty = block.getFieldValue('PROPERTY'); + type PropertyOption = keyof typeof PROPERTIES; + const dropdownProperty = block.getFieldValue('PROPERTY') as PropertyOption; const [suffix, inputOrder, outputOrder] = 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. - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; const functionName = generator.provideFunction_('math_isPrime', ` bool ${generator.FUNCTION_NAME_PLACEHOLDER_}(n) { @@ -252,7 +260,9 @@ num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { break; } case 'MIN': { - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; const functionName = generator.provideFunction_('math_min', ` num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { @@ -266,7 +276,9 @@ num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { break; } case 'MAX': { - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; const functionName = generator.provideFunction_('math_max', ` num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { @@ -317,7 +329,9 @@ num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { break; } case 'MODE': { - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; // As a list of numbers can contain more than one mode, // the returned result is provided as an array. @@ -356,7 +370,9 @@ List ${generator.FUNCTION_NAME_PLACEHOLDER_}(List values) { break; } case 'STD_DEV': { - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; const functionName = generator.provideFunction_('math_standard_deviation', ` @@ -378,7 +394,9 @@ num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { break; } case 'RANDOM': { - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; const functionName = generator.provideFunction_('math_random_item', ` dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { @@ -407,7 +425,9 @@ export function math_modulo(block: Block, generator: DartGenerator): [string, Or export function math_constrain(block: Block, generator: DartGenerator): [string, Order] { // Constrain a number between two limits. - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; const argument0 = generator.valueToCode(block, 'VALUE', Order.NONE) || '0'; @@ -421,7 +441,9 @@ export function math_constrain(block: Block, generator: DartGenerator): [string, export function math_random_int(block: Block, generator: DartGenerator): [string, Order] { // Random integer between [X] and [Y]. - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; const argument0 = generator.valueToCode(block, 'FROM', Order.NONE) || '0'; const argument1 = generator.valueToCode(block, 'TO', Order.NONE) || '0'; @@ -442,14 +464,18 @@ int ${generator.FUNCTION_NAME_PLACEHOLDER_}(num a, num b) { export function math_random_float(block: Block, generator: DartGenerator): [string, Order] { // Random fraction between 0 and 1. - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; return ['new Math.Random().nextDouble()', Order.UNARY_POSTFIX]; }; export function math_atan2(block: Block, generator: DartGenerator): [string, Order] { // Arctangent of point (X, Y) in degrees from -180 to 180. - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; const argument0 = generator.valueToCode(block, 'X', Order.NONE) || '0'; const argument1 = generator.valueToCode(block, 'Y', Order.NONE) || '0'; diff --git a/generators/dart/procedures.ts b/generators/dart/procedures.ts index a2144366ccb..33c0cb68972 100644 --- a/generators/dart/procedures.ts +++ b/generators/dart/procedures.ts @@ -11,6 +11,7 @@ // Former goog.module ID: Blockly.Dart.procedures import type {Block} from '../../core/block.js'; +import type {IfReturnBlock} from '../../blocks/procedures.js'; import type {DartGenerator} from './dart_generator.js'; import {Order} from './dart_generator.js'; @@ -56,7 +57,9 @@ export function procedures_defreturn(block: Block, generator: DartGenerator) { 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; }; @@ -81,7 +84,7 @@ export function procedures_callnoreturn(block: Block, generator: DartGenerator) // 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'; }; @@ -97,7 +100,7 @@ export function procedures_ifreturn(block: Block, generator: DartGenerator) { 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'; diff --git a/generators/dart/text.ts b/generators/dart/text.ts index bdca13b30f4..5ce4b016ce7 100644 --- a/generators/dart/text.ts +++ b/generators/dart/text.ts @@ -12,6 +12,7 @@ import type {Block} from '../../core/block.js'; import type {DartGenerator} from './dart_generator.js'; +import type {JoinMutatorBlock} from '../../blocks/text.js'; import {Order} from './dart_generator.js'; @@ -33,7 +34,8 @@ export function text_multiline(block: Block, generator: DartGenerator): [string, export function text_join(block: Block, generator: DartGenerator): [string, Order] { // Create a string made up of any number of elements of any type. - switch (block.itemCount_) { + const joinBlock = block as JoinMutatorBlock; + switch (joinBlock.itemCount_) { case 0: return ["''", Order.ATOMIC]; case 1: { @@ -43,8 +45,8 @@ export function text_join(block: Block, generator: DartGenerator): [string, Orde return [code, Order.UNARY_POSTFIX]; } default: { - const elements = new Array(block.itemCount_); - for (let i = 0; i < block.itemCount_; i++) { + const elements = new Array(joinBlock.itemCount_); + for (let i = 0; i < joinBlock.itemCount_; i++) { elements[i] = generator.valueToCode(block, 'ADD' + i, Order.NONE) || "''"; } @@ -124,7 +126,9 @@ String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String text, num x) { return [code, Order.UNARY_POSTFIX]; } case 'RANDOM': { - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; const functionName = generator.provideFunction_('text_random_letter', ` @@ -226,7 +230,8 @@ export function text_changeCase(block: Block, generator: DartGenerator): [string 'LOWERCASE': '.toLowerCase()', 'TITLECASE': null }; - const operator = OPERATORS[block.getFieldValue('CASE')]; + type OperatorOption = keyof typeof OPERATORS; + const operator = OPERATORS[block.getFieldValue('CASE') as OperatorOption]; const textOrder = operator ? Order.UNARY_POSTFIX : Order.NONE; const text = generator.valueToCode(block, 'TEXT', textOrder) || "''"; let code; @@ -263,7 +268,8 @@ export function text_trim(block: Block, generator: DartGenerator): [string, Orde 'RIGHT': '.replaceFirst(new RegExp(r\'\\s+$\'), \'\')', '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.UNARY_POSTFIX) || "''"; return [text + operator, Order.UNARY_POSTFIX]; @@ -277,7 +283,9 @@ export function text_print(block: Block, generator: DartGenerator) { export function text_prompt_ext(block: Block, generator: DartGenerator): [string, Order] { // Prompt function. - generator.definitions_['import_dart_html'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_html'] = 'import \'dart:html\' as Html;'; let msg; if (block.getField('TEXT')) { @@ -290,7 +298,9 @@ export function text_prompt_ext(block: Block, generator: DartGenerator): [string let code = 'Html.window.prompt(' + msg + ', \'\')'; const toNumber = block.getFieldValue('TYPE') === 'NUMBER'; if (toNumber) { - generator.definitions_['import_dart_math'] = + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; code = 'Math.parseDouble(' + code + ')'; } From d934aa12c4125c588f2616b5a0d5a84567b6cc33 Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Sun, 12 Nov 2023 19:25:57 +0000 Subject: [PATCH 5/8] fix(generators): Fix minor inconsistencies in JS and Python The migration of the JavaScript and Python generators inadvertently introduced some inconsistencies in the code, e.g.: - Incorrect import ordering. - Putting executable code before the initial comment line that most generator functions begin with, and/or deleting or replacing these comments. - N.B. however that these inline comments should have been JSDocs; a task to convert them has been added to #7600. --- generators/javascript/lists.ts | 2 +- generators/javascript/logic.ts | 2 +- generators/javascript/text.ts | 4 ++-- generators/python/lists.ts | 2 +- generators/python/text.ts | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/generators/javascript/lists.ts b/generators/javascript/lists.ts index 6283f1d274b..0a1fcc87f73 100644 --- a/generators/javascript/lists.ts +++ b/generators/javascript/lists.ts @@ -29,8 +29,8 @@ 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 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'; diff --git a/generators/javascript/logic.ts b/generators/javascript/logic.ts index 36604ea7d12..bdc51f99748 100644 --- a/generators/javascript/logic.ts +++ b/generators/javascript/logic.ts @@ -63,7 +63,7 @@ export function logic_compare( block: Block, generator: JavascriptGenerator, ): [string, Order] { - // Dictionary of OP comparison operators and their implementations. + // Comparison operator. const OPERATORS = { 'EQ': '==', 'NEQ': '!=', diff --git a/generators/javascript/text.ts b/generators/javascript/text.ts index 79008cba9a0..7e3b0e4c148 100644 --- a/generators/javascript/text.ts +++ b/generators/javascript/text.ts @@ -11,8 +11,8 @@ // Former goog.module ID: Blockly.JavaScript.texts import type {Block} from '../../core/block.js'; -import type {JoinMutatorBlock} from '../../blocks/text.js'; import type {JavascriptGenerator} from './javascript_generator.js'; +import type {JoinMutatorBlock} from '../../blocks/text.js'; import {Order} from './javascript_generator.js'; /** @@ -80,8 +80,8 @@ 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. + const joinBlock = block as JoinMutatorBlock; switch (joinBlock.itemCount_) { case 0: return ["''", Order.ATOMIC]; diff --git a/generators/python/lists.ts b/generators/python/lists.ts index 02bd56ee560..7577cc4178f 100644 --- a/generators/python/lists.ts +++ b/generators/python/lists.ts @@ -29,8 +29,8 @@ export function lists_create_with( block: Block, generator: PythonGenerator, ): [string, Order] { - const createWithBlock = block as CreateWithBlock; // Create a list with any number of elements of any type. + 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) || 'None'; diff --git a/generators/python/text.ts b/generators/python/text.ts index dca9e98322c..7b554efda40 100644 --- a/generators/python/text.ts +++ b/generators/python/text.ts @@ -61,9 +61,9 @@ export function text_join( block: Block, generator: PythonGenerator, ): [string, Order] { - const joinBlock = block as JoinMutatorBlock; // Create a string made up of any number of elements of any type. // Should we allow joining by '-' or ',' or any other characters? + const joinBlock = block as JoinMutatorBlock; switch (joinBlock.itemCount_) { case 0: return ["''", Order.ATOMIC]; From b84171e7f01d178911564cecb0ac291d0bccbdcc Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Sun, 12 Nov 2023 19:27:01 +0000 Subject: [PATCH 6/8] refactor(generators): Migrate generators/dart.js to TypeScript The way the generator functions are added to dartGenerator.forBlock has been modified so that incorrect generator function signatures will cause tsc to generate a type error. --- generators/{dart.js => dart.ts} | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) rename generators/{dart.js => dart.ts} (81%) diff --git a/generators/dart.js b/generators/dart.ts similarity index 81% rename from generators/dart.js rename to generators/dart.ts index 26a3c62c7e4..91eaf2e62e2 100644 --- a/generators/dart.js +++ b/generators/dart.ts @@ -36,8 +36,10 @@ export const dartGenerator = new DartGenerator(); dartGenerator.addReservedWords('Html,Math'); // Install per-block-type generator functions: -Object.assign( - dartGenerator.forBlock, - colour, lists, logic, loops, math, procedures, - text, variables, variablesDynamic -); +const generators: typeof dartGenerator.forBlock = { + ...colour, ...lists, ...logic, ...loops, ...math, + ...procedures, ...text, ...variables, ...variablesDynamic +}; +for (const name in generators) { + dartGenerator.forBlock[name] = generators[name]; +} From 569f9c5192da5b59dfcada97f95462602d5046e8 Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Sun, 12 Nov 2023 19:30:05 +0000 Subject: [PATCH 7/8] 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 is one of the items in the to-do list in #7600. --- generators/dart.ts | 11 +- generators/dart/colour.ts | 65 ++++--- generators/dart/dart_generator.ts | 64 ++++--- generators/dart/lists.ts | 264 +++++++++++++++++---------- generators/dart/logic.ts | 106 +++++++---- generators/dart/loops.ts | 138 +++++++++----- generators/dart/math.ts | 227 +++++++++++++++-------- generators/dart/procedures.ts | 63 ++++--- generators/dart/text.ts | 207 +++++++++++++-------- generators/dart/variables.ts | 18 +- generators/dart/variables_dynamic.ts | 1 - 11 files changed, 742 insertions(+), 422 deletions(-) diff --git a/generators/dart.ts b/generators/dart.ts index 91eaf2e62e2..3bd1271adfb 100644 --- a/generators/dart.ts +++ b/generators/dart.ts @@ -37,8 +37,15 @@ dartGenerator.addReservedWords('Html,Math'); // Install per-block-type generator functions: const generators: typeof dartGenerator.forBlock = { - ...colour, ...lists, ...logic, ...loops, ...math, - ...procedures, ...text, ...variables, ...variablesDynamic + ...colour, + ...lists, + ...logic, + ...loops, + ...math, + ...procedures, + ...text, + ...variables, + ...variablesDynamic, }; for (const name in generators) { dartGenerator.forBlock[name] = generators[name]; diff --git a/generators/dart/colour.ts b/generators/dart/colour.ts index 59af9ce2a62..b55fc5e6f07 100644 --- a/generators/dart/colour.ts +++ b/generators/dart/colour.ts @@ -14,22 +14,29 @@ import type {Block} from '../../core/block.js'; import type {DartGenerator} from './dart_generator.js'; import {Order} from './dart_generator.js'; - // RESERVED WORDS: 'Math' -export function colour_picker(block: Block, generator: DartGenerator): [string, Order] { +export function colour_picker( + block: Block, + generator: DartGenerator, +): [string, Order] { // Colour picker. const code = generator.quote_(block.getFieldValue('COLOUR')); return [code, Order.ATOMIC]; -}; +} -export function colour_random(block: Block, generator: DartGenerator): [string, Order] { +export function colour_random( + block: Block, + generator: DartGenerator, +): [string, Order] { // Generate a random colour. // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - "import 'dart:math' as Math;"; - const functionName = generator.provideFunction_('colour_random', ` + "import 'dart:math' as Math;"; + const functionName = generator.provideFunction_( + 'colour_random', + ` String ${generator.FUNCTION_NAME_PLACEHOLDER_}() { String hex = '0123456789abcdef'; var rnd = new Math.Random(); @@ -37,12 +44,16 @@ String ${generator.FUNCTION_NAME_PLACEHOLDER_}() { '\${hex[rnd.nextInt(16)]}\${hex[rnd.nextInt(16)]}' '\${hex[rnd.nextInt(16)]}\${hex[rnd.nextInt(16)]}'; } -`); +`, + ); const code = functionName + '()'; return [code, Order.UNARY_POSTFIX]; -}; +} -export function colour_rgb(block: Block, generator: DartGenerator): [string, Order] { +export function colour_rgb( + block: Block, + generator: DartGenerator, +): [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; @@ -51,8 +62,10 @@ export function colour_rgb(block: Block, generator: DartGenerator): [string, Ord // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - "import 'dart:math' as Math;"; - const functionName = generator.provideFunction_('colour_rgb', ` + "import 'dart:math' as Math;"; + const functionName = generator.provideFunction_( + 'colour_rgb', + ` String ${generator.FUNCTION_NAME_PLACEHOLDER_}(num r, num g, num b) { num rn = (Math.max(Math.min(r, 100), 0) * 2.55).round(); String rs = rn.toInt().toRadixString(16); @@ -68,25 +81,28 @@ String ${generator.FUNCTION_NAME_PLACEHOLDER_}(num r, num g, num b) { bs = bs.substring(bs.length - 2); return '#$rs$gs$bs'; } -`); +`, + ); const code = functionName + '(' + red + ', ' + green + ', ' + blue + ')'; return [code, Order.UNARY_POSTFIX]; -}; +} -export function colour_blend(block: Block, generator: DartGenerator): [string, Order] { +export function colour_blend( + block: Block, + generator: DartGenerator, +): [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 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; // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - "import 'dart:math' as Math;"; - const functionName = generator.provideFunction_('colour_blend', ` + "import 'dart:math' as Math;"; + const functionName = generator.provideFunction_( + 'colour_blend', + ` String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String c1, String c2, num ratio) { ratio = Math.max(Math.min(ratio, 1), 0); int r1 = int.parse('0x\${c1.substring(1, 3)}'); @@ -109,7 +125,8 @@ String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String c1, String c2, num ratio) bs = bs.substring(bs.length - 2); return '#$rs$gs$bs'; } -`); +`, + ); const code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')'; return [code, Order.UNARY_POSTFIX]; -}; +} diff --git a/generators/dart/dart_generator.ts b/generators/dart/dart_generator.ts index dc46bf2d29f..a2736c2bcf1 100644 --- a/generators/dart/dart_generator.ts +++ b/generators/dart/dart_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://dart.dev/guides/language/language-tour#operators */ +// prettier-ignore export enum Order { ATOMIC = 0, // 0 "" ... UNARY_POSTFIX = 1, // expr++ expr-- () [] . ?. @@ -82,19 +82,19 @@ export class DartGenerator extends CodeGenerator { // https://www.dartlang.org/docs/spec/latest/dart-language-specification.pdf // Section 16.1.1 'assert,break,case,catch,class,const,continue,default,do,else,enum,' + - 'extends,false,final,finally,for,if,in,is,new,null,rethrow,return,' + - 'super,switch,this,throw,true,try,var,void,while,with,' + - // https://api.dartlang.org/dart_core.html - 'print,identityHashCode,identical,BidirectionalIterator,Comparable,' + - 'double,Function,int,Invocation,Iterable,Iterator,List,Map,Match,num,' + - 'Pattern,RegExp,Set,StackTrace,String,StringSink,Type,bool,DateTime,' + - 'Deprecated,Duration,Expando,Null,Object,RuneIterator,Runes,Stopwatch,' + - 'StringBuffer,Symbol,Uri,Comparator,AbstractClassInstantiationError,' + - 'ArgumentError,AssertionError,CastError,ConcurrentModificationError,' + - 'CyclicInitializationError,Error,Exception,FallThroughError,' + - 'FormatException,IntegerDivisionByZeroException,NoSuchMethodError,' + - 'NullThrownError,OutOfMemoryError,RangeError,StackOverflowError,' + - 'StateError,TypeError,UnimplementedError,UnsupportedError' + 'extends,false,final,finally,for,if,in,is,new,null,rethrow,return,' + + 'super,switch,this,throw,true,try,var,void,while,with,' + + // https://api.dartlang.org/dart_core.html + 'print,identityHashCode,identical,BidirectionalIterator,Comparable,' + + 'double,Function,int,Invocation,Iterable,Iterator,List,Map,Match,num,' + + 'Pattern,RegExp,Set,StackTrace,String,StringSink,Type,bool,DateTime,' + + 'Deprecated,Duration,Expando,Null,Object,RuneIterator,Runes,Stopwatch,' + + 'StringBuffer,Symbol,Uri,Comparator,AbstractClassInstantiationError,' + + 'ArgumentError,AssertionError,CastError,ConcurrentModificationError,' + + 'CyclicInitializationError,Error,Exception,FallThroughError,' + + 'FormatException,IntegerDivisionByZeroException,NoSuchMethodError,' + + 'NullThrownError,OutOfMemoryError,RangeError,StackOverflowError,' + + 'StateError,TypeError,UnimplementedError,UnsupportedError', ); } @@ -120,21 +120,22 @@ export class DartGenerator extends CodeGenerator { // Add developer variables (not created or named by the user). const devVarList = Variables.allDeveloperVariables(workspace); for (let i = 0; i < devVarList.length; i++) { - defvars.push(this.nameDB_.getName(devVarList[i], - NameType.DEVELOPER_VARIABLE)); + defvars.push( + 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)); + defvars.push( + this.nameDB_.getName(variables[i].getId(), NameType.VARIABLE), + ); } // Declare all of the variables. if (defvars.length) { - this.definitions_['variables'] = - 'var ' + defvars.join(', ') + ';'; + this.definitions_['variables'] = 'var ' + defvars.join(', ') + ';'; } this.isInitialized = true; } @@ -191,11 +192,12 @@ export class DartGenerator extends CodeGenerator { */ quote_(string: string): string { // Can't use goog.string.quote since $ must also be escaped. - string = string.replace(/\\/g, '\\\\') - .replace(/\n/g, '\\\n') - .replace(/\$/g, '\\$') - .replace(/'/g, '\\\''); - return '\'' + string + '\''; + string = string + .replace(/\\/g, '\\\\') + .replace(/\n/g, '\\\n') + .replace(/\$/g, '\\$') + .replace(/'/g, "\\'"); + return "'" + string + "'"; } /** @@ -209,7 +211,7 @@ export class DartGenerator extends CodeGenerator { const lines = string.split(/\n/g).map(this.quote_); // Join with the following, plus a newline: // + '\n' + - return lines.join(' + \'\\n\' + \n'); + return lines.join(" + '\\n' + \n"); } /** @@ -252,7 +254,7 @@ export class DartGenerator extends CodeGenerator { } } const nextBlock = - block.nextConnection && block.nextConnection.targetBlock(); + block.nextConnection && block.nextConnection.targetBlock(); const nextCode = thisOnly ? '' : this.blockToCode(nextBlock); return commentCode + code + nextCode; } @@ -269,7 +271,13 @@ export class DartGenerator 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/dart/lists.ts b/generators/dart/lists.ts index e156a06dae7..d1b65cc5329 100644 --- a/generators/dart/lists.ts +++ b/generators/dart/lists.ts @@ -16,78 +16,98 @@ import type {DartGenerator} from './dart_generator.js'; import {NameType} from '../../core/names.js'; import {Order} from './dart_generator.js'; - // RESERVED WORDS: 'Math' -export function lists_create_empty(block: Block, generator: DartGenerator): [string, Order] { +export function lists_create_empty( + block: Block, + generator: DartGenerator, +): [string, Order] { // Create an empty list. return ['[]', Order.ATOMIC]; -}; +} -export function lists_create_with(block: Block, generator: DartGenerator): [string, Order] { +export function lists_create_with( + block: Block, + generator: DartGenerator, +): [string, Order] { // Create a list with any number of elements of any type. 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'; + 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: DartGenerator): [string, Order] { +export function lists_repeat( + block: Block, + generator: DartGenerator, +): [string, Order] { // Create a list with one element repeated. - 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 = 'new List.filled(' + repeatCount + ', ' + element + ')'; return [code, Order.UNARY_POSTFIX]; -}; +} -export function lists_length(block: Block, generator: DartGenerator): [string, Order] { +export function lists_length( + block: Block, + generator: DartGenerator, +): [string, Order] { // String or array length. const list = - generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || '[]'; + generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || '[]'; return [list + '.length', Order.UNARY_POSTFIX]; -}; +} -export function lists_isEmpty(block: Block, generator: DartGenerator): [string, Order] { +export function lists_isEmpty( + block: Block, + generator: DartGenerator, +): [string, Order] { // Is the string null or array empty? const list = - generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || '[]'; + generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || '[]'; return [list + '.isEmpty', Order.UNARY_POSTFIX]; -}; +} -export function lists_indexOf(block: Block, generator: DartGenerator): [string, Order] { +export function lists_indexOf( + block: Block, + generator: DartGenerator, +): [string, Order] { // Find an item in the list. const operator = - block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; + block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; const item = generator.valueToCode(block, 'FIND', Order.NONE) || "''"; const list = - generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || '[]'; + generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || '[]'; const code = list + '.' + operator + '(' + item + ')'; if (block.workspace.options.oneBasedIndex) { return [code + ' + 1', Order.ADDITIVE]; } return [code, Order.UNARY_POSTFIX]; -}; +} -export function lists_getIndex(block: Block, generator: DartGenerator): [string, Order] | string { +export function lists_getIndex( + block: Block, + generator: DartGenerator, +): [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' || where === 'FROM_END') ? - Order.NONE : - Order.UNARY_POSTFIX; + const listOrder = + where === 'RANDOM' || where === 'FROM_END' + ? Order.NONE + : Order.UNARY_POSTFIX; let list = generator.valueToCode(block, 'VALUE', listOrder) || '[]'; // Cache non-trivial values to variables to prevent repeated look-ups. // Closure, which accesses and modifies 'list'. function cacheList() { - const listVar = - generator.nameDB_!.getDistinctName('tmp_list', NameType.VARIABLE); + const listVar = generator.nameDB_!.getDistinctName( + 'tmp_list', + NameType.VARIABLE, + ); const code = 'List ' + listVar + ' = ' + list + ';\n'; list = listVar; return code; @@ -95,53 +115,60 @@ export function lists_getIndex(block: Block, generator: DartGenerator): [string, // If `list` would be evaluated more than once (which is the case for // RANDOM REMOVE and FROM_END) and is non-trivial, make sure to access it // only once. - if (((where === 'RANDOM' && mode === 'REMOVE') || where === 'FROM_END') && - !list.match(/^\w+$/)) { + if ( + ((where === 'RANDOM' && mode === 'REMOVE') || where === 'FROM_END') && + !list.match(/^\w+$/) + ) { // `list` is an expression, so we may not evaluate it more than once. if (where === 'RANDOM') { // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; + "import 'dart:math' as Math;"; // We can use multiple statements. let code = cacheList(); - const xVar = - generator.nameDB_!.getDistinctName('tmp_x', NameType.VARIABLE); - code += 'int ' + xVar + ' = new Math.Random().nextInt(' + list + - '.length);\n'; + const xVar = generator.nameDB_!.getDistinctName( + 'tmp_x', + NameType.VARIABLE, + ); + code += + 'int ' + xVar + ' = new Math.Random().nextInt(' + list + '.length);\n'; code += list + '.removeAt(' + xVar + ');\n'; return code; - } else { // where === 'FROM_END' + } else { + // where === 'FROM_END' if (mode === 'REMOVE') { // We can use multiple statements. - const at = - generator.getAdjusted(block, 'AT', 1, false, Order.ADDITIVE); + const at = generator.getAdjusted(block, 'AT', 1, false, Order.ADDITIVE); let code = cacheList(); - code += list + '.removeAt(' + list + '.length' + - ' - ' + at + ');\n'; + code += list + '.removeAt(' + list + '.length' + ' - ' + at + ');\n'; return code; - } else if (mode === 'GET') { const at = generator.getAdjusted(block, 'AT', 1); // We need to create a procedure to avoid reevaluating values. - const functionName = generator.provideFunction_('lists_get_from_end', ` + const functionName = generator.provideFunction_( + 'lists_get_from_end', + ` dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List my_list, num x) { x = my_list.length - x; return my_list[x]; } -`); +`, + ); const code = functionName + '(' + list + ', ' + at + ')'; return [code, Order.UNARY_POSTFIX]; } else if (mode === 'GET_REMOVE') { const at = generator.getAdjusted(block, 'AT', 1); // We need to create a procedure to avoid reevaluating values. - const functionName = - generator.provideFunction_('lists_remove_from_end', ` + const functionName = generator.provideFunction_( + 'lists_remove_from_end', + ` dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List my_list, num x) { x = my_list.length - x; return my_list.removeAt(x); } -`); +`, + ); const code = functionName + '(' + list + ', ' + at + ')'; return [code, Order.UNARY_POSTFIX]; } @@ -186,8 +213,7 @@ dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List my_list, num x) { break; } case 'FROM_END': { - const at = - generator.getAdjusted(block, 'AT', 1, false, Order.ADDITIVE); + const at = generator.getAdjusted(block, 'AT', 1, false, Order.ADDITIVE); if (mode === 'GET') { const code = list + '[' + list + '.length - ' + at + ']'; return [code, Order.UNARY_POSTFIX]; @@ -205,33 +231,43 @@ dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List my_list, num x) { // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; + "import 'dart:math' as Math;"; if (mode === 'REMOVE') { // We can use multiple statements. - const xVar = - generator.nameDB_!.getDistinctName('tmp_x', NameType.VARIABLE); - let code = 'int ' + xVar + ' = new Math.Random().nextInt(' + list + - '.length);\n'; + const xVar = generator.nameDB_!.getDistinctName( + 'tmp_x', + NameType.VARIABLE, + ); + let code = + 'int ' + + xVar + + ' = new Math.Random().nextInt(' + + list + + '.length);\n'; code += list + '.removeAt(' + xVar + ');\n'; return code; } else if (mode === 'GET') { - const functionName = - generator.provideFunction_('lists_get_random_item', ` + const functionName = generator.provideFunction_( + 'lists_get_random_item', + ` dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List my_list) { int x = new Math.Random().nextInt(my_list.length); return my_list[x]; } -`); +`, + ); const code = functionName + '(' + list + ')'; return [code, Order.UNARY_POSTFIX]; } else if (mode === 'GET_REMOVE') { - const functionName = - generator.provideFunction_('lists_remove_random_item', ` + const functionName = generator.provideFunction_( + 'lists_remove_random_item', + ` dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List my_list) { int x = new Math.Random().nextInt(my_list.length); return my_list.removeAt(x); } -`); +`, + ); const code = functionName + '(' + list + ')'; return [code, Order.UNARY_POSTFIX]; } @@ -239,25 +275,25 @@ dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List my_list) { } } throw Error('Unhandled combination (lists_getIndex).'); -}; +} export function lists_setIndex(block: Block, generator: DartGenerator) { // 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'; - let list = - generator.valueToCode(block, 'LIST', Order.UNARY_POSTFIX) || '[]'; - const value = - generator.valueToCode(block, 'TO', Order.ASSIGNMENT) || 'null'; + let list = generator.valueToCode(block, 'LIST', Order.UNARY_POSTFIX) || '[]'; + 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('tmp_list', NameType.VARIABLE); + const listVar = generator.nameDB_!.getDistinctName( + 'tmp_list', + NameType.VARIABLE, + ); const code = 'List ' + listVar + ' = ' + list + ';\n'; list = listVar; return code; @@ -289,15 +325,14 @@ export function lists_setIndex(block: Block, generator: DartGenerator) { break; } case 'FROM_END': { - const at = - generator.getAdjusted(block, 'AT', 1, false, Order.ADDITIVE); + const at = generator.getAdjusted(block, 'AT', 1, false, Order.ADDITIVE); let code = cacheList(); if (mode === 'SET') { code += list + '[' + list + '.length - ' + at + '] = ' + value + ';\n'; return code; } else if (mode === 'INSERT') { - code += list + '.insert(' + list + '.length - ' + at + ', ' + value + - ');\n'; + code += + list + '.insert(' + list + '.length - ' + at + ', ' + value + ');\n'; return code; } break; @@ -306,12 +341,14 @@ export function lists_setIndex(block: Block, generator: DartGenerator) { // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; + "import 'dart:math' as Math;"; let code = cacheList(); - const xVar = - generator.nameDB_!.getDistinctName('tmp_x', NameType.VARIABLE); - code += 'int ' + xVar + ' = new Math.Random().nextInt(' + list + - '.length);\n'; + const xVar = generator.nameDB_!.getDistinctName( + 'tmp_x', + NameType.VARIABLE, + ); + code += + 'int ' + xVar + ' = new Math.Random().nextInt(' + list + '.length);\n'; if (mode === 'SET') { code += list + '[' + xVar + '] = ' + value + ';\n'; return code; @@ -323,17 +360,22 @@ export function lists_setIndex(block: Block, generator: DartGenerator) { } } throw Error('Unhandled combination (lists_setIndex).'); -}; +} -export function lists_getSublist(block: Block, generator: DartGenerator): [string, Order] { +export function lists_getSublist( + block: Block, + generator: DartGenerator, +): [string, Order] { // Get sublist. const list = - generator.valueToCode(block, 'LIST', Order.UNARY_POSTFIX) || '[]'; + generator.valueToCode(block, 'LIST', Order.UNARY_POSTFIX) || '[]'; const where1 = block.getFieldValue('WHERE1'); const where2 = block.getFieldValue('WHERE2'); let code; - if (list.match(/^\w+$/) || - (where1 !== 'FROM_END' && where2 === 'FROM_START')) { + if ( + list.match(/^\w+$/) || + (where1 !== 'FROM_END' && where2 === 'FROM_START') + ) { // If the list is a is a variable or doesn't require a call for length, // don't generate a helper function. let at1; @@ -374,7 +416,9 @@ export function lists_getSublist(block: Block, generator: DartGenerator): [strin } 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', + ` List ${generator.FUNCTION_NAME_PLACEHOLDER_}(List list, String where1, num at1, String where2, num at2) { int getAt(String where, num at) { if (where == 'FROM_END') { @@ -392,19 +436,36 @@ List ${generator.FUNCTION_NAME_PLACEHOLDER_}(List list, String where1, num at1, at2 = getAt(where2, at2) + 1; return list.sublist(at1, at2); } -`); - code = functionName + '(' + list + ', \'' + where1 + '\', ' + at1 + ', \'' + - where2 + '\', ' + at2 + ')'; +`, + ); + code = + functionName + + '(' + + list + + ", '" + + where1 + + "', " + + at1 + + ", '" + + where2 + + "', " + + at2 + + ')'; } return [code, Order.UNARY_POSTFIX]; -}; +} -export function lists_sort(block: Block, generator: DartGenerator): [string, Order] { +export function lists_sort( + block: Block, + generator: DartGenerator, +): [string, Order] { // Block for sorting a list. const list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; const direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; const type = block.getFieldValue('TYPE'); - const sortFunctionName = generator.provideFunction_('lists_sort', ` + const sortFunctionName = generator.provideFunction_( + 'lists_sort', + ` List ${generator.FUNCTION_NAME_PLACEHOLDER_}(List list, String type, int direction) { var compareFuncs = { 'NUMERIC': (a, b) => (direction * a.compareTo(b)).toInt(), @@ -418,19 +479,21 @@ List ${generator.FUNCTION_NAME_PLACEHOLDER_}(List list, String type, int directi list.sort(compare); return list; } -`); +`, + ); return [ - sortFunctionName + '(' + list + ', ' + - '"' + type + '", ' + direction + ')', - Order.UNARY_POSTFIX + sortFunctionName + '(' + list + ', ' + '"' + type + '", ' + direction + ')', + Order.UNARY_POSTFIX, ]; -}; +} -export function lists_split(block: Block, generator: DartGenerator): [string, Order] { +export function lists_split( + block: Block, + generator: DartGenerator, +): [string, Order] { // Block for splitting text into a list, or joining a list into text. let input = generator.valueToCode(block, 'INPUT', Order.UNARY_POSTFIX); - 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') { @@ -448,12 +511,15 @@ export function lists_split(block: Block, generator: DartGenerator): [string, Or } const code = input + '.' + functionName + '(' + delimiter + ')'; return [code, Order.UNARY_POSTFIX]; -}; +} -export function lists_reverse(block: Block, generator: DartGenerator): [string, Order] { +export function lists_reverse( + block: Block, + generator: DartGenerator, +): [string, Order] { // Block for reversing a list. const list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; // XXX What should the operator precedence be for a `new`? const code = 'new List.from(' + list + '.reversed)'; return [code, Order.UNARY_POSTFIX]; -}; +} diff --git a/generators/dart/logic.ts b/generators/dart/logic.ts index cddbcb0e84e..82d9d563b56 100644 --- a/generators/dart/logic.ts +++ b/generators/dart/logic.ts @@ -14,28 +14,34 @@ import type {Block} from '../../core/block.js'; import type {DartGenerator} from './dart_generator.js'; import {Order} from './dart_generator.js'; - export function controls_if(block: Block, generator: DartGenerator) { // 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)); @@ -43,38 +49,48 @@ export function controls_if(block: Block, generator: DartGenerator) { 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: Block, generator: DartGenerator): [string, Order] { +export function logic_compare( + block: Block, + generator: DartGenerator, +): [string, Order] { // Comparison operator. - 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: DartGenerator): [string, Order] { +export function logic_operation( + block: Block, + generator: DartGenerator, +): [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) { @@ -83,7 +99,7 @@ export function logic_operation(block: Block, generator: DartGenerator): [string 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; } @@ -93,35 +109,47 @@ export function logic_operation(block: Block, generator: DartGenerator): [string } const code = argument0 + ' ' + operator + ' ' + argument1; return [code, order]; -}; +} -export function logic_negate(block: Block, generator: DartGenerator): [string, Order] { +export function logic_negate( + block: Block, + generator: DartGenerator, +): [string, Order] { // Negation. const order = Order.UNARY_PREFIX; const argument0 = generator.valueToCode(block, 'BOOL', order) || 'true'; const code = '!' + argument0; return [code, order]; -}; +} -export function logic_boolean(block: Block, generator: DartGenerator): [string, Order] { +export function logic_boolean( + block: Block, + generator: DartGenerator, +): [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: DartGenerator): [string, Order] { +export function logic_null( + block: Block, + generator: DartGenerator, +): [string, Order] { // Null data type. return ['null', Order.ATOMIC]; -}; +} -export function logic_ternary(block: Block, generator: DartGenerator): [string, Order] { +export function logic_ternary( + block: Block, + generator: DartGenerator, +): [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/dart/loops.ts b/generators/dart/loops.ts index adb335d905b..5f54d201837 100644 --- a/generators/dart/loops.ts +++ b/generators/dart/loops.ts @@ -17,7 +17,6 @@ import type {DartGenerator} from './dart_generator.js'; import {NameType} from '../../core/names.js'; import {Order} from './dart_generator.js'; - export function controls_repeat_ext(block: Block, generator: DartGenerator) { let repeats; // Repeat n times. @@ -26,24 +25,37 @@ export function controls_repeat_ext(block: Block, generator: DartGenerator) { 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 (int ' + loopVar + ' = 0; ' + loopVar + ' < ' + endVar + '; ' + - loopVar + '++) {\n' + branch + '}\n'; + code += + 'for (int ' + + loopVar + + ' = 0; ' + + loopVar + + ' < ' + + endVar + + '; ' + + loopVar + + '++) {\n' + + branch + + '}\n'; return code; -}; +} export const controls_repeat = controls_repeat_ext; @@ -51,36 +63,47 @@ export function controls_whileUntil(block: Block, generator: DartGenerator) { // Do while/until loop. const until = block.getFieldValue('MODE') === 'UNTIL'; let argument0 = - generator.valueToCode( - block, 'BOOL', until ? Order.UNARY_PREFIX : Order.NONE) || - 'false'; + generator.valueToCode( + block, + 'BOOL', + until ? Order.UNARY_PREFIX : 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: DartGenerator) { // 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 ? '++' : '--'; @@ -93,23 +116,26 @@ export function controls_for(block: Block, generator: DartGenerator) { // 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 += 'var ' + 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 += '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); + const incVar = generator.nameDB_!.getDistinctName( + variable0 + '_inc', + NameType.VARIABLE, + ); code += 'num ' + incVar + ' = '; if (stringUtils.isNumber(increment)) { code += Math.abs(Number(increment)) + ';\n'; @@ -119,28 +145,48 @@ export function controls_for(block: Block, generator: DartGenerator) { 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: DartGenerator) { // 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); const code = - 'for (var ' + variable0 + ' in ' + argument0 + ') {\n' + branch + '}\n'; + 'for (var ' + variable0 + ' in ' + argument0 + ') {\n' + branch + '}\n'; return code; -}; +} -export function controls_flow_statements(block: Block, generator: DartGenerator) { +export function controls_flow_statements( + block: Block, + generator: DartGenerator, +) { // Flow statements: continue, break. let xfix = ''; if (generator.STATEMENT_PREFIX) { @@ -168,4 +214,4 @@ export function controls_flow_statements(block: Block, generator: DartGenerator) return xfix + 'continue;\n'; } throw Error('Unknown flow statement.'); -}; +} diff --git a/generators/dart/math.ts b/generators/dart/math.ts index d0dc12307fa..01830a0b12c 100644 --- a/generators/dart/math.ts +++ b/generators/dart/math.ts @@ -14,10 +14,12 @@ import type {Block} from '../../core/block.js'; import type {DartGenerator} from './dart_generator.js'; import {Order} from './dart_generator.js'; - // RESERVED WORDS: 'Math' -export function math_number(block: Block, generator: DartGenerator): [string, Order] { +export function math_number( + block: Block, + generator: DartGenerator, +): [string, Order] { // Numeric value. const number = Number(block.getFieldValue('NUM')); if (number === Infinity) { @@ -30,16 +32,19 @@ export function math_number(block: Block, generator: DartGenerator): [string, Or // Reflect this in the order. return [String(number), number < 0 ? Order.UNARY_PREFIX : Order.ATOMIC]; } -}; +} -export function math_arithmetic(block: Block, generator: DartGenerator): [string, Order] { +export function math_arithmetic( + block: Block, + generator: DartGenerator, +): [string, Order] { // Basic arithmetic operators, and power. const OPERATORS: Record = { 'ADD': [' + ', Order.ADDITIVE], 'MINUS': [' - ', Order.ADDITIVE], 'MULTIPLY': [' * ', Order.MULTIPLICATIVE], 'DIVIDE': [' / ', Order.MULTIPLICATIVE], - '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]; @@ -53,15 +58,18 @@ export function math_arithmetic(block: Block, generator: DartGenerator): [string // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; + "import 'dart:math' as Math;"; code = 'Math.pow(' + argument0 + ', ' + argument1 + ')'; return [code, Order.UNARY_POSTFIX]; } code = argument0 + operator + argument1; return [code, order]; -}; +} -export function math_single(block: Block, generator: DartGenerator): [string, Order] { +export function math_single( + block: Block, + generator: DartGenerator, +): [string, Order] { // Math operators with single operand. const operator = block.getFieldValue('OP'); let code; @@ -79,7 +87,7 @@ export function math_single(block: Block, generator: DartGenerator): [string, Or // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; + "import 'dart:math' as Math;"; if (operator === 'ABS' || operator.substring(0, 5) === 'ROUND') { arg = generator.valueToCode(block, 'NUM', Order.UNARY_POSTFIX) || '0'; } else if (operator === 'SIN' || operator === 'COS' || operator === 'TAN') { @@ -146,11 +154,14 @@ export function math_single(block: Block, generator: DartGenerator): [string, Or throw Error('Unknown math operator: ' + operator); } return [code, Order.MULTIPLICATIVE]; -}; +} -export function math_constant(block: Block, generator: DartGenerator): [string, Order] { +export function math_constant( + block: Block, + generator: DartGenerator, +): [string, Order] { // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY. - const CONSTANTS: Record = { + const CONSTANTS: Record = { 'PI': ['Math.pi', Order.UNARY_POSTFIX], 'E': ['Math.e', Order.UNARY_POSTFIX], 'GOLDEN_RATIO': ['(1 + Math.sqrt(5)) / 2', Order.MULTIPLICATIVE], @@ -164,12 +175,15 @@ export function math_constant(block: Block, generator: DartGenerator): [string, // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; + "import 'dart:math' as Math;"; } return CONSTANTS[constant]; -}; +} -export function math_number_property(block: Block, generator: DartGenerator): [string, Order] { +export function math_number_property( + block: Block, + generator: DartGenerator, +): [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 = { @@ -184,16 +198,18 @@ export function math_number_property(block: Block, generator: DartGenerator): [s type PropertyOption = keyof typeof PROPERTIES; const dropdownProperty = block.getFieldValue('PROPERTY') as PropertyOption; const [suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty]; - const numberToCheck = generator.valueToCode(block, 'NUMBER_TO_CHECK', - inputOrder) || '0'; + 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. // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = generator.provideFunction_('math_isPrime', ` + "import 'dart:math' as Math;"; + const functionName = generator.provideFunction_( + 'math_isPrime', + ` bool ${generator.FUNCTION_NAME_PLACEHOLDER_}(n) { // https://en.wikipedia.org/wiki/Primality_test#Naive_methods if (n == 2 || n == 3) { @@ -212,11 +228,12 @@ bool ${generator.FUNCTION_NAME_PLACEHOLDER_}(n) { } return true; } -`); +`, + ); code = functionName + '(' + numberToCheck + ')'; } else if (dropdownProperty === 'DIVISIBLE_BY') { - const divisor = generator.valueToCode(block, 'DIVISOR', - Order.MULTIPLICATIVE) || '0'; + const divisor = + generator.valueToCode(block, 'DIVISOR', Order.MULTIPLICATIVE) || '0'; if (divisor === '0') { return ['false', Order.ATOMIC]; } @@ -225,37 +242,50 @@ bool ${generator.FUNCTION_NAME_PLACEHOLDER_}(n) { code = numberToCheck + suffix; } return [code, outputOrder]; -}; +} export function math_change(block: Block, generator: DartGenerator) { // Add to a variable in place. const argument0 = - generator.valueToCode(block, 'DELTA', Order.ADDITIVE) || '0'; - const varName = - generator.getVariableName(block.getFieldValue('VAR')); - return varName + ' = (' + varName + ' is num ? ' + varName + ' : 0) + ' + - argument0 + ';\n'; -}; + generator.valueToCode(block, 'DELTA', Order.ADDITIVE) || '0'; + const varName = generator.getVariableName(block.getFieldValue('VAR')); + return ( + varName + + ' = (' + + varName + + ' is num ? ' + + 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: DartGenerator): [string, Order] { +export function math_on_list( + block: Block, + generator: DartGenerator, +): [string, Order] { // Math functions for lists. const func = block.getFieldValue('OP'); const list = generator.valueToCode(block, 'LIST', Order.NONE) || '[]'; let code; switch (func) { case 'SUM': { - const functionName = generator.provideFunction_('math_sum', ` + const functionName = generator.provideFunction_( + 'math_sum', + ` num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { num sumVal = 0; myList.forEach((num entry) {sumVal += entry;}); return sumVal; } -`); +`, + ); code = functionName + '(' + list + ')'; break; } @@ -263,15 +293,18 @@ num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = generator.provideFunction_('math_min', ` + "import 'dart:math' as Math;"; + const functionName = generator.provideFunction_( + 'math_min', + ` num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { if (myList.isEmpty) return null; num minVal = myList[0]; myList.forEach((num entry) {minVal = Math.min(minVal, entry);}); return minVal; } -`); +`, + ); code = functionName + '(' + list + ')'; break; } @@ -279,22 +312,27 @@ num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = generator.provideFunction_('math_max', ` + "import 'dart:math' as Math;"; + const functionName = generator.provideFunction_( + 'math_max', + ` num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { if (myList.isEmpty) return null; num maxVal = myList[0]; myList.forEach((num entry) {maxVal = Math.max(maxVal, entry);}); return maxVal; } -`); +`, + ); code = functionName + '(' + list + ')'; break; } case 'AVERAGE': { // This operation exclude null and values that are not int or float: // math_mean([null,null,"aString",1,9]) -> 5.0 - const functionName = generator.provideFunction_('math_mean', ` + const functionName = generator.provideFunction_( + 'math_mean', + ` num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { // First filter list for numbers only. List localList = new List.from(myList); @@ -304,12 +342,15 @@ num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { localList.forEach((var entry) {sumVal += entry;}); return sumVal / localList.length; } -`); +`, + ); code = functionName + '(' + list + ')'; break; } case 'MEDIAN': { - const functionName = generator.provideFunction_('math_median', ` + const functionName = generator.provideFunction_( + 'math_median', + ` num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { // First filter list for numbers only, then sort, then return middle value // or the average of two middle values if list has an even number of elements. @@ -324,7 +365,8 @@ num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { return (localList[index - 1] + localList[index]) / 2; } } -`); +`, + ); code = functionName + '(' + list + ')'; break; } @@ -332,11 +374,13 @@ num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; + "import 'dart:math' as Math;"; // 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', + ` List ${generator.FUNCTION_NAME_PLACEHOLDER_}(List values) { List modes = []; List counts = []; @@ -365,7 +409,8 @@ List ${generator.FUNCTION_NAME_PLACEHOLDER_}(List values) { } return modes; } -`); +`, + ); code = functionName + '(' + list + ')'; break; } @@ -373,9 +418,10 @@ List ${generator.FUNCTION_NAME_PLACEHOLDER_}(List values) { // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = - generator.provideFunction_('math_standard_deviation', ` + "import 'dart:math' as Math;"; + const functionName = generator.provideFunction_( + 'math_standard_deviation', + ` num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { // First filter list for numbers only. List numbers = new List.from(myList); @@ -389,7 +435,8 @@ num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { numbers.forEach((x) => sumSquare += Math.pow(x - mean, 2)); return Math.sqrt(sumSquare / n); } -`); +`, + ); code = functionName + '(' + list + ')'; break; } @@ -397,13 +444,16 @@ num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = generator.provideFunction_('math_random_item', ` + "import 'dart:math' as Math;"; + const functionName = generator.provideFunction_( + 'math_random_item', + ` dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { int x = new Math.Random().nextInt(myList.length); return myList[x]; } -`); +`, + ); code = functionName + '(' + list + ')'; break; } @@ -411,43 +461,59 @@ dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { throw Error('Unknown operator: ' + func); } return [code, Order.UNARY_POSTFIX]; -}; +} -export function math_modulo(block: Block, generator: DartGenerator): [string, Order] { +export function math_modulo( + block: Block, + generator: DartGenerator, +): [string, Order] { // Remainder computation. const argument0 = - generator.valueToCode(block, 'DIVIDEND', Order.MULTIPLICATIVE) || '0'; + generator.valueToCode(block, 'DIVIDEND', Order.MULTIPLICATIVE) || '0'; const argument1 = - generator.valueToCode(block, 'DIVISOR', Order.MULTIPLICATIVE) || '0'; + generator.valueToCode(block, 'DIVISOR', Order.MULTIPLICATIVE) || '0'; const code = argument0 + ' % ' + argument1; return [code, Order.MULTIPLICATIVE]; -}; +} -export function math_constrain(block: Block, generator: DartGenerator): [string, Order] { +export function math_constrain( + block: Block, + generator: DartGenerator, +): [string, Order] { // Constrain a number between two limits. // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const argument0 = - generator.valueToCode(block, 'VALUE', Order.NONE) || '0'; + "import 'dart:math' as Math;"; + 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) || 'double.infinity'; - const code = 'Math.min(Math.max(' + argument0 + ', ' + argument1 + '), ' + - argument2 + ')'; + generator.valueToCode(block, 'HIGH', Order.NONE) || 'double.infinity'; + const code = + 'Math.min(Math.max(' + + argument0 + + ', ' + + argument1 + + '), ' + + argument2 + + ')'; return [code, Order.UNARY_POSTFIX]; -}; +} -export function math_random_int(block: Block, generator: DartGenerator): [string, Order] { +export function math_random_int( + block: Block, + generator: DartGenerator, +): [string, Order] { // Random integer between [X] and [Y]. // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; + "import 'dart:math' as Math;"; 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', + ` int ${generator.FUNCTION_NAME_PLACEHOLDER_}(num a, num b) { if (a > b) { // Swap a and b to ensure a is smaller. @@ -457,30 +523,37 @@ int ${generator.FUNCTION_NAME_PLACEHOLDER_}(num a, num b) { } return new Math.Random().nextInt(b - a + 1) + a; } -`); +`, + ); const code = functionName + '(' + argument0 + ', ' + argument1 + ')'; return [code, Order.UNARY_POSTFIX]; -}; +} -export function math_random_float(block: Block, generator: DartGenerator): [string, Order] { +export function math_random_float( + block: Block, + generator: DartGenerator, +): [string, Order] { // Random fraction between 0 and 1. // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; + "import 'dart:math' as Math;"; return ['new Math.Random().nextDouble()', Order.UNARY_POSTFIX]; -}; +} -export function math_atan2(block: Block, generator: DartGenerator): [string, Order] { +export function math_atan2( + block: Block, + generator: DartGenerator, +): [string, Order] { // Arctangent of point (X, Y) in degrees from -180 to 180. // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; + "import 'dart:math' as Math;"; 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.MULTIPLICATIVE + Order.MULTIPLICATIVE, ]; -}; +} diff --git a/generators/dart/procedures.ts b/generators/dart/procedures.ts index 33c0cb68972..934e523183f 100644 --- a/generators/dart/procedures.ts +++ b/generators/dart/procedures.ts @@ -15,11 +15,9 @@ import type {IfReturnBlock} from '../../blocks/procedures.js'; import type {DartGenerator} from './dart_generator.js'; import {Order} from './dart_generator.js'; - export function procedures_defreturn(block: Block, generator: DartGenerator) { // Define a procedure with a return value. - 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); @@ -33,12 +31,12 @@ export function procedures_defreturn(block: Block, generator: DartGenerator) { 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. @@ -53,24 +51,37 @@ export function procedures_defreturn(block: Block, generator: DartGenerator) { for (let i = 0; i < variables.length; i++) { args[i] = generator.getVariableName(variables[i]); } - let code = returnType + ' ' + funcName + '(' + args.join(', ') + ') {\n' + - xfix1 + loopTrap + branch + xfix2 + returnValue + '}'; + let code = + returnType + + ' ' + + 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: DartGenerator): [string, Order] { +export function procedures_callreturn( + block: Block, + generator: DartGenerator, +): [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++) { @@ -78,35 +89,41 @@ export function procedures_callreturn(block: Block, generator: DartGenerator): [ } let code = funcName + '(' + args.join(', ') + ')'; return [code, Order.UNARY_POSTFIX]; -}; +} -export function procedures_callnoreturn(block: Block, generator: DartGenerator) { +export function procedures_callnoreturn( + block: Block, + generator: DartGenerator, +) { // 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: DartGenerator) { // 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/dart/text.ts b/generators/dart/text.ts index 5ce4b016ce7..e8c9be63788 100644 --- a/generators/dart/text.ts +++ b/generators/dart/text.ts @@ -15,24 +15,28 @@ import type {DartGenerator} from './dart_generator.js'; import type {JoinMutatorBlock} from '../../blocks/text.js'; import {Order} from './dart_generator.js'; - // RESERVED WORDS: 'Html,Math' export function text(block: Block, generator: DartGenerator): [string, Order] { // Text value. const code = generator.quote_(block.getFieldValue('TEXT')); return [code, Order.ATOMIC]; -}; +} -export function text_multiline(block: Block, generator: DartGenerator): [string, Order] { +export function text_multiline( + block: Block, + generator: DartGenerator, +): [string, Order] { // Text value. const code = generator.multiline_quote_(block.getFieldValue('TEXT')); - const order = - code.indexOf('+') !== -1 ? Order.ADDITIVE : Order.ATOMIC; + const order = code.indexOf('+') !== -1 ? Order.ADDITIVE : Order.ATOMIC; return [code, order]; -}; +} -export function text_join(block: Block, generator: DartGenerator): [string, Order] { +export function text_join( + block: Block, + generator: DartGenerator, +): [string, Order] { // Create a string made up of any number of elements of any type. const joinBlock = block as JoinMutatorBlock; switch (joinBlock.itemCount_) { @@ -40,7 +44,7 @@ export function text_join(block: Block, generator: DartGenerator): [string, Orde return ["''", Order.ATOMIC]; case 1: { const element = - generator.valueToCode(block, 'ADD0', Order.UNARY_POSTFIX) || "''"; + generator.valueToCode(block, 'ADD0', Order.UNARY_POSTFIX) || "''"; const code = element + '.toString()'; return [code, Order.UNARY_POSTFIX]; } @@ -48,58 +52,69 @@ export function text_join(block: Block, generator: DartGenerator): [string, Orde const elements = new Array(joinBlock.itemCount_); for (let i = 0; i < joinBlock.itemCount_; i++) { elements[i] = - generator.valueToCode(block, 'ADD' + i, Order.NONE) || "''"; + generator.valueToCode(block, 'ADD' + i, Order.NONE) || "''"; } const code = '[' + elements.join(',') + '].join()'; return [code, Order.UNARY_POSTFIX]; } } -}; +} export function text_append(block: Block, generator: DartGenerator) { // Append to a variable in place. - const varName = - generator.getVariableName(block.getFieldValue('VAR')); + const varName = generator.getVariableName(block.getFieldValue('VAR')); const value = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; return varName + ' = [' + varName + ', ' + value + '].join();\n'; -}; +} -export function text_length(block: Block, generator: DartGenerator): [string, Order] { +export function text_length( + block: Block, + generator: DartGenerator, +): [string, Order] { // String or array length. const text = - generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || "''"; + generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || "''"; return [text + '.length', Order.UNARY_POSTFIX]; -}; +} -export function text_isEmpty(block: Block, generator: DartGenerator): [string, Order] { +export function text_isEmpty( + block: Block, + generator: DartGenerator, +): [string, Order] { // Is the string null or array empty? const text = - generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || "''"; + generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || "''"; return [text + '.isEmpty', Order.UNARY_POSTFIX]; -}; +} -export function text_indexOf(block: Block, generator: DartGenerator): [string, Order] { +export function text_indexOf( + block: Block, + generator: DartGenerator, +): [string, Order] { // Search the text for a substring. const operator = - block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; - const substring = - generator.valueToCode(block, 'FIND', Order.NONE) || "''"; + block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; + const substring = generator.valueToCode(block, 'FIND', Order.NONE) || "''"; const text = - generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || "''"; + generator.valueToCode(block, 'VALUE', Order.UNARY_POSTFIX) || "''"; const code = text + '.' + operator + '(' + substring + ')'; if (block.workspace.options.oneBasedIndex) { return [code + ' + 1', Order.ADDITIVE]; } return [code, Order.UNARY_POSTFIX]; -}; +} -export function text_charAt(block: Block, generator: DartGenerator): [string, Order] { +export function text_charAt( + block: Block, + generator: DartGenerator, +): [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 === 'FIRST' || where === 'FROM_START') ? - Order.UNARY_POSTFIX : - Order.NONE; + const textOrder = + where === 'FIRST' || where === 'FROM_START' + ? Order.UNARY_POSTFIX + : Order.NONE; const text = generator.valueToCode(block, 'VALUE', textOrder) || "''"; let at; switch (where) { @@ -114,14 +129,17 @@ export function text_charAt(block: Block, generator: DartGenerator): [string, Or } case 'LAST': at = 1; - // Fall through. + // Fall through. case 'FROM_END': { at = generator.getAdjusted(block, 'AT', 1); - const functionName = generator.provideFunction_('text_get_from_end', ` + const functionName = generator.provideFunction_( + 'text_get_from_end', + ` String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String text, num x) { return text[text.length - x]; } -`); +`, + ); const code = functionName + '(' + text + ', ' + at + ')'; return [code, Order.UNARY_POSTFIX]; } @@ -129,28 +147,32 @@ String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String text, num x) { // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = - generator.provideFunction_('text_random_letter', ` + "import 'dart:math' as Math;"; + const functionName = generator.provideFunction_( + 'text_random_letter', + ` String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String text) { int x = new Math.Random().nextInt(text.length); return text[x]; } -`); +`, + ); const code = functionName + '(' + text + ')'; return [code, Order.UNARY_POSTFIX]; } } throw Error('Unhandled option (text_charAt).'); -}; +} -export function text_getSubstring(block: Block, generator: DartGenerator): [string, Order] { +export function text_getSubstring( + block: Block, + generator: DartGenerator, +): [string, Order] { // Get substring. const where1 = block.getFieldValue('WHERE1'); const where2 = block.getFieldValue('WHERE2'); - const requiresLengthCall = (where1 !== 'FROM_END' && where2 === 'FROM_START'); - const textOrder = - requiresLengthCall ? Order.UNARY_POSTFIX : Order.NONE; + const requiresLengthCall = where1 !== 'FROM_END' && where2 === 'FROM_START'; + const textOrder = requiresLengthCall ? Order.UNARY_POSTFIX : Order.NONE; const text = generator.valueToCode(block, 'STRING', textOrder) || "''"; let code; if (where1 === 'FIRST' && where2 === 'LAST') { @@ -197,8 +219,9 @@ export function text_getSubstring(block: Block, generator: DartGenerator): [stri } 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', + ` String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String text, String where1, num at1, String where2, num at2) { int getAt(String where, num at) { if (where == 'FROM_END') { @@ -216,19 +239,34 @@ String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String text, String where1, num a at2 = getAt(where2, at2) + 1; return text.substring(at1, at2); } -`); - code = functionName + '(' + text + ', \'' + where1 + '\', ' + at1 + ', \'' + - where2 + '\', ' + at2 + ')'; +`, + ); + code = + functionName + + '(' + + text + + ", '" + + where1 + + "', " + + at1 + + ", '" + + where2 + + "', " + + at2 + + ')'; } return [code, Order.UNARY_POSTFIX]; -}; +} -export function text_changeCase(block: Block, generator: DartGenerator): [string, Order] { +export function text_changeCase( + block: Block, + generator: DartGenerator, +): [string, Order] { // Change capitalization. const OPERATORS = { 'UPPERCASE': '.toUpperCase()', 'LOWERCASE': '.toLowerCase()', - 'TITLECASE': null + 'TITLECASE': null, }; type OperatorOption = keyof typeof OPERATORS; const operator = OPERATORS[block.getFieldValue('CASE') as OperatorOption]; @@ -240,7 +278,9 @@ export function text_changeCase(block: Block, generator: DartGenerator): [string code = text + operator; } else { // Title case is not a native generator function. Define one. - const functionName = generator.provideFunction_('text_toTitleCase', ` + const functionName = generator.provideFunction_( + 'text_toTitleCase', + ` String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String str) { RegExp exp = new RegExp(r'\\b'); List list = str.split(exp); @@ -255,38 +295,45 @@ String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String str) { } return title.toString(); } -`); +`, + ); code = functionName + '(' + text + ')'; } return [code, Order.UNARY_POSTFIX]; -}; +} -export function text_trim(block: Block, generator: DartGenerator): [string, Order] { +export function text_trim( + block: Block, + generator: DartGenerator, +): [string, Order] { // Trim spaces. const OPERATORS = { - 'LEFT': '.replaceFirst(new RegExp(r\'^\\s+\'), \'\')', - 'RIGHT': '.replaceFirst(new RegExp(r\'\\s+$\'), \'\')', - 'BOTH': '.trim()' + 'LEFT': ".replaceFirst(new RegExp(r'^\\s+'), '')", + 'RIGHT': ".replaceFirst(new RegExp(r'\\s+$'), '')", + 'BOTH': '.trim()', }; type OperatorOption = keyof typeof OPERATORS; const operator = OPERATORS[block.getFieldValue('MODE') as OperatorOption]; const text = - generator.valueToCode(block, 'TEXT', Order.UNARY_POSTFIX) || "''"; + generator.valueToCode(block, 'TEXT', Order.UNARY_POSTFIX) || "''"; return [text + operator, Order.UNARY_POSTFIX]; -}; +} export function text_print(block: Block, generator: DartGenerator) { // Print statement. const msg = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; return 'print(' + msg + ');\n'; -}; +} -export function text_prompt_ext(block: Block, generator: DartGenerator): [string, Order] { +export function text_prompt_ext( + block: Block, + generator: DartGenerator, +): [string, Order] { // Prompt function. // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_html'] = - 'import \'dart:html\' as Html;'; + "import 'dart:html' as Html;"; let msg; if (block.getField('TEXT')) { // Internal message. @@ -295,25 +342,30 @@ export function text_prompt_ext(block: Block, generator: DartGenerator): [string // External message. msg = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; } - let code = 'Html.window.prompt(' + msg + ', \'\')'; + let code = 'Html.window.prompt(' + msg + ", '')"; const toNumber = block.getFieldValue('TYPE') === 'NUMBER'; if (toNumber) { // TODO(#7600): find better approach than casting to any to override // CodeGenerator declaring .definitions protected. (generator as AnyDuringMigration).definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; + "import 'dart:math' as Math;"; code = 'Math.parseDouble(' + code + ')'; } return [code, Order.UNARY_POSTFIX]; -}; +} export const text_prompt = text_prompt_ext; -export function text_count(block: Block, generator: DartGenerator): [string, Order] { +export function text_count( + block: Block, + generator: DartGenerator, +): [string, Order] { const text = generator.valueToCode(block, 'TEXT', Order.NONE) || "''"; const sub = generator.valueToCode(block, 'SUB', Order.NONE) || "''"; // Substring count is not a native generator function. Define one. - const functionName = generator.provideFunction_('text_count', ` + const functionName = generator.provideFunction_( + 'text_count', + ` int ${generator.FUNCTION_NAME_PLACEHOLDER_}(String haystack, String needle) { if (needle.length == 0) { return haystack.length + 1; @@ -329,26 +381,33 @@ int ${generator.FUNCTION_NAME_PLACEHOLDER_}(String haystack, String needle) { } return count; } -`); +`, + ); const code = functionName + '(' + text + ', ' + sub + ')'; return [code, Order.UNARY_POSTFIX]; -}; +} -export function text_replace(block: Block, generator: DartGenerator): [string, Order] { +export function text_replace( + block: Block, + generator: DartGenerator, +): [string, Order] { const text = - generator.valueToCode(block, 'TEXT', Order.UNARY_POSTFIX) || "''"; + generator.valueToCode(block, 'TEXT', Order.UNARY_POSTFIX) || "''"; const from = generator.valueToCode(block, 'FROM', Order.NONE) || "''"; const to = generator.valueToCode(block, 'TO', Order.NONE) || "''"; const code = text + '.replaceAll(' + from + ', ' + to + ')'; return [code, Order.UNARY_POSTFIX]; -}; +} -export function text_reverse(block: Block, generator: DartGenerator): [string, Order] { +export function text_reverse( + block: Block, + generator: DartGenerator, +): [string, Order] { // There isn't a sensible way to do this in generator. See: // http://stackoverflow.com/a/21613700/3529104 // Implementing something is possibly better than not implementing anything? const text = - generator.valueToCode(block, 'TEXT', Order.UNARY_POSTFIX) || "''"; + generator.valueToCode(block, 'TEXT', Order.UNARY_POSTFIX) || "''"; const code = 'new String.fromCharCodes(' + text + '.runes.toList().reversed)'; return [code, Order.UNARY_PREFIX]; -}; +} diff --git a/generators/dart/variables.ts b/generators/dart/variables.ts index 052c53fd5c4..1910befc9c2 100644 --- a/generators/dart/variables.ts +++ b/generators/dart/variables.ts @@ -14,19 +14,19 @@ import type {Block} from '../../core/block.js'; import type {DartGenerator} from './dart_generator.js'; import {Order} from './dart_generator.js'; - -export function variables_get(block: Block, generator: DartGenerator): [string, Order] { +export function variables_get( + block: Block, + generator: DartGenerator, +): [string, Order] { // Variable getter. - const code = - generator.getVariableName(block.getFieldValue('VAR')); + const code = generator.getVariableName(block.getFieldValue('VAR')); return [code, Order.ATOMIC]; -}; +} export function variables_set(block: Block, generator: DartGenerator) { // Variable setter. const argument0 = - generator.valueToCode(block, 'VALUE', Order.ASSIGNMENT) || '0'; - const varName = - generator.getVariableName(block.getFieldValue('VAR')); + generator.valueToCode(block, 'VALUE', Order.ASSIGNMENT) || '0'; + const varName = generator.getVariableName(block.getFieldValue('VAR')); return varName + ' = ' + argument0 + ';\n'; -}; +} diff --git a/generators/dart/variables_dynamic.ts b/generators/dart/variables_dynamic.ts index 22be966dcb7..6a92fff9125 100644 --- a/generators/dart/variables_dynamic.ts +++ b/generators/dart/variables_dynamic.ts @@ -10,7 +10,6 @@ // Former goog.module ID: Blockly.Dart.variablesDynamic - // generator is dynamically typed. export { variables_get as variables_get_dynamic, From 15574bfc3ca8b631584e3eac603482200ce65af1 Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Mon, 13 Nov 2023 19:09:19 +0000 Subject: [PATCH 8/8] fix(generators): Fix for PR #7646 --- generators/dart/dart_generator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generators/dart/dart_generator.ts b/generators/dart/dart_generator.ts index a2736c2bcf1..b2eb80cdd6b 100644 --- a/generators/dart/dart_generator.ts +++ b/generators/dart/dart_generator.ts @@ -39,7 +39,7 @@ export enum Order { LOGICAL_AND = 11, // && LOGICAL_OR = 12, // || IF_NULL = 13, // ?? - CONDITIONAL = 14, // expr ? expr = expr + CONDITIONAL = 14, // expr ? expr: expr CASCADE = 15, // .. ASSIGNMENT = 16, // = *= /= ~/= %= += -= <<= >>= &= ^= |= NONE = 99, // (...)