From ab9deb2d093a3f15bf378262d09d07b8136efe33 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Wed, 24 Jun 2020 12:47:34 -0500 Subject: [PATCH] Smaller fixes/changes - add looks_say - add flags for CompiledInput - add nullabe/NaNable --- src/blocks/scratch3_looks.js | 5 ++- .../blocks/compiler_scratch3_looks.js | 6 +++ .../blocks/compiler_scratch3_operators.js | 2 +- src/compiler/compiler.js | 41 ++++++++++++++++++- src/compiler/execute.js | 9 ++++ 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/blocks/scratch3_looks.js b/src/blocks/scratch3_looks.js index 49b82c58..ea27fcb8 100644 --- a/src/blocks/scratch3_looks.js +++ b/src/blocks/scratch3_looks.js @@ -301,11 +301,14 @@ class Scratch3LooksBlocks { say (args, util) { // @TODO in 2.0 calling say/think resets the right/left bias of the bubble let message = args.MESSAGE; + this._say(message, util.target); + } + _say (message, target) { // used by compiler if (typeof message === 'number') { message = parseFloat(message.toFixed(2)); } message = String(message).substr(0, Scratch3LooksBlocks.SAY_BUBBLE_LIMIT); - this.runtime.emit('SAY', util.target, 'say', message); + this.runtime.emit('SAY', target, 'say', message); } sayforsecs (args, util) { diff --git a/src/compiler/blocks/compiler_scratch3_looks.js b/src/compiler/blocks/compiler_scratch3_looks.js index 6f011174..ea384419 100644 --- a/src/compiler/blocks/compiler_scratch3_looks.js +++ b/src/compiler/blocks/compiler_scratch3_looks.js @@ -5,6 +5,7 @@ const { InputUtil, StatementUtil, CompiledInput } = require('../compiler'); */ module.exports.getStatements = () => { return { + looks_say: say, looks_changeeffectby: changeEffect, looks_seteffectto: setEffect, looks_hide: hide, @@ -27,6 +28,11 @@ module.exports.getInputs = () => { }; }; +const say = /** @param {StatementUtil} util */ (util) => { + const MESSAGE = util.input('MESSAGE'); + util.writeLn(`runtime.ext_scratch3_looks._say(${MESSAGE}, target);`); +}; + const changeEffect = /** @param {StatementUtil} util */ (util) => { const EFFECT = util.fieldValueUnsafe('EFFECT').toLowerCase(); const CHANGE = util.input('CHANGE'); diff --git a/src/compiler/blocks/compiler_scratch3_operators.js b/src/compiler/blocks/compiler_scratch3_operators.js index ba7ce025..a7253f45 100644 --- a/src/compiler/blocks/compiler_scratch3_operators.js +++ b/src/compiler/blocks/compiler_scratch3_operators.js @@ -113,7 +113,7 @@ const mathop = /** @param {InputUtil} util */ (util) => { case 'abs': return util.number(`Math.abs(${NUM})`); case 'floor': return util.number(`Math.floor(${NUM})`); case 'ceiling': return util.number(`Math.ceil(${NUM})`); - case 'sqrt': return util.number(`(Math.sqrt(${NUM}) || 0)`); + case 'sqrt': return util.number(`Math.sqrt(${NUM})`).nanable(); // TODO: consider rounding as Scratch 3 does case 'sin': return util.number(`Math.sin((Math.PI * ${NUM}) / 180)`); case 'cos': return util.number(`Math.cos((Math.PI * ${NUM}) / 180)`); diff --git a/src/compiler/compiler.js b/src/compiler/compiler.js index f8474b88..9bce7dd1 100644 --- a/src/compiler/compiler.js +++ b/src/compiler/compiler.js @@ -27,6 +27,9 @@ const TYPE_NUMBER = 1; const TYPE_BOOLEAN = 2; const TYPE_STRING = 3; +const FLAG_NULLABLE = 1; +const FLAG_NANABLE = 2; + defaultExtensions.forEach((ext) => { const extensionInputs = ext.getInputs(); for (const op in extensionInputs) { @@ -226,8 +229,39 @@ class CompiledInput { * @param {number} type The input's type at runtime. */ constructor(source, type) { + /** + * The input's source code. + * @readonly + * @private + */ this.source = source; + /** + * The input's type. + * @readonly + * @private + */ this.type = type; + /** + * Internal flags. + * @private + */ + this.flags = 0; + } + + /** + * Enable the NULLABLE flag. + */ + nullable() { + this.flags |= FLAG_NULLABLE; + return this; + } + + /** + * Enable the NANABLE flag. + */ + nanable() { + this.flags |= FLAG_NANABLE; + return this; } toString() { @@ -235,7 +269,12 @@ class CompiledInput { } asNumber() { - if (this.type === TYPE_NUMBER) return this.source; + if (this.type === TYPE_NUMBER) { + if (this.flags & FLAG_NANABLE) { + return 'toNotNaN(' + this.source + ')'; + } + return this.source; + } return 'toNumber(' + this.source + ')'; } diff --git a/src/compiler/execute.js b/src/compiler/execute.js index ecfce86b..3773f8f0 100644 --- a/src/compiler/execute.js +++ b/src/compiler/execute.js @@ -54,6 +54,15 @@ const toNumber = (value) => { return Number(value) || 0; }; +/** + * Convert a number to something other than NaN. + * @param {number} number The value to convert. + * @returns {number} + */ +const toNotNaN = (number) => { + return number || 0; +}; + /** * Scratch cast to boolean. * Similar to Cast.toBoolean()