smalruby-editor 0.1.6 → 0.1.7

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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/blocks/hardware.js.coffee.erb +40 -3
  3. data/app/assets/stylesheets/toolbox.css.scss.erb +1 -6
  4. data/app/controllers/source_codes_controller.rb +5 -1
  5. data/app/models/concerns/ruby_to_block.rb +17 -360
  6. data/app/models/concerns/ruby_to_block/block.rb +97 -0
  7. data/app/models/concerns/ruby_to_block/block/base.rb +251 -0
  8. data/app/models/concerns/ruby_to_block/block/character.rb +42 -0
  9. data/app/models/concerns/ruby_to_block/block/character_event.rb +27 -0
  10. data/app/models/concerns/ruby_to_block/block/character_method_call.rb +29 -0
  11. data/app/models/concerns/ruby_to_block/block/character_new.rb +7 -0
  12. data/app/models/concerns/ruby_to_block/block/character_operation.rb +80 -0
  13. data/app/models/concerns/ruby_to_block/block/control_if.rb +45 -0
  14. data/app/models/concerns/ruby_to_block/block/control_loop.rb +19 -0
  15. data/app/models/concerns/ruby_to_block/block/control_sleep.rb +18 -0
  16. data/app/models/concerns/ruby_to_block/block/do.rb +24 -0
  17. data/app/models/concerns/ruby_to_block/block/else.rb +12 -0
  18. data/app/models/concerns/ruby_to_block/block/end.rb +26 -0
  19. data/app/models/concerns/ruby_to_block/block/events_on_click.rb +14 -0
  20. data/app/models/concerns/ruby_to_block/block/events_on_key_push_or_down.rb +17 -0
  21. data/app/models/concerns/ruby_to_block/block/events_on_start.rb +14 -0
  22. data/app/models/concerns/ruby_to_block/block/hardware_init_hardware.rb +14 -0
  23. data/app/models/concerns/ruby_to_block/block/hardware_led_off.rb +16 -0
  24. data/app/models/concerns/ruby_to_block/block/hardware_led_on.rb +16 -0
  25. data/app/models/concerns/ruby_to_block/block/hardware_on_sensor_change.rb +17 -0
  26. data/app/models/concerns/ruby_to_block/block/hardware_rgb_led_off.rb +18 -0
  27. data/app/models/concerns/ruby_to_block/block/hardware_rgb_led_on.rb +23 -0
  28. data/app/models/concerns/ruby_to_block/block/hardware_sensor_value.rb +23 -0
  29. data/app/models/concerns/ruby_to_block/block/hardware_two_wheel_drive_car.rb +26 -0
  30. data/app/models/concerns/ruby_to_block/block/looks_hide.rb +8 -0
  31. data/app/models/concerns/ruby_to_block/block/looks_say.rb +20 -0
  32. data/app/models/concerns/ruby_to_block/block/looks_show.rb +8 -0
  33. data/app/models/concerns/ruby_to_block/block/looks_vanish.rb +8 -0
  34. data/app/models/concerns/ruby_to_block/block/math_number.rb +14 -0
  35. data/app/models/concerns/ruby_to_block/block/motion_change_x_by.rb +20 -0
  36. data/app/models/concerns/ruby_to_block/block/motion_change_y_by.rb +20 -0
  37. data/app/models/concerns/ruby_to_block/block/motion_move.rb +15 -0
  38. data/app/models/concerns/ruby_to_block/block/motion_point_towards_character.rb +24 -0
  39. data/app/models/concerns/ruby_to_block/block/motion_point_towards_mouse.rb +9 -0
  40. data/app/models/concerns/ruby_to_block/block/motion_reach_wall.rb +24 -0
  41. data/app/models/concerns/ruby_to_block/block/motion_rotate_left.rb +20 -0
  42. data/app/models/concerns/ruby_to_block/block/motion_rotate_right.rb +20 -0
  43. data/app/models/concerns/ruby_to_block/block/motion_self_angle.rb +23 -0
  44. data/app/models/concerns/ruby_to_block/block/motion_self_x.rb +23 -0
  45. data/app/models/concerns/ruby_to_block/block/motion_self_y.rb +23 -0
  46. data/app/models/concerns/ruby_to_block/block/motion_set_angle.rb +15 -0
  47. data/app/models/concerns/ruby_to_block/block/motion_set_x.rb +38 -0
  48. data/app/models/concerns/ruby_to_block/block/motion_set_y.rb +15 -0
  49. data/app/models/concerns/ruby_to_block/block/motion_turn.rb +8 -0
  50. data/app/models/concerns/ruby_to_block/block/motion_turn_if_reach_wall.rb +8 -0
  51. data/app/models/concerns/ruby_to_block/block/null.rb +17 -0
  52. data/app/models/concerns/ruby_to_block/block/operators_compare.rb +36 -0
  53. data/app/models/concerns/ruby_to_block/block/operators_false.rb +8 -0
  54. data/app/models/concerns/ruby_to_block/block/operators_true.rb +8 -0
  55. data/app/models/concerns/ruby_to_block/block/require_smalruby.rb +7 -0
  56. data/app/models/concerns/ruby_to_block/block/ruby_comment.rb +16 -0
  57. data/app/models/concerns/ruby_to_block/block/ruby_expression.rb +14 -0
  58. data/app/models/concerns/ruby_to_block/block/ruby_statement.rb +23 -0
  59. data/app/models/concerns/ruby_to_block/block/sensing_hit.rb +23 -0
  60. data/app/models/concerns/ruby_to_block/block/sound_play.rb +15 -0
  61. data/app/models/concerns/ruby_to_block/block/sound_preset_sounds.rb +31 -0
  62. data/app/models/concerns/ruby_to_block/block/text.rb +15 -0
  63. data/app/models/concerns/ruby_to_block/block/value.rb +12 -0
  64. data/app/models/concerns/ruby_to_block/context.rb +97 -0
  65. data/app/models/concerns/ruby_to_block/formatter.rb +26 -0
  66. data/app/models/source_code.rb +3 -1
  67. data/app/views/editor/_toolbox.html.haml +16 -0
  68. data/lib/smalruby_editor/version.rb +3 -3
  69. data/smalruby-editor.gemspec +1 -1
  70. data/spec/acceptance/block_mode/blocks/hardware/two_wheel_drive_car.feature +69 -0
  71. data/spec/acceptance/ruby_mode/translate.feature +1 -1
  72. data/spec/controllers/source_codes_controller_spec.rb +14 -11
  73. data/spec/models/concerns/ruby_to_block/block/character_method_call_spec.rb +175 -0
  74. data/spec/models/concerns/ruby_to_block/block/control_if_spec.rb +75 -0
  75. data/spec/models/concerns/ruby_to_block/block/control_loop_spec.rb +68 -0
  76. data/spec/models/concerns/ruby_to_block/block/events_on_start_spec.rb +120 -0
  77. data/spec/models/concerns/ruby_to_block/block/hardware_spec.rb +120 -0
  78. data/spec/models/concerns/ruby_to_block/block/looks_say_spec.rb +79 -0
  79. data/spec/models/concerns/ruby_to_block/block/looks_spec.rb +74 -0
  80. data/spec/models/concerns/ruby_to_block/block/motion_move_spec.rb +63 -0
  81. data/spec/models/concerns/ruby_to_block/block/motion_reach_wall_spec.rb +67 -0
  82. data/spec/models/concerns/ruby_to_block/block/motion_spec.rb +392 -0
  83. data/spec/models/concerns/ruby_to_block/block/motion_turn_spec.rb +54 -0
  84. data/spec/models/concerns/ruby_to_block/block/require_smalruby_spec.rb +39 -0
  85. data/spec/models/concerns/ruby_to_block/block/ruby_comment_spec.rb +55 -0
  86. data/spec/models/concerns/ruby_to_block/block/ruby_statement_spec.rb +113 -0
  87. data/spec/models/concerns/ruby_to_block/block/shared/block_examples.rb +44 -0
  88. data/spec/models/concerns/ruby_to_block/block/sound_spec.rb +46 -0
  89. data/spec/models/concerns/ruby_to_block/block_spec.rb +49 -0
  90. data/spec/models/concerns/ruby_to_block_spec.rb +421 -217
  91. data/spec/support/ruby_to_block.rb +10 -0
  92. metadata +108 -10
