blockly_interpreter 0.2.0

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.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGELOG.md +12 -0
  5. data/COPYING +8 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +91 -0
  8. data/Guardfile +22 -0
  9. data/README.md +72 -0
  10. data/Rakefile +10 -0
  11. data/app/assets/javascripts/blockly_interpreter/extension_blocks.js +1 -0
  12. data/app/assets/javascripts/blockly_interpreter/extension_blocks/dates.js +16 -0
  13. data/app/assets/javascripts/blockly_interpreter/extension_blocks/debugging.js +17 -0
  14. data/app/assets/javascripts/blockly_interpreter/extension_blocks/lists.js +45 -0
  15. data/app/assets/javascripts/blockly_interpreter/extension_blocks/logic.js +274 -0
  16. data/app/assets/javascripts/blockly_interpreter/extension_blocks/text.js.erb +21 -0
  17. data/bin/_guard-core +16 -0
  18. data/bin/console +14 -0
  19. data/bin/guard +16 -0
  20. data/bin/rake +16 -0
  21. data/bin/setup +7 -0
  22. data/blockly_interpreter.gemspec +33 -0
  23. data/exe/blocklyi +13 -0
  24. data/exe/rublocklyc +31 -0
  25. data/lib/blockly_interpreter.rb +25 -0
  26. data/lib/blockly_interpreter/block.rb +113 -0
  27. data/lib/blockly_interpreter/block_library.rb +14 -0
  28. data/lib/blockly_interpreter/console_interpreter.rb +15 -0
  29. data/lib/blockly_interpreter/core_blocks.rb +55 -0
  30. data/lib/blockly_interpreter/core_blocks/arithmetic_operator_block.rb +24 -0
  31. data/lib/blockly_interpreter/core_blocks/boolean_block.rb +23 -0
  32. data/lib/blockly_interpreter/core_blocks/comparison_operator_block.rb +50 -0
  33. data/lib/blockly_interpreter/core_blocks/for_block.rb +68 -0
  34. data/lib/blockly_interpreter/core_blocks/for_each_block.rb +42 -0
  35. data/lib/blockly_interpreter/core_blocks/get_variable_block.rb +19 -0
  36. data/lib/blockly_interpreter/core_blocks/if_block.rb +105 -0
  37. data/lib/blockly_interpreter/core_blocks/lists_create_empty_block.rb +17 -0
  38. data/lib/blockly_interpreter/core_blocks/lists_create_with_block.rb +48 -0
  39. data/lib/blockly_interpreter/core_blocks/lists_get_index_block.rb +95 -0
  40. data/lib/blockly_interpreter/core_blocks/logic_negate_block.rb +20 -0
  41. data/lib/blockly_interpreter/core_blocks/logical_operator_block.rb +22 -0
  42. data/lib/blockly_interpreter/core_blocks/number_block.rb +23 -0
  43. data/lib/blockly_interpreter/core_blocks/procedure_block.rb +49 -0
  44. data/lib/blockly_interpreter/core_blocks/procedures_call_no_return_block.rb +21 -0
  45. data/lib/blockly_interpreter/core_blocks/procedures_call_return_block.rb +21 -0
  46. data/lib/blockly_interpreter/core_blocks/procedures_def_no_return_block.rb +31 -0
  47. data/lib/blockly_interpreter/core_blocks/procedures_def_return_block.rb +64 -0
  48. data/lib/blockly_interpreter/core_blocks/procedures_if_return_block.rb +45 -0
  49. data/lib/blockly_interpreter/core_blocks/repeat_times_block.rb +33 -0
  50. data/lib/blockly_interpreter/core_blocks/set_variable_block.rb +24 -0
  51. data/lib/blockly_interpreter/core_blocks/text_block.rb +23 -0
  52. data/lib/blockly_interpreter/core_blocks/text_change_case_block.rb +32 -0
  53. data/lib/blockly_interpreter/core_blocks/text_join_block.rb +50 -0
  54. data/lib/blockly_interpreter/dsl.rb +291 -0
  55. data/lib/blockly_interpreter/dsl_generator.rb +147 -0
  56. data/lib/blockly_interpreter/engine.rb +8 -0
  57. data/lib/blockly_interpreter/execution_context.rb +72 -0
  58. data/lib/blockly_interpreter/extension_blocks.rb +25 -0
  59. data/lib/blockly_interpreter/extension_blocks/date_today_block.rb +17 -0
  60. data/lib/blockly_interpreter/extension_blocks/debug_message_block.rb +18 -0
  61. data/lib/blockly_interpreter/extension_blocks/lists_append_block.rb +34 -0
  62. data/lib/blockly_interpreter/extension_blocks/lists_concat_block.rb +37 -0
  63. data/lib/blockly_interpreter/extension_blocks/lists_include_operator_block.rb +52 -0
  64. data/lib/blockly_interpreter/extension_blocks/object_present_block.rb +21 -0
  65. data/lib/blockly_interpreter/extension_blocks/switch_block.rb +107 -0
  66. data/lib/blockly_interpreter/extension_blocks/text_inflect_block.rb +27 -0
  67. data/lib/blockly_interpreter/generic_block_dsl_generator.rb +64 -0
  68. data/lib/blockly_interpreter/interpreter.rb +25 -0
  69. data/lib/blockly_interpreter/parser.rb +117 -0
  70. data/lib/blockly_interpreter/program.rb +51 -0
  71. data/lib/blockly_interpreter/program_cache.rb +19 -0
  72. data/lib/blockly_interpreter/test_helper.rb +98 -0
  73. data/lib/blockly_interpreter/version.rb +3 -0
  74. metadata +272 -0
