smalruby-editor 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of smalruby-editor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/assets/javascripts/blocks/hardware.js.coffee.erb +40 -3
- data/app/assets/stylesheets/toolbox.css.scss.erb +1 -6
- data/app/controllers/source_codes_controller.rb +5 -1
- data/app/models/concerns/ruby_to_block.rb +17 -360
- data/app/models/concerns/ruby_to_block/block.rb +97 -0
- data/app/models/concerns/ruby_to_block/block/base.rb +251 -0
- data/app/models/concerns/ruby_to_block/block/character.rb +42 -0
- data/app/models/concerns/ruby_to_block/block/character_event.rb +27 -0
- data/app/models/concerns/ruby_to_block/block/character_method_call.rb +29 -0
- data/app/models/concerns/ruby_to_block/block/character_new.rb +7 -0
- data/app/models/concerns/ruby_to_block/block/character_operation.rb +80 -0
- data/app/models/concerns/ruby_to_block/block/control_if.rb +45 -0
- data/app/models/concerns/ruby_to_block/block/control_loop.rb +19 -0
- data/app/models/concerns/ruby_to_block/block/control_sleep.rb +18 -0
- data/app/models/concerns/ruby_to_block/block/do.rb +24 -0
- data/app/models/concerns/ruby_to_block/block/else.rb +12 -0
- data/app/models/concerns/ruby_to_block/block/end.rb +26 -0
- data/app/models/concerns/ruby_to_block/block/events_on_click.rb +14 -0
- data/app/models/concerns/ruby_to_block/block/events_on_key_push_or_down.rb +17 -0
- data/app/models/concerns/ruby_to_block/block/events_on_start.rb +14 -0
- data/app/models/concerns/ruby_to_block/block/hardware_init_hardware.rb +14 -0
- data/app/models/concerns/ruby_to_block/block/hardware_led_off.rb +16 -0
- data/app/models/concerns/ruby_to_block/block/hardware_led_on.rb +16 -0
- data/app/models/concerns/ruby_to_block/block/hardware_on_sensor_change.rb +17 -0
- data/app/models/concerns/ruby_to_block/block/hardware_rgb_led_off.rb +18 -0
- data/app/models/concerns/ruby_to_block/block/hardware_rgb_led_on.rb +23 -0
- data/app/models/concerns/ruby_to_block/block/hardware_sensor_value.rb +23 -0
- data/app/models/concerns/ruby_to_block/block/hardware_two_wheel_drive_car.rb +26 -0
- data/app/models/concerns/ruby_to_block/block/looks_hide.rb +8 -0
- data/app/models/concerns/ruby_to_block/block/looks_say.rb +20 -0
- data/app/models/concerns/ruby_to_block/block/looks_show.rb +8 -0
- data/app/models/concerns/ruby_to_block/block/looks_vanish.rb +8 -0
- data/app/models/concerns/ruby_to_block/block/math_number.rb +14 -0
- data/app/models/concerns/ruby_to_block/block/motion_change_x_by.rb +20 -0
- data/app/models/concerns/ruby_to_block/block/motion_change_y_by.rb +20 -0
- data/app/models/concerns/ruby_to_block/block/motion_move.rb +15 -0
- data/app/models/concerns/ruby_to_block/block/motion_point_towards_character.rb +24 -0
- data/app/models/concerns/ruby_to_block/block/motion_point_towards_mouse.rb +9 -0
- data/app/models/concerns/ruby_to_block/block/motion_reach_wall.rb +24 -0
- data/app/models/concerns/ruby_to_block/block/motion_rotate_left.rb +20 -0
- data/app/models/concerns/ruby_to_block/block/motion_rotate_right.rb +20 -0
- data/app/models/concerns/ruby_to_block/block/motion_self_angle.rb +23 -0
- data/app/models/concerns/ruby_to_block/block/motion_self_x.rb +23 -0
- data/app/models/concerns/ruby_to_block/block/motion_self_y.rb +23 -0
- data/app/models/concerns/ruby_to_block/block/motion_set_angle.rb +15 -0
- data/app/models/concerns/ruby_to_block/block/motion_set_x.rb +38 -0
- data/app/models/concerns/ruby_to_block/block/motion_set_y.rb +15 -0
- data/app/models/concerns/ruby_to_block/block/motion_turn.rb +8 -0
- data/app/models/concerns/ruby_to_block/block/motion_turn_if_reach_wall.rb +8 -0
- data/app/models/concerns/ruby_to_block/block/null.rb +17 -0
- data/app/models/concerns/ruby_to_block/block/operators_compare.rb +36 -0
- data/app/models/concerns/ruby_to_block/block/operators_false.rb +8 -0
- data/app/models/concerns/ruby_to_block/block/operators_true.rb +8 -0
- data/app/models/concerns/ruby_to_block/block/require_smalruby.rb +7 -0
- data/app/models/concerns/ruby_to_block/block/ruby_comment.rb +16 -0
- data/app/models/concerns/ruby_to_block/block/ruby_expression.rb +14 -0
- data/app/models/concerns/ruby_to_block/block/ruby_statement.rb +23 -0
- data/app/models/concerns/ruby_to_block/block/sensing_hit.rb +23 -0
- data/app/models/concerns/ruby_to_block/block/sound_play.rb +15 -0
- data/app/models/concerns/ruby_to_block/block/sound_preset_sounds.rb +31 -0
- data/app/models/concerns/ruby_to_block/block/text.rb +15 -0
- data/app/models/concerns/ruby_to_block/block/value.rb +12 -0
- data/app/models/concerns/ruby_to_block/context.rb +97 -0
- data/app/models/concerns/ruby_to_block/formatter.rb +26 -0
- data/app/models/source_code.rb +3 -1
- data/app/views/editor/_toolbox.html.haml +16 -0
- data/lib/smalruby_editor/version.rb +3 -3
- data/smalruby-editor.gemspec +1 -1
- data/spec/acceptance/block_mode/blocks/hardware/two_wheel_drive_car.feature +69 -0
- data/spec/acceptance/ruby_mode/translate.feature +1 -1
- data/spec/controllers/source_codes_controller_spec.rb +14 -11
- data/spec/models/concerns/ruby_to_block/block/character_method_call_spec.rb +175 -0
- data/spec/models/concerns/ruby_to_block/block/control_if_spec.rb +75 -0
- data/spec/models/concerns/ruby_to_block/block/control_loop_spec.rb +68 -0
- data/spec/models/concerns/ruby_to_block/block/events_on_start_spec.rb +120 -0
- data/spec/models/concerns/ruby_to_block/block/hardware_spec.rb +120 -0
- data/spec/models/concerns/ruby_to_block/block/looks_say_spec.rb +79 -0
- data/spec/models/concerns/ruby_to_block/block/looks_spec.rb +74 -0
- data/spec/models/concerns/ruby_to_block/block/motion_move_spec.rb +63 -0
- data/spec/models/concerns/ruby_to_block/block/motion_reach_wall_spec.rb +67 -0
- data/spec/models/concerns/ruby_to_block/block/motion_spec.rb +392 -0
- data/spec/models/concerns/ruby_to_block/block/motion_turn_spec.rb +54 -0
- data/spec/models/concerns/ruby_to_block/block/require_smalruby_spec.rb +39 -0
- data/spec/models/concerns/ruby_to_block/block/ruby_comment_spec.rb +55 -0
- data/spec/models/concerns/ruby_to_block/block/ruby_statement_spec.rb +113 -0
- data/spec/models/concerns/ruby_to_block/block/shared/block_examples.rb +44 -0
- data/spec/models/concerns/ruby_to_block/block/sound_spec.rb +46 -0
- data/spec/models/concerns/ruby_to_block/block_spec.rb +49 -0
- data/spec/models/concerns/ruby_to_block_spec.rb +421 -217
- data/spec/support/ruby_to_block.rb +10 -0
- metadata +108 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 481219ecbb11af4414b736a08666df8c1d4b2a98
|
4
|
+
data.tar.gz: c0158c87c4892af0db1130770e55d3f8966f6a73
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8eeaa4d664ad6fa1b258798787749a831b386d66ee40fa7e323d19686352c2b9f96e11f4fee0ed300b54dc59177e69c3103ce9a408a0bddaf8351b86b301561a
|
7
|
+
data.tar.gz: 4ace3c4984b24bc8f1996e11f6cbcd5de2aea320d00f5ce216b0c6ed7e47ec3128ee99b8cf396902249e0dca0b283976072397125adf01f88dc6fff4840ce7e2
|
@@ -235,13 +235,50 @@ Blockly.Blocks['<%= n %>'] =
|
|
235
235
|
@setTooltip('')
|
236
236
|
|
237
237
|
Blockly.Ruby['<%= n %>'] = (block) ->
|
238
|
-
pin = @getFieldValue('PIN')
|
239
|
-
#p sensor("A0").value.class
|
240
|
-
#p(sensor("A0").value / 6)
|
238
|
+
pin = @getFieldValue('PIN')
|
241
239
|
|
242
240
|
pos = Blockly.Ruby.valueToCode(@, 'POS', Blockly.Ruby.ORDER_FUNCTION_CALL) || '5'
|
243
241
|
Blockly.Ruby.characterMethodCall_("servo(#{Blockly.Ruby.quote_(pin)}).position = #{pos}")
|
244
242
|
|
243
|
+
# 「車」サブジャンル
|
244
|
+
|
245
|
+
# 「2WD車[▼ピン]を進める」ブロック
|
246
|
+
# 「2WD車[▼ピン]をバックさせる」ブロック
|
247
|
+
# 「2WD車[▼ピン]を左に曲げる」ブロック
|
248
|
+
# 「2WD車[▼ピン]を右に曲げる」ブロック
|
249
|
+
# 「2WD車[▼ピン]を止める」ブロック
|
250
|
+
<%
|
251
|
+
[
|
252
|
+
['forward', '進める'],
|
253
|
+
['backward', 'バックさせる'],
|
254
|
+
['turn_left', '左に曲げる'],
|
255
|
+
['turn_right', '右に曲げる'],
|
256
|
+
['stop', '止める'],
|
257
|
+
].each do |method, label|
|
258
|
+
n = "#{category}_two_wheel_drive_car_#{method}"
|
259
|
+
%>
|
260
|
+
|
261
|
+
Blockly.Blocks['<%= n %>'] =
|
262
|
+
init: ()->
|
263
|
+
dropdown = (["D#{n}", "D#{n}"] for n in [2..10])
|
264
|
+
|
265
|
+
@setHelpUrl('')
|
266
|
+
@setColour(<%= color %>)
|
267
|
+
@appendDummyInput()
|
268
|
+
.appendField('2WD車')
|
269
|
+
.appendField(new Blockly.FieldDropdown(dropdown), 'PIN')
|
270
|
+
.appendField('を<%= label %>')
|
271
|
+
@setInputsInline(true)
|
272
|
+
@setPreviousStatement(true)
|
273
|
+
@setNextStatement(true)
|
274
|
+
@setTooltip('')
|
275
|
+
|
276
|
+
Blockly.Ruby['<%= n %>'] = (block) ->
|
277
|
+
pin = @getFieldValue('PIN')
|
278
|
+
|
279
|
+
Blockly.Ruby.characterMethodCall_("two_wheel_drive_car(#{Blockly.Ruby.quote_(pin)}).<%= method %>")
|
280
|
+
<% end %>
|
281
|
+
|
245
282
|
# 「ボタン」サブジャンル
|
246
283
|
|
247
284
|
# 条件:ボタン[▼]を押している
|
@@ -42,10 +42,9 @@
|
|
42
42
|
208
|
43
43
|
208
|
44
44
|
208
|
45
|
+
208
|
45
46
|
).map(&:to_i)
|
46
47
|
|
47
|
-
ruby_genre_index = hues.index(340) + 1
|
48
|
-
|
49
48
|
colors = hues.map { |h|
|
50
49
|
[hsv_to_rgb(h, 100, 80), hsv_to_rgb(h, 100, 100)]
|
51
50
|
}
|
@@ -55,10 +54,6 @@
|
|
55
54
|
%>
|
56
55
|
|
57
56
|
#\:<%= ids[index] %> {
|
58
|
-
<% if index == ruby_genre_index %>
|
59
|
-
display: none;
|
60
|
-
|
61
|
-
<% end %>
|
62
57
|
.blocklyTreeRow {
|
63
58
|
padding-top: 0.25em;
|
64
59
|
padding-bottom: 0.25em;
|
@@ -61,7 +61,11 @@ class SourceCodesController < ApplicationController
|
|
61
61
|
source_code = SourceCode.new(source_code_params)
|
62
62
|
render text: source_code.to_blocks
|
63
63
|
rescue
|
64
|
-
|
64
|
+
if Rails.env == 'development'
|
65
|
+
raise
|
66
|
+
else
|
67
|
+
head :bad_request
|
68
|
+
end
|
65
69
|
end
|
66
70
|
|
67
71
|
private
|
@@ -1,391 +1,48 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'ruby_to_block/block'
|
4
|
+
require_relative 'ruby_to_block/context'
|
5
|
+
require_relative 'ruby_to_block/formatter'
|
4
6
|
|
5
7
|
# Rubyのソースコードをブロックに変換するモジュール
|
6
8
|
module RubyToBlock
|
7
9
|
extend ActiveSupport::Concern
|
8
10
|
|
9
|
-
SUCCESS_DATA_MOCK = <<-EOS.strip_heredoc
|
10
|
-
require "smalruby"
|
11
|
-
|
12
|
-
car1 = Character.new(costume: "car1.png", x: 0, y: 0, angle: 0)
|
13
|
-
|
14
|
-
car1.on(:start) do
|
15
|
-
loop do
|
16
|
-
move(10)
|
17
|
-
turn_if_reach_wall
|
18
|
-
end
|
19
|
-
end
|
20
|
-
EOS
|
21
|
-
|
22
|
-
SUCCESS_XML_MOCK = <<-XML.strip_heredoc
|
23
|
-
<xml xmlns="http://www.w3.org/1999/xhtml">
|
24
|
-
<character name="car1" x="0" y="0" angle="0" costumes="car1.png" />
|
25
|
-
<block type="character_new" x="4" y="4">
|
26
|
-
<field name="NAME">car1</field>
|
27
|
-
<statement name="DO">
|
28
|
-
<block type="events_on_start">
|
29
|
-
<statement name="DO">
|
30
|
-
<block type="control_loop">
|
31
|
-
<statement name="DO">
|
32
|
-
<block type="motion_move" inline="true">
|
33
|
-
<value name="STEP">
|
34
|
-
<block type="math_number">
|
35
|
-
<field name="NUM">10</field>
|
36
|
-
</block>
|
37
|
-
</value>
|
38
|
-
<next>
|
39
|
-
<block type="motion_turn_if_reach_wall" />
|
40
|
-
</next>
|
41
|
-
</block>
|
42
|
-
</statement>
|
43
|
-
</block>
|
44
|
-
</statement>
|
45
|
-
</block>
|
46
|
-
</statement>
|
47
|
-
</block>
|
48
|
-
</xml>
|
49
|
-
XML
|
50
|
-
|
51
11
|
# XML形式のブロックに変換する
|
52
12
|
def to_blocks
|
53
13
|
fail if data == '__FAIL__'
|
54
|
-
return SUCCESS_XML_MOCK if data == SUCCESS_DATA_MOCK
|
55
14
|
|
56
|
-
|
57
|
-
|
58
|
-
receiver_stack = []
|
59
|
-
statement_stack = []
|
60
|
-
blocks = []
|
61
|
-
current_block = nil
|
62
|
-
lines = data.lines
|
63
|
-
while (line = lines.shift)
|
15
|
+
context = Context.new(data.lines)
|
16
|
+
while (line = context.next_line)
|
64
17
|
line.chomp!
|
65
18
|
next if line.strip.empty?
|
19
|
+
md = Block.statement_regexp.match(line)
|
20
|
+
next if Block.process_match_data(md, context)
|
21
|
+
Block.process_match_data(md, context, 'ruby_statement')
|
22
|
+
end
|
66
23
|
|
67
|
-
|
68
|
-
|
69
|
-
unless md
|
70
|
-
block = Block.new('ruby_statement', fields: { STATEMENT: line })
|
71
|
-
if current_block # TODO: リファクタリング
|
72
|
-
current_block.sibling = block
|
73
|
-
else
|
74
|
-
blocks.push(block)
|
75
|
-
end
|
76
|
-
current_block = block
|
77
|
-
|
78
|
-
next
|
79
|
-
end
|
80
|
-
|
81
|
-
next if md[:require_smalruby]
|
82
|
-
|
83
|
-
if (s = md[:character])
|
84
|
-
md2 = /#{CHARACTER_RE}/.match(s)
|
85
|
-
name = md2[1]
|
86
|
-
|
87
|
-
characters[name] = Character.new(name: name, costumes: [md2[2]],
|
88
|
-
x: md2[3], y: md2[4], angle: md2[5])
|
89
|
-
next
|
90
|
-
end
|
91
|
-
|
92
|
-
if (s = md[:events_on_start])
|
93
|
-
md2 = /#{EVENTS_ON_START_RE}/.match(s)
|
94
|
-
name = md2[1] ? md2[1] : receiver_stack.last.name
|
95
|
-
c = characters[name]
|
96
|
-
character_stack.push(c)
|
97
|
-
|
98
|
-
do_block = Block.new('null')
|
99
|
-
events_on_start_block = Block.new('events_on_start', statements: { DO: do_block })
|
100
|
-
if current_block && current_block.type == 'character_new' &&
|
101
|
-
current_block[:NAME] == name
|
102
|
-
current_block.add_statement(:DO, events_on_start_block)
|
103
|
-
character_new_block = current_block
|
104
|
-
else
|
105
|
-
if c == receiver_stack.last
|
106
|
-
current_block.sibling = events_on_start_block
|
107
|
-
character_new_block = current_block.parent
|
108
|
-
else
|
109
|
-
character_new_block =
|
110
|
-
Block.new('character_new',
|
111
|
-
fields: { NAME: name }, statements: { DO: events_on_start_block })
|
112
|
-
if current_block
|
113
|
-
if current_block.type == 'character_new'
|
114
|
-
blocks.push(character_new_block)
|
115
|
-
else
|
116
|
-
current_block.sibling = character_new_block
|
117
|
-
end
|
118
|
-
else
|
119
|
-
blocks.push(character_new_block)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
statement_stack.push([:events_on_start, character_new_block])
|
124
|
-
|
125
|
-
receiver_stack.push(c)
|
126
|
-
|
127
|
-
current_block = do_block
|
128
|
-
|
129
|
-
next
|
130
|
-
end
|
131
|
-
|
132
|
-
if md[:end]
|
133
|
-
ends_num = lines.select { |l|
|
134
|
-
md2 = STATEMENT_REGEXP.match(l)
|
135
|
-
md2 && md2[:end]
|
136
|
-
}.length + 1
|
137
|
-
ends_num -= lines.select { |l|
|
138
|
-
md2 = STATEMENT_REGEXP.match(l)
|
139
|
-
md2 && (md2[:events_on_start])
|
140
|
-
}.length
|
141
|
-
if (ss = statement_stack.last) &&
|
142
|
-
ends_num <= statement_stack.length
|
143
|
-
case ss.first
|
144
|
-
when :events_on_start
|
145
|
-
current_block = ss[1]
|
146
|
-
|
147
|
-
receiver_stack.pop
|
148
|
-
character_stack.pop
|
149
|
-
statement_stack.pop
|
150
|
-
else
|
151
|
-
# TODO
|
152
|
-
end
|
153
|
-
else
|
154
|
-
block = Block.new('ruby_statement', fields: { STATEMENT: line })
|
155
|
-
if current_block # TODO: リファクタリング
|
156
|
-
current_block.sibling = block
|
157
|
-
else
|
158
|
-
blocks.push(block)
|
159
|
-
end
|
160
|
-
current_block = block
|
161
|
-
end
|
24
|
+
make_xml(context)
|
25
|
+
end
|
162
26
|
|
163
|
-
|
164
|
-
end
|
165
|
-
end
|
27
|
+
private
|
166
28
|
|
167
|
-
|
29
|
+
def make_xml(context)
|
30
|
+
return '' if context[:characters].empty? && context[:blocks].empty?
|
168
31
|
|
169
32
|
xml = REXML::Document.new('<xml xmlns="http://www.w3.org/1999/xhtml" />',
|
170
33
|
attribute_quote: :quote,
|
171
34
|
respect_whitespace: :all)
|
172
|
-
characters.values.each do |c|
|
35
|
+
context[:characters].values.each do |c|
|
173
36
|
c.to_xml(xml.root)
|
174
37
|
end
|
175
|
-
blocks.each do |b|
|
38
|
+
context[:blocks].each do |b|
|
176
39
|
b.to_xml(xml.root)
|
177
40
|
end
|
178
41
|
|
179
42
|
output = StringIO.new
|
180
|
-
formatter =
|
181
|
-
formatter.compact = true
|
182
|
-
# HACK: 行頭、行末などのスペースが1つになってしまうのを修正している
|
183
|
-
def formatter.write_text( node, output )
|
184
|
-
s = node.to_s()
|
185
|
-
s.gsub!(/\s/,' ')
|
186
|
-
if !node.is_a?(REXML::Text) ||
|
187
|
-
node.is_a?(REXML::Text) && !node.parent.whitespace()
|
188
|
-
s.squeeze!(" ")
|
189
|
-
end
|
190
|
-
s = wrap(s, @width - @level)
|
191
|
-
s = indent_text(s, @level, " ", true)
|
192
|
-
output << (' '*@level + s)
|
193
|
-
end
|
43
|
+
formatter = Formatter.new
|
194
44
|
formatter.write(xml, output)
|
195
45
|
|
196
46
|
output.string + "\n"
|
197
47
|
end
|
198
|
-
|
199
|
-
private
|
200
|
-
|
201
|
-
# require "smalruby"
|
202
|
-
REQUIRE_SMALRUBY_RE = '^require\ "smalruby"$'
|
203
|
-
|
204
|
-
# car1 = Character.new(costume: "car1.png", x: 0, y: 0, angle: 0)
|
205
|
-
CHARACTER_RE =
|
206
|
-
'^\s*(\S+)\ =\ Character\.new\(costume:\ "([^"]+)",\ x:\ (\d+),\ y:\ (\d+),\ angle:\ (\d+)\)$'
|
207
|
-
|
208
|
-
EVENTS_ON_START_RE = '^\s*(?:(\S+)\.)?on\(:start\)\ do$'
|
209
|
-
|
210
|
-
END_RE = '^\s*end$'
|
211
|
-
|
212
|
-
STATEMENT_REGEXP = %r{
|
213
|
-
(?<require_smalruby>#{REQUIRE_SMALRUBY_RE})
|
214
|
-
|
|
215
|
-
(?<character>#{CHARACTER_RE})
|
216
|
-
|
|
217
|
-
(?<events_on_start>#{EVENTS_ON_START_RE})
|
218
|
-
|
|
219
|
-
(?<end>#{END_RE})
|
220
|
-
}x
|
221
|
-
|
222
|
-
EXPRESSION_REGEXP = %r{}x
|
223
|
-
LITERAL_REGEXP = %r{}x
|
224
|
-
|
225
|
-
# ソースコードに含まれるキャラクターを表現するクラス
|
226
|
-
class Character
|
227
|
-
attr_accessor :name
|
228
|
-
attr_accessor :costumes
|
229
|
-
attr_accessor :x
|
230
|
-
attr_accessor :y
|
231
|
-
attr_accessor :angle
|
232
|
-
|
233
|
-
def initialize(options)
|
234
|
-
@name = options[:name]
|
235
|
-
@costumes = options[:costumes]
|
236
|
-
@x = options[:x]
|
237
|
-
@y = options[:y]
|
238
|
-
@angle = options[:angle]
|
239
|
-
end
|
240
|
-
|
241
|
-
def to_xml(parent)
|
242
|
-
parent.add_element('character',
|
243
|
-
'name' => @name,
|
244
|
-
'x' => @x, 'y' => @y, 'angle' => @angle,
|
245
|
-
'costumes' => @costumes.join(','))
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
# ブロック群を表現するモジュール
|
250
|
-
module Block
|
251
|
-
# ブロックのインスタンスを生成する
|
252
|
-
def self.new(type, *args)
|
253
|
-
const_get(type.camelize).new(*args)
|
254
|
-
end
|
255
|
-
|
256
|
-
# すべてのブロックのベースクラス
|
257
|
-
class Base
|
258
|
-
attr_accessor :parent
|
259
|
-
attr_accessor :sibling
|
260
|
-
attr_accessor :fields
|
261
|
-
attr_accessor :values
|
262
|
-
attr_accessor :statements
|
263
|
-
|
264
|
-
def initialize(options = {})
|
265
|
-
@fields = options[:fields] || {}
|
266
|
-
@values = options[:values] || {}
|
267
|
-
@statements = options[:statements] || {}
|
268
|
-
|
269
|
-
if @statements.length > 0
|
270
|
-
@statements.values.each do |s|
|
271
|
-
s.parent = self
|
272
|
-
end
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
def to_xml(parent)
|
277
|
-
e = parent.add_element('block', 'type' => type)
|
278
|
-
e.add_attribute('inline', 'true') if inline?
|
279
|
-
fields_to_xml(e)
|
280
|
-
values_to_xml(e)
|
281
|
-
statements_to_xml(e)
|
282
|
-
sibling_to_xml(e)
|
283
|
-
e
|
284
|
-
end
|
285
|
-
|
286
|
-
def type
|
287
|
-
@type ||= self.class.name.sub('RubyToBlock::Block::', '').underscore
|
288
|
-
end
|
289
|
-
|
290
|
-
def inline?
|
291
|
-
false
|
292
|
-
end
|
293
|
-
|
294
|
-
def null?
|
295
|
-
false
|
296
|
-
end
|
297
|
-
|
298
|
-
def [](name)
|
299
|
-
@fields[name]
|
300
|
-
end
|
301
|
-
|
302
|
-
def add_statement(name, block)
|
303
|
-
b = @statements[name]
|
304
|
-
b = b.sibling while b.sibling
|
305
|
-
b.sibling = block
|
306
|
-
end
|
307
|
-
|
308
|
-
def sibling=(block)
|
309
|
-
block.parent = self.parent
|
310
|
-
@sibling = block
|
311
|
-
end
|
312
|
-
|
313
|
-
def indent?
|
314
|
-
false
|
315
|
-
end
|
316
|
-
|
317
|
-
def indent_level
|
318
|
-
b = self
|
319
|
-
level = 0
|
320
|
-
while b.parent
|
321
|
-
level += 1 if b.indent?
|
322
|
-
b = b.parent
|
323
|
-
end
|
324
|
-
level
|
325
|
-
end
|
326
|
-
|
327
|
-
private
|
328
|
-
|
329
|
-
def fields_to_xml(parent)
|
330
|
-
@fields.each do |k, v|
|
331
|
-
e = parent.add_element('field', 'name' => k.to_s)
|
332
|
-
if v.is_a?(String)
|
333
|
-
e.text = v
|
334
|
-
else
|
335
|
-
# TODO
|
336
|
-
end
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
def values_to_xml(parent)
|
341
|
-
@values.each do |k, v|
|
342
|
-
# TODO
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
|
-
def statements_to_xml(parent)
|
347
|
-
@statements.each do |k, v|
|
348
|
-
next if v.null?
|
349
|
-
e = parent.add_element('statement', 'name' => k.to_s)
|
350
|
-
v.to_xml(e)
|
351
|
-
end
|
352
|
-
end
|
353
|
-
|
354
|
-
def sibling_to_xml(parent)
|
355
|
-
return nil unless @sibling
|
356
|
-
|
357
|
-
e = parent.add_element('next')
|
358
|
-
@sibling.to_xml(e)
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
class Null < Base
|
363
|
-
def to_xml(parent)
|
364
|
-
return nil unless @sibling
|
365
|
-
|
366
|
-
@sibling.to_xml(parent)
|
367
|
-
end
|
368
|
-
|
369
|
-
def null?
|
370
|
-
!@sibling
|
371
|
-
end
|
372
|
-
end
|
373
|
-
|
374
|
-
class CharacterNew < Base; end
|
375
|
-
|
376
|
-
class RubyStatement < Base
|
377
|
-
def parent=(block)
|
378
|
-
@parent = block
|
379
|
-
@original_statement ||= @fields[:STATEMENT]
|
380
|
-
@fields[:STATEMENT] =
|
381
|
-
@original_statement.sub(/^ {0,#{indent_level * 2}}/, '')
|
382
|
-
end
|
383
|
-
end
|
384
|
-
|
385
|
-
class EventsOnStart < Base
|
386
|
-
def indent?
|
387
|
-
true
|
388
|
-
end
|
389
|
-
end
|
390
|
-
end
|
391
48
|
end
|