From bdd9ee8f045713fb2d5445aaa19ba1710d68af8c Mon Sep 17 00:00:00 2001 From: tildeman Date: Mon, 1 May 2023 22:53:13 +0700 Subject: [PATCH] Minor additions --- blocks/datatypes.js | 45 +++++++++++++++++++++--- blocks/logic.js | 7 +++- blocks/text.js | 79 ++++++++++++++++++++++++++++++----------- categories/types.ts | 86 ++++++++++++++++++++++++++++++--------------- out/test.js | 20 +++++------ package.json | 2 +- toolbox.json | 20 +++++------ 7 files changed, 184 insertions(+), 75 deletions(-) diff --git a/blocks/datatypes.js b/blocks/datatypes.js index 4b20eb6..1943b4a 100644 --- a/blocks/datatypes.js +++ b/blocks/datatypes.js @@ -2,6 +2,26 @@ import * as Blockly from "blockly" import { removeType, identifyModelParams, findLegalName, rename } from "../categories/types.ts" const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([ + { + "type": "types_cast", + "message0": "cast %1 to %2", + "args0": [ + { + "type": "input_value", + "name": "VALUE" + }, + { + "type": "input_value", + "name": "TYPE", + "check": "Type" + } + ], + "output": null, + "style": "loop_blocks", + "tooltip": "Cast a value into a type.", + "inputsInline": true, + "helpUrl": "" + }, { "type": "types_list", "message0": "List of %1", @@ -59,14 +79,14 @@ const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([ "Integer", "Int" ], - [ - "Decimal", - "Float" - ], [ "Big decimal", "Double" ], + [ + "Decimal", + "Float" + ], [ "Character", "Char" @@ -80,7 +100,9 @@ const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([ ], "output": "Type", "style": "loop_blocks", - "tooltip": "A primitive data type.", + "extensions": [ + "types_tooltip" + ], "helpUrl": "" }, { @@ -173,6 +195,19 @@ const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([ }, ]) +const TOOLTIPS_BY_TYPE = { + "Integer": "An integer without constraints.", + "Int": "An integer from approximately -10^18 to 10^18.", + "Float": "A rough representation of decimals from -3*10^38 to 3*10^38.", + "Double": "A finer representation of decimals from -2*10^308 to 2*10^308.", + "Char": "A Unicode character.", + "Bool": "A truth value representing true or false." +} +Blockly.Extensions.register( + "types_tooltip", + Blockly.Extensions.buildTooltipForDropdown("TYPE", TOOLTIPS_BY_TYPE) +) + const productTypeMutator = { itemCount_: 2, diff --git a/blocks/logic.js b/blocks/logic.js index 7ff1e72..4c47598 100644 --- a/blocks/logic.js +++ b/blocks/logic.js @@ -3,8 +3,13 @@ import * as Blockly from "blockly" const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([ { "type": "logic_patternmatch", - "message0": "case %1 run %2", + "message0": "match %1 case %2 run %3", "args0": [ + { + "type": "input_value", + "name": "PATTERN", + "align": "RIGHT" + }, { "type": "input_value", "name": "CASE0", diff --git a/blocks/text.js b/blocks/text.js index 060301a..4d79d67 100644 --- a/blocks/text.js +++ b/blocks/text.js @@ -1,26 +1,6 @@ import * as Blockly from "blockly" const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([ - { - "type": "math_cast", - "message0": "cast %1 to %2", - "args0": [ - { - "type": "input_value", - "name": "VALUE" - }, - { - "type": "input_value", - "name": "TYPE", - "check": "Type" - } - ], - "output": "Number", - "style": "math_blocks", - "tooltip": "Convert a parsed value into a number of given type.", - "inputsInline": true, - "helpUrl": "" - }, { "type": "text_parse", "message0": "parse %1", @@ -52,7 +32,66 @@ const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([ "style": "text_blocks", "tooltip": "Converts a number to a string.", "helpUrl": "" + }, + { + "type": "text_charops", + "message0": "%1 of %2", + "args0": [ + { + "type": "field_dropdown", + "name": "ACTION", + "options": [ + [ + "order", + "ORD" + ], + [ + "character", + "CHR" + ] + ] + }, + { + "type": "input_value", + "name": "VALUE" + } + ], + "output": null, + "extensions": [ + "character_operations", + "character_validator_helper" + ], + "style": "text_blocks", + "tooltip": "", + "helpUrl": "" } ]) +const characterOperations = { + validator_: function(name) { + const block = this.getSourceBlock() + if (name === "ORD") { + block.getInput("VALUE").setCheck("String") + block.setOutput(true, "Number") + } + else if (name === "CHR") { + block.getInput("VALUE").setCheck("Number") + block.setOutput(true, "String") + } + return name + } +} +Blockly.Extensions.registerMixin( + "character_operations", + characterOperations +) + +function characterOperationsValidatorHelper() { + this.getField("ACTION").setValidator(this.validator_) +} +Blockly.Extensions.register( + "character_validator_helper", + characterOperationsValidatorHelper +) + Blockly.common.defineBlocks(blocks) diff --git a/categories/types.ts b/categories/types.ts index 5193d51..168a579 100644 --- a/categories/types.ts +++ b/categories/types.ts @@ -23,7 +23,8 @@ type TypeBlock = Blockly.BlockSvg & { type DataConstructorBlock = Blockly.BlockSvg & { isolate: () => void, - updateShape_: () => void + updateShape_: () => void, + renameDataConstructor: () => void } type DataConstructorGetBlock = Blockly.BlockSvg & { @@ -32,25 +33,50 @@ type DataConstructorGetBlock = Blockly.BlockSvg & { updateShape_: () => void } -// The workspace is the most important thing here. Ignore actual button things. -type FlyoutButtonWithWorkspace = { - workspace: Blockly.Workspace -} - var callback_idempotence = false // Let's start, shall we? -function typeFlyoutBlocks(workspace: TypeWorkspace): any[] { - const jsonList: any[] = [ +function typeFlyoutBlocks( + workspace: TypeWorkspace): Blockly.utils.toolbox.FlyoutDefinition { + let jsonList: any[] = [ { "kind": "block", "type": "types_primitive" }, + { + "kind": "block", + "type": "types_primitive", + "fields": { + "TYPE": "Double" + } + }, + { + "kind": "block", + "type": "types_primitive", + "fields": { + "TYPE": "Bool" + } + }, { "kind": "block", "type": "types_list" }, + { + "kind": "block", + "type": "types_list", + "inputs": { + "SUBTYPE": { + "block": { + "kind": "block", + "type": "types_primitive", + "fields": { + "TYPE": "Char" + } + } + } + } + }, { "kind": "block", "type": "types_tuple" @@ -62,6 +88,10 @@ function typeFlyoutBlocks(workspace: TypeWorkspace): any[] { "itemCount": 0 } }, + { + "kind": "block", + "type": "types_cast" + }, { "kind": "block", "type": "types_placeholder" @@ -121,8 +151,9 @@ function typeFlyoutBlocks(workspace: TypeWorkspace): any[] { return jsonList } -function updateDynamicCategory(workspace: TypeWorkspace): any[] { - let toolbox = [] +function updateDynamicCategory( + workspace: TypeWorkspace): Blockly.utils.toolbox.FlyoutDefinition { + let toolbox = [] const button = { "kind": "button", "text": "Create type...", @@ -137,25 +168,23 @@ function updateDynamicCategory(workspace: TypeWorkspace): any[] { } function addTypeCallback(bflyout: Blockly.FlyoutButton): void { - const targetwsp = bflyout.getTargetWorkspace() as TypeWorkspace - // Modern problems require modern solutions - const workspace = - (bflyout as unknown as FlyoutButtonWithWorkspace).workspace as TypeWorkspace + const workspace = bflyout.getTargetWorkspace() as TypeWorkspace + const flyoutwsp = workspace.getFlyout().getWorkspace() as TypeWorkspace if (!callback_idempotence){ - targetwsp.typeMap = workspace.typeMap = { + flyoutwsp.typeMap = workspace.typeMap = { types: {}, dataConstructors: {}, } - targetwsp.getTypeMap = workspace.getTypeMap = function(){ + flyoutwsp.getTypeMap = workspace.getTypeMap = function(){ return this.typeMap.types } - targetwsp.setTypeMap = workspace.setTypeMap = function(typename, value){ + flyoutwsp.setTypeMap = workspace.setTypeMap = function(typename, value){ this.typeMap.types[typename] = value } - targetwsp.getDataConsMap = workspace.getDataConsMap = function(){ + flyoutwsp.getDataConsMap = workspace.getDataConsMap = function(){ return this.typeMap.dataConstructors } - targetwsp.setDataConsMap = workspace.setDataConsMap = function(typename, value){ + flyoutwsp.setDataConsMap = workspace.setDataConsMap = function(typename, value){ this.typeMap.dataConstructors[typename] = value } } @@ -186,7 +215,7 @@ function addTypeCallback(bflyout: Blockly.FlyoutButton): void { )) // Update the dynamic category to include the new type - targetwsp.getToolbox().refreshSelection() + workspace.getToolbox().refreshSelection() } function generateModelParams(block: Blockly.Block | null): ITypeModel { @@ -275,9 +304,9 @@ function generateModelParams(block: Blockly.Block | null): ITypeModel { export function identifyModelParams(model: ITypeModel | null) : (string | string[] | null) { switch (model.kind) { - case 0: + case TypeKind.Placeholder: return null - case 1: + case TypeKind.Primitive: switch (model.name) { case "Int": case "Integer": @@ -291,10 +320,10 @@ export function identifyModelParams(model: ITypeModel | null) : (string | string case "Bool": return "Boolean" } - case 2: + case TypeKind.List: // Assuming strings are arrays of characters; this is not always the case. return ["Array", "String"] - case 3: + case TypeKind.Tuple: // Although there is no 1-tuple in Haskell and the fact that this library // is primarily designed with Haskell in mind, support for other languages // such as Python also exists, plus allowing users to define these is @@ -389,7 +418,8 @@ export function removeType(workspace: TypeWorkspace, typeName: string): void { delete workspace.getTypeMap()[typeName] } -export function typeFlyout(workspace: TypeWorkspace): any[] { +export function typeFlyout( + workspace: TypeWorkspace): Blockly.utils.toolbox.FlyoutDefinition { workspace.registerButtonCallback( "DATATYPE", addTypeCallback @@ -474,7 +504,7 @@ export function nameIsUsed(name: string, workspace: TypeWorkspace, opt_exclude?: } function nameIsLegal(name: string, workspace: TypeWorkspace, opt_exclude?: Blockly.Block): boolean { - return !nameIsUsed(name, workspace, opt_exclude); + return !nameIsUsed(name, workspace, opt_exclude) } export function findLegalName(name: string, block: Blockly.Block): string { @@ -485,7 +515,7 @@ export function findLegalName(name: string, block: Blockly.Block): string { name = name || "Something" while (!nameIsLegal(name, (block.workspace as TypeWorkspace), block)) { // Collision with another data constructor. - const r = name.match(/^(.*?)(\d+)$/); + const r = name.match(/^(.*?)(\d+)$/) if (!r) { name += "2" } else { @@ -496,7 +526,7 @@ export function findLegalName(name: string, block: Blockly.Block): string { } export function rename(this: Blockly.Field, name: string): string { - const block:any = this.getSourceBlock() + const block = this.getSourceBlock() as DataConstructorBlock // Strip leading and trailing whitespace. Beyond this, all names are legal. // Later in the code I'd have to enforce the first letter being capitalized. diff --git a/out/test.js b/out/test.js index 6efa195..b8c22c1 100644 --- a/out/test.js +++ b/out/test.js @@ -33,7 +33,7 @@ let toolbox = { "kind": "category", "name": "Colour", "categorystyle": "colour_category", - "contents":[ + "contents": [ { "kind": "block", "type": "colour_picker" @@ -56,7 +56,7 @@ let toolbox = { "kind": "category", "name": "Tuples", "colour": "65", - "contents":[ + "contents": [ { "kind": "block", "type": "tuples_create_empty" @@ -81,7 +81,7 @@ let toolbox = { "kind": "category", "name": "Text", "categorystyle": "text_category", - "contents":[ + "contents": [ { "kind": "block", "type": "text" @@ -98,6 +98,10 @@ let toolbox = { "kind": "block", "type": "text_length" }, + { + "kind": "block", + "type": "text_charops" + }, { "kind": "block", "type": "text_isEmpty" @@ -160,7 +164,7 @@ let toolbox = { "kind": "category", "name": "Logic", "categorystyle": "logic_category", - "contents":[ + "contents": [ { "kind": "block", "type": "logic_boolean" @@ -203,7 +207,7 @@ let toolbox = { "kind": "category", "name": "Math", "categorystyle": "math_category", - "contents":[ + "contents": [ { "kind": "block", "type": "math_number" @@ -255,10 +259,6 @@ let toolbox = { { "kind": "block", "type": "math_atan2" - }, - { - "kind": "block", - "type": "math_cast" } ] }, @@ -266,7 +266,7 @@ let toolbox = { "kind": "category", "name": "Lists", "categorystyle": "list_category", - "contents":[ + "contents": [ { "kind": "block", "type": "lists_create_empty" diff --git a/package.json b/package.json index 12076d3..d1c5e34 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "binalyi", - "version": "0.4.0", + "version": "0.4.1", "description": "(Purely) functional programming components for Blockly", "main": "index.js", "scripts": { diff --git a/toolbox.json b/toolbox.json index 408362e..d573b44 100644 --- a/toolbox.json +++ b/toolbox.json @@ -33,7 +33,7 @@ "kind": "category", "name": "Colour", "categorystyle": "colour_category", - "contents":[ + "contents": [ { "kind": "block", "type": "colour_picker" @@ -56,7 +56,7 @@ "kind": "category", "name": "Tuples", "colour": "65", - "contents":[ + "contents": [ { "kind": "block", "type": "tuples_create_empty" @@ -81,7 +81,7 @@ "kind": "category", "name": "Text", "categorystyle": "text_category", - "contents":[ + "contents": [ { "kind": "block", "type": "text" @@ -98,6 +98,10 @@ "kind": "block", "type": "text_length" }, + { + "kind": "block", + "type": "text_charops" + }, { "kind": "block", "type": "text_isEmpty" @@ -160,7 +164,7 @@ "kind": "category", "name": "Logic", "categorystyle": "logic_category", - "contents":[ + "contents": [ { "kind": "block", "type": "logic_boolean" @@ -203,7 +207,7 @@ "kind": "category", "name": "Math", "categorystyle": "math_category", - "contents":[ + "contents": [ { "kind": "block", "type": "math_number" @@ -255,10 +259,6 @@ { "kind": "block", "type": "math_atan2" - }, - { - "kind": "block", - "type": "math_cast" } ] }, @@ -266,7 +266,7 @@ "kind": "category", "name": "Lists", "categorystyle": "list_category", - "contents":[ + "contents": [ { "kind": "block", "type": "lists_create_empty"