@@ -0,0 +1,25 @@
1
+ module BlocklyInterpreter::ExtensionBlocks
2
+ extend ActiveSupport::Autoload
3
+ extend BlocklyInterpreter::BlockLibrary
4
+
5
+ autoload :DateHashBlock
6
+ autoload :DateTodayBlock
7
+ autoload :DebugMessageBlock
8
+ autoload :ListsAppendBlock
9
+ autoload :ListsConcatBlock
10
+ autoload :ListsIncludeOperatorBlock
11
+ autoload :ObjectPresentBlock
12
+ autoload :SwitchBlock
13
+ autoload :TextInflectBlock
14
+
15
+ self.block_classes = [
16
+ DateTodayBlock,
17
+ DebugMessageBlock,
18
+ ListsAppendBlock,
19
+ ListsConcatBlock,
20
+ ListsIncludeOperatorBlock,
21
+ ObjectPresentBlock,
22
+ SwitchBlock,
23
+ TextInflectBlock
24
+ ]
25
+ end
@@ -0,0 +1,17 @@
1
+ class BlocklyInterpreter::ExtensionBlocks::DateTodayBlock < BlocklyInterpreter::Block
2
+ self.block_type = :date_today
3
+
4
+ def value(execution_context)
5
+ Date.today
6
+ end
7
+
8
+ def to_dsl
9
+ "date_today"
10
+ end
11
+
12
+ module DSLMethods
13
+ def date_today
14
+ block :date_today
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ class BlocklyInterpreter::ExtensionBlocks::DebugMessageBlock < BlocklyInterpreter::Block
2
+ self.block_type = 'debug_message'
3
+
4
+ def execute_statement(execution_context)
5
+ message = values['MESSAGE'].value(execution_context)
6
+ message = message.inspect unless message.is_a?(String)
7
+ execution_context.add_debug_message message
8
+ end
9
+
10
+ module DSLMethods
11
+ def debug_message(msg = nil, &proc)
12
+ proc ||= Proc.new { text msg }
13
+ block 'debug_message' do
14
+ value 'MESSAGE', &proc
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,34 @@
1
+ class BlocklyInterpreter::ExtensionBlocks::ListsAppendBlock < BlocklyInterpreter::Block
2
+ include BlocklyInterpreter::DSLGenerator
3
+ self.block_type = :lists_append
4
+
5
+ def execute_statement(execution_context)
6
+ list = values['LIST'].value(execution_context)
7
+ list << values['VALUE'].value(execution_context)
8
+ end
9
+
10
+ def to_dsl
11
+ method_call_with_possible_block('lists_append', '', [
12
+ method_call_with_block_or_nothing('list', '', values['LIST']),
13
+ method_call_with_block_or_nothing('item', '', values['VALUE'])
14
+ ])
15
+ end
16
+
17
+ module DSLMethods
18
+ class ListsAppendBlockBuilder < BlocklyInterpreter::DSL::BlockBuilder
19
+ def list(&proc)
20
+ value("LIST", &proc)
21
+ end
22
+
23
+ def item(&proc)
24
+ value("VALUE", &proc)
25
+ end
26
+ end
27
+
28
+ def lists_append
29
+ @blocks << BlocklyInterpreter::ExtensionBlocks::ListsAppendBlock::DSLMethods::ListsAppendBlockBuilder.new('lists_append').tap do |builder|
30
+ builder.instance_exec(&proc) if proc
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,37 @@
1
+ class BlocklyInterpreter::ExtensionBlocks::ListsConcatBlock < BlocklyInterpreter::Block
2
+ include BlocklyInterpreter::DSLGenerator
3
+ self.block_type = :lists_concat
4
+
5
+ def value(execution_context)
6
+ list(execution_context, 1) + list(execution_context, 2)
7
+ end
8
+
9
+ def list(execution_context, n)
10
+ values["LIST#{n}"].try!(:value, execution_context) || []
11
+ end
12
+
13
+ def to_dsl
14
+ method_call_with_possible_block("lists_concat", "", [
15
+ method_call_with_block_or_nothing("list1", "", values['LIST1']),
16
+ method_call_with_block_or_nothing("list2", "", values['LIST2'])
17
+ ])
18
+ end
19
+
20
+ module DSLMethods
21
+ class ListsConcatBlockBuilder < BlocklyInterpreter::DSL::BlockBuilder
22
+ def list1(&proc)
23
+ value("LIST1", &proc)
24
+ end
25
+
26
+ def list2(&proc)
27
+ value("LIST2", &proc)
28
+ end
29
+ end
30
+
31
+ def lists_concat
32
+ @blocks << BlocklyInterpreter::ExtensionBlocks::ListsConcatBlock::DSLMethods::ListsConcatBlockBuilder.new('lists_concat').tap do |builder|
33
+ builder.instance_exec(&proc) if proc
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,52 @@
1
+ class BlocklyInterpreter::ExtensionBlocks::ListsIncludeOperatorBlock < BlocklyInterpreter::Block
2
+ self.block_type = :lists_include
3
+
4
+ def value(execution_context)
5
+ a = values['A'].value(execution_context)
6
+ b = values['B'].value(execution_context)
7
+
8
+ case fields['OP']
9
+ when 'INCLUDE' then a.try(:include?, b)
10
+ when 'NINCLUDE' then !a.try(:include?, b)
11
+ end
12
+ end
13
+
14
+ class DSLGenerator < BlocklyInterpreter::DSL::BinaryOperationDSLGenerator
15
+ def initialize(block)
16
+ super(block, nil)
17
+ end
18
+
19
+ def dsl_method_name
20
+ case block.fields['OP']
21
+ when 'INCLUDE' then "lists_include"
22
+ when 'NINCLUDE' then "lists_not_include"
23
+ end
24
+ end
25
+
26
+ def method_args
27
+ args = super
28
+ args.slice(1, args.size - 1)
29
+ end
30
+ end
31
+
32
+ def to_dsl
33
+ DSLGenerator.new(self).dsl
34
+ end
35
+
36
+ module DSLMethods
37
+ class ListIncludesBlockBuilder < BlocklyInterpreter::DSL::BinaryOperationBlockBuilder
38
+ end
39
+
40
+ def lists_include(a = nil, b = nil, &proc)
41
+ @blocks << BlocklyInterpreter::ExtensionBlocks::ListsIncludeOperatorBlock::DSLMethods::ListIncludesBlockBuilder.new('lists_include', "INCLUDE", a, b).tap do |builder|
42
+ builder.instance_exec(&proc) if proc
43
+ end
44
+ end
45
+
46
+ def lists_not_include(a = nil, b = nil, &proc)
47
+ @blocks << BlocklyInterpreter::ExtensionBlocks::ListsIncludeOperatorBlock::DSLMethods::ListIncludesBlockBuilder.new('lists_include', "NINCLUDE", a, b).tap do |builder|
48
+ builder.instance_exec(&proc) if proc
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,21 @@
1
+ class BlocklyInterpreter::ExtensionBlocks::ObjectPresentBlock < BlocklyInterpreter::Block
2
+ include BlocklyInterpreter::DSLGenerator
3
+
4
+ self.block_type = :object_present
5
+
6
+ def value(execution_context)
7
+ values['VALUE'].try!(:value, execution_context).present?
8
+ end
9
+
10
+ def to_dsl
11
+ method_call_with_possible_block("object_present", "", values['VALUE'])
12
+ end
13
+
14
+ module DSLMethods
15
+ def object_present(&proc)
16
+ block :object_present do
17
+ value :VALUE, &proc
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,107 @@
1
+ class BlocklyInterpreter::ExtensionBlocks::SwitchBlock < BlocklyInterpreter::Block
2
+ include BlocklyInterpreter::DSLGenerator
3
+ self.block_type = :controls_switch
4
+
5
+ class Conditional
6
+ attr_reader :predicate_block, :action_block
7
+
8
+ def initialize(switch_block, num)
9
+ @predicate_block = switch_block.values["CASE#{num}"]
10
+ @action_block = switch_block.statements["DO#{num}"]
11
+ end
12
+
13
+ def matches?(value, execution_context)
14
+ predicate_block.value(execution_context) == value
15
+ end
16
+
17
+ def execute_statement(execution_context)
18
+ execution_context.execute(action_block)
19
+ end
20
+ end
21
+
22
+ def predicates
23
+ @predicates ||= begin
24
+ case_count = mutation.try(:[], 'case').try(:to_i) || 0
25
+ (0...case_count).map { |num| Conditional.new(self, num) }
26
+ end
27
+ end
28
+
29
+ def else_block
30
+ statements['ELSE']
31
+ end
32
+
33
+ def execute_statement(execution_context)
34
+ (matching_predicate(execution_context) || else_block).try!(:execute_statement, execution_context)
35
+ end
36
+
37
+ def matching_predicate(execution_context)
38
+ val = switch_value(execution_context)
39
+ execution_context.set_variable(fields['CAPTURE_VAR'], val) if fields['CAPTURE_VAR'].present?
40
+ predicates.detect { |predicate| predicate.matches? val, execution_context }
41
+ end
42
+
43
+ def switch_value(execution_context)
44
+ values['SWITCH_VAL'].value(execution_context)
45
+ end
46
+
47
+ def to_dsl
48
+ preamble_dsl = [method_call_with_possible_block('switch_value', '', values['SWITCH_VAL'])]
49
+ preamble_dsl << "capture_as #{fields['CAPTURE_VAR'].inspect}" if fields['CAPTURE_VAR'].present?
50
+
51
+ clauses_dsl = predicates.map do |predicate|
52
+ [
53
+ method_call_with_possible_block('predicate', '', predicate.predicate_block),
54
+ method_call_with_possible_block('action', '', predicate.action_block)
55
+ ].join("\n")
56
+ end
57
+
58
+ clauses_dsl << method_call_with_block_or_nothing('else_action', '', statements['ELSE'])
59
+
60
+ method_call_with_possible_block('controls_switch', '', [preamble_dsl.join("\n"), clauses_dsl.join("\n")].join("\n"))
61
+ end
62
+
63
+ module DSLMethods
64
+ class SwitchBlockBuilder < BlocklyInterpreter::DSL::BlockBuilder
65
+ attr_reader :predicate_number
66
+
67
+ def initialize(block_type)
68
+ super
69
+ @predicate_number = 0
70
+ end
71
+
72
+ def capture_as(var_name)
73
+ field "CAPTURE_VAR", var_name
74
+ mutation_attr('capture', 1)
75
+ end
76
+
77
+ def switch_value(&proc)
78
+ value "SWITCH_VAL", &proc
79
+ end
80
+
81
+ def predicate(&proc)
82
+ value "CASE#{predicate_number}", &proc
83
+ end
84
+
85
+ def action(&proc)
86
+ statement "DO#{predicate_number}", &proc
87
+ @predicate_number += 1
88
+ end
89
+
90
+ def else_action(&proc)
91
+ mutation_attr "else", 1
92
+ statement "ELSE", &proc
93
+ end
94
+
95
+ def to_xml(node)
96
+ mutation_attr("case", @predicate_number) if @predicate_number > 0
97
+ super
98
+ end
99
+ end
100
+
101
+ def controls_switch(&proc)
102
+ @blocks << BlocklyInterpreter::ExtensionBlocks::SwitchBlock::DSLMethods::SwitchBlockBuilder.new("controls_switch").tap do |builder|
103
+ builder.instance_exec(&proc)
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,27 @@
1
+ class BlocklyInterpreter::ExtensionBlocks::TextInflectBlock < BlocklyInterpreter::Block
2
+ INFLECTIONS = %w(
3
+ humanize
4
+ pluralize
5
+ singularize
6
+ titleize
7
+ camelize
8
+ classify
9
+ dasherize
10
+ deconstantize
11
+ demodulize
12
+ parameterize
13
+ tableize
14
+ underscore
15
+ )
16
+
17
+ self.block_type = :text_inflect
18
+
19
+ def value(execution_context)
20
+ text = values['TEXT'].value(execution_context)
21
+
22
+ case fields['OP']
23
+ when *INFLECTIONS then text.public_send(fields['OP'])
24
+ else text
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,64 @@
1
+ class BlocklyInterpreter::GenericBlockDSLGenerator
2
+ include BlocklyInterpreter::DSLGenerator
3
+
4
+ attr_reader :block
5
+
6
+ def initialize(block)
7
+ @block = block
8
+ end
9
+
10
+ def dsl
11
+ method_call_with_possible_block("block", block.class.block_type.to_sym.inspect, block_contents)
12
+ end
13
+
14
+ private
15
+ def mutation_attributes_dsl(mutation)
16
+ mutation.attributes.keys.map do |attribute|
17
+ "mutation_attr #{attribute.inspect}, #{mutation[attribute].inspect}"
18
+ end
19
+ end
20
+
21
+ def mutation_child_dsl(child)
22
+ attributes_dsl = child.attributes.keys.map do |child_attribute|
23
+ "child[#{child_attribute.inspect}] = #{child[child_attribute].inspect}"
24
+ end
25
+
26
+ if child.inner_html.present?
27
+ attributes_dsl << "child.inner_html = #{child.inner_html.inspect}"
28
+ end
29
+
30
+ method_call_with_possible_block("mutation_child", child.name.inspect, attributes_dsl, "child")
31
+ end
32
+
33
+ def mutation_dsl
34
+ if block.mutation
35
+ mutation_attributes_dsl(block.mutation) + block.mutation.children.map do |child|
36
+ mutation_child_dsl(child)
37
+ end
38
+ else
39
+ []
40
+ end
41
+ end
42
+
43
+ def fields_dsl
44
+ block.fields.map do |key, value|
45
+ "field #{key.inspect}, #{value.inspect}"
46
+ end
47
+ end
48
+
49
+ def values_dsl
50
+ block.values.map do |key, value_block|
51
+ method_call_with_possible_block("value", key.inspect, value_block) + "\n"
52
+ end
53
+ end
54
+
55
+ def statements_dsl
56
+ block.statements.map do |key, statement_block|
57
+ method_call_with_possible_block("statement", key.inspect, statement_block) + "\n"
58
+ end
59
+ end
60
+
61
+ def block_contents
62
+ @block_contents ||= (mutation_dsl + fields_dsl + values_dsl + statements_dsl)
63
+ end
64
+ end
@@ -0,0 +1,25 @@
1
+ require 'nokogiri'
2
+
3
+ class BlocklyInterpreter::Interpreter
4
+ attr_reader :program, :debug_messages
5
+
6
+ def initialize(program)
7
+ @program = program
8
+ @debug_messages = []
9
+ end
10
+
11
+ def build_execution_context
12
+ BlocklyInterpreter::ExecutionContext.new(self)
13
+ end
14
+
15
+ def execute
16
+ build_execution_context.tap do |context|
17
+ context.execute(program.first_block)
18
+ add_debug_messages context.debug_messages
19
+ end
20
+ end
21
+
22
+ def add_debug_messages(debug_messages)
23
+ @debug_messages.push *debug_messages
24
+ end
25
+ end
@@ -0,0 +1,117 @@
1
+ require 'nokogiri'
2
+
3
+ class BlocklyInterpreter::Parser
4
+ class UnknownBlockTypeError < StandardError
5
+ end
6
+
7
+ def self.registered_block_types
8
+ @registered_block_types ||= {}
9
+ end
10
+
11
+ def self.register_block_class(block_class)
12
+ raise "Block class #{block_class} has no type specified" unless block_class.block_type
13
+ registered_block_types[block_class.block_type.to_sym] = block_class
14
+ end
15
+
16
+ def self.registered_block_class_for_type(block_type)
17
+ block_class = registered_block_types[block_type.to_sym]
18
+
19
+ if block_class
20
+ block_class
21
+ elsif superclass.respond_to?(:registered_block_class_for_type)
22
+ superclass.registered_block_class_for_type(block_type)
23
+ end
24
+ end
25
+
26
+ def parse(xml)
27
+ dom = Nokogiri::XML.parse(xml)
28
+
29
+ if dom.root
30
+ procs, nonprocs = dom.root.css('> block').partition do |element|
31
+ ['procedures_defreturn', 'procedures_defnoreturn'].include? element['type']
32
+ end
33
+
34
+ procedures = procs.map { |element| block_from_dom(element) }.index_by(&:procedure_name)
35
+ first_block = block_from_dom(nonprocs.first)
36
+ else
37
+ first_block = nil
38
+ procedures = {}
39
+ end
40
+
41
+ BlocklyInterpreter::Program.new(first_block, procedures)
42
+ end
43
+
44
+ def block_from_dom(element)
45
+ return unless element
46
+ block_class_for_element(element).new(
47
+ element['type'],
48
+ fields_from_element(element),
49
+ values_from_element(element),
50
+ statements_from_element(element),
51
+ next_block_from_element(element),
52
+ mutation_from_element(element),
53
+ comment_from_element(element),
54
+ comment_pinned_from_element(element),
55
+ element.name == 'shadow',
56
+ element['x'],
57
+ element['y']
58
+ )
59
+ end
60
+
61
+ private
62
+
63
+ def block_class_for_element(element)
64
+ block_type = element['type']
65
+ block_class = self.class.registered_block_class_for_type(block_type)
66
+
67
+ if block_class
68
+ block_class
69
+ else
70
+ raise UnknownBlockTypeError.new("#{element['type']} is not a registered block type")
71
+ end
72
+ end
73
+
74
+ def values_from_element(element)
75
+ children_by_name(element, '> value') do |child|
76
+ block_from_dom(child.css('> block').first || child.css('> shadow').first)
77
+ end
78
+ end
79
+
80
+ def statements_from_element(element)
81
+ children_by_name(element, '> statement') do |child|
82
+ block_from_dom(child.css('> block').first || child.css('> shadow').first)
83
+ end
84
+ end
85
+
86
+ def fields_from_element(element)
87
+ children_by_name(element, '> field') do |child|
88
+ child.text
89
+ end
90
+ end
91
+
92
+ def next_block_from_element(element)
93
+ next_block_element = element.css('> next > block').first || element.css('> next > shadow').first
94
+ block_from_dom(next_block_element) if next_block_element
95
+ end
96
+
97
+ def mutation_from_element(element)
98
+ element.css('> mutation').first
99
+ end
100
+
101
+ def comment_from_element(element)
102
+ element.css('> comment').first.try!(:text)
103
+ end
104
+
105
+ def comment_pinned_from_element(element)
106
+ element.css('> comment').first.try { |comment| comment['pinned'] == 'true' }
107
+ end
108
+
109
+ def children_by_name(xml, selector, &block)
110
+ xml.css(selector).map do |child|
111
+ processed_child = block_given? ? yield(child) : child
112
+ [child['name'], processed_child]
113
+ end.to_h
114
+ end
115
+ end
116
+
117
+ BlocklyInterpreter::CoreBlocks.register!