Skip to content

Commit 18e6809

Browse files
authored
Merge pull request #400 from smalruby/refactor/register-on-xxx-handlers
feat: implement registerOnXxx infrastructure for Ruby-to-blocks converter
2 parents 4caddb6 + 1a6521e commit 18e6809

File tree

6 files changed

+319
-263
lines changed

6 files changed

+319
-263
lines changed

src/lib/ruby-to-blocks-converter/control.js

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,36 +20,6 @@ const StopOptions = [
2020
*/
2121
const ControlConverter = {
2222

23-
onIf: function (cond, statement, elseStatement) {
24-
const block = this._createBlock('control_if', 'statement');
25-
if (!this._isFalse(cond)) {
26-
this._addInput(block, 'CONDITION', cond);
27-
}
28-
this._addSubstack(block, statement);
29-
if (elseStatement) {
30-
block.opcode = 'control_if_else';
31-
this._addSubstack(block, elseStatement, 2);
32-
}
33-
return block;
34-
},
35-
36-
onUntil: function (cond, statement) {
37-
statement = this._removeWaitBlocks(statement);
38-
39-
let opcode;
40-
if (statement === null) {
41-
opcode = 'control_wait_until';
42-
} else {
43-
opcode = 'control_repeat_until';
44-
}
45-
const block = this._createBlock(opcode, 'statement');
46-
if (!this._isFalse(cond)) {
47-
this._addInput(block, 'CONDITION', cond);
48-
}
49-
this._addSubstack(block, statement);
50-
return block;
51-
},
52-
5323
register: function (converter) {
5424
// sleep(duration) - control_wait
5525
converter.registerCallMethod('self', 'sleep', 1, params => {
@@ -145,6 +115,37 @@ const ControlConverter = {
145115

146116
return null;
147117
});
118+
119+
// Register onXxx handlers
120+
converter.registerOnIf((cond, statement, elseStatement) => {
121+
const block = converter._createBlock('control_if', 'statement');
122+
if (!converter._isFalse(cond)) {
123+
converter._addInput(block, 'CONDITION', cond);
124+
}
125+
converter._addSubstack(block, statement);
126+
if (elseStatement) {
127+
block.opcode = 'control_if_else';
128+
converter._addSubstack(block, elseStatement, 2);
129+
}
130+
return block;
131+
});
132+
133+
converter.registerOnUntil((cond, statement) => {
134+
statement = converter._removeWaitBlocks(statement);
135+
136+
let opcode;
137+
if (statement === null) {
138+
opcode = 'control_wait_until';
139+
} else {
140+
opcode = 'control_repeat_until';
141+
}
142+
const block = converter._createBlock(opcode, 'statement');
143+
if (!converter._isFalse(cond)) {
144+
converter._addInput(block, 'CONDITION', cond);
145+
}
146+
converter._addSubstack(block, statement);
147+
return block;
148+
});
148149
}
149150
};
150151

src/lib/ruby-to-blocks-converter/index.js

Lines changed: 80 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -89,27 +89,16 @@ class RubyToBlocksConverter {
8989
constructor (vm) {
9090
this.vm = vm;
9191
this._translator = message => message.defaultMessage;
92-
this._converters = [
93-
MusicConverter,
94-
PenConverter,
95-
EV3Converter,
96-
GdxForConverter,
97-
SmalrubotS1Converter,
98-
BoostConverter,
99-
TranslateConverter,
100-
MakeyMakeyConverter,
101-
102-
MotionConverter,
103-
LooksConverter,
104-
SoundConverter,
105-
ControlConverter,
106-
SensingConverter,
107-
OperatorsConverter,
108-
VariablesConverter,
109-
MyBlocksConverter
110-
];
11192
this._receiverToMethods = {};
11293
this._receiverToMyBlocks = {};
94+
this._onIfHandlers = [];
95+
this._onUntilHandlers = [];
96+
this._onOpAsgnHandlers = [];
97+
this._onAndHandlers = [];
98+
this._onOrHandlers = [];
99+
this._onVarHandlers = [];
100+
this._onVasgnHandlers = [];
101+
this._onDefsHandlers = [];
113102
this.reset();
114103

115104
[
@@ -368,6 +357,38 @@ class RubyToBlocksConverter {
368357
this._receiverToMyBlocks[receiverName].push(myBlockHandler);
369358
}
370359

360+
registerOnIf (handler) {
361+
this._onIfHandlers.push(handler);
362+
}
363+
364+
registerOnUntil (handler) {
365+
this._onUntilHandlers.push(handler);
366+
}
367+
368+
registerOnOpAsgn (handler) {
369+
this._onOpAsgnHandlers.push(handler);
370+
}
371+
372+
registerOnAnd (handler) {
373+
this._onAndHandlers.push(handler);
374+
}
375+
376+
registerOnOr (handler) {
377+
this._onOrHandlers.push(handler);
378+
}
379+
380+
registerOnVar (handler) {
381+
this._onVarHandlers.push(handler);
382+
}
383+
384+
registerOnVasgn (handler) {
385+
this._onVasgnHandlers.push(handler);
386+
}
387+
388+
registerOnDefs (handler) {
389+
this._onDefsHandlers.push(handler);
390+
}
391+
371392
callMethod (receiver, name, args, rubyBlockArgs, rubyBlock, node) {
372393
const receiverName = this._getReceiverName(receiver);
373394
if (!receiverName) return null;
@@ -531,15 +552,53 @@ class RubyToBlocksConverter {
531552
}
532553

533554
_callConvertersHandler (handlerName, ...args) {
534-
for (let i = 0; i < this._converters.length; i++) {
535-
const converter = this._converters[i];
555+
// First, check registered handlers based on handlerName
556+
const handlersMap = {
557+
onIf: this._onIfHandlers,
558+
onUntil: this._onUntilHandlers,
559+
onOpAsgn: this._onOpAsgnHandlers,
560+
onAnd: this._onAndHandlers,
561+
onOr: this._onOrHandlers,
562+
onVar: this._onVarHandlers,
563+
onVasgn: this._onVasgnHandlers,
564+
onDefs: this._onDefsHandlers
565+
};
566+
567+
const handlers = handlersMap[handlerName];
568+
if (handlers) {
569+
for (const handler of handlers) {
570+
const block = handler.apply(this, args);
571+
if (block) {
572+
return block;
573+
}
574+
}
575+
}
576+
577+
// Then, check legacy converter objects for remaining unmigrated handlers
578+
const legacyConverters = [
579+
MusicConverter,
580+
PenConverter,
581+
EV3Converter,
582+
GdxForConverter,
583+
SmalrubotS1Converter,
584+
BoostConverter,
585+
TranslateConverter,
586+
MakeyMakeyConverter,
587+
LooksConverter,
588+
SoundConverter,
589+
SensingConverter
590+
];
591+
592+
for (let i = 0; i < legacyConverters.length; i++) {
593+
const converter = legacyConverters[i];
536594
if (Object.prototype.hasOwnProperty.call(converter, handlerName)) {
537595
const block = converter[handlerName].apply(this, args);
538596
if (block) {
539597
return block;
540598
}
541599
}
542600
}
601+
543602
return null;
544603
}
545604

src/lib/ruby-to-blocks-converter/motion.js

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -139,31 +139,31 @@ const MotionConverter = {
139139
// direction getter
140140
converter.registerCallMethod('sprite', 'direction', 0, () =>
141141
converter._createBlock('motion_direction', 'value'));
142-
},
143-
144-
// Handle compound assignments like x += value, y += value
145-
onOpAsgn: function (lh, operator, rh) {
146-
let block;
147-
if (this._isBlock(lh) && operator === '+' && this._isNumberOrBlock(rh)) {
148-
let xy;
149-
switch (lh.opcode) {
150-
case 'motion_xposition':
151-
case 'motion_yposition':
152-
// All Motion blocks are sprite-only
153-
if (this._isStage()) {
154-
throw new RubyToBlocksConverterError(lh.node, 'Stage selected: no motion blocks');
155-
}
156-
if (lh.opcode === 'motion_xposition') {
157-
xy = 'x';
158-
} else {
159-
xy = 'y';
142+
143+
// Register onXxx handlers
144+
converter.registerOnOpAsgn((lh, operator, rh) => {
145+
let block;
146+
if (converter._isBlock(lh) && operator === '+' && converter._isNumberOrBlock(rh)) {
147+
let xy;
148+
switch (lh.opcode) {
149+
case 'motion_xposition':
150+
case 'motion_yposition':
151+
// All Motion blocks are sprite-only
152+
if (converter._isStage()) {
153+
throw new RubyToBlocksConverterError(lh.node, 'Stage selected: no motion blocks');
154+
}
155+
if (lh.opcode === 'motion_xposition') {
156+
xy = 'x';
157+
} else {
158+
xy = 'y';
159+
}
160+
block = converter._changeBlock(lh, `motion_change${xy}by`, 'statement');
161+
converter._addNumberInput(block, `D${_.toUpper(xy)}`, 'math_number', rh, 10);
162+
break;
160163
}
161-
block = this._changeBlock(lh, `motion_change${xy}by`, 'statement');
162-
this._addNumberInput(block, `D${_.toUpper(xy)}`, 'math_number', rh, 10);
163-
break;
164164
}
165-
}
166-
return block;
165+
return block;
166+
});
167167
}
168168
};
169169

0 commit comments

Comments
 (0)