From 1f6a1bd8d9a5208a7da8c4fd0d55c1a3516ee7e7 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Fri, 28 Jan 2022 17:58:43 -0800 Subject: [PATCH] chore: Use ES6 template strings in CSS and code generators (#5902) * Unindent CSS, save 3 kb of code. * Convert generator functions to template strings. This resolves #5761. --- core/comment.js | 20 +- core/css.js | 938 ++++++++++++------------- core/field_angle.js | 42 +- core/field_colour.js | 50 +- core/field_multilineinput.js | 20 +- core/flyout_button.js | 32 +- core/generator.js | 8 +- core/toolbox/category.js | 114 +-- core/toolbox/separator.js | 26 +- core/toolbox/toolbox.js | 52 +- core/workspace_comment_svg.js | 92 +-- core/zoom_controls.js | 18 +- generators/dart/colour.js | 120 ++-- generators/dart/lists.js | 111 +-- generators/dart/math.js | 275 ++++---- generators/dart/text.js | 144 ++-- generators/javascript/colour.js | 71 +- generators/javascript/lists.js | 93 +-- generators/javascript/math.js | 223 +++--- generators/javascript/text.js | 148 ++-- generators/lua/colour.js | 48 +- generators/lua/lists.js | 166 +++-- generators/lua/logic.js | 3 +- generators/lua/math.js | 289 ++++---- generators/lua/text.js | 215 +++--- generators/php/colour.js | 70 +- generators/php/lists.js | 198 +++--- generators/php/math.js | 147 ++-- generators/php/text.js | 124 ++-- generators/python/colour.js | 36 +- generators/python/lists.js | 68 +- generators/python/loops.js | 20 +- generators/python/math.js | 161 ++--- generators/python/text.js | 64 +- tests/generators/golden/generated.dart | 8 +- tests/generators/golden/generated.js | 12 +- tests/generators/golden/generated.lua | 15 +- tests/generators/golden/generated.php | 13 +- tests/generators/golden/generated.py | 2 +- 39 files changed, 2194 insertions(+), 2062 deletions(-) diff --git a/core/comment.js b/core/comment.js index 30f9aac36..d11e2b636 100644 --- a/core/comment.js +++ b/core/comment.js @@ -434,16 +434,16 @@ Comment.prototype.dispose = function() { * CSS for block comment. See css.js for use. */ Css.register(` - .blocklyCommentTextarea { - background-color: #fef49c; - border: 0; - display: block; - margin: 0; - outline: 0; - padding: 3px; - resize: none; - text-overflow: hidden; - } +.blocklyCommentTextarea { + background-color: #fef49c; + border: 0; + display: block; + margin: 0; + outline: 0; + padding: 3px; + resize: none; + text-overflow: hidden; +} `); exports.Comment = Comment; diff --git a/core/css.js b/core/css.js index 7cea9284f..e7ed4ec8c 100644 --- a/core/css.js +++ b/core/css.js @@ -89,480 +89,480 @@ exports.inject = inject; * @alias Blockly.Css.content */ let content = (` - .blocklySvg { - background-color: #fff; - outline: none; - overflow: hidden; /* IE overflows by default. */ - position: absolute; - display: block; - } +.blocklySvg { + background-color: #fff; + outline: none; + overflow: hidden; /* IE overflows by default. */ + position: absolute; + display: block; +} - .blocklyWidgetDiv { - display: none; - position: absolute; - z-index: 99999; /* big value for bootstrap3 compatibility */ - } +.blocklyWidgetDiv { + display: none; + position: absolute; + z-index: 99999; /* big value for bootstrap3 compatibility */ +} - .injectionDiv { - height: 100%; - position: relative; - overflow: hidden; /* So blocks in drag surface disappear at edges */ - touch-action: none; - } +.injectionDiv { + height: 100%; + position: relative; + overflow: hidden; /* So blocks in drag surface disappear at edges */ + touch-action: none; +} - .blocklyNonSelectable { - user-select: none; - -ms-user-select: none; - -webkit-user-select: none; - } +.blocklyNonSelectable { + user-select: none; + -ms-user-select: none; + -webkit-user-select: none; +} - .blocklyWsDragSurface { - display: none; - position: absolute; - top: 0; - left: 0; - } +.blocklyWsDragSurface { + display: none; + position: absolute; + top: 0; + left: 0; +} - /* Added as a separate rule with multiple classes to make it more specific - than a bootstrap rule that selects svg:root. See issue #1275 for context. +/* Added as a separate rule with multiple classes to make it more specific + than a bootstrap rule that selects svg:root. See issue #1275 for context. +*/ +.blocklyWsDragSurface.blocklyOverflowVisible { + overflow: visible; +} + +.blocklyBlockDragSurface { + display: none; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: visible !important; + z-index: 50; /* Display below toolbox, but above everything else. */ +} + +.blocklyBlockCanvas.blocklyCanvasTransitioning, +.blocklyBubbleCanvas.blocklyCanvasTransitioning { + transition: transform .5s; +} + +.blocklyTooltipDiv { + background-color: #ffffc7; + border: 1px solid #ddc; + box-shadow: 4px 4px 20px 1px rgba(0,0,0,.15); + color: #000; + display: none; + font: 9pt sans-serif; + opacity: .9; + padding: 2px; + position: absolute; + z-index: 100000; /* big value for bootstrap3 compatibility */ +} + +.blocklyDropDownDiv { + position: absolute; + left: 0; + top: 0; + z-index: 1000; + display: none; + border: 1px solid; + border-color: #dadce0; + background-color: #fff; + border-radius: 2px; + padding: 4px; + box-shadow: 0 0 3px 1px rgba(0,0,0,.3); +} + +.blocklyDropDownDiv.blocklyFocused { + box-shadow: 0 0 6px 1px rgba(0,0,0,.3); +} + +.blocklyDropDownContent { + max-height: 300px; // @todo: spec for maximum height. + overflow: auto; + overflow-x: hidden; + position: relative; +} + +.blocklyDropDownArrow { + position: absolute; + left: 0; + top: 0; + width: 16px; + height: 16px; + z-index: -1; + background-color: inherit; + border-color: inherit; +} + +.blocklyDropDownButton { + display: inline-block; + float: left; + padding: 0; + margin: 4px; + border-radius: 4px; + outline: none; + border: 1px solid; + transition: box-shadow .1s; + cursor: pointer; +} + +.blocklyArrowTop { + border-top: 1px solid; + border-left: 1px solid; + border-top-left-radius: 4px; + border-color: inherit; +} + +.blocklyArrowBottom { + border-bottom: 1px solid; + border-right: 1px solid; + border-bottom-right-radius: 4px; + border-color: inherit; +} + +.blocklyResizeSE { + cursor: se-resize; + fill: #aaa; +} + +.blocklyResizeSW { + cursor: sw-resize; + fill: #aaa; +} + +.blocklyResizeLine { + stroke: #515A5A; + stroke-width: 1; +} + +.blocklyHighlightedConnectionPath { + fill: none; + stroke: #fc3; + stroke-width: 4px; +} + +.blocklyPathLight { + fill: none; + stroke-linecap: round; + stroke-width: 1; +} + +.blocklySelected>.blocklyPathLight { + display: none; +} + +.blocklyDraggable { + /* backup for browsers (e.g. IE11) that don't support grab */ + cursor: url("<<>>/handopen.cur"), auto; + cursor: grab; + cursor: -webkit-grab; +} + + /* backup for browsers (e.g. IE11) that don't support grabbing */ +.blocklyDragging { + /* backup for browsers (e.g. IE11) that don't support grabbing */ + cursor: url("<<>>/handclosed.cur"), auto; + cursor: grabbing; + cursor: -webkit-grabbing; +} + + /* Changes cursor on mouse down. Not effective in Firefox because of + https://bugzilla.mozilla.org/show_bug.cgi?id=771241 */ +.blocklyDraggable:active { + /* backup for browsers (e.g. IE11) that don't support grabbing */ + cursor: url("<<>>/handclosed.cur"), auto; + cursor: grabbing; + cursor: -webkit-grabbing; +} + +/* Change the cursor on the whole drag surface in case the mouse gets + ahead of block during a drag. This way the cursor is still a closed hand. */ - .blocklyWsDragSurface.blocklyOverflowVisible { - overflow: visible; - } - - .blocklyBlockDragSurface { - display: none; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - overflow: visible !important; - z-index: 50; /* Display below toolbox, but above everything else. */ - } - - .blocklyBlockCanvas.blocklyCanvasTransitioning, - .blocklyBubbleCanvas.blocklyCanvasTransitioning { - transition: transform .5s; - } - - .blocklyTooltipDiv { - background-color: #ffffc7; - border: 1px solid #ddc; - box-shadow: 4px 4px 20px 1px rgba(0,0,0,.15); - color: #000; - display: none; - font: 9pt sans-serif; - opacity: .9; - padding: 2px; - position: absolute; - z-index: 100000; /* big value for bootstrap3 compatibility */ - } - - .blocklyDropDownDiv { - position: absolute; - left: 0; - top: 0; - z-index: 1000; - display: none; - border: 1px solid; - border-color: #dadce0; - background-color: #fff; - border-radius: 2px; - padding: 4px; - box-shadow: 0 0 3px 1px rgba(0,0,0,.3); - } - - .blocklyDropDownDiv.blocklyFocused { - box-shadow: 0 0 6px 1px rgba(0,0,0,.3); - } - - .blocklyDropDownContent { - max-height: 300px; // @todo: spec for maximum height. - overflow: auto; - overflow-x: hidden; - position: relative; - } - - .blocklyDropDownArrow { - position: absolute; - left: 0; - top: 0; - width: 16px; - height: 16px; - z-index: -1; - background-color: inherit; - border-color: inherit; - } - - .blocklyDropDownButton { - display: inline-block; - float: left; - padding: 0; - margin: 4px; - border-radius: 4px; - outline: none; - border: 1px solid; - transition: box-shadow .1s; - cursor: pointer; - } - - .blocklyArrowTop { - border-top: 1px solid; - border-left: 1px solid; - border-top-left-radius: 4px; - border-color: inherit; - } - - .blocklyArrowBottom { - border-bottom: 1px solid; - border-right: 1px solid; - border-bottom-right-radius: 4px; - border-color: inherit; - } - - .blocklyResizeSE { - cursor: se-resize; - fill: #aaa; - } - - .blocklyResizeSW { - cursor: sw-resize; - fill: #aaa; - } - - .blocklyResizeLine { - stroke: #515A5A; - stroke-width: 1; - } - - .blocklyHighlightedConnectionPath { - fill: none; - stroke: #fc3; - stroke-width: 4px; - } - - .blocklyPathLight { - fill: none; - stroke-linecap: round; - stroke-width: 1; - } - - .blocklySelected>.blocklyPathLight { - display: none; - } - - .blocklyDraggable { - /* backup for browsers (e.g. IE11) that don't support grab */ - cursor: url("<<>>/handopen.cur"), auto; - cursor: grab; - cursor: -webkit-grab; - } - - /* backup for browsers (e.g. IE11) that don't support grabbing */ - .blocklyDragging { - /* backup for browsers (e.g. IE11) that don't support grabbing */ - cursor: url("<<>>/handclosed.cur"), auto; - cursor: grabbing; - cursor: -webkit-grabbing; - } - - /* Changes cursor on mouse down. Not effective in Firefox because of - https://bugzilla.mozilla.org/show_bug.cgi?id=771241 */ - .blocklyDraggable:active { - /* backup for browsers (e.g. IE11) that don't support grabbing */ - cursor: url("<<>>/handclosed.cur"), auto; - cursor: grabbing; - cursor: -webkit-grabbing; - } - - /* Change the cursor on the whole drag surface in case the mouse gets - ahead of block during a drag. This way the cursor is still a closed hand. - */ - .blocklyBlockDragSurface .blocklyDraggable { - /* backup for browsers (e.g. IE11) that don't support grabbing */ - cursor: url("<<>>/handclosed.cur"), auto; - cursor: grabbing; - cursor: -webkit-grabbing; - } - - .blocklyDragging.blocklyDraggingDelete { - cursor: url("<<>>/handdelete.cur"), auto; - } - - .blocklyDragging>.blocklyPath, - .blocklyDragging>.blocklyPathLight { - fill-opacity: .8; - stroke-opacity: .8; - } - - .blocklyDragging>.blocklyPathDark { - display: none; - } - - .blocklyDisabled>.blocklyPath { - fill-opacity: .5; - stroke-opacity: .5; - } - - .blocklyDisabled>.blocklyPathLight, - .blocklyDisabled>.blocklyPathDark { - display: none; - } - - .blocklyInsertionMarker>.blocklyPath, - .blocklyInsertionMarker>.blocklyPathLight, - .blocklyInsertionMarker>.blocklyPathDark { - fill-opacity: .2; - stroke: none; - } - - .blocklyMultilineText { - font-family: monospace; - } - - .blocklyNonEditableText>text { - pointer-events: none; - } - - .blocklyFlyout { - position: absolute; - z-index: 20; - } - - .blocklyText text { - cursor: default; - } - - /* - Don't allow users to select text. It gets annoying when trying to - drag a block and selected text moves instead. - */ - .blocklySvg text, - .blocklyBlockDragSurface text { - user-select: none; - -ms-user-select: none; - -webkit-user-select: none; - cursor: inherit; - } - - .blocklyHidden { - display: none; - } - - .blocklyFieldDropdown:not(.blocklyHidden) { - display: block; - } - - .blocklyIconGroup { - cursor: default; - } - - .blocklyIconGroup:not(:hover), - .blocklyIconGroupReadonly { - opacity: .6; - } - - .blocklyIconShape { - fill: #00f; - stroke: #fff; - stroke-width: 1px; - } - - .blocklyIconSymbol { - fill: #fff; - } - - .blocklyMinimalBody { - margin: 0; - padding: 0; - } - - .blocklyHtmlInput { - border: none; - border-radius: 4px; - height: 100%; - margin: 0; - outline: none; - padding: 0; - width: 100%; - text-align: center; - display: block; - box-sizing: border-box; - } - - /* Edge and IE introduce a close icon when the input value is longer than a - certain length. This affects our sizing calculations of the text input. - Hiding the close icon to avoid that. */ - .blocklyHtmlInput::-ms-clear { - display: none; - } - - .blocklyMainBackground { - stroke-width: 1; - stroke: #c6c6c6; /* Equates to #ddd due to border being off-pixel. */ - } - - .blocklyMutatorBackground { - fill: #fff; - stroke: #ddd; - stroke-width: 1; - } - - .blocklyFlyoutBackground { - fill: #ddd; - fill-opacity: .8; - } - - .blocklyMainWorkspaceScrollbar { - z-index: 20; - } - - .blocklyFlyoutScrollbar { - z-index: 30; - } - - .blocklyScrollbarHorizontal, - .blocklyScrollbarVertical { - position: absolute; - outline: none; - } - - .blocklyScrollbarBackground { - opacity: 0; - } - - .blocklyScrollbarHandle { - fill: #ccc; - } - - .blocklyScrollbarBackground:hover+.blocklyScrollbarHandle, - .blocklyScrollbarHandle:hover { - fill: #bbb; - } - - /* Darken flyout scrollbars due to being on a grey background. */ - /* By contrast, workspace scrollbars are on a white background. */ - .blocklyFlyout .blocklyScrollbarHandle { - fill: #bbb; - } - - .blocklyFlyout .blocklyScrollbarBackground:hover+.blocklyScrollbarHandle, - .blocklyFlyout .blocklyScrollbarHandle:hover { - fill: #aaa; - } - - .blocklyInvalidInput { - background: #faa; - } - - .blocklyVerticalMarker { - stroke-width: 3px; - fill: rgba(255,255,255,.5); - pointer-events: none; - } - - .blocklyComputeCanvas { - position: absolute; - width: 0; - height: 0; - } - - .blocklyNoPointerEvents { - pointer-events: none; - } - - .blocklyContextMenu { - border-radius: 4px; - max-height: 100%; - } - - .blocklyDropdownMenu { - border-radius: 2px; - padding: 0 !important; - } - - .blocklyDropdownMenu .blocklyMenuItem { - /* 28px on the left for icon or checkbox. */ - padding-left: 28px; - } - - /* BiDi override for the resting state. */ - .blocklyDropdownMenu .blocklyMenuItemRtl { - /* Flip left/right padding for BiDi. */ - padding-left: 5px; - padding-right: 28px; - } - - .blocklyWidgetDiv .blocklyMenu { - background: #fff; - border: 1px solid transparent; - box-shadow: 0 0 3px 1px rgba(0,0,0,.3); - font: normal 13px Arial, sans-serif; - margin: 0; - outline: none; - padding: 4px 0; - position: absolute; - overflow-y: auto; - overflow-x: hidden; - max-height: 100%; - z-index: 20000; /* Arbitrary, but some apps depend on it... */ - } - - .blocklyWidgetDiv .blocklyMenu.blocklyFocused { - box-shadow: 0 0 6px 1px rgba(0,0,0,.3); - } - - .blocklyDropDownDiv .blocklyMenu { - background: inherit; /* Compatibility with gapi, reset from goog-menu */ - border: inherit; /* Compatibility with gapi, reset from goog-menu */ - font: normal 13px "Helvetica Neue", Helvetica, sans-serif; - outline: none; - position: relative; /* Compatibility with gapi, reset from goog-menu */ - z-index: 20000; /* Arbitrary, but some apps depend on it... */ - } - - /* State: resting. */ - .blocklyMenuItem { - border: none; - color: #000; - cursor: pointer; - list-style: none; - margin: 0; - /* 7em on the right for shortcut. */ - min-width: 7em; - padding: 6px 15px; - white-space: nowrap; - } - - /* State: disabled. */ - .blocklyMenuItemDisabled { - color: #ccc; - cursor: inherit; - } - - /* State: hover. */ - .blocklyMenuItemHighlight { - background-color: rgba(0,0,0,.1); - } - - /* State: selected/checked. */ - .blocklyMenuItemCheckbox { - height: 16px; - position: absolute; - width: 16px; - } - - .blocklyMenuItemSelected .blocklyMenuItemCheckbox { - background: url(<<>>/sprites.png) no-repeat -48px -16px; - float: left; - margin-left: -24px; - position: static; /* Scroll with the menu. */ - } - - .blocklyMenuItemRtl .blocklyMenuItemCheckbox { - float: right; - margin-right: -24px; - } +.blocklyBlockDragSurface .blocklyDraggable { + /* backup for browsers (e.g. IE11) that don't support grabbing */ + cursor: url("<<>>/handclosed.cur"), auto; + cursor: grabbing; + cursor: -webkit-grabbing; +} + +.blocklyDragging.blocklyDraggingDelete { + cursor: url("<<>>/handdelete.cur"), auto; +} + +.blocklyDragging>.blocklyPath, +.blocklyDragging>.blocklyPathLight { + fill-opacity: .8; + stroke-opacity: .8; +} + +.blocklyDragging>.blocklyPathDark { + display: none; +} + +.blocklyDisabled>.blocklyPath { + fill-opacity: .5; + stroke-opacity: .5; +} + +.blocklyDisabled>.blocklyPathLight, +.blocklyDisabled>.blocklyPathDark { + display: none; +} + +.blocklyInsertionMarker>.blocklyPath, +.blocklyInsertionMarker>.blocklyPathLight, +.blocklyInsertionMarker>.blocklyPathDark { + fill-opacity: .2; + stroke: none; +} + +.blocklyMultilineText { + font-family: monospace; +} + +.blocklyNonEditableText>text { + pointer-events: none; +} + +.blocklyFlyout { + position: absolute; + z-index: 20; +} + +.blocklyText text { + cursor: default; +} + +/* + Don't allow users to select text. It gets annoying when trying to + drag a block and selected text moves instead. +*/ +.blocklySvg text, +.blocklyBlockDragSurface text { + user-select: none; + -ms-user-select: none; + -webkit-user-select: none; + cursor: inherit; +} + +.blocklyHidden { + display: none; +} + +.blocklyFieldDropdown:not(.blocklyHidden) { + display: block; +} + +.blocklyIconGroup { + cursor: default; +} + +.blocklyIconGroup:not(:hover), +.blocklyIconGroupReadonly { + opacity: .6; +} + +.blocklyIconShape { + fill: #00f; + stroke: #fff; + stroke-width: 1px; +} + +.blocklyIconSymbol { + fill: #fff; +} + +.blocklyMinimalBody { + margin: 0; + padding: 0; +} + +.blocklyHtmlInput { + border: none; + border-radius: 4px; + height: 100%; + margin: 0; + outline: none; + padding: 0; + width: 100%; + text-align: center; + display: block; + box-sizing: border-box; +} + +/* Edge and IE introduce a close icon when the input value is longer than a + certain length. This affects our sizing calculations of the text input. + Hiding the close icon to avoid that. */ +.blocklyHtmlInput::-ms-clear { + display: none; +} + +.blocklyMainBackground { + stroke-width: 1; + stroke: #c6c6c6; /* Equates to #ddd due to border being off-pixel. */ +} + +.blocklyMutatorBackground { + fill: #fff; + stroke: #ddd; + stroke-width: 1; +} + +.blocklyFlyoutBackground { + fill: #ddd; + fill-opacity: .8; +} + +.blocklyMainWorkspaceScrollbar { + z-index: 20; +} + +.blocklyFlyoutScrollbar { + z-index: 30; +} + +.blocklyScrollbarHorizontal, +.blocklyScrollbarVertical { + position: absolute; + outline: none; +} + +.blocklyScrollbarBackground { + opacity: 0; +} + +.blocklyScrollbarHandle { + fill: #ccc; +} + +.blocklyScrollbarBackground:hover+.blocklyScrollbarHandle, +.blocklyScrollbarHandle:hover { + fill: #bbb; +} + +/* Darken flyout scrollbars due to being on a grey background. */ +/* By contrast, workspace scrollbars are on a white background. */ +.blocklyFlyout .blocklyScrollbarHandle { + fill: #bbb; +} + +.blocklyFlyout .blocklyScrollbarBackground:hover+.blocklyScrollbarHandle, +.blocklyFlyout .blocklyScrollbarHandle:hover { + fill: #aaa; +} + +.blocklyInvalidInput { + background: #faa; +} + +.blocklyVerticalMarker { + stroke-width: 3px; + fill: rgba(255,255,255,.5); + pointer-events: none; +} + +.blocklyComputeCanvas { + position: absolute; + width: 0; + height: 0; +} + +.blocklyNoPointerEvents { + pointer-events: none; +} + +.blocklyContextMenu { + border-radius: 4px; + max-height: 100%; +} + +.blocklyDropdownMenu { + border-radius: 2px; + padding: 0 !important; +} + +.blocklyDropdownMenu .blocklyMenuItem { + /* 28px on the left for icon or checkbox. */ + padding-left: 28px; +} + +/* BiDi override for the resting state. */ +.blocklyDropdownMenu .blocklyMenuItemRtl { + /* Flip left/right padding for BiDi. */ + padding-left: 5px; + padding-right: 28px; +} + +.blocklyWidgetDiv .blocklyMenu { + background: #fff; + border: 1px solid transparent; + box-shadow: 0 0 3px 1px rgba(0,0,0,.3); + font: normal 13px Arial, sans-serif; + margin: 0; + outline: none; + padding: 4px 0; + position: absolute; + overflow-y: auto; + overflow-x: hidden; + max-height: 100%; + z-index: 20000; /* Arbitrary, but some apps depend on it... */ +} + +.blocklyWidgetDiv .blocklyMenu.blocklyFocused { + box-shadow: 0 0 6px 1px rgba(0,0,0,.3); +} + +.blocklyDropDownDiv .blocklyMenu { + background: inherit; /* Compatibility with gapi, reset from goog-menu */ + border: inherit; /* Compatibility with gapi, reset from goog-menu */ + font: normal 13px "Helvetica Neue", Helvetica, sans-serif; + outline: none; + position: relative; /* Compatibility with gapi, reset from goog-menu */ + z-index: 20000; /* Arbitrary, but some apps depend on it... */ +} + +/* State: resting. */ +.blocklyMenuItem { + border: none; + color: #000; + cursor: pointer; + list-style: none; + margin: 0; + /* 7em on the right for shortcut. */ + min-width: 7em; + padding: 6px 15px; + white-space: nowrap; +} + +/* State: disabled. */ +.blocklyMenuItemDisabled { + color: #ccc; + cursor: inherit; +} + +/* State: hover. */ +.blocklyMenuItemHighlight { + background-color: rgba(0,0,0,.1); +} + +/* State: selected/checked. */ +.blocklyMenuItemCheckbox { + height: 16px; + position: absolute; + width: 16px; +} + +.blocklyMenuItemSelected .blocklyMenuItemCheckbox { + background: url(<<>>/sprites.png) no-repeat -48px -16px; + float: left; + margin-left: -24px; + position: static; /* Scroll with the menu. */ +} + +.blocklyMenuItemRtl .blocklyMenuItemCheckbox { + float: right; + margin-right: -24px; +} `); exports.content = content; diff --git a/core/field_angle.js b/core/field_angle.js index 8454f7900..bfc5843ea 100644 --- a/core/field_angle.js +++ b/core/field_angle.js @@ -540,30 +540,30 @@ FieldAngle.prototype.wrapValue_ = function(value) { * CSS for angle field. See css.js for use. */ Css.register(` - .blocklyAngleCircle { - stroke: #444; - stroke-width: 1; - fill: #ddd; - fill-opacity: .8; - } +.blocklyAngleCircle { + stroke: #444; + stroke-width: 1; + fill: #ddd; + fill-opacity: .8; +} - .blocklyAngleMarks { - stroke: #444; - stroke-width: 1; - } +.blocklyAngleMarks { + stroke: #444; + stroke-width: 1; +} - .blocklyAngleGauge { - fill: #f88; - fill-opacity: .8; - pointer-events: none; - } +.blocklyAngleGauge { + fill: #f88; + fill-opacity: .8; + pointer-events: none; +} - .blocklyAngleLine { - stroke: #f00; - stroke-width: 2; - stroke-linecap: round; - pointer-events: none; - } +.blocklyAngleLine { + stroke: #f00; + stroke-width: 2; + stroke-linecap: round; + pointer-events: none; +} `); fieldRegistry.register('field_angle', FieldAngle); diff --git a/core/field_colour.js b/core/field_colour.js index 4b4c037a9..a769844ef 100644 --- a/core/field_colour.js +++ b/core/field_colour.js @@ -668,34 +668,34 @@ FieldColour.prototype.dropdownDispose_ = function() { * CSS for colour picker. See css.js for use. */ Css.register(` - .blocklyColourTable { - border-collapse: collapse; - display: block; - outline: none; - padding: 1px; - } +.blocklyColourTable { + border-collapse: collapse; + display: block; + outline: none; + padding: 1px; +} - .blocklyColourTable>tr>td { - border: .5px solid #888; - box-sizing: border-box; - cursor: pointer; - display: inline-block; - height: 20px; - padding: 0; - width: 20px; - } +.blocklyColourTable>tr>td { + border: .5px solid #888; + box-sizing: border-box; + cursor: pointer; + display: inline-block; + height: 20px; + padding: 0; + width: 20px; +} - .blocklyColourTable>tr>td.blocklyColourHighlighted { - border-color: #eee; - box-shadow: 2px 2px 7px 2px rgba(0,0,0,.3); - position: relative; - } +.blocklyColourTable>tr>td.blocklyColourHighlighted { + border-color: #eee; + box-shadow: 2px 2px 7px 2px rgba(0,0,0,.3); + position: relative; +} - .blocklyColourSelected, .blocklyColourSelected:hover { - border-color: #eee !important; - outline: 1px solid #333; - position: relative; - } +.blocklyColourSelected, .blocklyColourSelected:hover { + border-color: #eee !important; + outline: 1px solid #333; + position: relative; +} `); fieldRegistry.register('field_colour', FieldColour); diff --git a/core/field_multilineinput.js b/core/field_multilineinput.js index 5496bdef8..9f7899d6c 100644 --- a/core/field_multilineinput.js +++ b/core/field_multilineinput.js @@ -431,17 +431,17 @@ FieldMultilineInput.prototype.onHtmlInputKeyDown_ = function(e) { * CSS for multiline field. See css.js for use. */ Css.register(` - .blocklyHtmlTextAreaInput { - font-family: monospace; - resize: none; - overflow: hidden; - height: 100%; - text-align: left; - } +.blocklyHtmlTextAreaInput { + font-family: monospace; + resize: none; + overflow: hidden; + height: 100%; + text-align: left; +} - .blocklyHtmlTextAreaInputOverflowedY { - overflow-y: scroll; - } +.blocklyHtmlTextAreaInputOverflowedY { + overflow-y: scroll; +} `); fieldRegistry.register('field_multilinetext', FieldMultilineInput); diff --git a/core/flyout_button.js b/core/flyout_button.js index 427f2b694..e85a0b1ed 100644 --- a/core/flyout_button.js +++ b/core/flyout_button.js @@ -331,26 +331,26 @@ FlyoutButton.TEXT_MARGIN_Y = 2; * CSS for buttons and labels. See css.js for use. */ Css.register(` - .blocklyFlyoutButton { - fill: #888; - cursor: default; - } +.blocklyFlyoutButton { + fill: #888; + cursor: default; +} - .blocklyFlyoutButtonShadow { - fill: #666; - } +.blocklyFlyoutButtonShadow { + fill: #666; +} - .blocklyFlyoutButton:hover { - fill: #aaa; - } +.blocklyFlyoutButton:hover { + fill: #aaa; +} - .blocklyFlyoutLabel { - cursor: default; - } +.blocklyFlyoutLabel { + cursor: default; +} - .blocklyFlyoutLabelBackground { - opacity: 0; - } +.blocklyFlyoutLabelBackground { + opacity: 0; +} `); exports.FlyoutButton = FlyoutButton; diff --git a/core/generator.js b/core/generator.js index 55b7f9095..15be91bd4 100644 --- a/core/generator.js +++ b/core/generator.js @@ -459,7 +459,8 @@ Object.defineProperties(Generator.prototype, { * * @param {string} desiredName The desired name of the function * (e.g. mathIsPrime). - * @param {!Array} code A list of statements. Use ' ' for indents. + * @param {!Array|string} code A list of statements or one multi-line + * code string. Use ' ' for indents (they will be replaced). * @return {string} The actual name of the new function. This may differ * from desiredName if the former has already been taken by the user. * @protected @@ -469,7 +470,10 @@ Generator.prototype.provideFunction_ = function(desiredName, code) { const functionName = this.nameDB_.getDistinctName(desiredName, NameType.PROCEDURE); this.functionNames_[desiredName] = functionName; - let codeText = code.join('\n').replace( + if (Array.isArray(code)) { + code = code.join('\n'); + } + let codeText = code.trim().replace( this.FUNCTION_NAME_PLACEHOLDER_REGEXP_, functionName); // Change all ' ' indents into the desired indent. // To avoid an infinite loop of replacements, change all indents to '\0' diff --git a/core/toolbox/category.js b/core/toolbox/category.js index 286be4011..97df680f3 100644 --- a/core/toolbox/category.js +++ b/core/toolbox/category.js @@ -645,77 +645,77 @@ ToolboxCategory.prototype.dispose = function() { * CSS for Toolbox. See css.js for use. */ Css.register(` - .blocklyTreeRow:not(.blocklyTreeSelected):hover { - background-color: rgba(255, 255, 255, 0.2); - } +.blocklyTreeRow:not(.blocklyTreeSelected):hover { + background-color: rgba(255, 255, 255, .2); +} - .blocklyToolboxDiv[layout="h"] .blocklyToolboxCategory { - margin: 1px 5px 1px 0; - } +.blocklyToolboxDiv[layout="h"] .blocklyToolboxCategory { + margin: 1px 5px 1px 0; +} - .blocklyToolboxDiv[dir="RTL"][layout="h"] .blocklyToolboxCategory { - margin: 1px 0 1px 5px; - } +.blocklyToolboxDiv[dir="RTL"][layout="h"] .blocklyToolboxCategory { + margin: 1px 0 1px 5px; +} - .blocklyTreeRow { - height: 22px; - line-height: 22px; - margin-bottom: 3px; - padding-right: 8px; - white-space: nowrap; - } +.blocklyTreeRow { + height: 22px; + line-height: 22px; + margin-bottom: 3px; + padding-right: 8px; + white-space: nowrap; +} - .blocklyToolboxDiv[dir="RTL"] .blocklyTreeRow { - margin-left: 8px; - padding-right: 0; - } +.blocklyToolboxDiv[dir="RTL"] .blocklyTreeRow { + margin-left: 8px; + padding-right: 0; +} - .blocklyTreeIcon { - background-image: url(<<>>/sprites.png); - height: 16px; - vertical-align: middle; - visibility: hidden; - width: 16px; - } +.blocklyTreeIcon { + background-image: url(<<>>/sprites.png); + height: 16px; + vertical-align: middle; + visibility: hidden; + width: 16px; +} - .blocklyTreeIconClosed { - background-position: -32px -1px; - } +.blocklyTreeIconClosed { + background-position: -32px -1px; +} - .blocklyToolboxDiv[dir="RTL"] .blocklyTreeIconClosed { - background-position: 0 -1px; - } +.blocklyToolboxDiv[dir="RTL"] .blocklyTreeIconClosed { + background-position: 0 -1px; +} - .blocklyTreeSelected>.blocklyTreeIconClosed { - background-position: -32px -17px; - } +.blocklyTreeSelected>.blocklyTreeIconClosed { + background-position: -32px -17px; +} - .blocklyToolboxDiv[dir="RTL"] .blocklyTreeSelected>.blocklyTreeIconClosed { - background-position: 0 -17px; - } +.blocklyToolboxDiv[dir="RTL"] .blocklyTreeSelected>.blocklyTreeIconClosed { + background-position: 0 -17px; +} - .blocklyTreeIconOpen { - background-position: -16px -1px; - } +.blocklyTreeIconOpen { + background-position: -16px -1px; +} - .blocklyTreeSelected>.blocklyTreeIconOpen { - background-position: -16px -17px; - } +.blocklyTreeSelected>.blocklyTreeIconOpen { + background-position: -16px -17px; +} - .blocklyTreeLabel { - cursor: default; - font: 16px sans-serif; - padding: 0 3px; - vertical-align: middle; - } +.blocklyTreeLabel { + cursor: default; + font: 16px sans-serif; + padding: 0 3px; + vertical-align: middle; +} - .blocklyToolboxDelete .blocklyTreeLabel { - cursor: url("<<>>/handdelete.cur"), auto; - } +.blocklyToolboxDelete .blocklyTreeLabel { + cursor: url("<<>>/handdelete.cur"), auto; +} - .blocklyTreeSelected .blocklyTreeLabel { - color: #fff; - } +.blocklyTreeSelected .blocklyTreeLabel { + color: #fff; +} `); registry.register( diff --git a/core/toolbox/separator.js b/core/toolbox/separator.js index 9d82dc246..c9fb6f391 100644 --- a/core/toolbox/separator.js +++ b/core/toolbox/separator.js @@ -101,20 +101,20 @@ ToolboxSeparator.prototype.dispose = function() { * CSS for Toolbox. See css.js for use. */ Css.register(` - .blocklyTreeSeparator { - border-bottom: solid #e5e5e5 1px; - height: 0; - margin: 5px 0; - } +.blocklyTreeSeparator { + border-bottom: solid #e5e5e5 1px; + height: 0; + margin: 5px 0; +} - .blocklyToolboxDiv[layout="h"] .blocklyTreeSeparator { - border-right: solid #e5e5e5 1px; - border-bottom: none; - height: auto; - margin: 0 5px 0 5px; - padding: 5px 0; - width: 0; - } +.blocklyToolboxDiv[layout="h"] .blocklyTreeSeparator { + border-right: solid #e5e5e5 1px; + border-bottom: none; + height: auto; + margin: 0 5px 0 5px; + padding: 5px 0; + width: 0; +} `); registry.register( diff --git a/core/toolbox/toolbox.js b/core/toolbox/toolbox.js index 438a3b4a3..a31174d4a 100644 --- a/core/toolbox/toolbox.js +++ b/core/toolbox/toolbox.js @@ -1116,36 +1116,36 @@ Toolbox.prototype.dispose = function() { * CSS for Toolbox. See css.js for use. */ Css.register(` - .blocklyToolboxDelete { - cursor: url("<<>>/handdelete.cur"), auto; - } +.blocklyToolboxDelete { + cursor: url("<<>>/handdelete.cur"), auto; +} - .blocklyToolboxGrab { - cursor: url("<<>>/handclosed.cur"), auto; - cursor: grabbing; - cursor: -webkit-grabbing; - } +.blocklyToolboxGrab { + cursor: url("<<>>/handclosed.cur"), auto; + cursor: grabbing; + cursor: -webkit-grabbing; +} - /* Category tree in Toolbox. */ - .blocklyToolboxDiv { - background-color: #ddd; - overflow-x: visible; - overflow-y: auto; - padding: 4px 0 4px 0; - position: absolute; - z-index: 70; /* so blocks go under toolbox when dragging */ - -webkit-tap-highlight-color: transparent; /* issue #1345 */ - } +/* Category tree in Toolbox. */ +.blocklyToolboxDiv { + background-color: #ddd; + overflow-x: visible; + overflow-y: auto; + padding: 4px 0 4px 0; + position: absolute; + z-index: 70; /* so blocks go under toolbox when dragging */ + -webkit-tap-highlight-color: transparent; /* issue #1345 */ +} - .blocklyToolboxContents { - display: flex; - flex-wrap: wrap; - flex-direction: column; - } +.blocklyToolboxContents { + display: flex; + flex-wrap: wrap; + flex-direction: column; +} - .blocklyToolboxContents:focus { - outline: none; - } +.blocklyToolboxContents:focus { + outline: none; +} `); registry.register(registry.Type.TOOLBOX, registry.DEFAULT, Toolbox); diff --git a/core/workspace_comment_svg.js b/core/workspace_comment_svg.js index 24d0461bc..b06276c75 100644 --- a/core/workspace_comment_svg.js +++ b/core/workspace_comment_svg.js @@ -1084,63 +1084,63 @@ WorkspaceCommentSvg.prototype.blurFocus = function() { * CSS for workspace comment. See css.js for use. */ Css.register(` - .blocklyCommentForeignObject { - position: relative; - z-index: 0; - } +.blocklyCommentForeignObject { + position: relative; + z-index: 0; +} - .blocklyCommentRect { - fill: #E7DE8E; - stroke: #bcA903; - stroke-width: 1px; - } +.blocklyCommentRect { + fill: #E7DE8E; + stroke: #bcA903; + stroke-width: 1px; +} - .blocklyCommentTarget { - fill: transparent; - stroke: #bcA903; - } +.blocklyCommentTarget { + fill: transparent; + stroke: #bcA903; +} - .blocklyCommentTargetFocused { - fill: none; - } +.blocklyCommentTargetFocused { + fill: none; +} - .blocklyCommentHandleTarget { - fill: none; - } +.blocklyCommentHandleTarget { + fill: none; +} - .blocklyCommentHandleTargetFocused { - fill: transparent; - } +.blocklyCommentHandleTargetFocused { + fill: transparent; +} - .blocklyFocused>.blocklyCommentRect { - fill: #B9B272; - stroke: #B9B272; - } +.blocklyFocused>.blocklyCommentRect { + fill: #B9B272; + stroke: #B9B272; +} - .blocklySelected>.blocklyCommentTarget { - stroke: #fc3; - stroke-width: 3px; - } +.blocklySelected>.blocklyCommentTarget { + stroke: #fc3; + stroke-width: 3px; +} - .blocklyCommentDeleteIcon { - cursor: pointer; - fill: #000; - display: none; - } +.blocklyCommentDeleteIcon { + cursor: pointer; + fill: #000; + display: none; +} - .blocklySelected > .blocklyCommentDeleteIcon { - display: block; - } +.blocklySelected > .blocklyCommentDeleteIcon { + display: block; +} - .blocklyDeleteIconShape { - fill: #000; - stroke: #000; - stroke-width: 1px; - } +.blocklyDeleteIconShape { + fill: #000; + stroke: #000; + stroke-width: 1px; +} - .blocklyDeleteIconShape.blocklyDeleteIconHighlighted { - stroke: #fc3; - } +.blocklyDeleteIconShape.blocklyDeleteIconHighlighted { + stroke: #fc3; +} `); exports.WorkspaceCommentSvg = WorkspaceCommentSvg; diff --git a/core/zoom_controls.js b/core/zoom_controls.js index 3235a8e98..417d65181 100644 --- a/core/zoom_controls.js +++ b/core/zoom_controls.js @@ -491,17 +491,17 @@ class ZoomControls { * CSS for zoom controls. See css.js for use. */ Css.register(` - .blocklyZoom>image, .blocklyZoom>svg>image { - opacity: .4; - } +.blocklyZoom>image, .blocklyZoom>svg>image { + opacity: .4; +} - .blocklyZoom>image:hover, .blocklyZoom>svg>image:hover { - opacity: .6; - } +.blocklyZoom>image:hover, .blocklyZoom>svg>image:hover { + opacity: .6; +} - .blocklyZoom>image:active, .blocklyZoom>svg>image:active { - opacity: .8; - } +.blocklyZoom>image:active, .blocklyZoom>svg>image:active { + opacity: .8; +} `); exports.ZoomControls = ZoomControls; diff --git a/generators/dart/colour.js b/generators/dart/colour.js index 7f6f621ac..96aeaea51 100644 --- a/generators/dart/colour.js +++ b/generators/dart/colour.js @@ -24,17 +24,16 @@ Dart['colour_picker'] = function(block) { Dart['colour_random'] = function(block) { // Generate a random colour. - Dart.definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = Dart.provideFunction_( - 'colour_random', - ['String ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '() {', - ' String hex = \'0123456789abcdef\';', - ' var rnd = new Math.Random();', - ' return \'#${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\'', - ' \'${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\'', - ' \'${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}\';', - '}']); + Dart.definitions_['import_dart_math'] = "import 'dart:math' as Math;"; + const functionName = Dart.provideFunction_('colour_random', ` +String ${Dart.FUNCTION_NAME_PLACEHOLDER_}() { + String hex = '0123456789abcdef'; + var rnd = new Math.Random(); + return '#\${hex[rnd.nextInt(16)]}\${hex[rnd.nextInt(16)]}' + '\${hex[rnd.nextInt(16)]}\${hex[rnd.nextInt(16)]}' + '\${hex[rnd.nextInt(16)]}\${hex[rnd.nextInt(16)]}'; +} +`); const code = functionName + '()'; return [code, Dart.ORDER_UNARY_POSTFIX]; }; @@ -48,66 +47,59 @@ Dart['colour_rgb'] = function(block) { const blue = Dart.valueToCode(block, 'BLUE', Dart.ORDER_NONE) || 0; - Dart.definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = Dart.provideFunction_( - 'colour_rgb', - ['String ' + Dart.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);', - ' rs = \'0$rs\';', - ' rs = rs.substring(rs.length - 2);', - ' num gn = (Math.max(Math.min(g, 100), 0) * 2.55).round();', - ' String gs = gn.toInt().toRadixString(16);', - ' gs = \'0$gs\';', - ' gs = gs.substring(gs.length - 2);', - ' num bn = (Math.max(Math.min(b, 100), 0) * 2.55).round();', - ' String bs = bn.toInt().toRadixString(16);', - ' bs = \'0$bs\';', - ' bs = bs.substring(bs.length - 2);', - ' return \'#$rs$gs$bs\';', - '}']); + Dart.definitions_['import_dart_math'] = "import 'dart:math' as Math;"; + const functionName = Dart.provideFunction_('colour_rgb', ` +String ${Dart.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); + rs = '0$rs'; + rs = rs.substring(rs.length - 2); + num gn = (Math.max(Math.min(g, 100), 0) * 2.55).round(); + String gs = gn.toInt().toRadixString(16); + gs = '0$gs'; + gs = gs.substring(gs.length - 2); + num bn = (Math.max(Math.min(b, 100), 0) * 2.55).round(); + String bs = bn.toInt().toRadixString(16); + bs = '0$bs'; + bs = bs.substring(bs.length - 2); + return '#$rs$gs$bs'; +} +`); const code = functionName + '(' + red + ', ' + green + ', ' + blue + ')'; return [code, Dart.ORDER_UNARY_POSTFIX]; }; Dart['colour_blend'] = function(block) { // Blend two colours together. - const c1 = Dart.valueToCode(block, 'COLOUR1', - Dart.ORDER_NONE) || '\'#000000\''; - const c2 = Dart.valueToCode(block, 'COLOUR2', - Dart.ORDER_NONE) || '\'#000000\''; - const ratio = Dart.valueToCode(block, 'RATIO', - Dart.ORDER_NONE) || 0.5; + const c1 = Dart.valueToCode(block, 'COLOUR1', Dart.ORDER_NONE) || "'#000000'"; + const c2 = Dart.valueToCode(block, 'COLOUR2', Dart.ORDER_NONE) || "'#000000'"; + const ratio = Dart.valueToCode(block, 'RATIO', Dart.ORDER_NONE) || 0.5; - Dart.definitions_['import_dart_math'] = - 'import \'dart:math\' as Math;'; - const functionName = Dart.provideFunction_( - 'colour_blend', - ['String ' + Dart.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)}\');', - ' int g1 = int.parse(\'0x${c1.substring(3, 5)}\');', - ' int b1 = int.parse(\'0x${c1.substring(5, 7)}\');', - ' int r2 = int.parse(\'0x${c2.substring(1, 3)}\');', - ' int g2 = int.parse(\'0x${c2.substring(3, 5)}\');', - ' int b2 = int.parse(\'0x${c2.substring(5, 7)}\');', - ' num rn = (r1 * (1 - ratio) + r2 * ratio).round();', - ' String rs = rn.toInt().toRadixString(16);', - ' num gn = (g1 * (1 - ratio) + g2 * ratio).round();', - ' String gs = gn.toInt().toRadixString(16);', - ' num bn = (b1 * (1 - ratio) + b2 * ratio).round();', - ' String bs = bn.toInt().toRadixString(16);', - ' rs = \'0$rs\';', - ' rs = rs.substring(rs.length - 2);', - ' gs = \'0$gs\';', - ' gs = gs.substring(gs.length - 2);', - ' bs = \'0$bs\';', - ' bs = bs.substring(bs.length - 2);', - ' return \'#$rs$gs$bs\';', - '}']); + Dart.definitions_['import_dart_math'] = "import 'dart:math' as Math;"; + const functionName = Dart.provideFunction_('colour_blend', ` +String ${Dart.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)}'); + int g1 = int.parse('0x\${c1.substring(3, 5)}'); + int b1 = int.parse('0x\${c1.substring(5, 7)}'); + int r2 = int.parse('0x\${c2.substring(1, 3)}'); + int g2 = int.parse('0x\${c2.substring(3, 5)}'); + int b2 = int.parse('0x\${c2.substring(5, 7)}'); + num rn = (r1 * (1 - ratio) + r2 * ratio).round(); + String rs = rn.toInt().toRadixString(16); + num gn = (g1 * (1 - ratio) + g2 * ratio).round(); + String gs = gn.toInt().toRadixString(16); + num bn = (b1 * (1 - ratio) + b2 * ratio).round(); + String bs = bn.toInt().toRadixString(16); + rs = '0$rs'; + rs = rs.substring(rs.length - 2); + gs = '0$gs'; + gs = gs.substring(gs.length - 2); + bs = '0$bs'; + bs = bs.substring(bs.length - 2); + return '#$rs$gs$bs'; +} +`); const code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')'; return [code, Dart.ORDER_UNARY_POSTFIX]; }; diff --git a/generators/dart/lists.js b/generators/dart/lists.js index 5eefea91b..493cd7a83 100644 --- a/generators/dart/lists.js +++ b/generators/dart/lists.js @@ -58,7 +58,7 @@ Dart['lists_indexOf'] = function(block) { // Find an item in the list. const operator = block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; - const item = Dart.valueToCode(block, 'FIND', Dart.ORDER_NONE) || '\'\''; + const item = Dart.valueToCode(block, 'FIND', Dart.ORDER_NONE) || "''"; const list = Dart.valueToCode(block, 'VALUE', Dart.ORDER_UNARY_POSTFIX) || '[]'; const code = list + '.' + operator + '(' + item + ')'; @@ -112,21 +112,23 @@ Dart['lists_getIndex'] = function(block) { } else if (mode === 'GET') { const at = Dart.getAdjusted(block, 'AT', 1); // We need to create a procedure to avoid reevaluating values. - const functionName = Dart.provideFunction_('lists_get_from_end', [ - 'dynamic ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(List my_list, num x) {', - ' x = my_list.length - x;', ' return my_list[x];', '}' - ]); + const functionName = Dart.provideFunction_('lists_get_from_end', ` +dynamic ${Dart.FUNCTION_NAME_PLACEHOLDER_}(List my_list, num x) { + x = my_list.length - x; + return my_list[x]; +} +`); const code = functionName + '(' + list + ', ' + at + ')'; return [code, Dart.ORDER_UNARY_POSTFIX]; } else if (mode === 'GET_REMOVE') { const at = Dart.getAdjusted(block, 'AT', 1); // We need to create a procedure to avoid reevaluating values. - const functionName = Dart.provideFunction_('lists_remove_from_end', [ - 'dynamic ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(List my_list, num x) {', - ' x = my_list.length - x;', ' return my_list.removeAt(x);', '}' - ]); + const functionName = Dart.provideFunction_('lists_remove_from_end', ` +dynamic ${Dart.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, Dart.ORDER_UNARY_POSTFIX]; } @@ -195,21 +197,22 @@ Dart['lists_getIndex'] = function(block) { code += list + '.removeAt(' + xVar + ');\n'; return code; } else if (mode === 'GET') { - const functionName = Dart.provideFunction_('lists_get_random_item', [ - 'dynamic ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List my_list) {', - ' int x = new Math.Random().nextInt(my_list.length);', - ' return my_list[x];', '}' - ]); + const functionName = Dart.provideFunction_('lists_get_random_item', ` +dynamic ${Dart.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, Dart.ORDER_UNARY_POSTFIX]; } else if (mode === 'GET_REMOVE') { const functionName = - Dart.provideFunction_('lists_remove_random_item', [ - 'dynamic ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(List my_list) {', - ' int x = new Math.Random().nextInt(my_list.length);', - ' return my_list.removeAt(x);', '}' - ]); + Dart.provideFunction_('lists_remove_random_item', ` +dynamic ${Dart.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, Dart.ORDER_UNARY_POSTFIX]; } @@ -344,18 +347,25 @@ Dart['lists_getSublist'] = function(block) { } else { const at1 = Dart.getAdjusted(block, 'AT1'); const at2 = Dart.getAdjusted(block, 'AT2'); - const functionName = Dart.provideFunction_('lists_get_sublist', [ - 'List ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(List list, String where1, num at1, String where2, num at2) {', - ' int getAt(String where, num at) {', ' if (where == \'FROM_END\') {', - ' at = list.length - 1 - at;', - ' } else if (where == \'FIRST\') {', ' at = 0;', - ' } else if (where == \'LAST\') {', ' at = list.length - 1;', - ' } else if (where != \'FROM_START\') {', - ' throw \'Unhandled option (lists_getSublist).\';', ' }', - ' return at;', ' }', ' at1 = getAt(where1, at1);', - ' at2 = getAt(where2, at2) + 1;', ' return list.sublist(at1, at2);', '}' - ]); + const functionName = Dart.provideFunction_('lists_get_sublist', ` +List ${Dart.FUNCTION_NAME_PLACEHOLDER_}(List list, String where1, num at1, String where2, num at2) { + int getAt(String where, num at) { + if (where == 'FROM_END') { + at = list.length - 1 - at; + } else if (where == 'FIRST') { + at = 0; + } else if (where == 'LAST') { + at = list.length - 1; + } else if (where != 'FROM_START') { + throw 'Unhandled option (lists_getSublist).'; + } + return at; + } + at1 = getAt(where1, at1); + at2 = getAt(where2, at2) + 1; + return list.sublist(at1, at2); +} +`); code = functionName + '(' + list + ', \'' + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; } @@ -367,20 +377,21 @@ Dart['lists_sort'] = function(block) { const list = Dart.valueToCode(block, 'LIST', Dart.ORDER_NONE) || '[]'; const direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; const type = block.getFieldValue('TYPE'); - const sortFunctionName = Dart.provideFunction_('lists_sort', [ - 'List ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(List list, String type, int direction) {', - ' var compareFuncs = {', - ' "NUMERIC": (a, b) => (direction * a.compareTo(b)).toInt(),', - ' "TEXT": (a, b) => direction * ' + - 'a.toString().compareTo(b.toString()),', - ' "IGNORE_CASE": ', ' (a, b) => direction * ', - ' a.toString().toLowerCase().compareTo(b.toString().toLowerCase())', - ' };', - ' list = new List.from(list);', // Clone the list. - ' var compare = compareFuncs[type];', ' list.sort(compare);', - ' return list;', '}' - ]); + const sortFunctionName = Dart.provideFunction_('lists_sort', ` +List ${Dart.FUNCTION_NAME_PLACEHOLDER_}(List list, String type, int direction) { + var compareFuncs = { + 'NUMERIC': (a, b) => (direction * a.compareTo(b)).toInt(), + 'TEXT': (a, b) => direction * a.toString().compareTo(b.toString()), + 'IGNORE_CASE': + (a, b) => direction * + a.toString().toLowerCase().compareTo(b.toString().toLowerCase()) + }; + list = new List.from(list); + var compare = compareFuncs[type]; + list.sort(compare); + return list; +} +`); return [ sortFunctionName + '(' + list + ', ' + '"' + type + '", ' + direction + ')', @@ -391,12 +402,12 @@ Dart['lists_sort'] = function(block) { Dart['lists_split'] = function(block) { // Block for splitting text into a list, or joining a list into text. let input = Dart.valueToCode(block, 'INPUT', Dart.ORDER_UNARY_POSTFIX); - const delimiter = Dart.valueToCode(block, 'DELIM', Dart.ORDER_NONE) || '\'\''; + const delimiter = Dart.valueToCode(block, 'DELIM', Dart.ORDER_NONE) || "''"; const mode = block.getFieldValue('MODE'); let functionName; if (mode === 'SPLIT') { if (!input) { - input = '\'\''; + input = "''"; } functionName = 'split'; } else if (mode === 'JOIN') { diff --git a/generators/dart/math.js b/generators/dart/math.js index ab2d0cc50..24c42c2ae 100644 --- a/generators/dart/math.js +++ b/generators/dart/math.js @@ -42,7 +42,7 @@ Dart['math_arithmetic'] = function(block) { 'MINUS': [' - ', Dart.ORDER_ADDITIVE], 'MULTIPLY': [' * ', Dart.ORDER_MULTIPLICATIVE], 'DIVIDE': [' / ', Dart.ORDER_MULTIPLICATIVE], - 'POWER': [null, Dart.ORDER_NONE] // Handle power separately. + 'POWER': [null, Dart.ORDER_NONE], // Handle power separately. }; const tuple = OPERATORS[block.getFieldValue('OP')]; const operator = tuple[0]; @@ -152,7 +152,7 @@ Dart['math_constant'] = function(block) { 'GOLDEN_RATIO': ['(1 + Math.sqrt(5)) / 2', Dart.ORDER_MULTIPLICATIVE], 'SQRT2': ['Math.sqrt2', Dart.ORDER_UNARY_POSTFIX], 'SQRT1_2': ['Math.sqrt1_2', Dart.ORDER_UNARY_POSTFIX], - 'INFINITY': ['double.infinity', Dart.ORDER_ATOMIC] + 'INFINITY': ['double.infinity', Dart.ORDER_ATOMIC], }; const constant = block.getFieldValue('CONSTANT'); if (constant !== 'INFINITY') { @@ -165,20 +165,13 @@ Dart['math_number_property'] = function(block) { // 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 = { - 'EVEN': [' % 2 == 0', Dart.ORDER_MULTIPLICATIVE, - Dart.ORDER_EQUALITY], - 'ODD': [' % 2 == 1', Dart.ORDER_MULTIPLICATIVE, - Dart.ORDER_EQUALITY], - 'WHOLE': [' % 1 == 0', Dart.ORDER_MULTIPLICATIVE, - Dart.ORDER_EQUALITY], - 'POSITIVE': [' > 0', Dart.ORDER_RELATIONAL, - Dart.ORDER_RELATIONAL], - 'NEGATIVE': [' < 0', Dart.ORDER_RELATIONAL, - Dart.ORDER_RELATIONAL], - 'DIVISIBLE_BY': [null, Dart.ORDER_MULTIPLICATIVE, - Dart.ORDER_EQUALITY], - 'PRIME': [null, Dart.ORDER_NONE, - Dart.ORDER_UNARY_POSTFIX] + 'EVEN': [' % 2 == 0', Dart.ORDER_MULTIPLICATIVE, Dart.ORDER_EQUALITY], + 'ODD': [' % 2 == 1', Dart.ORDER_MULTIPLICATIVE, Dart.ORDER_EQUALITY], + 'WHOLE': [' % 1 == 0', Dart.ORDER_MULTIPLICATIVE, Dart.ORDER_EQUALITY], + 'POSITIVE': [' > 0', Dart.ORDER_RELATIONAL, Dart.ORDER_RELATIONAL], + 'NEGATIVE': [' < 0', Dart.ORDER_RELATIONAL, Dart.ORDER_RELATIONAL], + 'DIVISIBLE_BY': [null, Dart.ORDER_MULTIPLICATIVE, Dart.ORDER_EQUALITY], + 'PRIME': [null, Dart.ORDER_NONE, Dart.ORDER_UNARY_POSTFIX], }; const dropdownProperty = block.getFieldValue('PROPERTY'); const [suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty]; @@ -189,27 +182,26 @@ Dart['math_number_property'] = function(block) { // Prime is a special case as it is not a one-liner test. Dart.definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; - const functionName = Dart.provideFunction_( - 'math_isPrime', - ['bool ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '(n) {', - ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' if (n == 2 || n == 3) {', - ' return true;', - ' }', - ' // False if n is null, negative, is 1, or not whole.', - ' // And false if n is divisible by 2 or 3.', - ' if (n == null || n <= 1 || n % 1 != 0 || n % 2 == 0 ||' + - ' n % 3 == 0) {', - ' return false;', - ' }', - ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {', - ' if (n % (x - 1) == 0 || n % (x + 1) == 0) {', - ' return false;', - ' }', - ' }', - ' return true;', - '}']); + const functionName = Dart.provideFunction_('math_isPrime', ` +bool ${Dart.FUNCTION_NAME_PLACEHOLDER_}(n) { + // https://en.wikipedia.org/wiki/Primality_test#Naive_methods + if (n == 2 || n == 3) { + return true; + } + // False if n is null, negative, is 1, or not whole. + // And false if n is divisible by 2 or 3. + if (n == null || n <= 1 || n % 1 != 0 || n % 2 == 0 || n % 3 == 0) { + return false; + } + // Check all the numbers of form 6k +/- 1, up to sqrt(n). + for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) { + if (n % (x - 1) == 0 || n % (x + 1) == 0) { + return false; + } + } + return true; +} +`); code = functionName + '(' + numberToCheck + ')'; } else if (dropdownProperty === 'DIVISIBLE_BY') { const divisor = Dart.valueToCode(block, 'DIVISOR', @@ -246,70 +238,76 @@ Dart['math_on_list'] = function(block) { let code; switch (func) { case 'SUM': { - const functionName = Dart.provideFunction_('math_sum', [ - 'num ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' num sumVal = 0;', - ' myList.forEach((num entry) {sumVal += entry;});', ' return sumVal;', - '}' - ]); + const functionName = Dart.provideFunction_('math_sum', ` +num ${Dart.FUNCTION_NAME_PLACEHOLDER_}(List myList) { + num sumVal = 0; + myList.forEach((num entry) {sumVal += entry;}); + return sumVal; +} +`); code = functionName + '(' + list + ')'; break; } case 'MIN': { Dart.definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; - const functionName = Dart.provideFunction_('math_min', [ - 'num ' + Dart.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;', '}' - ]); + const functionName = Dart.provideFunction_('math_min', ` +num ${Dart.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': { Dart.definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; - const functionName = Dart.provideFunction_('math_max', [ - 'num ' + Dart.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;', '}' - ]); + const functionName = Dart.provideFunction_('math_max', ` +num ${Dart.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 = Dart.provideFunction_('math_mean', [ - 'num ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' // First filter list for numbers only.', - ' List localList = new List.from(myList);', - ' localList.removeWhere((a) => a is! num);', - ' if (localList.isEmpty) return null;', ' num sumVal = 0;', - ' localList.forEach((var entry) {sumVal += entry;});', - ' return sumVal / localList.length;', '}' - ]); + const functionName = Dart.provideFunction_('math_mean', ` +num ${Dart.FUNCTION_NAME_PLACEHOLDER_}(List myList) { + // First filter list for numbers only. + List localList = new List.from(myList); + localList.removeWhere((a) => a is! num); + if (localList.isEmpty) return null; + num sumVal = 0; + localList.forEach((var entry) {sumVal += entry;}); + return sumVal / localList.length; +} +`); code = functionName + '(' + list + ')'; break; } case 'MEDIAN': { - const functionName = Dart.provideFunction_('math_median', [ - 'num ' + Dart.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.', - ' List localList = new List.from(myList);', - ' localList.removeWhere((a) => a is! num);', - ' if (localList.isEmpty) return null;', - ' localList.sort((a, b) => (a - b));', - ' int index = localList.length ~/ 2;', - ' if (localList.length % 2 == 1) {', ' return localList[index];', - ' } else {', - ' return (localList[index - 1] + localList[index]) / 2;', ' }', '}' - ]); + const functionName = Dart.provideFunction_('math_median', ` +num ${Dart.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. + List localList = new List.from(myList); + localList.removeWhere((a) => a is! num); + if (localList.isEmpty) return null; + localList.sort((a, b) => (a - b)); + int index = localList.length ~/ 2; + if (localList.length % 2 == 1) { + return localList[index]; + } else { + return (localList[index - 1] + localList[index]) / 2; + } +} +`); code = functionName + '(' + list + ')'; break; } @@ -318,63 +316,67 @@ Dart['math_on_list'] = function(block) { // 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 = Dart.provideFunction_('math_modes', [ - 'List ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List values) {', - ' List modes = [];', - ' List counts = [];', - ' int maxCount = 0;', - ' for (int i = 0; i < values.length; i++) {', - ' var value = values[i];', - ' bool found = false;', - ' int thisCount;', - ' for (int j = 0; j < counts.length; j++) {', - ' if (counts[j][0] == value) {', - ' thisCount = ++counts[j][1];', - ' found = true;', - ' break;', - ' }', - ' }', - ' if (!found) {', - ' counts.add([value, 1]);', - ' thisCount = 1;', - ' }', - ' maxCount = Math.max(thisCount, maxCount);', - ' }', - ' for (int j = 0; j < counts.length; j++) {', - ' if (counts[j][1] == maxCount) {', - ' modes.add(counts[j][0]);', - ' }', - ' }', - ' return modes;', - '}' - ]); + const functionName = Dart.provideFunction_('math_modes', ` +List ${Dart.FUNCTION_NAME_PLACEHOLDER_}(List values) { + List modes = []; + List counts = []; + int maxCount = 0; + for (int i = 0; i < values.length; i++) { + var value = values[i]; + bool found = false; + int thisCount; + for (int j = 0; j < counts.length; j++) { + if (counts[j][0] == value) { + thisCount = ++counts[j][1]; + found = true; + break; + } + } + if (!found) { + counts.add([value, 1]); + thisCount = 1; + } + maxCount = Math.max(thisCount, maxCount); + } + for (int j = 0; j < counts.length; j++) { + if (counts[j][1] == maxCount) { + modes.add(counts[j][0]); + } + } + return modes; +} +`); code = functionName + '(' + list + ')'; break; } case 'STD_DEV': { Dart.definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; - const functionName = Dart.provideFunction_('math_standard_deviation', [ - 'num ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' // First filter list for numbers only.', - ' List numbers = new List.from(myList);', - ' numbers.removeWhere((a) => a is! num);', - ' if (numbers.isEmpty) return null;', ' num n = numbers.length;', - ' num sum = 0;', ' numbers.forEach((x) => sum += x);', - ' num mean = sum / n;', ' num sumSquare = 0;', - ' numbers.forEach((x) => sumSquare += ' + - 'Math.pow(x - mean, 2));', - ' return Math.sqrt(sumSquare / n);', '}' - ]); + const functionName = Dart.provideFunction_('math_standard_deviation', ` +num ${Dart.FUNCTION_NAME_PLACEHOLDER_}(List myList) { + // First filter list for numbers only. + List numbers = new List.from(myList); + numbers.removeWhere((a) => a is! num); + if (numbers.isEmpty) return null; + num n = numbers.length; + num sum = 0; + numbers.forEach((x) => sum += x); + num mean = sum / n; + num sumSquare = 0; + numbers.forEach((x) => sumSquare += Math.pow(x - mean, 2)); + return Math.sqrt(sumSquare / n); +} +`); code = functionName + '(' + list + ')'; break; } case 'RANDOM': { Dart.definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; - const functionName = Dart.provideFunction_('math_random_item', [ - 'dynamic ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '(List myList) {', - ' int x = new Math.Random().nextInt(myList.length);', - ' return myList[x];', '}' - ]); + const functionName = Dart.provideFunction_('math_random_item', ` +dynamic ${Dart.FUNCTION_NAME_PLACEHOLDER_}(List myList) { + int x = new Math.Random().nextInt(myList.length); + return myList[x]; +} +`); code = functionName + '(' + list + ')'; break; } @@ -411,12 +413,17 @@ Dart['math_random_int'] = function(block) { Dart.definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; const argument0 = Dart.valueToCode(block, 'FROM', Dart.ORDER_NONE) || '0'; const argument1 = Dart.valueToCode(block, 'TO', Dart.ORDER_NONE) || '0'; - const functionName = Dart.provideFunction_('math_random_int', [ - 'int ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '(num a, num b) {', - ' if (a > b) {', ' // Swap a and b to ensure a is smaller.', - ' num c = a;', ' a = b;', ' b = c;', ' }', - ' return new Math.Random().nextInt(b - a + 1) + a;', '}' - ]); + const functionName = Dart.provideFunction_('math_random_int', ` +int ${Dart.FUNCTION_NAME_PLACEHOLDER_}(num a, num b) { + if (a > b) { + // Swap a and b to ensure a is smaller. + num c = a; + a = b; + b = c; + } + return new Math.Random().nextInt(b - a + 1) + a; +} +`); const code = functionName + '(' + argument0 + ', ' + argument1 + ')'; return [code, Dart.ORDER_UNARY_POSTFIX]; }; diff --git a/generators/dart/text.js b/generators/dart/text.js index 7078815ec..5dfb372b0 100644 --- a/generators/dart/text.js +++ b/generators/dart/text.js @@ -35,10 +35,10 @@ Dart['text_join'] = function(block) { // Create a string made up of any number of elements of any type. switch (block.itemCount_) { case 0: - return ['\'\'', Dart.ORDER_ATOMIC]; + return ["''", Dart.ORDER_ATOMIC]; case 1: { const element = - Dart.valueToCode(block, 'ADD0', Dart.ORDER_UNARY_POSTFIX) || '\'\''; + Dart.valueToCode(block, 'ADD0', Dart.ORDER_UNARY_POSTFIX) || "''"; const code = element + '.toString()'; return [code, Dart.ORDER_UNARY_POSTFIX]; } @@ -46,7 +46,7 @@ Dart['text_join'] = function(block) { const elements = new Array(block.itemCount_); for (let i = 0; i < block.itemCount_; i++) { elements[i] = - Dart.valueToCode(block, 'ADD' + i, Dart.ORDER_NONE) || '\'\''; + Dart.valueToCode(block, 'ADD' + i, Dart.ORDER_NONE) || "''"; } const code = '[' + elements.join(',') + '].join()'; return [code, Dart.ORDER_UNARY_POSTFIX]; @@ -58,21 +58,21 @@ Dart['text_append'] = function(block) { // Append to a variable in place. const varName = Dart.nameDB_.getName(block.getFieldValue('VAR'), NameType.VARIABLE); - const value = Dart.valueToCode(block, 'TEXT', Dart.ORDER_NONE) || '\'\''; + const value = Dart.valueToCode(block, 'TEXT', Dart.ORDER_NONE) || "''"; return varName + ' = [' + varName + ', ' + value + '].join();\n'; }; Dart['text_length'] = function(block) { // String or array length. const text = - Dart.valueToCode(block, 'VALUE', Dart.ORDER_UNARY_POSTFIX) || '\'\''; + Dart.valueToCode(block, 'VALUE', Dart.ORDER_UNARY_POSTFIX) || "''"; return [text + '.length', Dart.ORDER_UNARY_POSTFIX]; }; Dart['text_isEmpty'] = function(block) { // Is the string null or array empty? const text = - Dart.valueToCode(block, 'VALUE', Dart.ORDER_UNARY_POSTFIX) || '\'\''; + Dart.valueToCode(block, 'VALUE', Dart.ORDER_UNARY_POSTFIX) || "''"; return [text + '.isEmpty', Dart.ORDER_UNARY_POSTFIX]; }; @@ -80,9 +80,9 @@ Dart['text_indexOf'] = function(block) { // Search the text for a substring. const operator = block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; - const substring = Dart.valueToCode(block, 'FIND', Dart.ORDER_NONE) || '\'\''; + const substring = Dart.valueToCode(block, 'FIND', Dart.ORDER_NONE) || "''"; const text = - Dart.valueToCode(block, 'VALUE', Dart.ORDER_UNARY_POSTFIX) || '\'\''; + Dart.valueToCode(block, 'VALUE', Dart.ORDER_UNARY_POSTFIX) || "''"; const code = text + '.' + operator + '(' + substring + ')'; if (block.workspace.options.oneBasedIndex) { return [code + ' + 1', Dart.ORDER_ADDITIVE]; @@ -97,7 +97,7 @@ Dart['text_charAt'] = function(block) { const textOrder = (where === 'FIRST' || where === 'FROM_START') ? Dart.ORDER_UNARY_POSTFIX : Dart.ORDER_NONE; - const text = Dart.valueToCode(block, 'VALUE', textOrder) || '\'\''; + const text = Dart.valueToCode(block, 'VALUE', textOrder) || "''"; let at; switch (where) { case 'FIRST': { @@ -114,20 +114,22 @@ Dart['text_charAt'] = function(block) { // Fall through. case 'FROM_END': { at = Dart.getAdjusted(block, 'AT', 1); - const functionName = Dart.provideFunction_('text_get_from_end', [ - 'String ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '(String text, num x) {', - ' return text[text.length - x];', '}' - ]); + const functionName = Dart.provideFunction_('text_get_from_end', ` +String ${Dart.FUNCTION_NAME_PLACEHOLDER_}(String text, num x) { + return text[text.length - x]; +} +`); const code = functionName + '(' + text + ', ' + at + ')'; return [code, Dart.ORDER_UNARY_POSTFIX]; } case 'RANDOM': { Dart.definitions_['import_dart_math'] = 'import \'dart:math\' as Math;'; - const functionName = Dart.provideFunction_('text_random_letter', [ - 'String ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '(String text) {', - ' int x = new Math.Random().nextInt(text.length);', - ' return text[x];', '}' - ]); + const functionName = Dart.provideFunction_('text_random_letter', ` +String ${Dart.FUNCTION_NAME_PLACEHOLDER_}(String text) { + int x = new Math.Random().nextInt(text.length); + return text[x]; +} +`); const code = functionName + '(' + text + ')'; return [code, Dart.ORDER_UNARY_POSTFIX]; } @@ -142,7 +144,7 @@ Dart['text_getSubstring'] = function(block) { const requiresLengthCall = (where1 !== 'FROM_END' && where2 === 'FROM_START'); const textOrder = requiresLengthCall ? Dart.ORDER_UNARY_POSTFIX : Dart.ORDER_NONE; - const text = Dart.valueToCode(block, 'STRING', textOrder) || '\'\''; + const text = Dart.valueToCode(block, 'STRING', textOrder) || "''"; let code; if (where1 === 'FIRST' && where2 === 'LAST') { code = text; @@ -188,19 +190,25 @@ Dart['text_getSubstring'] = function(block) { } else { const at1 = Dart.getAdjusted(block, 'AT1'); const at2 = Dart.getAdjusted(block, 'AT2'); - const functionName = Dart.provideFunction_('text_get_substring', [ - 'String ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(String text, String where1, num at1, String where2, num at2) {', - ' int getAt(String where, num at) {', ' if (where == \'FROM_END\') {', - ' at = text.length - 1 - at;', - ' } else if (where == \'FIRST\') {', ' at = 0;', - ' } else if (where == \'LAST\') {', ' at = text.length - 1;', - ' } else if (where != \'FROM_START\') {', - ' throw \'Unhandled option (text_getSubstring).\';', ' }', - ' return at;', ' }', ' at1 = getAt(where1, at1);', - ' at2 = getAt(where2, at2) + 1;', ' return text.substring(at1, at2);', - '}' - ]); + const functionName = Dart.provideFunction_('text_get_substring', ` +String ${Dart.FUNCTION_NAME_PLACEHOLDER_}(String text, String where1, num at1, String where2, num at2) { + int getAt(String where, num at) { + if (where == 'FROM_END') { + at = text.length - 1 - at; + } else if (where == 'FIRST') { + at = 0; + } else if (where == 'LAST') { + at = text.length - 1; + } else if (where != 'FROM_START') { + throw 'Unhandled option (text_getSubstring).'; + } + return at; + } + at1 = getAt(where1, at1); + at2 = getAt(where2, at2) + 1; + return text.substring(at1, at2); +} +`); code = functionName + '(' + text + ', \'' + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; } @@ -216,23 +224,29 @@ Dart['text_changeCase'] = function(block) { }; const operator = OPERATORS[block.getFieldValue('CASE')]; const textOrder = operator ? Dart.ORDER_UNARY_POSTFIX : Dart.ORDER_NONE; - const text = Dart.valueToCode(block, 'TEXT', textOrder) || '\'\''; + const text = Dart.valueToCode(block, 'TEXT', textOrder) || "''"; let code; if (operator) { // Upper and lower case are functions built into Dart. code = text + operator; } else { // Title case is not a native Dart function. Define one. - const functionName = Dart.provideFunction_('text_toTitleCase', [ - 'String ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + '(String str) {', - ' RegExp exp = new RegExp(r\'\\b\');', - ' List list = str.split(exp);', - ' final title = new StringBuffer();', ' for (String part in list) {', - ' if (part.length > 0) {', ' title.write(part[0].toUpperCase());', - ' if (part.length > 0) {', - ' title.write(part.substring(1).toLowerCase());', ' }', - ' }', ' }', ' return title.toString();', '}' - ]); + const functionName = Dart.provideFunction_('text_toTitleCase', ` +String ${Dart.FUNCTION_NAME_PLACEHOLDER_}(String str) { + RegExp exp = new RegExp(r'\\b'); + List list = str.split(exp); + final title = new StringBuffer(); + for (String part in list) { + if (part.length > 0) { + title.write(part[0].toUpperCase()); + if (part.length > 0) { + title.write(part.substring(1).toLowerCase()); + } + } + } + return title.toString(); +} +`); code = functionName + '(' + text + ')'; } return [code, Dart.ORDER_UNARY_POSTFIX]; @@ -247,13 +261,13 @@ Dart['text_trim'] = function(block) { }; const operator = OPERATORS[block.getFieldValue('MODE')]; const text = - Dart.valueToCode(block, 'TEXT', Dart.ORDER_UNARY_POSTFIX) || '\'\''; + Dart.valueToCode(block, 'TEXT', Dart.ORDER_UNARY_POSTFIX) || "''"; return [text + operator, Dart.ORDER_UNARY_POSTFIX]; }; Dart['text_print'] = function(block) { // Print statement. - const msg = Dart.valueToCode(block, 'TEXT', Dart.ORDER_NONE) || '\'\''; + const msg = Dart.valueToCode(block, 'TEXT', Dart.ORDER_NONE) || "''"; return 'print(' + msg + ');\n'; }; @@ -266,7 +280,7 @@ Dart['text_prompt_ext'] = function(block) { msg = Dart.quote_(block.getFieldValue('TEXT')); } else { // External message. - msg = Dart.valueToCode(block, 'TEXT', Dart.ORDER_NONE) || '\'\''; + msg = Dart.valueToCode(block, 'TEXT', Dart.ORDER_NONE) || "''"; } let code = 'Html.window.prompt(' + msg + ', \'\')'; const toNumber = block.getFieldValue('TYPE') === 'NUMBER'; @@ -280,27 +294,35 @@ Dart['text_prompt_ext'] = function(block) { Dart['text_prompt'] = Dart['text_prompt_ext']; Dart['text_count'] = function(block) { - const text = Dart.valueToCode(block, 'TEXT', Dart.ORDER_NONE) || '\'\''; - const sub = Dart.valueToCode(block, 'SUB', Dart.ORDER_NONE) || '\'\''; + const text = Dart.valueToCode(block, 'TEXT', Dart.ORDER_NONE) || "''"; + const sub = Dart.valueToCode(block, 'SUB', Dart.ORDER_NONE) || "''"; // Substring count is not a native Dart function. Define one. - const functionName = Dart.provideFunction_('text_count', [ - 'int ' + Dart.FUNCTION_NAME_PLACEHOLDER_ + - '(String haystack, String needle) {', - ' if (needle.length == 0) {', ' return haystack.length + 1;', ' }', - ' int index = 0;', ' int count = 0;', ' while (index != -1) {', - ' index = haystack.indexOf(needle, index);', ' if (index != -1) {', - ' count++;', ' index += needle.length;', ' }', ' }', - ' return count;', '}' - ]); + const functionName = Dart.provideFunction_('text_count', ` +int ${Dart.FUNCTION_NAME_PLACEHOLDER_}(String haystack, String needle) { + if (needle.length == 0) { + return haystack.length + 1; + } + int index = 0; + int count = 0; + while (index != -1) { + index = haystack.indexOf(needle, index); + if (index != -1) { + count++; + index += needle.length; + } + } + return count; +} +`); const code = functionName + '(' + text + ', ' + sub + ')'; return [code, Dart.ORDER_UNARY_POSTFIX]; }; Dart['text_replace'] = function(block) { const text = - Dart.valueToCode(block, 'TEXT', Dart.ORDER_UNARY_POSTFIX) || '\'\''; - const from = Dart.valueToCode(block, 'FROM', Dart.ORDER_NONE) || '\'\''; - const to = Dart.valueToCode(block, 'TO', Dart.ORDER_NONE) || '\'\''; + Dart.valueToCode(block, 'TEXT', Dart.ORDER_UNARY_POSTFIX) || "''"; + const from = Dart.valueToCode(block, 'FROM', Dart.ORDER_NONE) || "''"; + const to = Dart.valueToCode(block, 'TO', Dart.ORDER_NONE) || "''"; const code = text + '.replaceAll(' + from + ', ' + to + ')'; return [code, Dart.ORDER_UNARY_POSTFIX]; }; @@ -310,7 +332,7 @@ Dart['text_reverse'] = function(block) { // http://stackoverflow.com/a/21613700/3529104 // Implementing something is possibly better than not implementing anything? const text = - Dart.valueToCode(block, 'TEXT', Dart.ORDER_UNARY_POSTFIX) || '\'\''; + Dart.valueToCode(block, 'TEXT', Dart.ORDER_UNARY_POSTFIX) || "''"; const code = 'new String.fromCharCodes(' + text + '.runes.toList().reversed)'; return [code, Dart.ORDER_UNARY_PREFIX]; }; diff --git a/generators/javascript/colour.js b/generators/javascript/colour.js index 3b6632575..a2cc64a5a 100644 --- a/generators/javascript/colour.js +++ b/generators/javascript/colour.js @@ -22,11 +22,12 @@ JavaScript['colour_picker'] = function(block) { JavaScript['colour_random'] = function(block) { // Generate a random colour. - const functionName = JavaScript.provideFunction_('colourRandom', [ - 'function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '() {', - ' var num = Math.floor(Math.random() * Math.pow(2, 24));', - ' return \'#\' + (\'00000\' + num.toString(16)).substr(-6);', '}' - ]); + const functionName = JavaScript.provideFunction_('colourRandom', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}() { + var num = Math.floor(Math.random() * Math.pow(2, 24)); + return '#' + ('00000' + num.toString(16)).substr(-6); +} +`); const code = functionName + '()'; return [code, JavaScript.ORDER_FUNCTION_CALL]; }; @@ -38,16 +39,17 @@ JavaScript['colour_rgb'] = function(block) { JavaScript.valueToCode(block, 'GREEN', JavaScript.ORDER_NONE) || 0; const blue = JavaScript.valueToCode(block, 'BLUE', JavaScript.ORDER_NONE) || 0; - const functionName = JavaScript.provideFunction_('colourRgb', [ - 'function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(r, g, b) {', - ' r = Math.max(Math.min(Number(r), 100), 0) * 2.55;', - ' g = Math.max(Math.min(Number(g), 100), 0) * 2.55;', - ' b = Math.max(Math.min(Number(b), 100), 0) * 2.55;', - ' r = (\'0\' + (Math.round(r) || 0).toString(16)).slice(-2);', - ' g = (\'0\' + (Math.round(g) || 0).toString(16)).slice(-2);', - ' b = (\'0\' + (Math.round(b) || 0).toString(16)).slice(-2);', - ' return \'#\' + r + g + b;', '}' - ]); + const functionName = JavaScript.provideFunction_('colourRgb', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(r, g, b) { + r = Math.max(Math.min(Number(r), 100), 0) * 2.55; + g = Math.max(Math.min(Number(g), 100), 0) * 2.55; + b = Math.max(Math.min(Number(b), 100), 0) * 2.55; + r = ('0' + (Math.round(r) || 0).toString(16)).slice(-2); + g = ('0' + (Math.round(g) || 0).toString(16)).slice(-2); + b = ('0' + (Math.round(b) || 0).toString(16)).slice(-2); + return '#' + r + g + b; +} +`); const code = functionName + '(' + red + ', ' + green + ', ' + blue + ')'; return [code, JavaScript.ORDER_FUNCTION_CALL]; }; @@ -55,28 +57,29 @@ JavaScript['colour_rgb'] = function(block) { JavaScript['colour_blend'] = function(block) { // Blend two colours together. const c1 = JavaScript.valueToCode(block, 'COLOUR1', JavaScript.ORDER_NONE) || - '\'#000000\''; + "'#000000'"; const c2 = JavaScript.valueToCode(block, 'COLOUR2', JavaScript.ORDER_NONE) || - '\'#000000\''; + "'#000000'"; const ratio = JavaScript.valueToCode(block, 'RATIO', JavaScript.ORDER_NONE) || 0.5; - const functionName = JavaScript.provideFunction_('colourBlend', [ - 'function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(c1, c2, ratio) {', - ' ratio = Math.max(Math.min(Number(ratio), 1), 0);', - ' var r1 = parseInt(c1.substring(1, 3), 16);', - ' var g1 = parseInt(c1.substring(3, 5), 16);', - ' var b1 = parseInt(c1.substring(5, 7), 16);', - ' var r2 = parseInt(c2.substring(1, 3), 16);', - ' var g2 = parseInt(c2.substring(3, 5), 16);', - ' var b2 = parseInt(c2.substring(5, 7), 16);', - ' var r = Math.round(r1 * (1 - ratio) + r2 * ratio);', - ' var g = Math.round(g1 * (1 - ratio) + g2 * ratio);', - ' var b = Math.round(b1 * (1 - ratio) + b2 * ratio);', - ' r = (\'0\' + (r || 0).toString(16)).slice(-2);', - ' g = (\'0\' + (g || 0).toString(16)).slice(-2);', - ' b = (\'0\' + (b || 0).toString(16)).slice(-2);', - ' return \'#\' + r + g + b;', '}' - ]); + const functionName = JavaScript.provideFunction_('colourBlend', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(c1, c2, ratio) { + ratio = Math.max(Math.min(Number(ratio), 1), 0); + var r1 = parseInt(c1.substring(1, 3), 16); + var g1 = parseInt(c1.substring(3, 5), 16); + var b1 = parseInt(c1.substring(5, 7), 16); + var r2 = parseInt(c2.substring(1, 3), 16); + var g2 = parseInt(c2.substring(3, 5), 16); + var b2 = parseInt(c2.substring(5, 7), 16); + var r = Math.round(r1 * (1 - ratio) + r2 * ratio); + var g = Math.round(g1 * (1 - ratio) + g2 * ratio); + var b = Math.round(b1 * (1 - ratio) + b2 * ratio); + r = ('0' + (r || 0).toString(16)).slice(-2); + g = ('0' + (g || 0).toString(16)).slice(-2); + b = ('0' + (b || 0).toString(16)).slice(-2); + return '#' + r + g + b; +} +`); const code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')'; return [code, JavaScript.ORDER_FUNCTION_CALL]; }; diff --git a/generators/javascript/lists.js b/generators/javascript/lists.js index a033710b8..e9fbc3598 100644 --- a/generators/javascript/lists.js +++ b/generators/javascript/lists.js @@ -35,11 +35,15 @@ JavaScript['lists_create_with'] = function(block) { JavaScript['lists_repeat'] = function(block) { // Create a list with one element repeated. - const functionName = JavaScript.provideFunction_('listsRepeat', [ - 'function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(value, n) {', - ' var array = [];', ' for (var i = 0; i < n; i++) {', - ' array[i] = value;', ' }', ' return array;', '}' - ]); + const functionName = JavaScript.provideFunction_('listsRepeat', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(value, n) { + var array = []; + for (var i = 0; i < n; i++) { + array[i] = value; + } + return array; +} +`); const element = JavaScript.valueToCode(block, 'ITEM', JavaScript.ORDER_NONE) || 'null'; const repeatCount = @@ -67,7 +71,7 @@ JavaScript['lists_indexOf'] = function(block) { const operator = block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; const item = - JavaScript.valueToCode(block, 'FIND', JavaScript.ORDER_NONE) || '\'\''; + JavaScript.valueToCode(block, 'FIND', JavaScript.ORDER_NONE) || "''"; const list = JavaScript.valueToCode(block, 'VALUE', JavaScript.ORDER_MEMBER) || '[]'; const code = list + '.' + operator + '(' + item + ')'; @@ -136,13 +140,16 @@ JavaScript['lists_getIndex'] = function(block) { break; } case ('RANDOM'): { - const functionName = JavaScript.provideFunction_('listsGetRandomItem', [ - 'function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(list, remove) {', - ' var x = Math.floor(Math.random() * list.length);', ' if (remove) {', - ' return list.splice(x, 1)[0];', ' } else {', ' return list[x];', - ' }', '}' - ]); + const functionName = JavaScript.provideFunction_('listsGetRandomItem', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(list, remove) { + var x = Math.floor(Math.random() * list.length); + if (remove) { + return list.splice(x, 1)[0]; + } else { + return list[x]; + } +} +`); const code = functionName + '(' + list + ', ' + (mode !== 'GET') + ')'; if (mode === 'GET' || mode === 'GET_REMOVE') { return [code, JavaScript.ORDER_FUNCTION_CALL]; @@ -309,23 +316,22 @@ JavaScript['lists_getSublist'] = function(block) { 'FIRST': 'First', 'LAST': 'Last', 'FROM_START': 'FromStart', - 'FROM_END': 'FromEnd' + 'FROM_END': 'FromEnd', }; + // The value for 'FROM_END' and'FROM_START' depends on `at` so + // we add it as a parameter. + const at1Param = + (where1 === 'FROM_END' || where1 === 'FROM_START') ? ', at1' : ''; + const at2Param = + (where2 === 'FROM_END' || where2 === 'FROM_START') ? ', at2' : ''; const functionName = JavaScript.provideFunction_( - 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], [ - 'function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(sequence' + - // The value for 'FROM_END' and'FROM_START' depends on `at` so - // we add it as a parameter. - ((where1 === 'FROM_END' || where1 === 'FROM_START') ? ', at1' : - '') + - ((where2 === 'FROM_END' || where2 === 'FROM_START') ? ', at2' : - '') + - ') {', - getSubstringIndex('sequence', where1, 'at1') + ';', - ' var end = ' + getSubstringIndex('sequence', where2, 'at2') + - ' + 1;', - ' return sequence.slice(start, end);', '}' - ]); + 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(sequence${at1Param}${at2Param}) { + var start = ${getSubstringIndex('sequence', where1, 'at1')}; + var end = ${getSubstringIndex('sequence', where2, 'at2')} + 1; + return sequence.slice(start, end); +} +`); code = functionName + '(' + list + // The value for 'FROM_END' and 'FROM_START' depends on `at` so we // pass it. @@ -344,19 +350,20 @@ JavaScript['lists_sort'] = function(block) { const direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; const type = block.getFieldValue('TYPE'); const getCompareFunctionName = - JavaScript.provideFunction_('listsGetSortCompare', [ - 'function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(type, direction) {', - ' var compareFuncs = {', ' "NUMERIC": function(a, b) {', - ' return Number(a) - Number(b); },', - ' "TEXT": function(a, b) {', - ' return a.toString() > b.toString() ? 1 : -1; },', - ' "IGNORE_CASE": function(a, b) {', - ' return a.toString().toLowerCase() > ' + - 'b.toString().toLowerCase() ? 1 : -1; },', - ' };', ' var compare = compareFuncs[type];', - ' return function(a, b) { return compare(a, b) * direction; }', '}' - ]); + JavaScript.provideFunction_('listsGetSortCompare', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(type, direction) { + var compareFuncs = { + 'NUMERIC': function(a, b) { + return Number(a) - Number(b); }, + 'TEXT': function(a, b) { + return a.toString() > b.toString() ? 1 : -1; }, + 'IGNORE_CASE': function(a, b) { + return a.toString().toLowerCase() > b.toString().toLowerCase() ? 1 : -1; }, + }; + var compare = compareFuncs[type]; + return function(a, b) { return compare(a, b) * direction; }; +} + `); return [ list + '.slice().sort(' + getCompareFunctionName + '("' + type + '", ' + direction + '))', @@ -368,12 +375,12 @@ JavaScript['lists_split'] = function(block) { // Block for splitting text into a list, or joining a list into text. let input = JavaScript.valueToCode(block, 'INPUT', JavaScript.ORDER_MEMBER); const delimiter = - JavaScript.valueToCode(block, 'DELIM', JavaScript.ORDER_NONE) || '\'\''; + JavaScript.valueToCode(block, 'DELIM', JavaScript.ORDER_NONE) || "''"; const mode = block.getFieldValue('MODE'); let functionName; if (mode === 'SPLIT') { if (!input) { - input = '\'\''; + input = "''"; } functionName = 'split'; } else if (mode === 'JOIN') { diff --git a/generators/javascript/math.js b/generators/javascript/math.js index 55ee3de82..f370ba559 100644 --- a/generators/javascript/math.js +++ b/generators/javascript/math.js @@ -31,7 +31,7 @@ JavaScript['math_arithmetic'] = function(block) { 'MINUS': [' - ', JavaScript.ORDER_SUBTRACTION], 'MULTIPLY': [' * ', JavaScript.ORDER_MULTIPLICATION], 'DIVIDE': [' / ', JavaScript.ORDER_DIVISION], - 'POWER': [null, JavaScript.ORDER_NONE] // Handle power separately. + 'POWER': [null, JavaScript.ORDER_NONE], // Handle power separately. }; const tuple = OPERATORS[block.getFieldValue('OP')]; const operator = tuple[0]; @@ -137,11 +137,10 @@ JavaScript['math_constant'] = function(block) { const CONSTANTS = { 'PI': ['Math.PI', JavaScript.ORDER_MEMBER], 'E': ['Math.E', JavaScript.ORDER_MEMBER], - 'GOLDEN_RATIO': - ['(1 + Math.sqrt(5)) / 2', JavaScript.ORDER_DIVISION], + 'GOLDEN_RATIO': ['(1 + Math.sqrt(5)) / 2', JavaScript.ORDER_DIVISION], 'SQRT2': ['Math.SQRT2', JavaScript.ORDER_MEMBER], 'SQRT1_2': ['Math.SQRT1_2', JavaScript.ORDER_MEMBER], - 'INFINITY': ['Infinity', JavaScript.ORDER_ATOMIC] + 'INFINITY': ['Infinity', JavaScript.ORDER_ATOMIC], }; return CONSTANTS[block.getFieldValue('CONSTANT')]; }; @@ -150,20 +149,16 @@ JavaScript['math_number_property'] = function(block) { // 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 = { - 'EVEN': [' % 2 === 0', JavaScript.ORDER_MODULUS, - JavaScript.ORDER_EQUALITY], - 'ODD': [' % 2 === 1', JavaScript.ORDER_MODULUS, - JavaScript.ORDER_EQUALITY], + 'EVEN': [' % 2 === 0', JavaScript.ORDER_MODULUS, JavaScript.ORDER_EQUALITY], + 'ODD': [' % 2 === 1', JavaScript.ORDER_MODULUS, JavaScript.ORDER_EQUALITY], 'WHOLE': [' % 1 === 0', JavaScript.ORDER_MODULUS, JavaScript.ORDER_EQUALITY], 'POSITIVE': [' > 0', JavaScript.ORDER_RELATIONAL, JavaScript.ORDER_RELATIONAL], 'NEGATIVE': [' < 0', JavaScript.ORDER_RELATIONAL, JavaScript.ORDER_RELATIONAL], - 'DIVISIBLE_BY': [null, JavaScript.ORDER_MODULUS, - JavaScript.ORDER_EQUALITY], - 'PRIME': [null, JavaScript.ORDER_NONE, - JavaScript.ORDER_FUNCTION_CALL] + 'DIVISIBLE_BY': [null, JavaScript.ORDER_MODULUS, JavaScript.ORDER_EQUALITY], + 'PRIME': [null, JavaScript.ORDER_NONE, JavaScript.ORDER_FUNCTION_CALL], }; const dropdownProperty = block.getFieldValue('PROPERTY'); const [suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty]; @@ -172,27 +167,26 @@ JavaScript['math_number_property'] = function(block) { let code; if (dropdownProperty === 'PRIME') { // Prime is a special case as it is not a one-liner test. - const functionName = JavaScript.provideFunction_( - 'mathIsPrime', - ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(n) {', - ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' if (n == 2 || n == 3) {', - ' return true;', - ' }', - ' // False if n is NaN, negative, is 1, or not whole.', - ' // And false if n is divisible by 2 or 3.', - ' if (isNaN(n) || n <= 1 || n % 1 !== 0 || n % 2 === 0 ||' + - ' n % 3 === 0) {', - ' return false;', - ' }', - ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {', - ' if (n % (x - 1) === 0 || n % (x + 1) === 0) {', - ' return false;', - ' }', - ' }', - ' return true;', - '}']); + const functionName = JavaScript.provideFunction_('mathIsPrime', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(n) { + // https://en.wikipedia.org/wiki/Primality_test#Naive_methods + if (n == 2 || n == 3) { + return true; + } + // False if n is NaN, negative, is 1, or not whole. + // And false if n is divisible by 2 or 3. + if (isNaN(n) || n <= 1 || n % 1 !== 0 || n % 2 === 0 || n % 3 === 0) { + return false; + } + // Check all the numbers of form 6k +/- 1, up to sqrt(n). + for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) { + if (n % (x - 1) === 0 || n % (x + 1) === 0) { + return false; + } + } + return true; +} +`); code = functionName + '(' + numberToCheck + ')'; } else if (dropdownProperty === 'DIVISIBLE_BY') { const divisor = JavaScript.valueToCode(block, 'DIVISOR', @@ -242,13 +236,11 @@ JavaScript['math_on_list'] = function(block) { break; case 'AVERAGE': { // mathMean([null,null,1,3]) === 2.0. - const functionName = JavaScript.provideFunction_( - 'mathMean', - ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(myList) {', - ' return myList.reduce(function(x, y) {return x + y;}) / ' + - 'myList.length;', - '}']); + const functionName = JavaScript.provideFunction_('mathMean', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(myList) { + return myList.reduce(function(x, y) {return x + y;}) / myList.length; +} +`); list = JavaScript.valueToCode(block, 'LIST', JavaScript.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; @@ -256,21 +248,18 @@ JavaScript['math_on_list'] = function(block) { } case 'MEDIAN': { // mathMedian([null,null,1,3]) === 2.0. - const functionName = JavaScript.provideFunction_( - 'mathMedian', - ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(myList) {', - ' var localList = myList.filter(function (x) ' + - '{return typeof x === \'number\';});', - ' if (!localList.length) return null;', - ' localList.sort(function(a, b) {return b - a;});', - ' if (localList.length % 2 === 0) {', - ' return (localList[localList.length / 2 - 1] + ' + - 'localList[localList.length / 2]) / 2;', - ' } else {', - ' return localList[(localList.length - 1) / 2];', - ' }', - '}']); + const functionName = JavaScript.provideFunction_('mathMedian', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(myList) { + var localList = myList.filter(function (x) {return typeof x === \'number\';}); + if (!localList.length) return null; + localList.sort(function(a, b) {return b - a;}); + if (localList.length % 2 === 0) { + return (localList[localList.length / 2 - 1] + localList[localList.length / 2]) / 2; + } else { + return localList[(localList.length - 1) / 2]; + } +} +`); list = JavaScript.valueToCode(block, 'LIST', JavaScript.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; @@ -280,70 +269,67 @@ JavaScript['math_on_list'] = function(block) { // 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 = JavaScript.provideFunction_( - 'mathModes', - ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(values) {', - ' var modes = [];', - ' var counts = [];', - ' var maxCount = 0;', - ' for (var i = 0; i < values.length; i++) {', - ' var value = values[i];', - ' var found = false;', - ' var thisCount;', - ' for (var j = 0; j < counts.length; j++) {', - ' if (counts[j][0] === value) {', - ' thisCount = ++counts[j][1];', - ' found = true;', - ' break;', - ' }', - ' }', - ' if (!found) {', - ' counts.push([value, 1]);', - ' thisCount = 1;', - ' }', - ' maxCount = Math.max(thisCount, maxCount);', - ' }', - ' for (var j = 0; j < counts.length; j++) {', - ' if (counts[j][1] === maxCount) {', - ' modes.push(counts[j][0]);', - ' }', - ' }', - ' return modes;', - '}']); + const functionName = JavaScript.provideFunction_('mathModes', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(values) { + var modes = []; + var counts = []; + var maxCount = 0; + for (var i = 0; i < values.length; i++) { + var value = values[i]; + var found = false; + var thisCount; + for (var j = 0; j < counts.length; j++) { + if (counts[j][0] === value) { + thisCount = ++counts[j][1]; + found = true; + break; + } + } + if (!found) { + counts.push([value, 1]); + thisCount = 1; + } + maxCount = Math.max(thisCount, maxCount); + } + for (var j = 0; j < counts.length; j++) { + if (counts[j][1] === maxCount) { + modes.push(counts[j][0]); + } + } + return modes; +} +`); list = JavaScript.valueToCode(block, 'LIST', JavaScript.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; break; } case 'STD_DEV': { - const functionName = JavaScript.provideFunction_( - 'mathStandardDeviation', - ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(numbers) {', - ' var n = numbers.length;', - ' if (!n) return null;', - ' var mean = numbers.reduce(function(x, y) {return x + y;}) / n;', - ' var variance = 0;', - ' for (var j = 0; j < n; j++) {', - ' variance += Math.pow(numbers[j] - mean, 2);', - ' }', - ' variance = variance / n;', - ' return Math.sqrt(variance);', - '}']); + const functionName = JavaScript.provideFunction_('mathStandardDeviation', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(numbers) { + var n = numbers.length; + if (!n) return null; + var mean = numbers.reduce(function(x, y) {return x + y;}) / n; + var variance = 0; + for (var j = 0; j < n; j++) { + variance += Math.pow(numbers[j] - mean, 2); + } + variance = variance / n; + return Math.sqrt(variance); +} +`); list = JavaScript.valueToCode(block, 'LIST', JavaScript.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; break; } case 'RANDOM': { - const functionName = JavaScript.provideFunction_( - 'mathRandomList', - ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(list) {', - ' var x = Math.floor(Math.random() * list.length);', - ' return list[x];', - '}']); + const functionName = JavaScript.provideFunction_('mathRandomList', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(list) { + var x = Math.floor(Math.random() * list.length); + return list[x]; +} +`); list = JavaScript.valueToCode(block, 'LIST', JavaScript.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; @@ -384,18 +370,17 @@ JavaScript['math_random_int'] = function(block) { JavaScript.ORDER_NONE) || '0'; const argument1 = JavaScript.valueToCode(block, 'TO', JavaScript.ORDER_NONE) || '0'; - const functionName = JavaScript.provideFunction_( - 'mathRandomInt', - ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(a, b) {', - ' if (a > b) {', - ' // Swap a and b to ensure a is smaller.', - ' var c = a;', - ' a = b;', - ' b = c;', - ' }', - ' return Math.floor(Math.random() * (b - a + 1) + a);', - '}']); + const functionName = JavaScript.provideFunction_('mathRandomInt', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(a, b) { + if (a > b) { + // Swap a and b to ensure a is smaller. + var c = a; + a = b; + b = c; + } + return Math.floor(Math.random() * (b - a + 1) + a); +} +`); const code = functionName + '(' + argument0 + ', ' + argument1 + ')'; return [code, JavaScript.ORDER_FUNCTION_CALL]; }; diff --git a/generators/javascript/text.js b/generators/javascript/text.js index 419ffbfec..9c60bfa6c 100644 --- a/generators/javascript/text.js +++ b/generators/javascript/text.js @@ -71,18 +71,18 @@ JavaScript['text_join'] = function(block) { // Create a string made up of any number of elements of any type. switch (block.itemCount_) { case 0: - return ['\'\'', JavaScript.ORDER_ATOMIC]; + return ["''", JavaScript.ORDER_ATOMIC]; case 1: { const element = JavaScript.valueToCode(block, 'ADD0', - JavaScript.ORDER_NONE) || '\'\''; + JavaScript.ORDER_NONE) || "''"; const codeAndOrder = forceString(element); return codeAndOrder; } case 2: { const element0 = JavaScript.valueToCode(block, 'ADD0', - JavaScript.ORDER_NONE) || '\'\''; + JavaScript.ORDER_NONE) || "''"; const element1 = JavaScript.valueToCode(block, 'ADD1', - JavaScript.ORDER_NONE) || '\'\''; + JavaScript.ORDER_NONE) || "''"; const code = forceString(element0)[0] + ' + ' + forceString(element1)[0]; return [code, JavaScript.ORDER_ADDITION]; @@ -91,7 +91,7 @@ JavaScript['text_join'] = function(block) { const elements = new Array(block.itemCount_); for (let i = 0; i < block.itemCount_; i++) { elements[i] = JavaScript.valueToCode(block, 'ADD' + i, - JavaScript.ORDER_NONE) || '\'\''; + JavaScript.ORDER_NONE) || "''"; } const code = '[' + elements.join(',') + '].join(\'\')'; return [code, JavaScript.ORDER_FUNCTION_CALL]; @@ -104,7 +104,7 @@ JavaScript['text_append'] = function(block) { const varName = JavaScript.nameDB_.getName( block.getFieldValue('VAR'), NameType.VARIABLE); const value = JavaScript.valueToCode(block, 'TEXT', - JavaScript.ORDER_NONE) || '\'\''; + JavaScript.ORDER_NONE) || "''"; const code = varName + ' += ' + forceString(value)[0] + ';\n'; return code; @@ -113,14 +113,14 @@ JavaScript['text_append'] = function(block) { JavaScript['text_length'] = function(block) { // String or array length. const text = JavaScript.valueToCode(block, 'VALUE', - JavaScript.ORDER_MEMBER) || '\'\''; + JavaScript.ORDER_MEMBER) || "''"; return [text + '.length', JavaScript.ORDER_MEMBER]; }; JavaScript['text_isEmpty'] = function(block) { // Is the string null or array empty? const text = JavaScript.valueToCode(block, 'VALUE', - JavaScript.ORDER_MEMBER) || '\'\''; + JavaScript.ORDER_MEMBER) || "''"; return ['!' + text + '.length', JavaScript.ORDER_LOGICAL_NOT]; }; @@ -129,9 +129,9 @@ JavaScript['text_indexOf'] = function(block) { const operator = block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; const substring = JavaScript.valueToCode(block, 'FIND', - JavaScript.ORDER_NONE) || '\'\''; + JavaScript.ORDER_NONE) || "''"; const text = JavaScript.valueToCode(block, 'VALUE', - JavaScript.ORDER_MEMBER) || '\'\''; + JavaScript.ORDER_MEMBER) || "''"; const code = text + '.' + operator + '(' + substring + ')'; // Adjust index if using one-based indices. if (block.workspace.options.oneBasedIndex) { @@ -146,8 +146,7 @@ JavaScript['text_charAt'] = function(block) { const where = block.getFieldValue('WHERE') || 'FROM_START'; const textOrder = (where === 'RANDOM') ? JavaScript.ORDER_NONE : JavaScript.ORDER_MEMBER; - const text = JavaScript.valueToCode(block, 'VALUE', - textOrder) || '\'\''; + const text = JavaScript.valueToCode(block, 'VALUE', textOrder) || "''"; switch (where) { case 'FIRST': { const code = text + '.charAt(0)'; @@ -169,13 +168,12 @@ JavaScript['text_charAt'] = function(block) { return [code, JavaScript.ORDER_FUNCTION_CALL]; } case 'RANDOM': { - const functionName = JavaScript.provideFunction_( - 'textRandomLetter', - ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(text) {', - ' var x = Math.floor(Math.random() * text.length);', - ' return text[x];', - '}']); + const functionName = JavaScript.provideFunction_('textRandomLetter', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(text) { + var x = Math.floor(Math.random() * text.length); + return text[x]; +} +`); const code = functionName + '(' + text + ')'; return [code, JavaScript.ORDER_FUNCTION_CALL]; } @@ -191,8 +189,7 @@ JavaScript['text_getSubstring'] = function(block) { where2 !== 'FROM_END' && where2 !== 'LAST'); const textOrder = requiresLengthCall ? JavaScript.ORDER_MEMBER : JavaScript.ORDER_NONE; - const text = JavaScript.valueToCode(block, 'STRING', - textOrder) || '\'\''; + const text = JavaScript.valueToCode(block, 'STRING', textOrder) || "''"; let code; if (where1 === 'FIRST' && where2 === 'LAST') { code = text; @@ -238,22 +235,20 @@ JavaScript['text_getSubstring'] = function(block) { const at2 = JavaScript.getAdjusted(block, 'AT2'); const wherePascalCase = {'FIRST': 'First', 'LAST': 'Last', 'FROM_START': 'FromStart', 'FROM_END': 'FromEnd'}; + // The value for 'FROM_END' and'FROM_START' depends on `at` so + // we add it as a parameter. + const at1Param = + (where1 === 'FROM_END' || where1 === 'FROM_START') ? ', at1' : ''; + const at2Param = + (where2 === 'FROM_END' || where2 === 'FROM_START') ? ', at2' : ''; const functionName = JavaScript.provideFunction_( - 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], [ - 'function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(sequence' + - // The value for 'FROM_END' and'FROM_START' depends on `at` so - // we add it as a parameter. - ((where1 === 'FROM_END' || where1 === 'FROM_START') ? ', at1' : - '') + - ((where2 === 'FROM_END' || where2 === 'FROM_START') ? ', at2' : - '') + - ') {', - ' var start = ' + getSubstringIndex('sequence', where1, 'at1') + ';', - ' var end = ' + getSubstringIndex('sequence', where2, 'at2') + - ' + 1;', - ' return sequence.slice(start, end);', '}' - ]); + 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(sequence${at1Param}${at2Param}) { + var start = ${getSubstringIndex('sequence', where1, 'at1')}; + var end = ${getSubstringIndex('sequence', where2, 'at2')} + 1; + return sequence.slice(start, end); +} +`); code = functionName + '(' + text + // The value for 'FROM_END' and 'FROM_START' depends on `at` so we // pass it. @@ -269,27 +264,23 @@ JavaScript['text_changeCase'] = function(block) { const OPERATORS = { 'UPPERCASE': '.toUpperCase()', 'LOWERCASE': '.toLowerCase()', - 'TITLECASE': null + 'TITLECASE': null, }; const operator = OPERATORS[block.getFieldValue('CASE')]; - const textOrder = operator ? JavaScript.ORDER_MEMBER : - JavaScript.ORDER_NONE; - const text = JavaScript.valueToCode(block, 'TEXT', - textOrder) || '\'\''; + const textOrder = operator ? JavaScript.ORDER_MEMBER : JavaScript.ORDER_NONE; + const text = JavaScript.valueToCode(block, 'TEXT', textOrder) || "''"; let code; if (operator) { // Upper and lower case are functions built into JavaScript. code = text + operator; } else { // Title case is not a native JavaScript function. Define one. - const functionName = JavaScript.provideFunction_( - 'textToTitleCase', - ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(str) {', - ' return str.replace(/\\S+/g,', - ' function(txt) {return txt[0].toUpperCase() + ' + - 'txt.substring(1).toLowerCase();});', - '}']); + const functionName = JavaScript.provideFunction_('textToTitleCase', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(str) { + return str.replace(/\\S+/g, + function(txt) {return txt[0].toUpperCase() + txt.substring(1).toLowerCase();}); +} +`); code = functionName + '(' + text + ')'; } return [code, JavaScript.ORDER_FUNCTION_CALL]; @@ -300,18 +291,18 @@ JavaScript['text_trim'] = function(block) { const OPERATORS = { 'LEFT': ".replace(/^[\\s\\xa0]+/, '')", 'RIGHT': ".replace(/[\\s\\xa0]+$/, '')", - 'BOTH': '.trim()' + 'BOTH': '.trim()', }; const operator = OPERATORS[block.getFieldValue('MODE')]; const text = JavaScript.valueToCode(block, 'TEXT', - JavaScript.ORDER_MEMBER) || '\'\''; + JavaScript.ORDER_MEMBER) || "''"; return [text + operator, JavaScript.ORDER_FUNCTION_CALL]; }; JavaScript['text_print'] = function(block) { // Print statement. const msg = JavaScript.valueToCode(block, 'TEXT', - JavaScript.ORDER_NONE) || '\'\''; + JavaScript.ORDER_NONE) || "''"; return 'window.alert(' + msg + ');\n'; }; @@ -323,8 +314,7 @@ JavaScript['text_prompt_ext'] = function(block) { msg = JavaScript.quote_(block.getFieldValue('TEXT')); } else { // External message. - msg = JavaScript.valueToCode(block, 'TEXT', - JavaScript.ORDER_NONE) || '\'\''; + msg = JavaScript.valueToCode(block, 'TEXT', JavaScript.ORDER_NONE) || "''"; } let code = 'window.prompt(' + msg + ')'; const toNumber = block.getFieldValue('TYPE') === 'NUMBER'; @@ -338,48 +328,44 @@ JavaScript['text_prompt'] = JavaScript['text_prompt_ext']; JavaScript['text_count'] = function(block) { const text = JavaScript.valueToCode(block, 'TEXT', - JavaScript.ORDER_NONE) || '\'\''; + JavaScript.ORDER_NONE) || "''"; const sub = JavaScript.valueToCode(block, 'SUB', - JavaScript.ORDER_NONE) || '\'\''; - const functionName = JavaScript.provideFunction_( - 'textCount', - ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(haystack, needle) {', - ' if (needle.length === 0) {', - ' return haystack.length + 1;', - ' } else {', - ' return haystack.split(needle).length - 1;', - ' }', - '}']); + JavaScript.ORDER_NONE) || "''"; + const functionName = JavaScript.provideFunction_('textCount', ` +function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(haystack, needle) { + if (needle.length === 0) { + return haystack.length + 1; + } else { + return haystack.split(needle).length - 1; + } +} +`); const code = functionName + '(' + text + ', ' + sub + ')'; return [code, JavaScript.ORDER_FUNCTION_CALL]; }; JavaScript['text_replace'] = function(block) { const text = JavaScript.valueToCode(block, 'TEXT', - JavaScript.ORDER_NONE) || '\'\''; + JavaScript.ORDER_NONE) || "''"; const from = JavaScript.valueToCode(block, 'FROM', - JavaScript.ORDER_NONE) || '\'\''; - const to = JavaScript.valueToCode(block, 'TO', - JavaScript.ORDER_NONE) || '\'\''; + JavaScript.ORDER_NONE) || "''"; + const to = JavaScript.valueToCode(block, 'TO', JavaScript.ORDER_NONE) || "''"; // The regex escaping code below is taken from the implementation of // goog.string.regExpEscape. - const functionName = JavaScript.provideFunction_( - 'textReplace', - ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + - '(haystack, needle, replacement) {', - ' needle = ' + - 'needle.replace(/([-()\\[\\]{}+?*.$\\^|,:# 0 ? 'else' : '') + 'if ' + conditionCode + ' then\n' + branchCode; diff --git a/generators/lua/math.js b/generators/lua/math.js index c51274830..b17821a33 100644 --- a/generators/lua/math.js +++ b/generators/lua/math.js @@ -25,11 +25,11 @@ Lua['math_number'] = function(block) { Lua['math_arithmetic'] = function(block) { // Basic arithmetic operators, and power. const OPERATORS = { - ADD: [' + ', Lua.ORDER_ADDITIVE], - MINUS: [' - ', Lua.ORDER_ADDITIVE], - MULTIPLY: [' * ', Lua.ORDER_MULTIPLICATIVE], - DIVIDE: [' / ', Lua.ORDER_MULTIPLICATIVE], - POWER: [' ^ ', Lua.ORDER_EXPONENTIATION] + 'ADD': [' + ', Lua.ORDER_ADDITIVE], + 'MINUS': [' - ', Lua.ORDER_ADDITIVE], + 'MULTIPLY': [' * ', Lua.ORDER_MULTIPLICATIVE], + 'DIVIDE': [' / ', Lua.ORDER_MULTIPLICATIVE], + 'POWER': [' ^ ', Lua.ORDER_EXPONENTIATION], }; const tuple = OPERATORS[block.getFieldValue('OP')]; const operator = tuple[0]; @@ -113,12 +113,12 @@ Lua['math_single'] = function(block) { Lua['math_constant'] = function(block) { // Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY. const CONSTANTS = { - PI: ['math.pi', Lua.ORDER_HIGH], - E: ['math.exp(1)', Lua.ORDER_HIGH], - GOLDEN_RATIO: ['(1 + math.sqrt(5)) / 2', Lua.ORDER_MULTIPLICATIVE], - SQRT2: ['math.sqrt(2)', Lua.ORDER_HIGH], - SQRT1_2: ['math.sqrt(1 / 2)', Lua.ORDER_HIGH], - INFINITY: ['math.huge', Lua.ORDER_HIGH] + 'PI': ['math.pi', Lua.ORDER_HIGH], + 'E': ['math.exp(1)', Lua.ORDER_HIGH], + 'GOLDEN_RATIO': ['(1 + math.sqrt(5)) / 2', Lua.ORDER_MULTIPLICATIVE], + 'SQRT2': ['math.sqrt(2)', Lua.ORDER_HIGH], + 'SQRT1_2': ['math.sqrt(1 / 2)', Lua.ORDER_HIGH], + 'INFINITY': ['math.huge', Lua.ORDER_HIGH], }; return CONSTANTS[block.getFieldValue('CONSTANT')]; }; @@ -127,20 +127,13 @@ Lua['math_number_property'] = function(block) { // 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 = { - 'EVEN': [' % 2 == 0', Lua.ORDER_MULTIPLICATIVE, - Lua.ORDER_RELATIONAL], - 'ODD': [' % 2 == 1', Lua.ORDER_MULTIPLICATIVE, - Lua.ORDER_RELATIONAL], - 'WHOLE': [' % 1 == 0', Lua.ORDER_MULTIPLICATIVE, - Lua.ORDER_RELATIONAL], - 'POSITIVE': [' > 0', Lua.ORDER_RELATIONAL, - Lua.ORDER_RELATIONAL], - 'NEGATIVE': [' < 0', Lua.ORDER_RELATIONAL, - Lua.ORDER_RELATIONAL], - 'DIVISIBLE_BY': [null, Lua.ORDER_MULTIPLICATIVE, - Lua.ORDER_RELATIONAL], - 'PRIME': [null, Lua.ORDER_NONE, - Lua.ORDER_HIGH] + 'EVEN': [' % 2 == 0', Lua.ORDER_MULTIPLICATIVE, Lua.ORDER_RELATIONAL], + 'ODD': [' % 2 == 1', Lua.ORDER_MULTIPLICATIVE, Lua.ORDER_RELATIONAL], + 'WHOLE': [' % 1 == 0', Lua.ORDER_MULTIPLICATIVE, Lua.ORDER_RELATIONAL], + 'POSITIVE': [' > 0', Lua.ORDER_RELATIONAL, Lua.ORDER_RELATIONAL], + 'NEGATIVE': [' < 0', Lua.ORDER_RELATIONAL, Lua.ORDER_RELATIONAL], + 'DIVISIBLE_BY': [null, Lua.ORDER_MULTIPLICATIVE, Lua.ORDER_RELATIONAL], + 'PRIME': [null, Lua.ORDER_NONE, Lua.ORDER_HIGH], }; const dropdownProperty = block.getFieldValue('PROPERTY'); const [suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty]; @@ -149,28 +142,28 @@ Lua['math_number_property'] = function(block) { let code; if (dropdownProperty === 'PRIME') { // Prime is a special case as it is not a one-liner test. - const functionName = Lua.provideFunction_( - 'math_isPrime', - ['function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(n)', - ' -- https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' if n == 2 or n == 3 then', - ' return true', - ' end', - ' -- False if n is NaN, negative, is 1, or not whole.', - ' -- And false if n is divisible by 2 or 3.', - ' if not(n > 1) or n % 1 ~= 0 or n % 2 == 0 or n % 3 == 0 then', - ' return false', - ' end', - ' -- Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for x = 6, math.sqrt(n) + 1.5, 6 do', - ' if n % (x - 1) == 0 or n % (x + 1) == 0 then', - ' return false', - ' end', - ' end', - ' return true', - 'end']); + const functionName = Lua.provideFunction_('math_isPrime', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(n) + -- https://en.wikipedia.org/wiki/Primality_test#Naive_methods + if n == 2 or n == 3 then + return true + end + -- False if n is NaN, negative, is 1, or not whole. + -- And false if n is divisible by 2 or 3. + if not(n > 1) or n % 1 ~= 0 or n % 2 == 0 or n % 3 == 0 then + return false + end + -- Check all the numbers of form 6k +/- 1, up to sqrt(n). + for x = 6, math.sqrt(n) + 1.5, 6 do + if n % (x - 1) == 0 or n % (x + 1) == 0 then + return false + end + end + return true +end +`); code = functionName + '(' + numberToCheck + ')'; - } else if (dropdownProperty === 'DIVISIBLE_BY') { + } else if (dropdownProperty === 'DIVISIBLE_BY') { const divisor = Lua.valueToCode(block, 'DIVISOR', Lua.ORDER_MULTIPLICATIVE) || '0'; // If 'divisor' is some code that evals to 0, Lua will produce a nan. @@ -209,11 +202,15 @@ Lua['math_on_list'] = function(block) { // Functions needed in more than one case. function provideSum() { - return Lua.provideFunction_('math_sum', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)', - ' local result = 0', ' for _, v in ipairs(t) do', - ' result = result + v', ' end', ' return result', 'end' - ]); + return Lua.provideFunction_('math_sum', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(t) + local result = 0 + for _, v in ipairs(t) do + result = result + v + end + return result +end +`); } switch (func) { @@ -223,103 +220,139 @@ Lua['math_on_list'] = function(block) { case 'MIN': // Returns 0 for the empty list. - functionName = Lua.provideFunction_('math_min', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)', - ' if #t == 0 then', ' return 0', ' end', - ' local result = math.huge', ' for _, v in ipairs(t) do', - ' if v < result then', ' result = v', ' end', ' end', - ' return result', 'end' - ]); + functionName = Lua.provideFunction_('math_min', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(t) + if #t == 0 then + return 0 + end + local result = math.huge + for _, v in ipairs(t) do + if v < result then + result = v + end + end + return result +end +`); break; case 'AVERAGE': // Returns 0 for the empty list. - functionName = Lua.provideFunction_('math_average', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)', - ' if #t == 0 then', ' return 0', ' end', - ' return ' + provideSum() + '(t) / #t', 'end' - ]); + functionName = Lua.provideFunction_('math_average', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(t) + if #t == 0 then + return 0 + end + return ${provideSum()}(t) / #t +end +`); break; case 'MAX': // Returns 0 for the empty list. - functionName = Lua.provideFunction_('math_max', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)', - ' if #t == 0 then', ' return 0', ' end', - ' local result = -math.huge', ' for _, v in ipairs(t) do', - ' if v > result then', ' result = v', ' end', ' end', - ' return result', 'end' - ]); + functionName = Lua.provideFunction_('math_max', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(t) + if #t == 0 then + return 0 + end + local result = -math.huge + for _, v in ipairs(t) do + if v > result then + result = v + end + end + return result +end +`); break; case 'MEDIAN': - functionName = Lua.provideFunction_( - 'math_median', - // This operation excludes non-numbers. - [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)', - ' -- Source: http://lua-users.org/wiki/SimpleStats', - ' if #t == 0 then', ' return 0', ' end', ' local temp={}', - ' for _, v in ipairs(t) do', ' if type(v) == "number" then', - ' table.insert(temp, v)', ' end', ' end', - ' table.sort(temp)', ' if #temp % 2 == 0 then', - ' return (temp[#temp/2] + temp[(#temp/2)+1]) / 2', ' else', - ' return temp[math.ceil(#temp/2)]', ' end', 'end' - ]); + // This operation excludes non-numbers. + functionName = Lua.provideFunction_('math_median', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(t) + -- Source: http://lua-users.org/wiki/SimpleStats + if #t == 0 then + return 0 + end + local temp = {} + for _, v in ipairs(t) do + if type(v) == 'number' then + table.insert(temp, v) + end + end + table.sort(temp) + if #temp % 2 == 0 then + return (temp[#temp / 2] + temp[(#temp / 2) + 1]) / 2 + else + return temp[math.ceil(#temp / 2)] + end +end +`); break; case 'MODE': - functionName = Lua.provideFunction_( - 'math_modes', - // As a list of numbers can contain more than one mode, - // the returned result is provided as an array. - // The Lua version includes non-numbers. - [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)', - ' -- Source: http://lua-users.org/wiki/SimpleStats', - ' local counts={}', - ' for _, v in ipairs(t) do', - ' if counts[v] == nil then', - ' counts[v] = 1', - ' else', - ' counts[v] = counts[v] + 1', - ' end', - ' end', - ' local biggestCount = 0', - ' for _, v in pairs(counts) do', - ' if v > biggestCount then', - ' biggestCount = v', - ' end', - ' end', - ' local temp={}', - ' for k, v in pairs(counts) do', - ' if v == biggestCount then', - ' table.insert(temp, k)', - ' end', - ' end', - ' return temp', - 'end' - ]); + // As a list of numbers can contain more than one mode, + // the returned result is provided as an array. + // The Lua version includes non-numbers. + functionName = Lua.provideFunction_('math_modes', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(t) + -- Source: http://lua-users.org/wiki/SimpleStats + local counts = {} + for _, v in ipairs(t) do + if counts[v] == nil then + counts[v] = 1 + else + counts[v] = counts[v] + 1 + end + end + local biggestCount = 0 + for _, v in pairs(counts) do + if v > biggestCount then + biggestCount = v + end + end + local temp = {} + for k, v in pairs(counts) do + if v == biggestCount then + table.insert(temp, k) + end + end + return temp +end +`); break; case 'STD_DEV': - functionName = Lua.provideFunction_('math_standard_deviation', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)', ' local m', - ' local vm', ' local total = 0', ' local count = 0', - ' local result', ' m = #t == 0 and 0 or ' + provideSum() + '(t) / #t', - ' for _, v in ipairs(t) do', ' if type(v) == \'number\' then', - ' vm = v - m', ' total = total + (vm * vm)', - ' count = count + 1', ' end', ' end', - ' result = math.sqrt(total / (count-1))', ' return result', 'end' - ]); + functionName = Lua.provideFunction_('math_standard_deviation', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(t) + local m + local vm + local total = 0 + local count = 0 + local result + m = #t == 0 and 0 or ${provideSum()}(t) / #t + for _, v in ipairs(t) do + if type(v) == 'number' then + vm = v - m + total = total + (vm * vm) + count = count + 1 + end + end + result = math.sqrt(total / (count-1)) + return result +end +`); break; case 'RANDOM': - functionName = Lua.provideFunction_('math_random_list', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)', - ' if #t == 0 then', ' return nil', ' end', - ' return t[math.random(#t)]', 'end' - ]); + functionName = Lua.provideFunction_('math_random_list', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(t) + if #t == 0 then + return nil + end + return t[math.random(#t)] +end +`); break; default: diff --git a/generators/lua/text.js b/generators/lua/text.js index a8dae86c9..74fe842d8 100644 --- a/generators/lua/text.js +++ b/generators/lua/text.js @@ -32,22 +32,22 @@ Lua['text_multiline'] = function(block) { Lua['text_join'] = function(block) { // Create a string made up of any number of elements of any type. if (block.itemCount_ === 0) { - return ['\'\'', Lua.ORDER_ATOMIC]; + return ["''", Lua.ORDER_ATOMIC]; } else if (block.itemCount_ === 1) { - const element = Lua.valueToCode(block, 'ADD0', Lua.ORDER_NONE) || '\'\''; + const element = Lua.valueToCode(block, 'ADD0', Lua.ORDER_NONE) || "''"; const code = 'tostring(' + element + ')'; return [code, Lua.ORDER_HIGH]; } else if (block.itemCount_ === 2) { const element0 = - Lua.valueToCode(block, 'ADD0', Lua.ORDER_CONCATENATION) || '\'\''; + Lua.valueToCode(block, 'ADD0', Lua.ORDER_CONCATENATION) || "''"; const element1 = - Lua.valueToCode(block, 'ADD1', Lua.ORDER_CONCATENATION) || '\'\''; + Lua.valueToCode(block, 'ADD1', Lua.ORDER_CONCATENATION) || "''"; const code = element0 + ' .. ' + element1; return [code, Lua.ORDER_CONCATENATION]; } else { const elements = []; for (let i = 0; i < block.itemCount_; i++) { - elements[i] = Lua.valueToCode(block, 'ADD' + i, Lua.ORDER_NONE) || '\'\''; + elements[i] = Lua.valueToCode(block, 'ADD' + i, Lua.ORDER_NONE) || "''"; } const code = 'table.concat({' + elements.join(', ') + '})'; return [code, Lua.ORDER_HIGH]; @@ -59,41 +59,47 @@ Lua['text_append'] = function(block) { const varName = Lua.nameDB_.getName(block.getFieldValue('VAR'), NameType.VARIABLE); const value = - Lua.valueToCode(block, 'TEXT', Lua.ORDER_CONCATENATION) || '\'\''; + Lua.valueToCode(block, 'TEXT', Lua.ORDER_CONCATENATION) || "''"; return varName + ' = ' + varName + ' .. ' + value + '\n'; }; Lua['text_length'] = function(block) { // String or array length. - const text = Lua.valueToCode(block, 'VALUE', Lua.ORDER_UNARY) || '\'\''; + const text = Lua.valueToCode(block, 'VALUE', Lua.ORDER_UNARY) || "''"; return ['#' + text, Lua.ORDER_UNARY]; }; Lua['text_isEmpty'] = function(block) { // Is the string null or array empty? - const text = Lua.valueToCode(block, 'VALUE', Lua.ORDER_UNARY) || '\'\''; + const text = Lua.valueToCode(block, 'VALUE', Lua.ORDER_UNARY) || "''"; return ['#' + text + ' == 0', Lua.ORDER_RELATIONAL]; }; Lua['text_indexOf'] = function(block) { // Search the text for a substring. - const substring = Lua.valueToCode(block, 'FIND', Lua.ORDER_NONE) || '\'\''; - const text = Lua.valueToCode(block, 'VALUE', Lua.ORDER_NONE) || '\'\''; + const substring = Lua.valueToCode(block, 'FIND', Lua.ORDER_NONE) || "''"; + const text = Lua.valueToCode(block, 'VALUE', Lua.ORDER_NONE) || "''"; let functionName; if (block.getFieldValue('END') === 'FIRST') { - functionName = Lua.provideFunction_('firstIndexOf', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(str, substr) ', - ' local i = string.find(str, substr, 1, true)', ' if i == nil then', - ' return 0', ' else', ' return i', ' end', 'end' - ]); + functionName = Lua.provideFunction_('firstIndexOf', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(str, substr) + local i = string.find(str, substr, 1, true) + if i == nil then + return 0 + end + return i +end +`); } else { - functionName = Lua.provideFunction_('lastIndexOf', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(str, substr)', - ' local i = string.find(string.reverse(str), ' + - 'string.reverse(substr), 1, true)', - ' if i then', ' return #str + 2 - i - #substr', ' end', ' return 0', - 'end' - ]); + functionName = Lua.provideFunction_('lastIndexOf', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(str, substr) + local i = string.find(string.reverse(str), string.reverse(substr), 1, true) + if i then + return #str + 2 - i - #substr + end + return 0 +end +`); } const code = functionName + '(' + text + ', ' + substring + ')'; return [code, Lua.ORDER_HIGH]; @@ -105,14 +111,15 @@ Lua['text_charAt'] = function(block) { const where = block.getFieldValue('WHERE') || 'FROM_START'; const atOrder = (where === 'FROM_END') ? Lua.ORDER_UNARY : Lua.ORDER_NONE; const at = Lua.valueToCode(block, 'AT', atOrder) || '1'; - const text = Lua.valueToCode(block, 'VALUE', Lua.ORDER_NONE) || '\'\''; + const text = Lua.valueToCode(block, 'VALUE', Lua.ORDER_NONE) || "''"; let code; if (where === 'RANDOM') { - const functionName = Lua.provideFunction_('text_random_letter', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(str)', - ' local index = math.random(string.len(str))', - ' return string.sub(str, index, index)', 'end' - ]); + const functionName = Lua.provideFunction_('text_random_letter', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(str) + local index = math.random(string.len(str)) + return string.sub(str, index, index) +end +`); code = functionName + '(' + text + ')'; } else { let start; @@ -133,10 +140,11 @@ Lua['text_charAt'] = function(block) { code = 'string.sub(' + text + ', ' + start + ', ' + start + ')'; } else { // use function to avoid reevaluation - const functionName = Lua.provideFunction_('text_char_at', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(str, index)', - ' return string.sub(str, index, index)', 'end' - ]); + const functionName = Lua.provideFunction_('text_char_at', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(str, index) + return string.sub(str, index, index) +end +`); code = functionName + '(' + text + ', ' + start + ')'; } } @@ -145,7 +153,7 @@ Lua['text_charAt'] = function(block) { Lua['text_getSubstring'] = function(block) { // Get substring. - const text = Lua.valueToCode(block, 'STRING', Lua.ORDER_NONE) || '\'\''; + const text = Lua.valueToCode(block, 'STRING', Lua.ORDER_NONE) || "''"; // Get start index. const where1 = block.getFieldValue('WHERE1'); @@ -183,28 +191,35 @@ Lua['text_getSubstring'] = function(block) { Lua['text_changeCase'] = function(block) { // Change capitalization. const operator = block.getFieldValue('CASE'); - const text = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || '\'\''; + const text = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || "''"; let functionName; if (operator === 'UPPERCASE') { functionName = 'string.upper'; } else if (operator === 'LOWERCASE') { functionName = 'string.lower'; } else if (operator === 'TITLECASE') { - functionName = Lua.provideFunction_( - 'text_titlecase', - // There are shorter versions at - // http://lua-users.org/wiki/SciteTitleCase - // that do not preserve whitespace. - [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(str)', - ' local buf = {}', ' local inWord = false', ' for i = 1, #str do', - ' local c = string.sub(str, i, i)', ' if inWord then', - ' table.insert(buf, string.lower(c))', - ' if string.find(c, "%s") then', ' inWord = false', - ' end', ' else', ' table.insert(buf, string.upper(c))', - ' inWord = true', ' end', ' end', - ' return table.concat(buf)', 'end' - ]); + // There are shorter versions at + // http://lua-users.org/wiki/SciteTitleCase + // that do not preserve whitespace. + functionName = Lua.provideFunction_('text_titlecase', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(str) + local buf = {} + local inWord = false + for i = 1, #str do + local c = string.sub(str, i, i) + if inWord then + table.insert(buf, string.lower(c)) + if string.find(c, "%s") then + inWord = false + end + else + table.insert(buf, string.upper(c)) + inWord = true + end + end + return table.concat(buf) +end +`); } const code = functionName + '(' + text + ')'; return [code, Lua.ORDER_HIGH]; @@ -214,14 +229,14 @@ Lua['text_trim'] = function(block) { // Trim spaces. const OPERATORS = {LEFT: '^%s*(,-)', RIGHT: '(.-)%s*$', BOTH: '^%s*(.-)%s*$'}; const operator = OPERATORS[block.getFieldValue('MODE')]; - const text = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || '\'\''; + const text = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || "''"; const code = 'string.gsub(' + text + ', "' + operator + '", "%1")'; return [code, Lua.ORDER_HIGH]; }; Lua['text_print'] = function(block) { // Print statement. - const msg = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || '\'\''; + const msg = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || "''"; return 'print(' + msg + ')\n'; }; @@ -233,13 +248,16 @@ Lua['text_prompt_ext'] = function(block) { msg = Lua.quote_(block.getFieldValue('TEXT')); } else { // External message. - msg = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || '\'\''; + msg = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || "''"; } - const functionName = Lua.provideFunction_('text_prompt', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(msg)', ' io.write(msg)', - ' io.flush()', ' return io.read()', 'end' - ]); + const functionName = Lua.provideFunction_('text_prompt', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(msg) + io.write(msg) + io.flush() + return io.read() +end +`); let code = functionName + '(' + msg + ')'; const toNumber = block.getFieldValue('TYPE') === 'NUMBER'; @@ -252,59 +270,58 @@ Lua['text_prompt_ext'] = function(block) { Lua['text_prompt'] = Lua['text_prompt_ext']; Lua['text_count'] = function(block) { - const text = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || '\'\''; - const sub = Lua.valueToCode(block, 'SUB', Lua.ORDER_NONE) || '\'\''; - const functionName = Lua.provideFunction_('text_count', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + '(haystack, needle)', - ' if #needle == 0 then', - ' return #haystack + 1', - ' end', - ' local i = 1', - ' local count = 0', - ' while true do', - ' i = string.find(haystack, needle, i, true)', - ' if i == nil then', - ' break', - ' end', - ' count = count + 1', - ' i = i + #needle', - ' end', - ' return count', - 'end', - ]); + const text = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || "''"; + const sub = Lua.valueToCode(block, 'SUB', Lua.ORDER_NONE) || "''"; + const functionName = Lua.provideFunction_('text_count', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(haystack, needle) + if #needle == 0 then + return #haystack + 1 + end + local i = 1 + local count = 0 + while true do + i = string.find(haystack, needle, i, true) + if i == nil then + break + end + count = count + 1 + i = i + #needle + end + return count +end +`); const code = functionName + '(' + text + ', ' + sub + ')'; return [code, Lua.ORDER_HIGH]; }; Lua['text_replace'] = function(block) { - const text = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || '\'\''; - const from = Lua.valueToCode(block, 'FROM', Lua.ORDER_NONE) || '\'\''; - const to = Lua.valueToCode(block, 'TO', Lua.ORDER_NONE) || '\'\''; - const functionName = Lua.provideFunction_('text_replace', [ - 'function ' + Lua.FUNCTION_NAME_PLACEHOLDER_ + - '(haystack, needle, replacement)', - ' local buf = {}', - ' local i = 1', - ' while i <= #haystack do', - ' if string.sub(haystack, i, i + #needle - 1) == needle then', - ' for j = 1, #replacement do', - ' table.insert(buf, string.sub(replacement, j, j))', - ' end', - ' i = i + #needle', - ' else', - ' table.insert(buf, string.sub(haystack, i, i))', - ' i = i + 1', - ' end', - ' end', - ' return table.concat(buf)', - 'end', - ]); + const text = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || "''"; + const from = Lua.valueToCode(block, 'FROM', Lua.ORDER_NONE) || "''"; + const to = Lua.valueToCode(block, 'TO', Lua.ORDER_NONE) || "''"; + const functionName = Lua.provideFunction_('text_replace', ` +function ${Lua.FUNCTION_NAME_PLACEHOLDER_}(haystack, needle, replacement) + local buf = {} + local i = 1 + while i <= #haystack do + if string.sub(haystack, i, i + #needle - 1) == needle then + for j = 1, #replacement do + table.insert(buf, string.sub(replacement, j, j)) + end + i = i + #needle + else + table.insert(buf, string.sub(haystack, i, i)) + i = i + 1 + end + end + return table.concat(buf) +end +`); const code = functionName + '(' + text + ', ' + from + ', ' + to + ')'; return [code, Lua.ORDER_HIGH]; }; Lua['text_reverse'] = function(block) { - const text = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || '\'\''; + const text = Lua.valueToCode(block, 'TEXT', Lua.ORDER_NONE) || "''"; const code = 'string.reverse(' + text + ')'; return [code, Lua.ORDER_HIGH]; }; diff --git a/generators/php/colour.js b/generators/php/colour.js index d50cbfb63..bbd2fd9d7 100644 --- a/generators/php/colour.js +++ b/generators/php/colour.js @@ -22,12 +22,11 @@ PHP['colour_picker'] = function(block) { PHP['colour_random'] = function(block) { // Generate a random colour. - const functionName = PHP.provideFunction_('colour_random', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '() {', - ' return \'#\' . str_pad(dechex(mt_rand(0, 0xFFFFFF)), ' + - '6, \'0\', STR_PAD_LEFT);', - '}' - ]); + const functionName = PHP.provideFunction_('colour_random', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}() { + return '#' . str_pad(dechex(mt_rand(0, 0xFFFFFF)), 6, '0', STR_PAD_LEFT); +} +`); const code = functionName + '()'; return [code, PHP.ORDER_FUNCTION_CALL]; }; @@ -37,39 +36,46 @@ PHP['colour_rgb'] = function(block) { const red = PHP.valueToCode(block, 'RED', PHP.ORDER_NONE) || 0; const green = PHP.valueToCode(block, 'GREEN', PHP.ORDER_NONE) || 0; const blue = PHP.valueToCode(block, 'BLUE', PHP.ORDER_NONE) || 0; - const functionName = PHP.provideFunction_('colour_rgb', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($r, $g, $b) {', - ' $r = round(max(min($r, 100), 0) * 2.55);', - ' $g = round(max(min($g, 100), 0) * 2.55);', - ' $b = round(max(min($b, 100), 0) * 2.55);', ' $hex = \'#\';', - ' $hex .= str_pad(dechex($r), 2, \'0\', STR_PAD_LEFT);', - ' $hex .= str_pad(dechex($g), 2, \'0\', STR_PAD_LEFT);', - ' $hex .= str_pad(dechex($b), 2, \'0\', STR_PAD_LEFT);', ' return $hex;', - '}' - ]); + const functionName = PHP.provideFunction_('colour_rgb', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($r, $g, $b) { + $r = round(max(min($r, 100), 0) * 2.55); + $g = round(max(min($g, 100), 0) * 2.55); + $b = round(max(min($b, 100), 0) * 2.55); + $hex = '#'; + $hex .= str_pad(dechex($r), 2, '0', STR_PAD_LEFT); + $hex .= str_pad(dechex($g), 2, '0', STR_PAD_LEFT); + $hex .= str_pad(dechex($b), 2, '0', STR_PAD_LEFT); + return $hex; +} +`); const code = functionName + '(' + red + ', ' + green + ', ' + blue + ')'; return [code, PHP.ORDER_FUNCTION_CALL]; }; PHP['colour_blend'] = function(block) { // Blend two colours together. - const c1 = PHP.valueToCode(block, 'COLOUR1', PHP.ORDER_NONE) || '\'#000000\''; - const c2 = PHP.valueToCode(block, 'COLOUR2', PHP.ORDER_NONE) || '\'#000000\''; + const c1 = PHP.valueToCode(block, 'COLOUR1', PHP.ORDER_NONE) || "'#000000'"; + const c2 = PHP.valueToCode(block, 'COLOUR2', PHP.ORDER_NONE) || "'#000000'"; const ratio = PHP.valueToCode(block, 'RATIO', PHP.ORDER_NONE) || 0.5; - const functionName = PHP.provideFunction_('colour_blend', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($c1, $c2, $ratio) {', - ' $ratio = max(min($ratio, 1), 0);', ' $r1 = hexdec(substr($c1, 1, 2));', - ' $g1 = hexdec(substr($c1, 3, 2));', ' $b1 = hexdec(substr($c1, 5, 2));', - ' $r2 = hexdec(substr($c2, 1, 2));', ' $g2 = hexdec(substr($c2, 3, 2));', - ' $b2 = hexdec(substr($c2, 5, 2));', - ' $r = round($r1 * (1 - $ratio) + $r2 * $ratio);', - ' $g = round($g1 * (1 - $ratio) + $g2 * $ratio);', - ' $b = round($b1 * (1 - $ratio) + $b2 * $ratio);', ' $hex = \'#\';', - ' $hex .= str_pad(dechex($r), 2, \'0\', STR_PAD_LEFT);', - ' $hex .= str_pad(dechex($g), 2, \'0\', STR_PAD_LEFT);', - ' $hex .= str_pad(dechex($b), 2, \'0\', STR_PAD_LEFT);', ' return $hex;', - '}' - ]); + const functionName = PHP.provideFunction_('colour_blend', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($c1, $c2, $ratio) { + $ratio = max(min($ratio, 1), 0); + $r1 = hexdec(substr($c1, 1, 2)); + $g1 = hexdec(substr($c1, 3, 2)); + $b1 = hexdec(substr($c1, 5, 2)); + $r2 = hexdec(substr($c2, 1, 2)); + $g2 = hexdec(substr($c2, 3, 2)); + $b2 = hexdec(substr($c2, 5, 2)); + $r = round($r1 * (1 - $ratio) + $r2 * $ratio); + $g = round($g1 * (1 - $ratio) + $g2 * $ratio); + $b = round($b1 * (1 - $ratio) + $b2 * $ratio); + $hex = '#'; + $hex .= str_pad(dechex($r), 2, '0', STR_PAD_LEFT); + $hex .= str_pad(dechex($g), 2, '0', STR_PAD_LEFT); + $hex .= str_pad(dechex($b), 2, '0', STR_PAD_LEFT); + return $hex; +} +`); const code = functionName + '(' + c1 + ', ' + c2 + ', ' + ratio + ')'; return [code, PHP.ORDER_FUNCTION_CALL]; }; diff --git a/generators/php/lists.js b/generators/php/lists.js index efc237887..1c39a1ab6 100644 --- a/generators/php/lists.js +++ b/generators/php/lists.js @@ -44,11 +44,15 @@ PHP['lists_create_with'] = function(block) { PHP['lists_repeat'] = function(block) { // Create a list with one element repeated. - const functionName = PHP.provideFunction_('lists_repeat', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value, $count) {', - ' $array = array();', ' for ($index = 0; $index < $count; $index++) {', - ' $array[] = $value;', ' }', ' return $array;', '}' - ]); + const functionName = PHP.provideFunction_('lists_repeat', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($value, $count) { + $array = array(); + for ($index = 0; $index < $count; $index++) { + $array[] = $value; + } + return $array; +} +`); const element = PHP.valueToCode(block, 'ITEM', PHP.ORDER_NONE) || 'null'; const repeatCount = PHP.valueToCode(block, 'NUM', PHP.ORDER_NONE) || '0'; const code = functionName + '(' + element + ', ' + repeatCount + ')'; @@ -57,12 +61,16 @@ PHP['lists_repeat'] = function(block) { PHP['lists_length'] = function(block) { // String or array length. - const functionName = PHP.provideFunction_('length', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value) {', - ' if (is_string($value)) {', ' return strlen($value);', ' } else {', - ' return count($value);', ' }', '}' - ]); - const list = PHP.valueToCode(block, 'VALUE', PHP.ORDER_NONE) || '\'\''; + const functionName = PHP.provideFunction_('length', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($value) { + if (is_string($value)) { + return strlen($value); + } else { + return count($value); + } +} +`); + const list = PHP.valueToCode(block, 'VALUE', PHP.ORDER_NONE) || "''"; return [functionName + '(' + list + ')', PHP.ORDER_FUNCTION_CALL]; }; @@ -75,7 +83,7 @@ PHP['lists_isEmpty'] = function(block) { PHP['lists_indexOf'] = function(block) { // Find an item in the list. - const argument0 = PHP.valueToCode(block, 'FIND', PHP.ORDER_NONE) || '\'\''; + const argument0 = PHP.valueToCode(block, 'FIND', PHP.ORDER_NONE) || "''"; const argument1 = PHP.valueToCode(block, 'VALUE', PHP.ORDER_MEMBER) || '[]'; let errorIndex = ' -1'; let indexAdjustment = ''; @@ -86,23 +94,25 @@ PHP['lists_indexOf'] = function(block) { let functionName; if (block.getFieldValue('END') === 'FIRST') { // indexOf - functionName = PHP.provideFunction_('indexOf', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($haystack, $needle) {', - ' for ($index = 0; $index < count($haystack); $index++) {', - ' if ($haystack[$index] == $needle) return $index' + indexAdjustment + - ';', - ' }', ' return ' + errorIndex + ';', '}' - ]); + functionName = PHP.provideFunction_('indexOf', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($haystack, $needle) { + for ($index = 0; $index < count($haystack); $index++) { + if ($haystack[$index] == $needle) return $index${indexAdjustment}; + } + return ${errorIndex}; +} +`); } else { // lastIndexOf - functionName = PHP.provideFunction_('lastIndexOf', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($haystack, $needle) {', - ' $last = ' + errorIndex + ';', - ' for ($index = 0; $index < count($haystack); $index++) {', - ' if ($haystack[$index] == $needle) $last = $index' + indexAdjustment + - ';', - ' }', ' return $last;', '}' - ]); + functionName = PHP.provideFunction_('lastIndexOf', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($haystack, $needle) { + $last = ${errorIndex}; + for ($index = 0; $index < count($haystack); $index++) { + if ($haystack[$index] == $needle) $last = $index${indexAdjustment}; + } + return $last; +} +`); } const code = functionName + '(' + argument1 + ', ' + argument0 + ')'; @@ -191,26 +201,30 @@ PHP['lists_getIndex'] = function(block) { case 'RANDOM': { const list = PHP.valueToCode(block, 'VALUE', PHP.ORDER_NONE) || 'array()'; if (mode === 'GET') { - const functionName = PHP.provideFunction_('lists_get_random_item', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($list) {', - ' return $list[rand(0,count($list)-1)];', '}' - ]); + const functionName = PHP.provideFunction_('lists_get_random_item', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($list) { + return $list[rand(0,count($list)-1)]; +} +`); const code = functionName + '(' + list + ')'; return [code, PHP.ORDER_FUNCTION_CALL]; } else if (mode === 'GET_REMOVE') { const functionName = - PHP.provideFunction_('lists_get_remove_random_item', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '(&$list) {', - ' $x = rand(0,count($list)-1);', ' unset($list[$x]);', - ' return array_values($list);', '}' - ]); + PHP.provideFunction_('lists_get_remove_random_item', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}(&$list) { + $x = rand(0,count($list)-1); + unset($list[$x]); + return array_values($list); +} +`); const code = functionName + '(' + list + ')'; return [code, PHP.ORDER_FUNCTION_CALL]; } else if (mode === 'REMOVE') { - const functionName = PHP.provideFunction_('lists_remove_random_item', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '(&$list) {', - ' unset($list[rand(0,count($list)-1)]);', '}' - ]); + const functionName = PHP.provideFunction_('lists_remove_random_item', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}(&$list) { + unset($list[rand(0,count($list)-1)]); +} +`); return functionName + '(' + list + ');\n'; } break; @@ -252,10 +266,11 @@ PHP['lists_setIndex'] = function(block) { case 'LAST': { const list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || 'array()'; if (mode === 'SET') { - const functionName = PHP.provideFunction_('lists_set_last_item', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '(&$list, $value) {', - ' $list[count($list) - 1] = $value;', '}' - ]); + const functionName = PHP.provideFunction_('lists_set_last_item', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}(&$list, $value) { + $list[count($list) - 1] = $value; +} +`); return functionName + '(' + list + ', ' + value + ');\n'; } else if (mode === 'INSERT') { return 'array_push(' + list + ', ' + value + ');\n'; @@ -279,18 +294,18 @@ PHP['lists_setIndex'] = function(block) { const list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || 'array()'; const at = PHP.getAdjusted(block, 'AT', 1); if (mode === 'SET') { - const functionName = PHP.provideFunction_('lists_set_from_end', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + - '(&$list, $at, $value) {', - ' $list[count($list) - $at] = $value;', '}' - ]); + const functionName = PHP.provideFunction_('lists_set_from_end', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}(&$list, $at, $value) { + $list[count($list) - $at] = $value; +} +`); return functionName + '(' + list + ', ' + at + ', ' + value + ');\n'; } else if (mode === 'INSERT') { - const functionName = PHP.provideFunction_('lists_insert_from_end', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + - '(&$list, $at, $value) {', - ' return array_splice($list, count($list) - $at, 0, $value);', '}' - ]); + const functionName = PHP.provideFunction_('lists_insert_from_end', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}(&$list, $at, $value) { + return array_splice($list, count($list) - $at, 0, $value); +} +`); return functionName + '(' + list + ', ' + at + ', ' + value + ');\n'; } break; @@ -382,29 +397,28 @@ PHP['lists_getSublist'] = function(block) { } else { const at1 = PHP.getAdjusted(block, 'AT1'); const at2 = PHP.getAdjusted(block, 'AT2'); - const functionName = PHP.provideFunction_('lists_get_sublist', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($list, $where1, $at1, $where2, $at2) {', - ' if ($where1 == \'FROM_END\') {', - ' $at1 = count($list) - 1 - $at1;', - ' } else if ($where1 == \'FIRST\') {', - ' $at1 = 0;', - ' } else if ($where1 != \'FROM_START\') {', - ' throw new Exception(\'Unhandled option (lists_get_sublist).\');', - ' }', - ' $length = 0;', - ' if ($where2 == \'FROM_START\') {', - ' $length = $at2 - $at1 + 1;', - ' } else if ($where2 == \'FROM_END\') {', - ' $length = count($list) - $at1 - $at2;', - ' } else if ($where2 == \'LAST\') {', - ' $length = count($list) - $at1;', - ' } else {', - ' throw new Exception(\'Unhandled option (lists_get_sublist).\');', - ' }', - ' return array_slice($list, $at1, $length);', - '}' - ]); + const functionName = PHP.provideFunction_('lists_get_sublist', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($list, $where1, $at1, $where2, $at2) { + if ($where1 == 'FROM_END') { + $at1 = count($list) - 1 - $at1; + } else if ($where1 == 'FIRST') { + $at1 = 0; + } else if ($where1 != 'FROM_START') { + throw new Exception('Unhandled option (lists_get_sublist).'); + } + $length = 0; + if ($where2 == 'FROM_START') { + $length = $at2 - $at1 + 1; + } else if ($where2 == 'FROM_END') { + $length = count($list) - $at1 - $at2; + } else if ($where2 == 'LAST') { + $length = count($list) - $at1; + } else { + throw new Exception('Unhandled option (lists_get_sublist).'); + } + return array_slice($list, $at1, $length); +} +`); code = functionName + '(' + list + ', \'' + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; } @@ -416,16 +430,22 @@ PHP['lists_sort'] = function(block) { const listCode = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || 'array()'; const direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; const type = block.getFieldValue('TYPE'); - const functionName = PHP.provideFunction_('lists_sort', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($list, $type, $direction) {', - ' $sortCmpFuncs = array(', ' "NUMERIC" => "strnatcasecmp",', - ' "TEXT" => "strcmp",', ' "IGNORE_CASE" => "strcasecmp"', ' );', - ' $sortCmp = $sortCmpFuncs[$type];', - ' $list2 = $list;', // Clone list. - ' usort($list2, $sortCmp);', ' if ($direction == -1) {', - ' $list2 = array_reverse($list2);', ' }', ' return $list2;', '}' - ]); + const functionName = PHP.provideFunction_('lists_sort', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($list, $type, $direction) { + $sortCmpFuncs = array( + 'NUMERIC' => 'strnatcasecmp', + 'TEXT' => 'strcmp', + 'IGNORE_CASE' => 'strcasecmp' + ); + $sortCmp = $sortCmpFuncs[$type]; + $list2 = $list; + usort($list2, $sortCmp); + if ($direction == -1) { + $list2 = array_reverse($list2); + } + return $list2; +} +`); const sortCode = functionName + '(' + listCode + ', "' + type + '", ' + direction + ')'; return [sortCode, PHP.ORDER_FUNCTION_CALL]; @@ -434,12 +454,12 @@ PHP['lists_sort'] = function(block) { PHP['lists_split'] = function(block) { // Block for splitting text into a list, or joining a list into text. let value_input = PHP.valueToCode(block, 'INPUT', PHP.ORDER_NONE); - const value_delim = PHP.valueToCode(block, 'DELIM', PHP.ORDER_NONE) || '\'\''; + const value_delim = PHP.valueToCode(block, 'DELIM', PHP.ORDER_NONE) || "''"; const mode = block.getFieldValue('MODE'); let functionName; if (mode === 'SPLIT') { if (!value_input) { - value_input = '\'\''; + value_input = "''"; } functionName = 'explode'; } else if (mode === 'JOIN') { diff --git a/generators/php/math.js b/generators/php/math.js index 1c1e3e621..6c8c5ffb4 100644 --- a/generators/php/math.js +++ b/generators/php/math.js @@ -34,7 +34,7 @@ PHP['math_arithmetic'] = function(block) { 'MINUS': [' - ', PHP.ORDER_SUBTRACTION], 'MULTIPLY': [' * ', PHP.ORDER_MULTIPLICATION], 'DIVIDE': [' / ', PHP.ORDER_DIVISION], - 'POWER': [' ** ', PHP.ORDER_POWER] + 'POWER': [' ** ', PHP.ORDER_POWER], }; const tuple = OPERATORS[block.getFieldValue('OP')]; const operator = tuple[0]; @@ -134,7 +134,7 @@ PHP['math_constant'] = function(block) { 'GOLDEN_RATIO': ['(1 + sqrt(5)) / 2', PHP.ORDER_DIVISION], 'SQRT2': ['M_SQRT2', PHP.ORDER_ATOMIC], 'SQRT1_2': ['M_SQRT1_2', PHP.ORDER_ATOMIC], - 'INFINITY': ['INF', PHP.ORDER_ATOMIC] + 'INFINITY': ['INF', PHP.ORDER_ATOMIC], }; return CONSTANTS[block.getFieldValue('CONSTANT')]; }; @@ -143,20 +143,13 @@ PHP['math_number_property'] = function(block) { // 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 = { - 'EVEN': ['', ' % 2 == 0', PHP.ORDER_MODULUS, - PHP.ORDER_EQUALITY], - 'ODD': ['', ' % 2 == 1', PHP.ORDER_MODULUS, - PHP.ORDER_EQUALITY], - 'WHOLE': ['is_int(', ')', PHP.ORDER_NONE, - PHP.ORDER_FUNCTION_CALL], - 'POSITIVE': ['', ' > 0', PHP.ORDER_RELATIONAL, - PHP.ORDER_RELATIONAL], - 'NEGATIVE': ['', ' < 0', PHP.ORDER_RELATIONAL, - PHP.ORDER_RELATIONAL], - 'DIVISIBLE_BY': [null, null, PHP.ORDER_MODULUS, - PHP.ORDER_EQUALITY], - 'PRIME': [null, null, PHP.ORDER_NONE, - PHP.ORDER_FUNCTION_CALL] + 'EVEN': ['', ' % 2 == 0', PHP.ORDER_MODULUS, PHP.ORDER_EQUALITY], + 'ODD': ['', ' % 2 == 1', PHP.ORDER_MODULUS, PHP.ORDER_EQUALITY], + 'WHOLE': ['is_int(', ')', PHP.ORDER_NONE, PHP.ORDER_FUNCTION_CALL], + 'POSITIVE': ['', ' > 0', PHP.ORDER_RELATIONAL, PHP.ORDER_RELATIONAL], + 'NEGATIVE': ['', ' < 0', PHP.ORDER_RELATIONAL, PHP.ORDER_RELATIONAL], + 'DIVISIBLE_BY': [null, null, PHP.ORDER_MODULUS, PHP.ORDER_EQUALITY], + 'PRIME': [null, null, PHP.ORDER_NONE, PHP.ORDER_FUNCTION_CALL], }; const dropdownProperty = block.getFieldValue('PROPERTY'); const [prefix, suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty]; @@ -165,27 +158,26 @@ PHP['math_number_property'] = function(block) { let code; if (dropdownProperty === 'PRIME') { // Prime is a special case as it is not a one-liner test. - const functionName = PHP.provideFunction_( - 'math_isPrime', - ['function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($n) {', - ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' if ($n == 2 || $n == 3) {', - ' return true;', - ' }', - ' // False if n is NaN, negative, is 1, or not whole.', - ' // And false if n is divisible by 2 or 3.', - ' if (!is_numeric($n) || $n <= 1 || $n % 1 != 0 || $n % 2 == 0 ||' + - ' $n % 3 == 0) {', - ' return false;', - ' }', - ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for ($x = 6; $x <= sqrt($n) + 1; $x += 6) {', - ' if ($n % ($x - 1) == 0 || $n % ($x + 1) == 0) {', - ' return false;', - ' }', - ' }', - ' return true;', - '}']); + const functionName = PHP.provideFunction_('math_isPrime', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($n) { + // https://en.wikipedia.org/wiki/Primality_test#Naive_methods + if ($n == 2 || $n == 3) { + return true; + } + // False if n is NaN, negative, is 1, or not whole. + // And false if n is divisible by 2 or 3. + if (!is_numeric($n) || $n <= 1 || $n % 1 != 0 || $n % 2 == 0 || $n % 3 == 0) { + return false; + } + // Check all the numbers of form 6k +/- 1, up to sqrt(n). + for ($x = 6; $x <= sqrt($n) + 1; $x += 6) { + if ($n % ($x - 1) == 0 || $n % ($x + 1) == 0) { + return false; + } + } + return true; +} +`); code = functionName + '(' + numberToCheck + ')'; } else if (dropdownProperty === 'DIVISIBLE_BY') { const divisor = PHP.valueToCode(block, 'DIVISOR', @@ -236,23 +228,23 @@ PHP['math_on_list'] = function(block) { code = 'max(' + list + ')'; break; case 'AVERAGE': { - const functionName = PHP.provideFunction_('math_mean', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($myList) {', - ' return array_sum($myList) / count($myList);', '}' - ]); + const functionName = PHP.provideFunction_('math_mean', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($myList) { + return array_sum($myList) / count($myList); +} +`); list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || 'array()'; code = functionName + '(' + list + ')'; break; } case 'MEDIAN': { - const functionName = PHP.provideFunction_('math_median', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($arr) {', - ' sort($arr,SORT_NUMERIC);', - ' return (count($arr) % 2) ? $arr[floor(count($arr)/2)] : ', - ' ($arr[floor(count($arr)/2)] + $arr[floor(count($arr)/2)' + - ' - 1]) / 2;', - '}' - ]); + const functionName = PHP.provideFunction_('math_median', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($arr) { + sort($arr,SORT_NUMERIC); + return (count($arr) % 2) ? $arr[floor(count($arr) / 2)] : + ($arr[floor(count($arr) / 2)] + $arr[floor(count($arr) / 2) - 1]) / 2; +} +`); list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; break; @@ -261,36 +253,40 @@ PHP['math_on_list'] = function(block) { // 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 = PHP.provideFunction_('math_modes', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($values) {', - ' if (empty($values)) return array();', - ' $counts = array_count_values($values);', - ' arsort($counts); // Sort counts in descending order', - ' $modes = array_keys($counts, current($counts), true);', - ' return $modes;', '}' - ]); + const functionName = PHP.provideFunction_('math_modes', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($values) { + if (empty($values)) return array(); + $counts = array_count_values($values); + arsort($counts); // Sort counts in descending order + $modes = array_keys($counts, current($counts), true); + return $modes; +} +`); list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; break; } case 'STD_DEV': { - const functionName = PHP.provideFunction_('math_standard_deviation', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($numbers) {', - ' $n = count($numbers);', ' if (!$n) return null;', - ' $mean = array_sum($numbers) / count($numbers);', - ' foreach($numbers as $key => $num) $devs[$key] = ' + - 'pow($num - $mean, 2);', - ' return sqrt(array_sum($devs) / (count($devs) - 1));', '}' - ]); + const functionName = PHP.provideFunction_('math_standard_deviation', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($numbers) { + $n = count($numbers); + if (!$n) return null; + $mean = array_sum($numbers) / count($numbers); + foreach($numbers as $key => $num) $devs[$key] = pow($num - $mean, 2); + return sqrt(array_sum($devs) / (count($devs) - 1)); +} +`); list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; break; } case 'RANDOM': { - const functionName = PHP.provideFunction_('math_random_list', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($list) {', - ' $x = rand(0, count($list)-1);', ' return $list[$x];', '}' - ]); + const functionName = PHP.provideFunction_('math_random_list', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($list) { + $x = rand(0, count($list)-1); + return $list[$x]; +} +`); list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || '[]'; code = functionName + '(' + list + ')'; break; @@ -325,11 +321,14 @@ PHP['math_random_int'] = function(block) { // Random integer between [X] and [Y]. const argument0 = PHP.valueToCode(block, 'FROM', PHP.ORDER_NONE) || '0'; const argument1 = PHP.valueToCode(block, 'TO', PHP.ORDER_NONE) || '0'; - const functionName = PHP.provideFunction_('math_random_int', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($a, $b) {', - ' if ($a > $b) {', ' return rand($b, $a);', ' }', - ' return rand($a, $b);', '}' - ]); + const functionName = PHP.provideFunction_('math_random_int', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($a, $b) { + if ($a > $b) { + return rand($b, $a); + } + return rand($a, $b); +} +`); const code = functionName + '(' + argument0 + ', ' + argument1 + ')'; return [code, PHP.ORDER_FUNCTION_CALL]; }; diff --git a/generators/php/text.js b/generators/php/text.js index eadf4cf5b..bc62a95cf 100644 --- a/generators/php/text.js +++ b/generators/php/text.js @@ -32,22 +32,22 @@ PHP['text_multiline'] = function(block) { PHP['text_join'] = function(block) { // Create a string made up of any number of elements of any type. if (block.itemCount_ === 0) { - return ['\'\'', PHP.ORDER_ATOMIC]; + return ["''", PHP.ORDER_ATOMIC]; } else if (block.itemCount_ === 1) { - const element = PHP.valueToCode(block, 'ADD0', PHP.ORDER_NONE) || '\'\''; + const element = PHP.valueToCode(block, 'ADD0', PHP.ORDER_NONE) || "''"; const code = element; return [code, PHP.ORDER_NONE]; } else if (block.itemCount_ === 2) { const element0 = - PHP.valueToCode(block, 'ADD0', PHP.ORDER_STRING_CONCAT) || '\'\''; + PHP.valueToCode(block, 'ADD0', PHP.ORDER_STRING_CONCAT) || "''"; const element1 = - PHP.valueToCode(block, 'ADD1', PHP.ORDER_STRING_CONCAT) || '\'\''; + PHP.valueToCode(block, 'ADD1', PHP.ORDER_STRING_CONCAT) || "''"; const code = element0 + ' . ' + element1; return [code, PHP.ORDER_STRING_CONCAT]; } else { const elements = new Array(block.itemCount_); for (let i = 0; i < block.itemCount_; i++) { - elements[i] = PHP.valueToCode(block, 'ADD' + i, PHP.ORDER_NONE) || '\'\''; + elements[i] = PHP.valueToCode(block, 'ADD' + i, PHP.ORDER_NONE) || "''"; } const code = 'implode(\'\', array(' + elements.join(',') + '))'; return [code, PHP.ORDER_FUNCTION_CALL]; @@ -58,24 +58,27 @@ PHP['text_append'] = function(block) { // Append to a variable in place. const varName = PHP.nameDB_.getName(block.getFieldValue('VAR'), NameType.VARIABLE); - const value = PHP.valueToCode(block, 'TEXT', PHP.ORDER_ASSIGNMENT) || '\'\''; + const value = PHP.valueToCode(block, 'TEXT', PHP.ORDER_ASSIGNMENT) || "''"; return varName + ' .= ' + value + ';\n'; }; PHP['text_length'] = function(block) { // String or array length. - const functionName = PHP.provideFunction_('length', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value) {', - ' if (is_string($value)) {', ' return strlen($value);', ' } else {', - ' return count($value);', ' }', '}' - ]); - const text = PHP.valueToCode(block, 'VALUE', PHP.ORDER_NONE) || '\'\''; + const functionName = PHP.provideFunction_('length', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($value) { + if (is_string($value)) { + return strlen($value); + } + return count($value); +} +`); + const text = PHP.valueToCode(block, 'VALUE', PHP.ORDER_NONE) || "''"; return [functionName + '(' + text + ')', PHP.ORDER_FUNCTION_CALL]; }; PHP['text_isEmpty'] = function(block) { // Is the string null or array empty? - const text = PHP.valueToCode(block, 'VALUE', PHP.ORDER_NONE) || '\'\''; + const text = PHP.valueToCode(block, 'VALUE', PHP.ORDER_NONE) || "''"; return ['empty(' + text + ')', PHP.ORDER_FUNCTION_CALL]; }; @@ -83,8 +86,8 @@ PHP['text_indexOf'] = function(block) { // Search the text for a substring. const operator = block.getFieldValue('END') === 'FIRST' ? 'strpos' : 'strrpos'; - const substring = PHP.valueToCode(block, 'FIND', PHP.ORDER_NONE) || '\'\''; - const text = PHP.valueToCode(block, 'VALUE', PHP.ORDER_NONE) || '\'\''; + const substring = PHP.valueToCode(block, 'FIND', PHP.ORDER_NONE) || "''"; + const text = PHP.valueToCode(block, 'VALUE', PHP.ORDER_NONE) || "''"; let errorIndex = ' -1'; let indexAdjustment = ''; if (block.workspace.options.oneBasedIndex) { @@ -94,13 +97,12 @@ PHP['text_indexOf'] = function(block) { const functionName = PHP.provideFunction_( block.getFieldValue('END') === 'FIRST' ? 'text_indexOf' : 'text_lastIndexOf', - [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($text, $search) {', - ' $pos = ' + operator + '($text, $search);', - ' return $pos === false ? ' + errorIndex + ' : $pos' + - indexAdjustment + ';', - '}' - ]); + ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($text, $search) { + $pos = ${operator}($text, $search); + return $pos === false ? ${errorIndex} : $pos${indexAdjustment}; +} +`); const code = functionName + '(' + text + ', ' + substring + ')'; return [code, PHP.ORDER_FUNCTION_CALL]; }; @@ -109,7 +111,7 @@ PHP['text_charAt'] = function(block) { // Get letter at index. const where = block.getFieldValue('WHERE') || 'FROM_START'; const textOrder = (where === 'RANDOM') ? PHP.ORDER_NONE : PHP.ORDER_NONE; - const text = PHP.valueToCode(block, 'VALUE', textOrder) || '\'\''; + const text = PHP.valueToCode(block, 'VALUE', textOrder) || "''"; switch (where) { case 'FIRST': { const code = 'substr(' + text + ', 0, 1)'; @@ -130,10 +132,11 @@ PHP['text_charAt'] = function(block) { return [code, PHP.ORDER_FUNCTION_CALL]; } case 'RANDOM': { - const functionName = PHP.provideFunction_('text_random_letter', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($text) {', - ' return $text[rand(0, strlen($text) - 1)];', '}' - ]); + const functionName = PHP.provideFunction_('text_random_letter', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($text) { + return $text[rand(0, strlen($text) - 1)]; +} +`); const code = functionName + '(' + text + ')'; return [code, PHP.ORDER_FUNCTION_CALL]; } @@ -145,36 +148,35 @@ PHP['text_getSubstring'] = function(block) { // Get substring. const where1 = block.getFieldValue('WHERE1'); const where2 = block.getFieldValue('WHERE2'); - const text = PHP.valueToCode(block, 'STRING', PHP.ORDER_NONE) || '\'\''; + const text = PHP.valueToCode(block, 'STRING', PHP.ORDER_NONE) || "''"; if (where1 === 'FIRST' && where2 === 'LAST') { const code = text; return [code, PHP.ORDER_NONE]; } else { const at1 = PHP.getAdjusted(block, 'AT1'); const at2 = PHP.getAdjusted(block, 'AT2'); - const functionName = PHP.provideFunction_('text_get_substring', [ - 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + - '($text, $where1, $at1, $where2, $at2) {', - ' if ($where1 == \'FROM_END\') {', - ' $at1 = strlen($text) - 1 - $at1;', - ' } else if ($where1 == \'FIRST\') {', - ' $at1 = 0;', - ' } else if ($where1 != \'FROM_START\') {', - ' throw new Exception(\'Unhandled option (text_get_substring).\');', - ' }', - ' $length = 0;', - ' if ($where2 == \'FROM_START\') {', - ' $length = $at2 - $at1 + 1;', - ' } else if ($where2 == \'FROM_END\') {', - ' $length = strlen($text) - $at1 - $at2;', - ' } else if ($where2 == \'LAST\') {', - ' $length = strlen($text) - $at1;', - ' } else {', - ' throw new Exception(\'Unhandled option (text_get_substring).\');', - ' }', - ' return substr($text, $at1, $length);', - '}' - ]); + const functionName = PHP.provideFunction_('text_get_substring', ` +function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($text, $where1, $at1, $where2, $at2) { + if ($where1 == 'FROM_END') { + $at1 = strlen($text) - 1 - $at1; + } else if ($where1 == 'FIRST') { + $at1 = 0; + } else if ($where1 != 'FROM_START') { + throw new Exception('Unhandled option (text_get_substring).'); + } + $length = 0; + if ($where2 == 'FROM_START') { + $length = $at2 - $at1 + 1; + } else if ($where2 == 'FROM_END') { + $length = strlen($text) - $at1 - $at2; + } else if ($where2 == 'LAST') { + $length = strlen($text) - $at1; + } else { + throw new Exception('Unhandled option (text_get_substring).'); + } + return substr($text, $at1, $length); +} +`); const code = functionName + '(' + text + ', \'' + where1 + '\', ' + at1 + ', \'' + where2 + '\', ' + at2 + ')'; return [code, PHP.ORDER_FUNCTION_CALL]; @@ -183,7 +185,7 @@ PHP['text_getSubstring'] = function(block) { PHP['text_changeCase'] = function(block) { // Change capitalization. - const text = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || '\'\''; + const text = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || "''"; let code; if (block.getFieldValue('CASE') === 'UPPERCASE') { code = 'strtoupper(' + text + ')'; @@ -199,13 +201,13 @@ PHP['text_trim'] = function(block) { // Trim spaces. const OPERATORS = {'LEFT': 'ltrim', 'RIGHT': 'rtrim', 'BOTH': 'trim'}; const operator = OPERATORS[block.getFieldValue('MODE')]; - const text = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || '\'\''; + const text = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || "''"; return [operator + '(' + text + ')', PHP.ORDER_FUNCTION_CALL]; }; PHP['text_print'] = function(block) { // Print statement. - const msg = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || '\'\''; + const msg = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || "''"; return 'print(' + msg + ');\n'; }; @@ -217,7 +219,7 @@ PHP['text_prompt_ext'] = function(block) { msg = PHP.quote_(block.getFieldValue('TEXT')); } else { // External message. - msg = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || '\'\''; + msg = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || "''"; } let code = 'readline(' + msg + ')'; const toNumber = block.getFieldValue('TYPE') === 'NUMBER'; @@ -230,8 +232,8 @@ PHP['text_prompt_ext'] = function(block) { PHP['text_prompt'] = PHP['text_prompt_ext']; PHP['text_count'] = function(block) { - const text = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || '\'\''; - const sub = PHP.valueToCode(block, 'SUB', PHP.ORDER_NONE) || '\'\''; + const text = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || "''"; + const sub = PHP.valueToCode(block, 'SUB', PHP.ORDER_NONE) || "''"; const code = 'strlen(' + sub + ') === 0' + ' ? strlen(' + text + ') + 1' + ' : substr_count(' + text + ', ' + sub + ')'; @@ -239,15 +241,15 @@ PHP['text_count'] = function(block) { }; PHP['text_replace'] = function(block) { - const text = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || '\'\''; - const from = PHP.valueToCode(block, 'FROM', PHP.ORDER_NONE) || '\'\''; - const to = PHP.valueToCode(block, 'TO', PHP.ORDER_NONE) || '\'\''; + const text = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || "''"; + const from = PHP.valueToCode(block, 'FROM', PHP.ORDER_NONE) || "''"; + const to = PHP.valueToCode(block, 'TO', PHP.ORDER_NONE) || "''"; const code = 'str_replace(' + from + ', ' + to + ', ' + text + ')'; return [code, PHP.ORDER_FUNCTION_CALL]; }; PHP['text_reverse'] = function(block) { - const text = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || '\'\''; + const text = PHP.valueToCode(block, 'TEXT', PHP.ORDER_NONE) || "''"; const code = 'strrev(' + text + ')'; return [code, PHP.ORDER_FUNCTION_CALL]; }; diff --git a/generators/python/colour.js b/generators/python/colour.js index 30cb6f463..1a24c2780 100644 --- a/generators/python/colour.js +++ b/generators/python/colour.js @@ -29,13 +29,13 @@ Python['colour_random'] = function(block) { Python['colour_rgb'] = function(block) { // Compose a colour from RGB components expressed as percentages. - const functionName = Python.provideFunction_('colour_rgb', [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(r, g, b):', - ' r = round(min(100, max(0, r)) * 2.55)', - ' g = round(min(100, max(0, g)) * 2.55)', - ' b = round(min(100, max(0, b)) * 2.55)', - ' return \'#%02x%02x%02x\' % (r, g, b)' - ]); + const functionName = Python.provideFunction_('colour_rgb', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(r, g, b): + r = round(min(100, max(0, r)) * 2.55) + g = round(min(100, max(0, g)) * 2.55) + b = round(min(100, max(0, b)) * 2.55) + return \'#%02x%02x%02x\' % (r, g, b) +`); const r = Python.valueToCode(block, 'RED', Python.ORDER_NONE) || 0; const g = Python.valueToCode(block, 'GREEN', Python.ORDER_NONE) || 0; const b = Python.valueToCode(block, 'BLUE', Python.ORDER_NONE) || 0; @@ -45,17 +45,17 @@ Python['colour_rgb'] = function(block) { Python['colour_blend'] = function(block) { // Blend two colours together. - const functionName = Python.provideFunction_('colour_blend', [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(colour1, colour2, ratio):', - ' r1, r2 = int(colour1[1:3], 16), int(colour2[1:3], 16)', - ' g1, g2 = int(colour1[3:5], 16), int(colour2[3:5], 16)', - ' b1, b2 = int(colour1[5:7], 16), int(colour2[5:7], 16)', - ' ratio = min(1, max(0, ratio))', - ' r = round(r1 * (1 - ratio) + r2 * ratio)', - ' g = round(g1 * (1 - ratio) + g2 * ratio)', - ' b = round(b1 * (1 - ratio) + b2 * ratio)', - ' return \'#%02x%02x%02x\' % (r, g, b)' - ]); + const functionName = Python.provideFunction_('colour_blend', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(colour1, colour2, ratio): + r1, r2 = int(colour1[1:3], 16), int(colour2[1:3], 16) + g1, g2 = int(colour1[3:5], 16), int(colour2[3:5], 16) + b1, b2 = int(colour1[5:7], 16), int(colour2[5:7], 16) + ratio = min(1, max(0, ratio)) + r = round(r1 * (1 - ratio) + r2 * ratio) + g = round(g1 * (1 - ratio) + g2 * ratio) + b = round(b1 * (1 - ratio) + b2 * ratio) + return \'#%02x%02x%02x\' % (r, g, b) +`); const colour1 = Python.valueToCode(block, 'COLOUR1', Python.ORDER_NONE) || '\'#000000\''; const colour2 = diff --git a/generators/python/lists.js b/generators/python/lists.js index 607f0671b..7026162ae 100644 --- a/generators/python/lists.js +++ b/generators/python/lists.js @@ -57,7 +57,7 @@ Python['lists_isEmpty'] = function(block) { Python['lists_indexOf'] = function(block) { // Find an item in the list. const item = Python.valueToCode(block, 'FIND', Python.ORDER_NONE) || '[]'; - const list = Python.valueToCode(block, 'VALUE', Python.ORDER_NONE) || '\'\''; + const list = Python.valueToCode(block, 'VALUE', Python.ORDER_NONE) || "''"; let errorIndex = ' -1'; let firstIndexAdjustment = ''; let lastIndexAdjustment = ' - 1'; @@ -68,21 +68,22 @@ Python['lists_indexOf'] = function(block) { lastIndexAdjustment = ''; } + let functionName; if (block.getFieldValue('END') === 'FIRST') { - const functionName = Python.provideFunction_('first_index', [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(my_list, elem):', - ' try: index = my_list.index(elem)' + firstIndexAdjustment, - ' except: index =' + errorIndex, ' return index' - ]); - const code = functionName + '(' + list + ', ' + item + ')'; - return [code, Python.ORDER_FUNCTION_CALL]; + functionName = Python.provideFunction_('first_index', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(my_list, elem): + try: index = my_list.index(elem)${firstIndexAdjustment} + except: index =${errorIndex} + return index +`); + } else { + functionName = Python.provideFunction_('last_index', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(my_list, elem): + try: index = len(my_list) - my_list[::-1].index(elem)${lastIndexAdjustment} + except: index =${errorIndex} + return index +`); } - const functionName = Python.provideFunction_('last_index', [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(my_list, elem):', - ' try: index = len(my_list) - my_list[::-1].index(elem)' + - lastIndexAdjustment, - ' except: index =' + errorIndex, ' return index' - ]); const code = functionName + '(' + list + ', ' + item + ')'; return [code, Python.ORDER_FUNCTION_CALL]; }; @@ -152,11 +153,11 @@ Python['lists_getIndex'] = function(block) { return [code, Python.ORDER_FUNCTION_CALL]; } else { const functionName = - Python.provideFunction_('lists_remove_random_item', [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(myList):', - ' x = int(random.random() * len(myList))', - ' return myList.pop(x)' - ]); + Python.provideFunction_('lists_remove_random_item', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(myList): + x = int(random.random() * len(myList)) + return myList.pop(x) +`); const code = functionName + '(' + list + ')'; if (mode === 'GET_REMOVE') { return [code, Python.ORDER_FUNCTION_CALL]; @@ -294,15 +295,22 @@ Python['lists_sort'] = function(block) { const list = (Python.valueToCode(block, 'LIST', Python.ORDER_NONE) || '[]'); const type = block.getFieldValue('TYPE'); const reverse = block.getFieldValue('DIRECTION') === '1' ? 'False' : 'True'; - const sortFunctionName = Python.provideFunction_('lists_sort', [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(my_list, type, reverse):', - ' def try_float(s):', ' try:', ' return float(s)', ' except:', - ' return 0', ' key_funcs = {', ' "NUMERIC": try_float,', - ' "TEXT": str,', ' "IGNORE_CASE": lambda s: str(s).lower()', ' }', - ' key_func = key_funcs[type]', - ' list_cpy = list(my_list)', // Clone the list. - ' return sorted(list_cpy, key=key_func, reverse=reverse)' - ]); + const sortFunctionName = Python.provideFunction_('lists_sort', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(my_list, type, reverse): + def try_float(s): + try: + return float(s) + except: + return 0 + key_funcs = { + "NUMERIC": try_float, + "TEXT": str, + "IGNORE_CASE": lambda s: str(s).lower() + } + key_func = key_funcs[type] + list_cpy = list(my_list) + return sorted(list_cpy, key=key_func, reverse=reverse) +`); const code = sortFunctionName + '(' + list + ', "' + type + '", ' + reverse + ')'; @@ -315,14 +323,14 @@ Python['lists_split'] = function(block) { let code; if (mode === 'SPLIT') { const value_input = - Python.valueToCode(block, 'INPUT', Python.ORDER_MEMBER) || '\'\''; + Python.valueToCode(block, 'INPUT', Python.ORDER_MEMBER) || "''"; const value_delim = Python.valueToCode(block, 'DELIM', Python.ORDER_NONE); code = value_input + '.split(' + value_delim + ')'; } else if (mode === 'JOIN') { const value_input = Python.valueToCode(block, 'INPUT', Python.ORDER_NONE) || '[]'; const value_delim = - Python.valueToCode(block, 'DELIM', Python.ORDER_MEMBER) || '\'\''; + Python.valueToCode(block, 'DELIM', Python.ORDER_MEMBER) || "''"; code = value_delim + '.join(' + value_input + ')'; } else { throw Error('Unknown mode: ' + mode); diff --git a/generators/python/loops.js b/generators/python/loops.js index aff791d21..b3c267f86 100644 --- a/generators/python/loops.js +++ b/generators/python/loops.js @@ -70,16 +70,20 @@ Python['controls_for'] = function(block) { // Helper functions. const defineUpRange = function() { - return Python.provideFunction_('upRange', [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(start, stop, step):', - ' while start <= stop:', ' yield start', ' start += abs(step)' - ]); + return Python.provideFunction_('upRange', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(start, stop, step): + while start <= stop: + yield start + start += abs(step) +`); }; const defineDownRange = function() { - return Python.provideFunction_('downRange', [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(start, stop, step):', - ' while start >= stop:', ' yield start', ' start -= abs(step)' - ]); + return Python.provideFunction_('downRange', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(start, stop, step): + while start >= stop: + yield start + start -= abs(step) +`); }; // Arguments are legal Python code (numbers or strings returned by scrub()). const generateUpDownRange = function(start, end, inc) { diff --git a/generators/python/math.js b/generators/python/math.js index 93d4b8355..ca23c1770 100644 --- a/generators/python/math.js +++ b/generators/python/math.js @@ -41,7 +41,7 @@ Python['math_arithmetic'] = function(block) { 'MINUS': [' - ', Python.ORDER_ADDITIVE], 'MULTIPLY': [' * ', Python.ORDER_MULTIPLICATIVE], 'DIVIDE': [' / ', Python.ORDER_MULTIPLICATIVE], - 'POWER': [' ** ', Python.ORDER_EXPONENTIATION] + 'POWER': [' ** ', Python.ORDER_EXPONENTIATION], }; const tuple = OPERATORS[block.getFieldValue('OP')]; const operator = tuple[0]; @@ -142,7 +142,7 @@ Python['math_constant'] = function(block) { 'GOLDEN_RATIO': ['(1 + math.sqrt(5)) / 2', Python.ORDER_MULTIPLICATIVE], 'SQRT2': ['math.sqrt(2)', Python.ORDER_MEMBER], 'SQRT1_2': ['math.sqrt(1.0 / 2)', Python.ORDER_MEMBER], - 'INFINITY': ['float(\'inf\')', Python.ORDER_ATOMIC] + 'INFINITY': ['float(\'inf\')', Python.ORDER_ATOMIC], }; const constant = block.getFieldValue('CONSTANT'); if (constant !== 'INFINITY') { @@ -155,20 +155,15 @@ Python['math_number_property'] = function(block) { // 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 = { - 'EVEN': [' % 2 == 0', Python.ORDER_MULTIPLICATIVE, - Python.ORDER_RELATIONAL], - 'ODD': [' % 2 == 1', Python.ORDER_MULTIPLICATIVE, - Python.ORDER_RELATIONAL], + 'EVEN': [' % 2 == 0', Python.ORDER_MULTIPLICATIVE, Python.ORDER_RELATIONAL], + 'ODD': [' % 2 == 1', Python.ORDER_MULTIPLICATIVE, Python.ORDER_RELATIONAL], 'WHOLE': [' % 1 == 0', Python.ORDER_MULTIPLICATIVE, Python.ORDER_RELATIONAL], - 'POSITIVE': [' > 0', Python.ORDER_RELATIONAL, - Python.ORDER_RELATIONAL], - 'NEGATIVE': [' < 0', Python.ORDER_RELATIONAL, - Python.ORDER_RELATIONAL], + 'POSITIVE': [' > 0', Python.ORDER_RELATIONAL, Python.ORDER_RELATIONAL], + 'NEGATIVE': [' < 0', Python.ORDER_RELATIONAL, Python.ORDER_RELATIONAL], 'DIVISIBLE_BY': [null, Python.ORDER_MULTIPLICATIVE, Python.ORDER_RELATIONAL], - 'PRIME': [null, Python.ORDER_NONE, - Python.ORDER_FUNCTION_CALL] + 'PRIME': [null, Python.ORDER_NONE, Python.ORDER_FUNCTION_CALL], } const dropdownProperty = block.getFieldValue('PROPERTY'); const [suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty]; @@ -180,27 +175,26 @@ Python['math_number_property'] = function(block) { Python.definitions_['import_math'] = 'import math'; Python.definitions_['from_numbers_import_Number'] = 'from numbers import Number'; - const functionName = Python.provideFunction_( - 'math_isPrime', - ['def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(n):', - ' # https://en.wikipedia.org/wiki/Primality_test#Naive_methods', - ' # If n is not a number but a string, try parsing it.', - ' if not isinstance(n, Number):', - ' try:', - ' n = float(n)', - ' except:', - ' return False', - ' if n == 2 or n == 3:', - ' return True', - ' # False if n is negative, is 1, or not whole,' + - ' or if n is divisible by 2 or 3.', - ' if n <= 1 or n % 1 != 0 or n % 2 == 0 or n % 3 == 0:', - ' return False', - ' # Check all the numbers of form 6k +/- 1, up to sqrt(n).', - ' for x in range(6, int(math.sqrt(n)) + 2, 6):', - ' if n % (x - 1) == 0 or n % (x + 1) == 0:', - ' return False', - ' return True']); + const functionName = Python.provideFunction_('math_isPrime', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(n): + # https://en.wikipedia.org/wiki/Primality_test#Naive_methods + # If n is not a number but a string, try parsing it. + if not isinstance(n, Number): + try: + n = float(n) + except: + return False + if n == 2 or n == 3: + return True + # False if n is negative, is 1, or not whole, or if n is divisible by 2 or 3. + if n <= 1 or n % 1 != 0 or n % 2 == 0 or n % 3 == 0: + return False + # Check all the numbers of form 6k +/- 1, up to sqrt(n). + for x in range(6, int(math.sqrt(n)) + 2, 6): + if n % (x - 1) == 0 or n % (x + 1) == 0: + return False + return True +`); code = functionName + '(' + numberToCheck + ')'; } else if (dropdownProperty === 'DIVISIBLE_BY') { const divisor = Python.valueToCode(block, 'DIVISOR', @@ -251,71 +245,72 @@ Python['math_on_list'] = function(block) { case 'AVERAGE': { Python.definitions_['from_numbers_import_Number'] = 'from numbers import Number'; - const functionName = Python.provideFunction_( - 'math_mean', - // This operation excludes null and values that aren't int or float: - // math_mean([null, null, "aString", 1, 9]) -> 5.0 - [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(myList):', - ' localList = [e for e in myList if isinstance(e, Number)]', - ' if not localList: return', - ' return float(sum(localList)) / len(localList)' - ]); + // This operation excludes null and values that aren't int or float: + // math_mean([null, null, "aString", 1, 9]) -> 5.0 + const functionName = Python.provideFunction_('math_mean', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(myList): + localList = [e for e in myList if isinstance(e, Number)] + if not localList: return + return float(sum(localList)) / len(localList) +`); code = functionName + '(' + list + ')'; break; } case 'MEDIAN': { Python.definitions_['from_numbers_import_Number'] = 'from numbers import Number'; - const functionName = Python.provideFunction_( - 'math_median', - // This operation excludes null values: - // math_median([null, null, 1, 3]) -> 2.0 - [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(myList):', - ' localList = sorted([e for e in myList if isinstance(e, Number)])', - ' if not localList: return', ' if len(localList) % 2 == 0:', - ' return (localList[len(localList) // 2 - 1] + ' + - 'localList[len(localList) // 2]) / 2.0', - ' else:', ' return localList[(len(localList) - 1) // 2]' - ]); + // This operation excludes null values: + // math_median([null, null, 1, 3]) -> 2.0 + const functionName = Python.provideFunction_( 'math_median', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(myList): + localList = sorted([e for e in myList if isinstance(e, Number)]) + if not localList: return + if len(localList) % 2 == 0: + return (localList[len(localList) // 2 - 1] + localList[len(localList) // 2]) / 2.0 + else: + return localList[(len(localList) - 1) // 2] +`); code = functionName + '(' + list + ')'; break; } case 'MODE': { - const functionName = Python.provideFunction_( - 'math_modes', - // 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] - [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(some_list):', - ' modes = []', - ' # Using a lists of [item, count] to keep count rather than dict', - ' # to avoid "unhashable" errors when the counted item is ' + - 'itself a list or dict.', - ' counts = []', ' maxCount = 1', ' for item in some_list:', - ' found = False', ' for count in counts:', - ' if count[0] == item:', ' count[1] += 1', - ' maxCount = max(maxCount, count[1])', - ' found = True', - ' if not found:', ' counts.append([item, 1])', - ' for counted_item, item_count in counts:', - ' if item_count == maxCount:', - ' modes.append(counted_item)', ' return modes' - ]); + // 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 = Python.provideFunction_('math_modes', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(some_list): + modes = [] + # Using a lists of [item, count] to keep count rather than dict + # to avoid "unhashable" errors when the counted item is itself a list or dict. + counts = [] + maxCount = 1 + for item in some_list: + found = False + for count in counts: + if count[0] == item: + count[1] += 1 + maxCount = max(maxCount, count[1]) + found = True + if not found: + counts.append([item, 1]) + for counted_item, item_count in counts: + if item_count == maxCount: + modes.append(counted_item) + return modes +`); code = functionName + '(' + list + ')'; break; } case 'STD_DEV': { Python.definitions_['import_math'] = 'import math'; - const functionName = Python.provideFunction_('math_standard_deviation', [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(numbers):', - ' n = len(numbers)', ' if n == 0: return', - ' mean = float(sum(numbers)) / n', - ' variance = sum((x - mean) ** 2 for x in numbers) / n', - ' return math.sqrt(variance)' - ]); + const functionName = Python.provideFunction_('math_standard_deviation', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(numbers): + n = len(numbers) + if n == 0: return + mean = float(sum(numbers)) / n + variance = sum((x - mean) ** 2 for x in numbers) / n + return math.sqrt(variance) +`); code = functionName + '(' + list + ')'; break; } diff --git a/generators/python/text.js b/generators/python/text.js index 4edb5d3b2..a097cbee2 100644 --- a/generators/python/text.js +++ b/generators/python/text.js @@ -55,18 +55,18 @@ Python['text_join'] = function(block) { // Should we allow joining by '-' or ',' or any other characters? switch (block.itemCount_) { case 0: - return ['\'\'', Python.ORDER_ATOMIC]; + return ["''", Python.ORDER_ATOMIC]; case 1: { const element = - Python.valueToCode(block, 'ADD0', Python.ORDER_NONE) || '\'\''; + Python.valueToCode(block, 'ADD0', Python.ORDER_NONE) || "''"; const codeAndOrder = forceString(element); return codeAndOrder; } case 2: { const element0 = - Python.valueToCode(block, 'ADD0', Python.ORDER_NONE) || '\'\''; + Python.valueToCode(block, 'ADD0', Python.ORDER_NONE) || "''"; const element1 = - Python.valueToCode(block, 'ADD1', Python.ORDER_NONE) || '\'\''; + Python.valueToCode(block, 'ADD1', Python.ORDER_NONE) || "''"; const code = forceString(element0)[0] + ' + ' + forceString(element1)[0]; return [code, Python.ORDER_ADDITIVE]; } @@ -74,7 +74,7 @@ Python['text_join'] = function(block) { const elements = []; for (let i = 0; i < block.itemCount_; i++) { elements[i] = - Python.valueToCode(block, 'ADD' + i, Python.ORDER_NONE) || '\'\''; + Python.valueToCode(block, 'ADD' + i, Python.ORDER_NONE) || "''"; } const tempVar = Python.nameDB_.getDistinctName('x', NameType.VARIABLE); const code = '\'\'.join([str(' + tempVar + ') for ' + tempVar + ' in [' + @@ -88,19 +88,19 @@ Python['text_append'] = function(block) { // Append to a variable in place. const varName = Python.nameDB_.getName(block.getFieldValue('VAR'), NameType.VARIABLE); - const value = Python.valueToCode(block, 'TEXT', Python.ORDER_NONE) || '\'\''; + const value = Python.valueToCode(block, 'TEXT', Python.ORDER_NONE) || "''"; return varName + ' = str(' + varName + ') + ' + forceString(value)[0] + '\n'; }; Python['text_length'] = function(block) { // Is the string null or array empty? - const text = Python.valueToCode(block, 'VALUE', Python.ORDER_NONE) || '\'\''; + const text = Python.valueToCode(block, 'VALUE', Python.ORDER_NONE) || "''"; return ['len(' + text + ')', Python.ORDER_FUNCTION_CALL]; }; Python['text_isEmpty'] = function(block) { // Is the string null or array empty? - const text = Python.valueToCode(block, 'VALUE', Python.ORDER_NONE) || '\'\''; + const text = Python.valueToCode(block, 'VALUE', Python.ORDER_NONE) || "''"; const code = 'not len(' + text + ')'; return [code, Python.ORDER_LOGICAL_NOT]; }; @@ -110,9 +110,9 @@ Python['text_indexOf'] = function(block) { // Should we allow for non-case sensitive??? const operator = block.getFieldValue('END') === 'FIRST' ? 'find' : 'rfind'; const substring = - Python.valueToCode(block, 'FIND', Python.ORDER_NONE) || '\'\''; + Python.valueToCode(block, 'FIND', Python.ORDER_NONE) || "''"; const text = - Python.valueToCode(block, 'VALUE', Python.ORDER_MEMBER) || '\'\''; + Python.valueToCode(block, 'VALUE', Python.ORDER_MEMBER) || "''"; const code = text + '.' + operator + '(' + substring + ')'; if (block.workspace.options.oneBasedIndex) { return [code + ' + 1', Python.ORDER_ADDITIVE]; @@ -126,7 +126,7 @@ Python['text_charAt'] = function(block) { const where = block.getFieldValue('WHERE') || 'FROM_START'; const textOrder = (where === 'RANDOM') ? Python.ORDER_NONE : Python.ORDER_MEMBER; - const text = Python.valueToCode(block, 'VALUE', textOrder) || '\'\''; + const text = Python.valueToCode(block, 'VALUE', textOrder) || "''"; switch (where) { case 'FIRST': { const code = text + '[0]'; @@ -148,10 +148,11 @@ Python['text_charAt'] = function(block) { } case 'RANDOM': { Python.definitions_['import_random'] = 'import random'; - const functionName = Python.provideFunction_('text_random_letter', [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(text):', - ' x = int(random.random() * len(text))', ' return text[x];' - ]); + const functionName = Python.provideFunction_('text_random_letter', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(text): + x = int(random.random() * len(text)) + return text[x] +`); const code = functionName + '(' + text + ')'; return [code, Python.ORDER_FUNCTION_CALL]; } @@ -164,7 +165,7 @@ Python['text_getSubstring'] = function(block) { const where1 = block.getFieldValue('WHERE1'); const where2 = block.getFieldValue('WHERE2'); const text = - Python.valueToCode(block, 'STRING', Python.ORDER_MEMBER) || '\'\''; + Python.valueToCode(block, 'STRING', Python.ORDER_MEMBER) || "''"; let at1; switch (where1) { case 'FROM_START': @@ -217,7 +218,7 @@ Python['text_changeCase'] = function(block) { 'TITLECASE': '.title()' }; const operator = OPERATORS[block.getFieldValue('CASE')]; - const text = Python.valueToCode(block, 'TEXT', Python.ORDER_MEMBER) || '\'\''; + const text = Python.valueToCode(block, 'TEXT', Python.ORDER_MEMBER) || "''"; const code = text + operator; return [code, Python.ORDER_FUNCTION_CALL]; }; @@ -230,30 +231,33 @@ Python['text_trim'] = function(block) { 'BOTH': '.strip()' }; const operator = OPERATORS[block.getFieldValue('MODE')]; - const text = Python.valueToCode(block, 'TEXT', Python.ORDER_MEMBER) || '\'\''; + const text = Python.valueToCode(block, 'TEXT', Python.ORDER_MEMBER) || "''"; const code = text + operator; return [code, Python.ORDER_FUNCTION_CALL]; }; Python['text_print'] = function(block) { // Print statement. - const msg = Python.valueToCode(block, 'TEXT', Python.ORDER_NONE) || '\'\''; + const msg = Python.valueToCode(block, 'TEXT', Python.ORDER_NONE) || "''"; return 'print(' + msg + ')\n'; }; Python['text_prompt_ext'] = function(block) { // Prompt function. - const functionName = Python.provideFunction_('text_prompt', [ - 'def ' + Python.FUNCTION_NAME_PLACEHOLDER_ + '(msg):', ' try:', - ' return raw_input(msg)', ' except NameError:', ' return input(msg)' - ]); + const functionName = Python.provideFunction_('text_prompt', ` +def ${Python.FUNCTION_NAME_PLACEHOLDER_}(msg): + try: + return raw_input(msg) + except NameError: + return input(msg) +`); let msg; if (block.getField('TEXT')) { // Internal message. msg = Python.quote_(block.getFieldValue('TEXT')); } else { // External message. - msg = Python.valueToCode(block, 'TEXT', Python.ORDER_NONE) || '\'\''; + msg = Python.valueToCode(block, 'TEXT', Python.ORDER_NONE) || "''"; } let code = functionName + '(' + msg + ')'; const toNumber = block.getFieldValue('TYPE') === 'NUMBER'; @@ -266,22 +270,22 @@ Python['text_prompt_ext'] = function(block) { Python['text_prompt'] = Python['text_prompt_ext']; Python['text_count'] = function(block) { - const text = Python.valueToCode(block, 'TEXT', Python.ORDER_MEMBER) || '\'\''; - const sub = Python.valueToCode(block, 'SUB', Python.ORDER_NONE) || '\'\''; + const text = Python.valueToCode(block, 'TEXT', Python.ORDER_MEMBER) || "''"; + const sub = Python.valueToCode(block, 'SUB', Python.ORDER_NONE) || "''"; const code = text + '.count(' + sub + ')'; return [code, Python.ORDER_FUNCTION_CALL]; }; Python['text_replace'] = function(block) { - const text = Python.valueToCode(block, 'TEXT', Python.ORDER_MEMBER) || '\'\''; - const from = Python.valueToCode(block, 'FROM', Python.ORDER_NONE) || '\'\''; - const to = Python.valueToCode(block, 'TO', Python.ORDER_NONE) || '\'\''; + const text = Python.valueToCode(block, 'TEXT', Python.ORDER_MEMBER) || "''"; + const from = Python.valueToCode(block, 'FROM', Python.ORDER_NONE) || "''"; + const to = Python.valueToCode(block, 'TO', Python.ORDER_NONE) || "''"; const code = text + '.replace(' + from + ', ' + to + ')'; return [code, Python.ORDER_MEMBER]; }; Python['text_reverse'] = function(block) { - const text = Python.valueToCode(block, 'TEXT', Python.ORDER_MEMBER) || '\'\''; + const text = Python.valueToCode(block, 'TEXT', Python.ORDER_MEMBER) || "''"; const code = text + '[::-1]'; return [code, Python.ORDER_MEMBER]; }; diff --git a/tests/generators/golden/generated.dart b/tests/generators/golden/generated.dart index c067942e1..a47886588 100644 --- a/tests/generators/golden/generated.dart +++ b/tests/generators/golden/generated.dart @@ -1384,10 +1384,10 @@ void test_split() { List lists_sort(List list, String type, int direction) { var compareFuncs = { - "NUMERIC": (a, b) => (direction * a.compareTo(b)).toInt(), - "TEXT": (a, b) => direction * a.toString().compareTo(b.toString()), - "IGNORE_CASE": - (a, b) => direction * + 'NUMERIC': (a, b) => (direction * a.compareTo(b)).toInt(), + 'TEXT': (a, b) => direction * a.toString().compareTo(b.toString()), + 'IGNORE_CASE': + (a, b) => direction * a.toString().toLowerCase().compareTo(b.toString().toLowerCase()) }; list = new List.from(list); diff --git a/tests/generators/golden/generated.js b/tests/generators/golden/generated.js index 019d51d19..48a4e20ac 100644 --- a/tests/generators/golden/generated.js +++ b/tests/generators/golden/generated.js @@ -904,8 +904,8 @@ function test_text_reverse() { } function textReplace(haystack, needle, replacement) { - needle = needle.replace(/([-()\[\]{}+?*.$\^|,:# b.toString() ? 1 : -1; }, - "IGNORE_CASE": function(a, b) { + 'IGNORE_CASE': function(a, b) { return a.toString().toLowerCase() > b.toString().toLowerCase() ? 1 : -1; }, }; var compare = compareFuncs[type]; - return function(a, b) { return compare(a, b) * direction; } + return function(a, b) { return compare(a, b) * direction; }; } // Tests the "alphabetic sort" block. diff --git a/tests/generators/golden/generated.lua b/tests/generators/golden/generated.lua index ad71d3aca..085a76070 100644 --- a/tests/generators/golden/generated.lua +++ b/tests/generators/golden/generated.lua @@ -525,23 +525,23 @@ function math_median(t) if #t == 0 then return 0 end - local temp={} + local temp = {} for _, v in ipairs(t) do - if type(v) == "number" then + if type(v) == 'number' then table.insert(temp, v) end end table.sort(temp) if #temp % 2 == 0 then - return (temp[#temp/2] + temp[(#temp/2)+1]) / 2 + return (temp[#temp / 2] + temp[(#temp / 2) + 1]) / 2 else - return temp[math.ceil(#temp/2)] + return temp[math.ceil(#temp / 2)] end end function math_modes(t) -- Source: http://lua-users.org/wiki/SimpleStats - local counts={} + local counts = {} for _, v in ipairs(t) do if counts[v] == nil then counts[v] = 1 @@ -555,7 +555,7 @@ function math_modes(t) biggestCount = v end end - local temp={} + local temp = {} for k, v in pairs(counts) do if v == biggestCount then table.insert(temp, k) @@ -707,9 +707,8 @@ function firstIndexOf(str, substr) local i = string.find(str, substr, 1, true) if i == nil then return 0 - else - return i end + return i end function lastIndexOf(str, substr) diff --git a/tests/generators/golden/generated.php b/tests/generators/golden/generated.php index 728273d52..4a16b6f5c 100644 --- a/tests/generators/golden/generated.php +++ b/tests/generators/golden/generated.php @@ -478,8 +478,8 @@ function math_mean($myList) { function math_median($arr) { sort($arr,SORT_NUMERIC); - return (count($arr) % 2) ? $arr[floor(count($arr)/2)] : - ($arr[floor(count($arr)/2)] + $arr[floor(count($arr)/2) - 1]) / 2; + return (count($arr) % 2) ? $arr[floor(count($arr) / 2)] : + ($arr[floor(count($arr) / 2)] + $arr[floor(count($arr) / 2) - 1]) / 2; } function math_modes($values) { @@ -603,9 +603,8 @@ function test_empty_text() { function length($value) { if (is_string($value)) { return strlen($value); - } else { - return count($value); } + return count($value); } // Tests the "length" block. @@ -1384,9 +1383,9 @@ function test_split() { function lists_sort($list, $type, $direction) { $sortCmpFuncs = array( - "NUMERIC" => "strnatcasecmp", - "TEXT" => "strcmp", - "IGNORE_CASE" => "strcasecmp" + 'NUMERIC' => 'strnatcasecmp', + 'TEXT' => 'strcmp', + 'IGNORE_CASE' => 'strcasecmp' ); $sortCmp = $sortCmpFuncs[$type]; $list2 = $list; diff --git a/tests/generators/golden/generated.py b/tests/generators/golden/generated.py index 12185286e..28a673b74 100644 --- a/tests/generators/golden/generated.py +++ b/tests/generators/golden/generated.py @@ -599,7 +599,7 @@ def test_find_text_complex(): def text_random_letter(text): x = int(random.random() * len(text)) - return text[x]; + return text[x] # Tests the "get letter" block with a variable. def test_get_text_simple():