@@ -0,0 +1,97 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module RubyToBlock
4
+ # ブロック群を表現するモジュール
5
+ module Block
6
+ @blocks = {}
7
+
8
+ # ブロックのインスタンスを生成する
9
+ def self.new(type, *args)
10
+ @blocks[type].new(*args)
11
+ end
12
+
13
+ # ステートメントを表現するブロックの正規表現を返す
14
+ def self.statement_regexp
15
+ @statement_regexp ||= make_regexp(:statement?)
16
+ end
17
+
18
+ # 値を表現するブロックの正規表現を返す
19
+ def self.value_regexp
20
+ @value_regexp ||= make_regexp(:value?)
21
+ end
22
+
23
+ # ブロックを登録する
24
+ def self.register(klass)
25
+ @blocks[klass.type] = klass
26
+ end
27
+
28
+ # ブロックの正規表現を返す
29
+ def self.regexp(type)
30
+ @blocks[type.to_s].regexp
31
+ end
32
+
33
+ # MatchDataを処理する
34
+ def self.process_match_data(md, context, type = nil)
35
+ type = md.names.find { |n| md[n.to_sym] } unless type
36
+ @blocks[type].process_match_data(md, context)
37
+ rescue
38
+ false
39
+ end
40
+
41
+ # elseを処理する
42
+ def self.process_else(context)
43
+ st = context.statement
44
+ @blocks[st.first].process_else(context)
45
+ rescue
46
+ false
47
+ end
48
+
49
+ # endを処理する
50
+ def self.process_end(context)
51
+ st = context.statement
52
+ @blocks[st.first].process_end(context)
53
+ rescue
54
+ false
55
+ end
56
+
57
+ # ブロックを表現するクラスを返す
58
+ def self.[](type)
59
+ @blocks[type.to_s]
60
+ end
61
+
62
+ def self.make_regexp(method_symbol)
63
+ regexps = @blocks.values.select(&method_symbol).sort_by(&:priority)
64
+ .reverse.map { |klass|
65
+ "(?<#{klass.type}>#{klass.regexp_string})"
66
+ }
67
+ Regexp.new(regexps.join('|'), 'x')
68
+ end
69
+ private_class_method :make_regexp
70
+ end
71
+ end
72
+
73
+ preloads = %w(
74
+ base
75
+ value
76
+ character_operation
77
+ character_method_call
78
+ character_event
79
+ ).map { |s|
80
+ "#{s}.rb"
81
+ }
82
+ preloads.each do |preload|
83
+ path = Pathname(__FILE__).dirname.join('block', preload)
84
+ silence_warnings do
85
+ load path.expand_path
86
+ end
87
+ end
88
+
89
+ block_pattern = Pathname(__FILE__).dirname.join('block', '*.rb')
90
+ block_files = Pathname.glob(block_pattern)
91
+ block_files.each do |path|
92
+ next if preloads.include?(path.basename)
93
+
94
+ silence_warnings do
95
+ load path.expand_path
96
+ end
97
+ end
@@ -0,0 +1,251 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module RubyToBlock
4
+ module Block
5
+ # すべてのブロックのベースクラス
6
+ class Base
7
+ attr_accessor :parent
8
+ attr_accessor :prev_sibling
9
+ attr_accessor :sibling
10
+ attr_accessor :fields
11
+ attr_accessor :values
12
+ attr_accessor :statements
13
+
14
+ def self.inherited(child)
15
+ Block.register(child)
16
+ end
17
+
18
+ def self.blocknize(regexp, options = {})
19
+ klass = (class << self; self; end)
20
+ klass.instance_eval do
21
+ define_method(:regexp_string) do
22
+ regexp
23
+ end
24
+
25
+ %w(
26
+ statement
27
+ value
28
+ indent
29
+ inline
30
+ priority
31
+ ).each do |name|
32
+ sym = name.to_sym
33
+ if options.key?(sym)
34
+ v = options[sym]
35
+ if v.is_a?(TrueClass) || v.is_a?(FalseClass)
36
+ sym = "#{name}?".to_sym
37
+ end
38
+ define_method(sym) do
39
+ v
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ def self.type
47
+ name.sub('RubyToBlock::Block::', '').underscore
48
+ end
49
+
50
+ # 正規表現を返す
51
+ def self.regexp
52
+ @regexp ||= Regexp.new(regexp_string)
53
+ end
54
+
55
+ # ステートメントかどうかを返す
56
+ #
57
+ # trueの場合、Block.statement_regexpに追加される
58
+ def self.statement?
59
+ false
60
+ end
61
+
62
+ # 値かどうかを返す
63
+ #
64
+ # trueの場合、Block.value_regexpに追加される
65
+ def self.value?
66
+ false
67
+ end
68
+
69
+ # 正規表現の優先度を返す
70
+ def self.priority
71
+ 0
72
+ end
73
+
74
+ # インデントする必要があるかどうかを返す
75
+ def self.indent?
76
+ false
77
+ end
78
+
79
+ # インラインのブロックかどうかを返す
80
+ def self.inline?
81
+ false
82
+ end
83
+
84
+ # 正規表現にマッチしたデータを解析する
85
+ #
86
+ # @return [true] これ以上解析する必要がない
87
+ # @return [false] 解析できなかったのでさらなる解析が必要
88
+ def self.process_match_data(md, context)
89
+ true
90
+ end
91
+
92
+ # 値を格納した文字列を解析する
93
+ #
94
+ # @return [true] これ以上解析する必要がない
95
+ # @return [false] 解析できなかったのでさらなる解析が必要
96
+ def self.process_value_string(context, block, string, name)
97
+ # HACK: 最初と最後の括弧を取り除く
98
+ string = string[1..-2] while string[0] == '(' && string[-1] == ')'
99
+
100
+ value_md = Block.value_regexp.match(string)
101
+ return false unless value_md
102
+
103
+ _current_block = context.current_block
104
+ context.current_block = block
105
+ context.value_name_stack.push(name)
106
+
107
+ unless Block.process_match_data(value_md, context)
108
+ Block.process_match_data(value_md, context, 'ruby_expression')
109
+ end
110
+
111
+ context.value_name_stack.pop
112
+ context.current_block = _current_block
113
+
114
+ true
115
+ end
116
+
117
+ # elseを発見した時の処理
118
+ #
119
+ # @return [true] これ以上処理する必要がない
120
+ # @return [false] 処理できなかった
121
+ def self.process_else(context)
122
+ false
123
+ end
124
+
125
+ # endを発見した時の処理
126
+ #
127
+ # @return [true] これ以上処理する必要がない
128
+ # @return [false] 処理できなかった
129
+ def self.process_end(context)
130
+ context.current_block = context.statement[1]
131
+ context.statement_stack.pop
132
+
133
+ true
134
+ end
135
+
136
+ def initialize(options = {})
137
+ @fields = options[:fields] || {}
138
+ @values = options[:values] || {}
139
+ @statements = options[:statements] || {}
140
+
141
+ if @statements.length > 0
142
+ @statements.values.each do |s|
143
+ s.parent = self
144
+ end
145
+ end
146
+ end
147
+
148
+ def to_xml(parent)
149
+ e = parent.add_element('block', 'type' => type)
150
+ e.add_attribute('inline', 'true') if inline?
151
+ fields_to_xml(e)
152
+ values_to_xml(e)
153
+ statements_to_xml(e)
154
+ sibling_to_xml(e)
155
+ e
156
+ end
157
+
158
+ def type
159
+ @type ||= self.class.type
160
+ end
161
+
162
+ def inline?
163
+ @inline ||= self.class.inline?
164
+ end
165
+
166
+ def null?
167
+ false
168
+ end
169
+
170
+ def [](name)
171
+ @fields[name]
172
+ end
173
+
174
+ def add_statement(name, block)
175
+ b = @statements[name]
176
+ if b
177
+ b = b.sibling while b.sibling
178
+ b.sibling = block
179
+ else
180
+ block.parent = self
181
+ @statements[name] = block
182
+ end
183
+ self
184
+ end
185
+
186
+ def add_value(name, block)
187
+ b = @values[name]
188
+ if b
189
+ b = b.sibling while b.sibling
190
+ b.sibling = block
191
+ else
192
+ block.parent = self
193
+ @values[name] = block
194
+ end
195
+ self
196
+ end
197
+
198
+ def sibling=(block)
199
+ block.parent = parent
200
+ @sibling = block
201
+ block.prev_sibling = self
202
+ end
203
+
204
+ def indent_level
205
+ b = self
206
+ level = 0
207
+ while b.parent
208
+ b = b.parent
209
+ level += 1 if b.class.indent?
210
+ end
211
+ level
212
+ end
213
+
214
+ private
215
+
216
+ def fields_to_xml(parent)
217
+ @fields.each do |k, v|
218
+ e = parent.add_element('field', 'name' => k.to_s)
219
+ if v.is_a?(String)
220
+ e.text = v
221
+ else
222
+ # TODO
223
+ end
224
+ end
225
+ end
226
+
227
+ def values_to_xml(parent)
228
+ @values.each do |k, v|
229
+ next if v.null?
230
+ e = parent.add_element('value', 'name' => k.to_s)
231
+ v.to_xml(e)
232
+ end
233
+ end
234
+
235
+ def statements_to_xml(parent)
236
+ @statements.each do |k, v|
237
+ next if v.null?
238
+ e = parent.add_element('statement', 'name' => k.to_s)
239
+ v.to_xml(e)
240
+ end
241
+ end
242
+
243
+ def sibling_to_xml(parent)
244
+ return nil unless @sibling
245
+
246
+ e = parent.add_element('next')
247
+ @sibling.to_xml(e)
248
+ end
249
+ end
250
+ end
251
+ end
@@ -0,0 +1,42 @@
1
+ # -*- coding: utf-8 -*-
2
+ module RubyToBlock
3
+ module Block
4
+ # ソースコードに含まれるキャラクターを表現するクラス
5
+ class Character < Base
6
+ # rubocop:disable LineLength
7
+ blocknize '^\s*(\S+)\s*=\s*Character\.new\(costume:\s*"(\s*[^"]+\s*)"\s*,\s*x:\s*(\s*\d+\s*)\s*,\s*y:\s*(\s*\d+\s*)\s*,\s*angle:\s*(\s*\d+\s*)\)\s*$',
8
+ statement: true
9
+ # rubocop:enable LineLength
10
+
11
+ attr_accessor :name
12
+ attr_accessor :costumes
13
+ attr_accessor :x
14
+ attr_accessor :y
15
+ attr_accessor :angle
16
+
17
+ def self.process_match_data(md, context)
18
+ md2 = regexp.match(md[type])
19
+ name = md2[1]
20
+ context[:characters][name] = new(name: name, costumes: [md2[2]],
21
+ x: md2[3], y: md2[4], angle: md2[5])
22
+
23
+ true
24
+ end
25
+
26
+ def initialize(options)
27
+ @name = options[:name]
28
+ @costumes = options[:costumes]
29
+ @x = options[:x]
30
+ @y = options[:y]
31
+ @angle = options[:angle]
32
+ end
33
+
34
+ def to_xml(parent)
35
+ parent.add_element('character',
36
+ 'name' => @name,
37
+ 'x' => @x, 'y' => @y, 'angle' => @angle,
38
+ 'costumes' => @costumes.join(','))
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,27 @@
1
+ module RubyToBlock
2
+ module Block
3
+ class CharacterEvent < CharacterMethodCall
4
+ def self.process_end(context)
5
+ context.receiver_stack.pop
6
+ context.character_stack.pop
7
+ super
8
+ end
9
+
10
+ def self.add_character_event_blocks(context, name, block)
11
+ do_block = Block.new('null')
12
+ block.add_statement(:DO, do_block)
13
+
14
+ character = get_character(context, name)
15
+ context.character_stack.push(character)
16
+
17
+ character_new_block, _ =
18
+ add_child_or_create_character_new_block(context, name, block)
19
+
20
+ context.receiver_stack.push(character)
21
+
22
+ context.statement_stack.push([type, character_new_block])
23
+ context.current_block = do_block
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ module RubyToBlock
2
+ module Block
3
+ class CharacterMethodCall < Base
4
+ include CharacterOperation
5
+
6
+ def self.process_match_data(md, context)
7
+ md2 = regexp.match(md[type])
8
+
9
+ block = new
10
+ _, context.current_block =
11
+ *add_child_or_create_character_new_block(context, md2[1], block)
12
+
13
+ true
14
+ end
15
+
16
+ def self.add_character_method_call_block(context, name, block,
17
+ values = {})
18
+ _, context.current_block =
19
+ *add_child_or_create_character_new_block(context, name, block)
20
+
21
+ values.each do |k, v|
22
+ process_value_string(context, block, v, k)
23
+ end
24
+
25
+ block
26
+ end
27
+ end
28
+ end
29
+ end