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..3bd1271adfb 100644 --- a/generators/dart.js +++ b/generators/dart.ts @@ -36,8 +36,17 @@ 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]; +} diff --git a/generators/dart/colour.js b/generators/dart/colour.ts similarity index 62% rename from generators/dart/colour.js rename to generators/dart/colour.ts index b5405a34fc2..b55fc5e6f07 100644 --- a/generators/dart/colour.js +++ b/generators/dart/colour.ts @@ -10,22 +10,33 @@ // 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;"; - const functionName = generator.provideFunction_('colour_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;"; + const functionName = generator.provideFunction_( + 'colour_random', + ` String ${generator.FUNCTION_NAME_PLACEHOLDER_}() { String hex = '0123456789abcdef'; var rnd = new Math.Random(); @@ -33,20 +44,28 @@ 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, 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; const blue = generator.valueToCode(block, 'BLUE', Order.NONE) || 0; - generator.definitions_['import_dart_math'] = - "import 'dart:math' as Math;"; - const functionName = generator.provideFunction_('colour_rgb', ` + // 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) { num rn = (Math.max(Math.min(r, 100), 0) * 2.55).round(); String rs = rn.toInt().toRadixString(16); @@ -62,23 +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, generator) { +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; - generator.definitions_['import_dart_math'] = - "import 'dart:math' as Math;"; - const functionName = generator.provideFunction_('colour_blend', ` + // 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) { ratio = Math.max(Math.min(ratio, 1), 0); int r1 = int.parse('0x\${c1.substring(1, 3)}'); @@ -101,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.js b/generators/dart/dart_generator.js deleted file mode 100644 index 412af49ee52..00000000000 --- a/generators/dart/dart_generator.js +++ /dev/null @@ -1,308 +0,0 @@ -/** - * @license - * Copyright 2014 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Helper functions for generating Dart for blocks. - * @suppress {checkTypes|globalThis} - */ - -// 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 {CodeGenerator} from '../../core/generator.js'; -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 - * @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, // (...) -}; - -/** - * Dart code generator class. - */ -export class DartGenerator extends CodeGenerator { - constructor(name) { - super(name ?? 'Dart'); - this.isInitialized = false; - - // Copy Order values onto instance for backwards compatibility - // while ensuring they are not part of the publically-advertised - // API. - // - // TODO(#7085): deprecate these in due course. (Could initially - // replace data properties with get accessors that call - // deprecate.warn().) - for (const key in Order) { - this['ORDER_' + key] = Order[key]; - } - - // List of illegal variable names. This is not intended to be a - // security feature. Blockly is 100% client-side, so bypassing - // this list is trivial. This is intended to prevent users from - // accidentally clobbering a built-in object or function. - this.addReservedWords( - // 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' - ); - } - - /** - * Initialise the database of variable names. - * @param {!Workspace} workspace Workspace to generate code from. - */ - init(workspace) { - super.init(); - - if (!this.nameDB_) { - this.nameDB_ = new Names(this.RESERVED_WORDS_); - } else { - this.nameDB_.reset(); - } - - this.nameDB_.setVariableMap(workspace.getVariableMap()); - this.nameDB_.populateVariables(workspace); - this.nameDB_.populateProcedures(workspace); - - const defvars = []; - // 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)); - } - - // 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)); - } - - // Declare all of the variables. - if (defvars.length) { - this.definitions_['variables'] = - 'var ' + defvars.join(', ') + ';'; - } - this.isInitialized = true; - } - - /** - * Prepend the generated code with import statements and variable definitions. - * @param {string} code Generated code. - * @return {string} Completed code. - */ - finish(code) { - // Indent every line. - if (code) { - code = this.prefixLines(code, this.INDENT); - } - code = 'main() {\n' + code + '}'; - - // Convert the definitions dictionary into a list. - const imports = []; - const definitions = []; - for (let name in this.definitions_) { - const def = this.definitions_[name]; - if (def.match(/^import\s/)) { - imports.push(def); - } else { - definitions.push(def); - } - } - // Call Blockly.CodeGenerator's finish. - code = super.finish(code); - this.isInitialized = false; - - 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. - */ - scrubNakedValue(line) { - 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. - */ - quote_(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 + '\''; - } - - /** - * Encode a string as a properly escaped multiline Dart string, complete with - * quotes. - * @param {string} string Text to encode. - * @return {string} Dart string. - */ - multiline_quote_(string) { - const lines = string.split(/\n/g).map(this.quote_); - // Join with the following, plus a newline: - // + '\n' + - return lines.join(' + \'\\n\' + \n'); - } - - /** - * 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 - */ - scrub_(block, code, opt_thisOnly) { - let commentCode = ''; - // Only collect comments for blocks that aren't inline. - if (!block.outputConnection || !block.outputConnection.targetConnection) { - // Collect comment for this block. - let comment = block.getCommentText(); - if (comment) { - comment = stringUtils.wrap(comment, this.COMMENT_WRAP - 3); - if (block.getProcedureDef) { - // Use documentation comment for function comments. - commentCode += this.prefixLines(comment + '\n', '/// '); - } else { - commentCode += this.prefixLines(comment + '\n', '// '); - } - } - // Collect comments for all value arguments. - // Don't collect comments for nested statements. - for (let i = 0; i < block.inputList.length; i++) { - if (block.inputList[i].type === inputTypes.VALUE) { - const childBlock = block.inputList[i].connection.targetBlock(); - if (childBlock) { - comment = this.allNestedComments(childBlock); - if (comment) { - commentCode += this.prefixLines(comment, '// '); - } - } - } - } - } - const nextBlock = - block.nextConnection && block.nextConnection.targetBlock(); - const nextCode = opt_thisOnly ? '' : this.blockToCode(nextBlock); - return commentCode + code + nextCode; - } - - /** - * Gets a property and adjusts the value while taking into account indexing. - * @param {!Block} block The block. - * @param {string} atId The property ID of the element to get. - * @param {number=} opt_delta Value to add. - * @param {boolean=} opt_negate Whether to negate the value. - * @param {number=} opt_order The highest order acting on this value. - * @return {string|number} - */ - getAdjusted(block, atId, opt_delta, opt_negate, opt_order) { - let delta = opt_delta || 0; - let order = opt_order || this.ORDER_NONE; - if (block.workspace.options.oneBasedIndex) { - delta--; - } - const defaultAtIndex = block.workspace.options.oneBasedIndex ? '1' : '0'; - - /** @type {number} */ - let outerOrder; - let innerOrder; - if (delta) { - outerOrder = this.ORDER_ADDITIVE; - innerOrder = this.ORDER_ADDITIVE; - } else if (opt_negate) { - outerOrder = this.ORDER_UNARY_PREFIX; - innerOrder = this.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; - } - } else { - // If the index is dynamic, adjust it in code. - if (delta > 0) { - at = at + ' + ' + delta; - } else if (delta < 0) { - at = at + ' - ' + -delta; - } - if (opt_negate) { - if (delta) { - at = '-(' + at + ')'; - } else { - at = '-' + at; - } - } - innerOrder = Math.floor(innerOrder); - order = Math.floor(order); - if (innerOrder && order >= innerOrder) { - at = '(' + at + ')'; - } - } - return at; - } -} diff --git a/generators/dart/dart_generator.ts b/generators/dart/dart_generator.ts new file mode 100644 index 00000000000..b2eb80cdd6b --- /dev/null +++ b/generators/dart/dart_generator.ts @@ -0,0 +1,321 @@ +/** + * @license + * Copyright 2014 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @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 {CodeGenerator} from '../../core/generator.js'; +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-- () [] . ?. + 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 { + /** @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 + // while ensuring they are not part of the publically-advertised + // API. + // + // TODO(#7085): deprecate these in due course. (Could initially + // replace data properties with get accessors that call + // deprecate.warn().) + for (const key in Order) { + // Must assign Order[key] to a temporary to get the type guard to work; + // see https://github.com/microsoft/TypeScript/issues/10530. + const value = Order[key]; + // Skip reverse-lookup entries in the enum. Due to + // https://github.com/microsoft/TypeScript/issues/55713 this (as + // of TypeScript 5.5.2) actually narrows the type of value to + // never - but that still allows the following assignment to + // succeed. + if (typeof value === 'string') continue; + (this as unknown as Record)['ORDER_' + key] = value; + } + + // List of illegal variable names. This is not intended to be a + // security feature. Blockly is 100% client-side, so bypassing + // this list is trivial. This is intended to prevent users from + // accidentally clobbering a built-in object or function. + this.addReservedWords( + // 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', + ); + } + + /** + * Initialise the database of variable names. + * + * @param workspace Workspace to generate code from. + */ + init(workspace: Workspace) { + super.init(workspace); + + if (!this.nameDB_) { + this.nameDB_ = new Names(this.RESERVED_WORDS_); + } else { + this.nameDB_.reset(); + } + + this.nameDB_.setVariableMap(workspace.getVariableMap()); + this.nameDB_.populateVariables(workspace); + this.nameDB_.populateProcedures(workspace); + + const defvars = []; + // 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), + ); + } + + // 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), + ); + } + + // Declare all of the variables. + if (defvars.length) { + this.definitions_['variables'] = 'var ' + defvars.join(', ') + ';'; + } + this.isInitialized = true; + } + + /** + * Prepend the generated code with import statements and variable definitions. + * + * @param code Generated code. + * @returns Completed code. + */ + finish(code: string): string { + // Indent every line. + if (code) { + code = this.prefixLines(code, this.INDENT); + } + code = 'main() {\n' + code + '}'; + + // Convert the definitions dictionary into a list. + const imports = []; + const definitions = []; + for (let name in this.definitions_) { + const def = this.definitions_[name]; + if (def.match(/^import\s/)) { + imports.push(def); + } else { + definitions.push(def); + } + } + // Call Blockly.CodeGenerator's finish. + code = super.finish(code); + this.isInitialized = false; + + 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. + * + * @param line Line of generated code. + * @returns Legal line of code. + */ + scrubNakedValue(line: string): string { + return line + ';\n'; + } + + /** + * Encode a string as a properly escaped Dart string, complete with quotes. + * + * @param string Text to encode. + * @returns Dart string. + */ + 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 + "'"; + } + + /** + * Encode a string as a properly escaped multiline Dart string, complete + * with quotes. + * + * @param string Text to encode. + * @returns Dart string. + */ + multiline_quote_(string: string): string { + const lines = string.split(/\n/g).map(this.quote_); + // Join with the following, plus a newline: + // + '\n' + + return lines.join(" + '\\n' + \n"); + } + + /** + * 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 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: Block, code: string, thisOnly = false): string { + let commentCode = ''; + // Only collect comments for blocks that aren't inline. + if (!block.outputConnection || !block.outputConnection.targetConnection) { + // Collect comment for this block. + let comment = block.getCommentText(); + if (comment) { + comment = stringUtils.wrap(comment, this.COMMENT_WRAP - 3); + if ((block as AnyDuringMigration).getProcedureDef) { + // Use documentation comment for function comments. + commentCode += this.prefixLines(comment + '\n', '/// '); + } else { + commentCode += this.prefixLines(comment + '\n', '// '); + } + } + // Collect comments for all value arguments. + // Don't collect comments for nested statements. + for (let i = 0; i < block.inputList.length; i++) { + if (block.inputList[i].type === inputTypes.VALUE) { + const childBlock = block.inputList[i].connection!.targetBlock(); + if (childBlock) { + comment = this.allNestedComments(childBlock); + if (comment) { + commentCode += this.prefixLines(comment, '// '); + } + } + } + } + } + const nextBlock = + block.nextConnection && block.nextConnection.targetBlock(); + const nextCode = thisOnly ? '' : this.blockToCode(nextBlock); + return commentCode + code + nextCode; + } + + /** + * Generate code representing the specified value input, adjusted to take into + * account indexing (zero- or one-based) and optionally by a specified delta + * and/or by negation. + * + * @param block The block. + * @param atId The ID of the input block to get (and adjust) the value of. + * @param delta Value to add. + * @param negate Whether to negate the value. + * @param order The highest order acting on this value. + * @returns The adjusted value. + */ + 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'; + + let orderForInput = order; + if (delta) { + orderForInput = Order.ADDITIVE; + } else if (negate) { + orderForInput = Order.UNARY_PREFIX; + } + + let at = this.valueToCode(block, atId, orderForInput) || defaultAtIndex; + + // Easy case: no adjustments. + if (delta === 0 && !negate) { + return at; + } + // If the index is a naked number, adjust it right now. + if (stringUtils.isNumber(at)) { + at = String(Number(at) + delta); + if (negate) { + at = String(-Number(at)); + } + return at; + } + // If the index is dynamic, adjust it in code. + if (delta > 0) { + at = `${at} + ${delta}`; + } else if (delta < 0) { + at = `${at} - ${-delta}`; + } + if (negate) { + at = delta ? `-(${at})` : `-${at}`; + } + if (Math.floor(order) >= Math.floor(orderForInput)) { + at = `(${at})`; + } + return at; + } +} diff --git a/generators/dart/lists.js b/generators/dart/lists.ts similarity index 66% rename from generators/dart/lists.js rename to generators/dart/lists.ts index c7a540b69c6..d1b65cc5329 100644 --- a/generators/dart/lists.js +++ b/generators/dart/lists.ts @@ -10,80 +10,104 @@ // 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'; - // 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++) { - elements[i] = - generator.valueToCode(block, 'ADD' + i, Order.NONE) || 'null'; + const createWithBlock = block as CreateWithBlock; + const elements = new Array(createWithBlock.itemCount_); + for (let i = 0; i < createWithBlock.itemCount_; i++) { + elements[i] = generator.valueToCode(block, 'ADD' + i, Order.NONE) || 'null'; } const code = '[' + elements.join(', ') + ']'; 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'; - 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, generator) { +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, 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) || '[]'; + 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'; + 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, 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'; 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; @@ -91,51 +115,60 @@ export function lists_getIndex(block, generator) { // 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') { - generator.definitions_['import_dart_math'] = - 'import \'dart:math\' as 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); - 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]; } @@ -180,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]; @@ -196,34 +228,46 @@ dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List my_list, num x) { break; } case 'RANDOM': - generator.definitions_['import_dart_math'] = - 'import \'dart:math\' as 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); - 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]; } @@ -231,25 +275,25 @@ 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'; 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; @@ -281,27 +325,30 @@ export function lists_setIndex(block, generator) { 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; } case 'RANDOM': { - generator.definitions_['import_dart_math'] = - 'import \'dart:math\' as 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); - 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; @@ -313,17 +360,22 @@ 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) || '[]'; + 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; @@ -364,7 +416,9 @@ export function lists_getSublist(block, generator) { } else { const at1 = generator.getAdjusted(block, 'AT1'); const at2 = generator.getAdjusted(block, 'AT2'); - const functionName = generator.provideFunction_('lists_get_sublist', ` + const functionName = generator.provideFunction_( + 'lists_get_sublist', + ` 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') { @@ -382,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, 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; 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(), @@ -408,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, 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 = - generator.valueToCode(block, 'DELIM', Order.NONE) || "''"; + const delimiter = generator.valueToCode(block, 'DELIM', Order.NONE) || "''"; const mode = block.getFieldValue('MODE'); let functionName; if (mode === 'SPLIT') { @@ -438,12 +511,15 @@ export function lists_split(block, generator) { } const code = input + '.' + functionName + '(' + delimiter + ')'; 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`? const code = 'new List.from(' + list + '.reversed)'; return [code, Order.UNARY_POSTFIX]; -}; +} diff --git a/generators/dart/logic.js b/generators/dart/logic.ts similarity index 52% rename from generators/dart/logic.js rename to generators/dart/logic.ts index f3cbcb8ae43..82d9d563b56 100644 --- a/generators/dart/logic.js +++ b/generators/dart/logic.ts @@ -10,30 +10,38 @@ // 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; + let code = '', + branchCode, + conditionCode; if (generator.STATEMENT_PREFIX) { // Automatic prefix insertion is switched off for this block. Add manually. code += generator.injectId(generator.STATEMENT_PREFIX, block); } do { conditionCode = - generator.valueToCode(block, 'IF' + n, Order.NONE) || 'false'; + generator.valueToCode(block, 'IF' + n, Order.NONE) || 'false'; branchCode = generator.statementToCode(block, 'DO' + n); if (generator.STATEMENT_SUFFIX) { branchCode = - generator.prefixLines( - generator.injectId( - generator.STATEMENT_SUFFIX, block), generator.INDENT) + - branchCode; + generator.prefixLines( + generator.injectId(generator.STATEMENT_SUFFIX, block), + generator.INDENT, + ) + branchCode; } - code += (n > 0 ? 'else ' : '') + 'if (' + conditionCode + ') {\n' + - branchCode + '}'; + code += + (n > 0 ? 'else ' : '') + + 'if (' + + conditionCode + + ') {\n' + + branchCode + + '}'; n++; } while (block.getInput('IF' + n)); @@ -41,37 +49,48 @@ export function controls_if(block, generator) { branchCode = generator.statementToCode(block, 'ELSE'); if (generator.STATEMENT_SUFFIX) { branchCode = - generator.prefixLines( - generator.injectId( - generator.STATEMENT_SUFFIX, block), generator.INDENT) + - branchCode; + generator.prefixLines( + generator.injectId(generator.STATEMENT_SUFFIX, block), + generator.INDENT, + ) + branchCode; } code += ' else {\n' + branchCode + '}'; } return code + '\n'; -}; +} export const controls_ifelse = controls_if; -export function logic_compare(block, generator) { +export function logic_compare( + block: Block, + generator: DartGenerator, +): [string, Order] { // Comparison operator. - const OPERATORS = - {'EQ': '==', 'NEQ': '!=', 'LT': '<', 'LTE': '<=', 'GT': '>', 'GTE': '>='}; - const operator = OPERATORS[block.getFieldValue('OP')]; - const order = (operator === '==' || operator === '!=') ? - Order.EQUALITY : - Order.RELATIONAL; + const OPERATORS = { + 'EQ': '==', + 'NEQ': '!=', + 'LT': '<', + 'LTE': '<=', + 'GT': '>', + 'GTE': '>=', + }; + type OperatorOption = keyof typeof OPERATORS; + const operator = OPERATORS[block.getFieldValue('OP') as OperatorOption]; + const order = + operator === '==' || operator === '!=' ? Order.EQUALITY : Order.RELATIONAL; const argument0 = generator.valueToCode(block, 'A', order) || '0'; const argument1 = generator.valueToCode(block, 'B', order) || '0'; const code = argument0 + ' ' + operator + ' ' + argument1; return [code, order]; -}; +} -export function logic_operation(block, generator) { +export function logic_operation( + block: Block, + generator: 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) { @@ -80,7 +99,7 @@ export function logic_operation(block, generator) { argument1 = 'false'; } else { // Single missing arguments have no effect on the return value. - const defaultArgument = (operator === '&&') ? 'true' : 'false'; + const defaultArgument = operator === '&&' ? 'true' : 'false'; if (!argument0) { argument0 = defaultArgument; } @@ -90,35 +109,47 @@ export function logic_operation(block, generator) { } const code = argument0 + ' ' + operator + ' ' + argument1; return [code, order]; -}; +} -export function logic_negate(block, generator) { +export function logic_negate( + block: Block, + generator: 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, generator) { +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, 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'; + 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.js b/generators/dart/loops.ts similarity index 57% rename from generators/dart/loops.js rename to generators/dart/loops.ts index 1ead2d7e86b..5f54d201837 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 {ControlFlowInLoopBlock} from '../../blocks/loops.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')) { @@ -23,61 +25,85 @@ export function controls_repeat_ext(block, generator) { 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; -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 = - 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, generator) { +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 ? '++' : '--'; @@ -90,54 +116,77 @@ export function controls_for(block, generator) { // Cache non-trivial values to variables to prevent repeated look-ups. let startVar = argument0; if (!argument0.match(/^\w+$/) && !stringUtils.isNumber(argument0)) { - startVar = - generator.nameDB_.getDistinctName( - variable0 + '_start', NameType.VARIABLE); + startVar = generator.nameDB_!.getDistinctName( + variable0 + '_start', + NameType.VARIABLE, + ); code += '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(increment) + ';\n'; + code += Math.abs(Number(increment)) + ';\n'; } else { code += '(' + increment + ').abs();\n'; } code += 'if (' + startVar + ' > ' + endVar + ') {\n'; code += generator.INDENT + incVar + ' = -' + incVar + ';\n'; code += '}\n'; - code += 'for (' + variable0 + ' = ' + startVar + '; ' + incVar + - ' >= 0 ? ' + variable0 + ' <= ' + endVar + ' : ' + variable0 + - ' >= ' + endVar + '; ' + variable0 + ' += ' + incVar + ') {\n' + - branch + '}\n'; + code += + 'for (' + + variable0 + + ' = ' + + startVar + + '; ' + + incVar + + ' >= 0 ? ' + + variable0 + + ' <= ' + + endVar + + ' : ' + + variable0 + + ' >= ' + + endVar + + '; ' + + variable0 + + ' += ' + + incVar + + ') {\n' + + branch + + '}\n'; } return code; -}; +} -export function controls_forEach(block, generator) { +export function controls_forEach(block: Block, generator: 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, generator) { +export function controls_flow_statements( + block: Block, + generator: DartGenerator, +) { // Flow statements: continue, break. let xfix = ''; if (generator.STATEMENT_PREFIX) { @@ -150,7 +199,7 @@ export function controls_flow_statements(block, generator) { xfix += generator.injectId(generator.STATEMENT_SUFFIX, block); } if (generator.STATEMENT_PREFIX) { - const loop = block.getSurroundLoop(); + const loop = (block as ControlFlowInLoopBlock).getSurroundLoop(); if (loop && !loop.suppressPrefixSuffix) { // Inject loop's statement prefix here since the regular one at the end // of the loop will not get executed if 'continue' is triggered. @@ -165,4 +214,4 @@ export function controls_flow_statements(block, generator) { return xfix + 'continue;\n'; } throw Error('Unknown flow statement.'); -}; +} diff --git a/generators/dart/math.js b/generators/dart/math.ts similarity index 60% rename from generators/dart/math.js rename to generators/dart/math.ts index 8541b95a87a..01830a0b12c 100644 --- a/generators/dart/math.js +++ b/generators/dart/math.ts @@ -10,39 +10,44 @@ // 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; - 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, generator) { +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. + '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'; @@ -50,16 +55,21 @@ export function math_arithmetic(block, generator) { let code; // Power in generator requires a special case since it has no operator. if (!operator) { - generator.definitions_['import_dart_math'] = - 'import \'dart:math\' as 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]; } code = argument0 + operator + argument1; 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; @@ -74,8 +84,10 @@ export function math_single(block, generator) { code = '-' + arg; return [code, Order.UNARY_PREFIX]; } - generator.definitions_['import_dart_math'] = - 'import \'dart:math\' as 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'; } else if (operator === 'SIN' || operator === 'COS' || operator === 'TAN') { @@ -142,11 +154,14 @@ export function math_single(block, generator) { throw Error('Unknown math operator: ' + operator); } 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 = { + const CONSTANTS: Record = { 'PI': ['Math.pi', Order.UNARY_POSTFIX], 'E': ['Math.e', Order.UNARY_POSTFIX], 'GOLDEN_RATIO': ['(1 + Math.sqrt(5)) / 2', Order.MULTIPLICATIVE], @@ -154,18 +169,24 @@ export function math_constant(block, generator) { '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'] = - 'import \'dart:math\' as 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]; -}; +} -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 = { + const PROPERTIES: Record = { 'EVEN': [' % 2 == 0', Order.MULTIPLICATIVE, Order.EQUALITY], 'ODD': [' % 2 == 1', Order.MULTIPLICATIVE, Order.EQUALITY], 'WHOLE': [' % 1 == 0', Order.MULTIPLICATIVE, Order.EQUALITY], @@ -174,16 +195,21 @@ export function math_number_property(block, generator) { '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'; + 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'] = - 'import \'dart:math\' as Math;'; - const functionName = generator.provideFunction_('math_isPrime', ` + // 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) { // https://en.wikipedia.org/wiki/Primality_test#Naive_methods if (n == 2 || n == 3) { @@ -202,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]; } @@ -215,72 +242,97 @@ bool ${generator.FUNCTION_NAME_PLACEHOLDER_}(n) { code = numberToCheck + suffix; } 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'; - 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, 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) || '[]'; 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; } case 'MIN': { - generator.definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = generator.provideFunction_('math_min', ` + // 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) { if (myList.isEmpty) return null; num minVal = myList[0]; myList.forEach((num entry) {minVal = Math.min(minVal, entry);}); return minVal; } -`); +`, + ); code = functionName + '(' + list + ')'; break; } case 'MAX': { - generator.definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = generator.provideFunction_('math_max', ` + // 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) { 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); @@ -290,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. @@ -310,17 +365,22 @@ num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { return (localList[index - 1] + localList[index]) / 2; } } -`); +`, + ); code = functionName + '(' + list + ')'; break; } case 'MODE': { - generator.definitions_['import_dart_math'] = - 'import \'dart:math\' as 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. // 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 = []; @@ -349,15 +409,19 @@ List ${generator.FUNCTION_NAME_PLACEHOLDER_}(List values) { } return modes; } -`); +`, + ); code = functionName + '(' + list + ')'; break; } case 'STD_DEV': { - generator.definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = - generator.provideFunction_('math_standard_deviation', ` + // 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', + ` num ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { // First filter list for numbers only. List numbers = new List.from(myList); @@ -371,19 +435,25 @@ 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; } case 'RANDOM': { - generator.definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = generator.provideFunction_('math_random_item', ` + // 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) { int x = new Math.Random().nextInt(myList.length); return myList[x]; } -`); +`, + ); code = functionName + '(' + list + ')'; break; } @@ -391,39 +461,59 @@ dynamic ${generator.FUNCTION_NAME_PLACEHOLDER_}(List myList) { throw Error('Unknown operator: ' + func); } 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'; + 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, 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;'; - const argument0 = - generator.valueToCode(block, 'VALUE', Order.NONE) || '0'; + // 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'; 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, 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;'; + // 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'; - 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. @@ -433,26 +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, 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;'; + // 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, 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;'; + // 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'; return [ 'Math.atan2(' + argument1 + ', ' + argument0 + ') / Math.pi * 180', - Order.MULTIPLICATIVE + Order.MULTIPLICATIVE, ]; -}; +} diff --git a/generators/dart/procedures.js b/generators/dart/procedures.ts similarity index 60% rename from generators/dart/procedures.js rename to generators/dart/procedures.ts index e782abb6808..934e523183f 100644 --- a/generators/dart/procedures.js +++ b/generators/dart/procedures.ts @@ -10,13 +10,14 @@ // 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'; - -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')); + const funcName = generator.getProcedureName(block.getFieldValue('NAME')); let xfix1 = ''; if (generator.STATEMENT_PREFIX) { xfix1 += generator.injectId(generator.STATEMENT_PREFIX, block); @@ -30,12 +31,12 @@ export function procedures_defreturn(block, generator) { let loopTrap = ''; if (generator.INFINITE_LOOP_TRAP) { loopTrap = generator.prefixLines( - generator.injectId(generator.INFINITE_LOOP_TRAP, block), - generator.INDENT); + generator.injectId(generator.INFINITE_LOOP_TRAP, block), + generator.INDENT, + ); } const branch = generator.statementToCode(block, 'STACK'); - let returnValue = - generator.valueToCode(block, 'RETURN', Order.NONE) || ''; + let returnValue = generator.valueToCode(block, 'RETURN', Order.NONE) || ''; let xfix2 = ''; if (branch && returnValue) { // After executing the function body, revisit this block for the return. @@ -50,22 +51,37 @@ export function procedures_defreturn(block, generator) { 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. - generator.definitions_['%' + funcName] = code; + // TODO(#7600): find better approach than casting to any to override + // CodeGenerator declaring .definitions protected. + (generator as AnyDuringMigration).definitions_['%' + funcName] = code; return null; -}; +} // Defining a procedure without a return value uses the same generator as // a procedure with a return value. export const procedures_defnoreturn = procedures_defreturn; -export function procedures_callreturn(block, generator) { +export function procedures_callreturn( + block: Block, + generator: 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++) { @@ -73,35 +89,41 @@ export function procedures_callreturn(block, generator) { } let code = funcName + '(' + args.join(', ') + ')'; 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. - const tuple = generator.forBlock['procedures_callreturn'](block, generator); + const tuple = generator.forBlock['procedures_callreturn']( + block, + generator, + ) as [string, Order]; return tuple[0] + ';\n'; -}; +} -export function procedures_ifreturn(block, generator) { +export function procedures_ifreturn(block: Block, generator: 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.hasReturnValue_) { - const value = - generator.valueToCode(block, 'VALUE', Order.NONE) || 'null'; + if ((block as IfReturnBlock).hasReturnValue_) { + 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.js b/generators/dart/text.ts similarity index 61% rename from generators/dart/text.js rename to generators/dart/text.ts index 69ffbf7df6a..e8c9be63788 100644 --- a/generators/dart/text.js +++ b/generators/dart/text.ts @@ -10,92 +10,111 @@ // Former goog.module ID: Blockly.Dart.texts +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'; - // 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 = - code.indexOf('+') !== -1 ? Order.ADDITIVE : Order.ATOMIC; + const order = code.indexOf('+') !== -1 ? Order.ADDITIVE : Order.ATOMIC; 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_) { + const joinBlock = block as JoinMutatorBlock; + switch (joinBlock.itemCount_) { case 0: 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]; } 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) || "''"; + generator.valueToCode(block, 'ADD' + i, Order.NONE) || "''"; } const code = '[' + elements.join(',') + '].join()'; return [code, Order.UNARY_POSTFIX]; } } -}; +} -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')); + 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, generator) { +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, 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) || "''"; + 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'; - 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, 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'; - 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) { @@ -110,41 +129,50 @@ export function text_charAt(block, generator) { } 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]; } case 'RANDOM': { - generator.definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = - generator.provideFunction_('text_random_letter', ` + // 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', + ` 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, generator) { +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') { @@ -191,8 +219,9 @@ export function text_getSubstring(block, generator) { } else { const at1 = generator.getAdjusted(block, 'AT1'); const at2 = generator.getAdjusted(block, 'AT2'); - const functionName = - generator.provideFunction_('text_get_substring', ` + const functionName = generator.provideFunction_( + 'text_get_substring', + ` 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') { @@ -210,21 +239,37 @@ 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, generator) { +export function text_changeCase( + block: Block, + generator: DartGenerator, +): [string, Order] { // Change capitalization. const OPERATORS = { 'UPPERCASE': '.toUpperCase()', 'LOWERCASE': '.toLowerCase()', - 'TITLECASE': null + '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; @@ -233,7 +278,9 @@ export function text_changeCase(block, generator) { 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); @@ -248,35 +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, generator) { +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()', }; - 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) || "''"; + generator.valueToCode(block, 'TEXT', Order.UNARY_POSTFIX) || "''"; 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;'; + // 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')) { // Internal message. @@ -285,23 +342,30 @@ export function text_prompt_ext(block, generator) { // 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) { - generator.definitions_['import_dart_math'] = - 'import \'dart:math\' as 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 + ')'; } return [code, Order.UNARY_POSTFIX]; -}; +} 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. - 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; @@ -317,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, generator) { +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, 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? 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.js b/generators/dart/variables.js deleted file mode 100644 index c088437603b..00000000000 --- a/generators/dart/variables.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @license - * Copyright 2014 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Generating Dart for variable blocks. - */ - -// Former goog.module ID: Blockly.Dart.variables - -import {Order} from './dart_generator.js'; - - -export function variables_get(block, generator) { - // Variable getter. - const code = - generator.getVariableName(block.getFieldValue('VAR')); - return [code, Order.ATOMIC]; -}; - -export function variables_set(block, generator) { - // Variable setter. - const argument0 = - generator.valueToCode(block, 'VALUE', Order.ASSIGNMENT) || '0'; - const varName = - generator.getVariableName(block.getFieldValue('VAR')); - return varName + ' = ' + argument0 + ';\n'; -}; diff --git a/generators/dart/variables.ts b/generators/dart/variables.ts new file mode 100644 index 00000000000..1910befc9c2 --- /dev/null +++ b/generators/dart/variables.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright 2014 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Generating Dart for variable blocks. + */ + +// 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: Block, + generator: DartGenerator, +): [string, Order] { + // Variable getter. + 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')); + return varName + ' = ' + argument0 + ';\n'; +} diff --git a/generators/dart/variables_dynamic.js b/generators/dart/variables_dynamic.ts similarity index 99% rename from generators/dart/variables_dynamic.js rename to generators/dart/variables_dynamic.ts index 22be966dcb7..6a92fff9125 100644 --- a/generators/dart/variables_dynamic.js +++ 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, 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];