@triflare/fx 2.1.0 → 2.2.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -2
- package/product.yml +1 -1
- package/src/catalog.js +11 -0
- package/src/compiler.js +63 -1
- package/vendor/pm-packager-dist/.fx-build.json +1 -1
- package/vendor/pm-vm-dist/.fx-build.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@triflare/fx",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0-alpha.1",
|
|
4
4
|
"description": "Code PenguinMod projects with text.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"./language": "./src/language.js",
|
|
19
19
|
"./lint": "./src/lint/analyze.js"
|
|
20
20
|
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public",
|
|
23
|
+
"provenance": true
|
|
24
|
+
},
|
|
21
25
|
"files": [
|
|
22
26
|
"bin",
|
|
23
27
|
"src",
|
|
@@ -64,10 +68,15 @@
|
|
|
64
68
|
},
|
|
65
69
|
"devDependencies": {
|
|
66
70
|
"@eslint/js": "^10.0.1",
|
|
71
|
+
"@semantic-release/changelog": "^7.0.0",
|
|
72
|
+
"@semantic-release/exec": "^7.1.0",
|
|
73
|
+
"@semantic-release/git": "^11.0.1",
|
|
74
|
+
"conventional-changelog-conventionalcommits": "^8.0.0",
|
|
67
75
|
"eslint": "^10.14.0",
|
|
68
76
|
"eslint-config-prettier": "^10.1.8",
|
|
69
77
|
"globals": "^17.13.0",
|
|
70
|
-
"prettier": "^3.9.10"
|
|
78
|
+
"prettier": "^3.9.10",
|
|
79
|
+
"semantic-release": "^25.0.9"
|
|
71
80
|
},
|
|
72
81
|
"overrides": {
|
|
73
82
|
"minimatch": "^11.0.4",
|
package/product.yml
CHANGED
package/src/catalog.js
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
* - `args` describes a hat's parameters (`field` or `input`).
|
|
14
14
|
* - `extension` names the providing extension for non-core opcodes.
|
|
15
15
|
* - `dynamicNext` / `stopBlock` flag blocks with special stack behaviour.
|
|
16
|
+
* - `extendable` marks a two-input block whose call may pass extra values;
|
|
17
|
+
* compilation folds them into a chain of the same block (see the
|
|
18
|
+
* compiler's `emitExtendableCall`).
|
|
16
19
|
*
|
|
17
20
|
* Input descriptors:
|
|
18
21
|
* NUMBER, STRING, BOOLEAN, ANGLE, COLOR, INTEGER, POSITIVE, WHOLE, SUBSTACK
|
|
@@ -387,21 +390,25 @@ const CORE_CATALOG = {
|
|
|
387
390
|
"operators.add": {
|
|
388
391
|
opcode: "operator_add",
|
|
389
392
|
kind: "reporter",
|
|
393
|
+
extendable: true,
|
|
390
394
|
inputs: { NUM1: NUMBER, NUM2: NUMBER },
|
|
391
395
|
},
|
|
392
396
|
"operators.subtract": {
|
|
393
397
|
opcode: "operator_subtract",
|
|
394
398
|
kind: "reporter",
|
|
399
|
+
extendable: true,
|
|
395
400
|
inputs: { NUM1: NUMBER, NUM2: NUMBER },
|
|
396
401
|
},
|
|
397
402
|
"operators.multiply": {
|
|
398
403
|
opcode: "operator_multiply",
|
|
399
404
|
kind: "reporter",
|
|
405
|
+
extendable: true,
|
|
400
406
|
inputs: { NUM1: NUMBER, NUM2: NUMBER },
|
|
401
407
|
},
|
|
402
408
|
"operators.divide": {
|
|
403
409
|
opcode: "operator_divide",
|
|
404
410
|
kind: "reporter",
|
|
411
|
+
extendable: true,
|
|
405
412
|
inputs: { NUM1: NUMBER, NUM2: NUMBER },
|
|
406
413
|
},
|
|
407
414
|
"operators.random": {
|
|
@@ -427,11 +434,13 @@ const CORE_CATALOG = {
|
|
|
427
434
|
"operators.and": {
|
|
428
435
|
opcode: "operator_and",
|
|
429
436
|
kind: "boolean",
|
|
437
|
+
extendable: true,
|
|
430
438
|
inputs: { OPERAND1: BOOLEAN, OPERAND2: BOOLEAN },
|
|
431
439
|
},
|
|
432
440
|
"operators.or": {
|
|
433
441
|
opcode: "operator_or",
|
|
434
442
|
kind: "boolean",
|
|
443
|
+
extendable: true,
|
|
435
444
|
inputs: { OPERAND1: BOOLEAN, OPERAND2: BOOLEAN },
|
|
436
445
|
},
|
|
437
446
|
"operators.not": {
|
|
@@ -442,6 +451,7 @@ const CORE_CATALOG = {
|
|
|
442
451
|
"operators.join": {
|
|
443
452
|
opcode: "operator_join",
|
|
444
453
|
kind: "reporter",
|
|
454
|
+
extendable: true,
|
|
445
455
|
inputs: { STRING1: STRING, STRING2: STRING },
|
|
446
456
|
},
|
|
447
457
|
"operators.letterOf": {
|
|
@@ -462,6 +472,7 @@ const CORE_CATALOG = {
|
|
|
462
472
|
"operators.mod": {
|
|
463
473
|
opcode: "operator_mod",
|
|
464
474
|
kind: "reporter",
|
|
475
|
+
extendable: true,
|
|
465
476
|
inputs: { NUM1: NUMBER, NUM2: NUMBER },
|
|
466
477
|
},
|
|
467
478
|
"operators.round": {
|
package/src/compiler.js
CHANGED
|
@@ -1076,10 +1076,22 @@ export class Compiler {
|
|
|
1076
1076
|
if (id && !this.requiredExtensions.has(id))
|
|
1077
1077
|
this.requiredExtensions.set(id, node.loc);
|
|
1078
1078
|
}
|
|
1079
|
-
const id = this.addBlock(ctx, { opcode: entry.opcode, parent: parentId });
|
|
1080
1079
|
const fieldNames = Object.keys(entry.fields || {});
|
|
1081
1080
|
const inputNames = Object.keys(entry.inputs || {});
|
|
1082
1081
|
|
|
1082
|
+
// Extendable blocks take any number of arguments. A call with more than
|
|
1083
|
+
// the block's own inputs folds into a left-associated chain of the same
|
|
1084
|
+
// two-input vanilla block, so `operators.join(a, b, c)` becomes
|
|
1085
|
+
// `join(join(a, b), c)` and still loads in plain Scratch.
|
|
1086
|
+
if (
|
|
1087
|
+
entry.extendable &&
|
|
1088
|
+
node.args.length > inputNames.length + fieldNames.length
|
|
1089
|
+
) {
|
|
1090
|
+
return this.emitExtendableCall(ctx, node, entry, parentId);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
const id = this.addBlock(ctx, { opcode: entry.opcode, parent: parentId });
|
|
1094
|
+
|
|
1083
1095
|
if (node.args.length > inputNames.length + fieldNames.length) {
|
|
1084
1096
|
this.fail(
|
|
1085
1097
|
`${node.name} expects at most ${inputNames.length + fieldNames.length} argument(s)`,
|
|
@@ -1160,6 +1172,56 @@ export class Compiler {
|
|
|
1160
1172
|
return { id, kind: entry.kind, entry };
|
|
1161
1173
|
}
|
|
1162
1174
|
|
|
1175
|
+
/**
|
|
1176
|
+
* Emit an extendable block called with more arguments than it has inputs.
|
|
1177
|
+
* The first two arguments fill the block and every further argument wraps
|
|
1178
|
+
* the result so far in another copy, keeping left-associative order:
|
|
1179
|
+
* `operators.subtract(10, 3, 2)` is `(10 - 3) - 2`.
|
|
1180
|
+
* @param {Context} ctx
|
|
1181
|
+
* @param {object} node
|
|
1182
|
+
* @param {object} entry
|
|
1183
|
+
* @param {string|null} parentId
|
|
1184
|
+
*/
|
|
1185
|
+
emitExtendableCall(ctx, node, entry, parentId) {
|
|
1186
|
+
const specs = entry.inputs || {};
|
|
1187
|
+
const inputNames = Object.keys(specs);
|
|
1188
|
+
if (Object.keys(entry.fields || {}).length || inputNames.length !== 2)
|
|
1189
|
+
this.fail(
|
|
1190
|
+
`Internal error: ${node.name} is marked extendable but is not a two-input block`,
|
|
1191
|
+
node.loc,
|
|
1192
|
+
);
|
|
1193
|
+
|
|
1194
|
+
const [first, second] = inputNames;
|
|
1195
|
+
// A chain position holds a reporter directly; boolean inputs take the
|
|
1196
|
+
// reporter without the shadow-value fallback.
|
|
1197
|
+
const chainInput = (spec, id) =>
|
|
1198
|
+
spec.type === "boolean" ? [2, id] : [3, id, this.defaultPrim(spec)];
|
|
1199
|
+
|
|
1200
|
+
let id = this.addBlock(ctx, { opcode: entry.opcode, parent: parentId });
|
|
1201
|
+
let inputs = {
|
|
1202
|
+
[first]: this.buildInputForSpec(specs[first], node.args[0], id),
|
|
1203
|
+
[second]: this.buildInputForSpec(specs[second], node.args[1], id),
|
|
1204
|
+
};
|
|
1205
|
+
ctx.target.blocks[id].inputs = inputs;
|
|
1206
|
+
this.reparentInputs(ctx, id, inputs);
|
|
1207
|
+
|
|
1208
|
+
for (let i = 2; i < node.args.length; i++) {
|
|
1209
|
+
const wrapId = this.addBlock(ctx, {
|
|
1210
|
+
opcode: entry.opcode,
|
|
1211
|
+
parent: parentId,
|
|
1212
|
+
});
|
|
1213
|
+
inputs = {
|
|
1214
|
+
[first]: chainInput(specs[first], id),
|
|
1215
|
+
[second]: this.buildInputForSpec(specs[second], node.args[i], wrapId),
|
|
1216
|
+
};
|
|
1217
|
+
ctx.target.blocks[wrapId].inputs = inputs;
|
|
1218
|
+
this.reparentInputs(ctx, wrapId, inputs);
|
|
1219
|
+
id = wrapId;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
return { id, kind: entry.kind, entry };
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1163
1225
|
emitListCall(ctx, node, listInfo, parentId) {
|
|
1164
1226
|
const method = node.segments[1];
|
|
1165
1227
|
let spec = null;
|