rsyntaxtree 0.9.3 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.tags +179 -0
  4. data/Gemfile +2 -0
  5. data/README.md +35 -29
  6. data/Rakefile +7 -0
  7. data/bin/rsyntaxtree +42 -31
  8. data/fonts/OpenMoji-Black.ttf +0 -0
  9. data/fonts/OpenMoji-Color.ttf +0 -0
  10. data/img/elements/circle.png +0 -0
  11. data/img/elements/circle_abc.png +0 -0
  12. data/img/elements/circle_bold.png +0 -0
  13. data/img/elements/circle_hatched.png +0 -0
  14. data/img/elements/circle_one.png +0 -0
  15. data/img/elements/connector.png +0 -0
  16. data/img/elements/connector_bold.png +0 -0
  17. data/img/elements/square.png +0 -0
  18. data/img/elements/square_abc.png +0 -0
  19. data/img/elements/square_bold.png +0 -0
  20. data/img/elements/square_hatched.png +0 -0
  21. data/img/elements/square_one.png +0 -0
  22. data/img/rsyntaxtree.png +0 -0
  23. data/img/sample.png +0 -0
  24. data/img/sample.svg +38 -0
  25. data/lib/rsyntaxtree/base_graph.rb +262 -0
  26. data/lib/rsyntaxtree/element.rb +156 -25
  27. data/lib/rsyntaxtree/elementlist.rb +16 -13
  28. data/lib/rsyntaxtree/markup_parser.rb +208 -0
  29. data/lib/rsyntaxtree/string_parser.rb +187 -204
  30. data/lib/rsyntaxtree/svg_graph.rb +471 -298
  31. data/lib/rsyntaxtree/utils.rb +49 -6
  32. data/lib/rsyntaxtree/version.rb +1 -1
  33. data/lib/rsyntaxtree.rb +144 -161
  34. data/rsyntaxtree.gemspec +2 -0
  35. data/test/markup_parser_test.rb +207 -0
  36. metadata +52 -11
  37. data/fonts/latinmodern-math.otf +0 -0
  38. data/fonts/lmroman10-bold.otf +0 -0
  39. data/fonts/lmroman10-bolditalic.otf +0 -0
  40. data/fonts/lmroman10-italic.otf +0 -0
  41. data/fonts/lmroman10-regular.otf +0 -0
  42. data/lib/rsyntaxtree/error_message.rb +0 -68
  43. data/lib/rsyntaxtree/graph.rb +0 -312
  44. data/lib/rsyntaxtree/tree_graph.rb +0 -327
@@ -0,0 +1,208 @@
1
+ require 'parslet'
2
+
3
+ class MarkupParser < Parslet::Parser
4
+ rule(:cr) { str('\\n')}
5
+ rule(:eof) { any.absent? }
6
+ rule(:border) { match('[^\-]').absent? >> str('-').repeat(3).as(:border) >> (eof | cr) }
7
+ rule(:bborder) { match('[^=]').absent? >> str('=').repeat(3).as(:bborder) >> (eof | cr) }
8
+
9
+ rule(:brectangle) { str('###') }
10
+ rule(:rectangle) { str('##') }
11
+ rule(:brackets) { str('#') }
12
+ rule(:triangle) { str('^') }
13
+
14
+ rule(:path) { (str('+') >> str('>').maybe >> match('\d').repeat(1)).as(:path) }
15
+ rule(:escaped) { str('\\') >> (match('[#<>{}\\^+*_=~\|\n\-]')).as(:chr) }
16
+ rule(:non_escaped) { ((match('[#<>{}\\^+*_=~\|\-]') | str('\\n')).absent? >> any).as(:chr) }
17
+ rule(:text) { (escaped | non_escaped).repeat(1).as(:text) }
18
+
19
+ rule(:horizontal_bar) { str('--').as(:horizontal_bar) }
20
+ rule(:arrow_both) { str('<->').as(:arrow_both) }
21
+ rule(:arrow_to_r) { str('->').as(:arrow_to_r) }
22
+ rule(:arrow_to_l) { str('<-').as(:arrow_to_l) }
23
+ rule(:empty_circle) { str('{}').as(:empty_circle) }
24
+ rule(:empty_box) { str('||').as(:empty_box) }
25
+ rule(:hatched_circle) { str('{/}').as(:hatched_circle) }
26
+ rule(:hatched_box) { str('|/|').as(:hatched_box) }
27
+ rule(:circle) { str('{') >> (text|decoration).as(:circle) >> str('}')}
28
+ rule(:box) { str('|') >> (text|decoration).as(:box) >> str('|')}
29
+
30
+ rule(:bolditalic) { str('***') >> (text|decoration).as(:bolditalic) >> str('***')}
31
+ rule(:bold) { str('**') >> (text|decoration).as(:bold) >> str('**')}
32
+ rule(:italic) { str('*') >> (text|decoration).as(:italic) >> str('*')}
33
+
34
+ rule(:bstroke) { str('*') >> shape.as(:bstroke) >> str('*')}
35
+
36
+ rule(:overline) { str('=') >> (text|decoration).as(:overline) >> str('=')}
37
+ rule(:underline) { str('-') >> (text|decoration).as(:underline) >> str('-')}
38
+ rule(:linethrough) { str('~') >> (text|decoration).as(:linethrough) >> str('~')}
39
+
40
+ rule(:small) { str('___') >> (text|decoration|shape).as(:small) >> str('___')}
41
+ rule(:superscript) { str('__') >> (text|decoration|shape).as(:superscript) >> str('__')}
42
+ rule(:subscript) { str('_') >> (text|decoration|shape).as(:subscript) >> str('_')}
43
+
44
+ rule(:decoration) {(bolditalic | bold | italic | small | superscript | subscript |
45
+ overline | underline | linethrough) }
46
+
47
+ rule(:shape) {(hatched_circle | hatched_box | empty_circle | empty_box |
48
+ horizontal_bar | arrow_both | arrow_to_l | arrow_to_r |
49
+ circle | box ) }
50
+
51
+ rule(:markup) {(text | decoration | shape | bstroke)}
52
+
53
+ rule(:line) { ( cr.as(:extracr) | border | bborder | markup.repeat(1).as(:line) >> (cr | eof | str('+').present?))}
54
+ rule(:lines) { triangle.maybe.as(:triangle) >>
55
+ (brectangle | rectangle | brackets).maybe.as(:enclosure) >>
56
+ line.repeat(1) >>
57
+ path.repeat(0).as(:paths) >> (cr | eof) }
58
+ root :lines
59
+ end
60
+
61
+ module Markup
62
+ @parser = MarkupParser.new
63
+
64
+ @evaluator = Parslet::Transform.new do
65
+ rule(:chr => simple(:chr)) { chr.to_s }
66
+ rule(:text => sequence(:text)) {{:text => text.join(""), :decoration => []} }
67
+
68
+ rule(:horizontal_bar => subtree(:empty)) {
69
+ {:text => " ", :decoration => [:bar]}
70
+ }
71
+ rule(:arrow_both => subtree(:empty)) {
72
+ {:text => " ", :decoration => [:bar, :arrow_to_l, :arrow_to_r]}
73
+ }
74
+ rule(:arrow_to_l => subtree(:empty)) {
75
+ {:text => " ", :decoration => [:bar, :arrow_to_l]}
76
+ }
77
+ rule(:arrow_to_r => subtree(:empty)) {
78
+ {:text => " ", :decoration => [:bar, :arrow_to_r]}
79
+ }
80
+
81
+ rule(:empty_circle => subtree(:empty)) {
82
+ {:text => " ", :decoration => [:circle]}
83
+ }
84
+ rule(:empty_box => subtree(:empty)) {
85
+ {:text => " ", :decoration => [:box]}
86
+ }
87
+ rule(:hatched_circle => subtree(:empty)) {
88
+ {:text => " ", :decoration => [:hatched, :circle]}
89
+ }
90
+ rule(:hatched_box => subtree(:empty)) {
91
+ {:text => " ", :decoration => [:hatched, :box]}
92
+ }
93
+
94
+ rule(:bolditalic => subtree(:text)) {
95
+ text[:decoration] << :bolditalic; text
96
+ }
97
+ rule(:bold => subtree(:text)) {
98
+ text[:decoration] << :bold; text
99
+ }
100
+ rule(:italic => subtree(:text)) {
101
+ text[:decoration] << :italic; text
102
+ }
103
+
104
+ rule(:bstroke => subtree(:box)) {
105
+ box[:decoration] << :bstroke; box
106
+ }
107
+ rule(:bstroke => subtree(:circle)) {
108
+ circle[:decoration] << :bstroke; circle
109
+ }
110
+ rule(:bstroke => subtree(:horizontal_bar)) {
111
+ horizontal_bar[:decoration] << :bstroke; horizontal_bar
112
+ }
113
+ rule(:bstroke => subtree(:empty_circle)) {
114
+ empty_circle[:decoration] << :bstroke; empty_circle
115
+ }
116
+ rule(:bstroke => subtree(:empty_box)) {
117
+ empty_box[:decoration] << :bstroke; empty_box
118
+ }
119
+ rule(:bstroke => subtree(:hatched_circle)) {
120
+ hatched_circle[:decoration] << :bstroke; hatched_circle
121
+ }
122
+ rule(:bstroke => subtree(:hatched_box)) {
123
+ hatched_box[:decoration] << :bstroke; hatched_box
124
+ }
125
+
126
+ rule(:overline => subtree(:text)) {
127
+ text[:decoration] << :overline; text
128
+ }
129
+ rule(:underline => subtree(:text)) {
130
+ text[:decoration] << :underline; text
131
+ }
132
+ rule(:linethrough => subtree(:text)) {
133
+ text[:decoration] << :linethrough; text
134
+ }
135
+ rule(:subscript => subtree(:text)) {
136
+ text[:decoration] << :subscript; text
137
+ }
138
+ rule(:superscript => subtree(:text)) {
139
+ text[:decoration] << :superscript; text
140
+ }
141
+ rule(:small => subtree(:text)) {
142
+ text[:decoration] << :small; text
143
+ }
144
+ rule(:box => subtree(:text)) {
145
+ text[:decoration] << :box; text
146
+ }
147
+ rule(:circle => subtree(:text)) {
148
+ text[:decoration] << :circle; text
149
+ }
150
+ rule(:math => subtree(:text)) {
151
+ text[:decoration] << :math; text
152
+ }
153
+ rule(:border => simple(:border)) {
154
+ {:type => :border}
155
+ }
156
+ rule(:bborder => simple(:bborder)) {
157
+ {:type => :bborder}
158
+ }
159
+ rule(:line => subtree(:line)) {
160
+ {:type => :text, :elements => line }
161
+ }
162
+ rule(:extracr => subtree(:extracr)) {
163
+ {:type => :text, :elements=>[{:text=>" ", :decoration=>[]}]}
164
+ }
165
+
166
+ end
167
+
168
+ def parse(txt)
169
+ begin
170
+ parsed = @parser.parse(txt)
171
+ rescue Parslet::ParseFailed => e
172
+ # puts e.parse_failure_cause.ascii_tree
173
+ return {:status => :error, :text => txt}
174
+ end
175
+
176
+ applied = @evaluator.apply(parsed)
177
+
178
+ results = {:enclosure => :none, :triangle => false, :paths => [], :contents => []}
179
+ applied.each do |h|
180
+ if h[:enclosure]
181
+ if h[:enclosure].to_s == '###'
182
+ results[:enclosure] = :brectangle
183
+ elsif h[:enclosure].to_s == '##'
184
+ results[:enclosure] = :rectangle
185
+ elsif h[:enclosure].to_s == '#'
186
+ results[:enclosure] = :brackets
187
+ else
188
+ results[:enclosure] = :none
189
+ end
190
+ end
191
+ if h[:triangle]
192
+ results[:triangle] = h[:triangle].to_s == '^' ? true : false
193
+ end
194
+ if h[:paths]
195
+ results[:paths] = h[:paths]
196
+ end
197
+ if h[:type] == :text || h[:type] == :border || h[:type] == :bborder
198
+ results[:contents] << h
199
+ end
200
+ end
201
+ result = {:status => :success, :results => results}
202
+ result
203
+ end
204
+
205
+ module_function :parse
206
+ end
207
+
208
+ # pp results = Markup.parse('^#\_\#\+あり\-がとう**_X_**\\n----\\n933\\n__|Y|__+>3+2+1343+>5464')
@@ -7,248 +7,231 @@
7
7
  #
8
8
  # Parses a phrase into leafs and nodes and store the result in an element list
9
9
  # (see element_list.rb)
10
- #
11
- # This file is part of RSyntaxTree, which is a ruby port of Andre Eisenbach's
12
- # excellent program phpSyntaxTree.
13
- #
14
10
  # Copyright (c) 2007-2021 Yoichiro Hasebe <yohasebe@gmail.com>
15
- # Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
16
11
 
17
12
  require 'elementlist'
18
13
  require 'element'
19
-
20
- # def escape_high_ascii(string)
21
- # html = ""
22
- # string.length.times do |i|
23
- # ch = string[i]
24
- # if(ch < 127)
25
- # html += ch.chr
26
- # else
27
- # html += sprintf("&#%d;", ch)
28
- # end
29
- # end
30
- # html
31
- # end
32
-
33
- class StringParser
34
-
35
- attr_accessor :data, :elist, :pos, :id, :level, :tncnt
36
- def initialize(str)
37
- # Clean up the data a little to make processing easier
38
- string = str.gsub(/\t/, "") rescue ""
39
- string.gsub!(/\s+/, " ")
40
- string.gsub!(/\] \[/, "][")
41
- string.gsub!(/ \[/, "[")
42
-
43
- @data = string # Store it for later...
44
- @elist = ElementList.new # Initialize internal element list
45
- @pos = 0 # Position in the sentence
46
- @id = 1 # ID for the next element
47
- @level = 0 # Level in the diagram
48
- @tncnt = Hash.new # Node type counts
49
- end
50
-
51
- # caution: quick and dirty solution
52
- def valid?
53
- if(@data.length < 1)
54
- return false
55
- end
56
-
57
- if /\[\s*\]/m =~ @data
58
- return false
59
- end
60
-
61
- if /\[\_/ =~ @data
62
- return false
63
- end
64
-
65
- text = @data.strip
66
- text_r = text.split(//)
67
- open_br, close_br = [], []
68
- escape = false
69
- text_r.each do |chr|
70
- if chr == "\\"
71
- escape = true
72
- elsif chr == '[' && !escape
73
- open_br.push(chr)
74
- elsif chr == ']' && !escape
75
- close_br.push(chr)
76
- if open_br.length < close_br.length
77
- break
14
+ require 'utils'
15
+
16
+ module RSyntaxTree
17
+ class StringParser
18
+
19
+ attr_accessor :data, :elist, :pos, :id, :level
20
+ def initialize(str, fontset, fontsize)
21
+ # Clean up the data a little to make processing easier
22
+ # repeated newlines => a newline
23
+ string = str.gsub(/[\n\r]+/m, "\n")
24
+ # a backslash followed by a newline => a backslash followed by an 'n'
25
+ string.gsub!(/\\\n\s*/m, "\\n")
26
+ # repeated whitespace characters => " "
27
+ string.gsub!(/\s+/, " ")
28
+ string.gsub!(/\]\s+\[/, "][")
29
+ string.gsub!(/\s+\[/, "[")
30
+ string.gsub!(/\[\s+/, "[")
31
+ string.gsub!(/\s+\]/, "]")
32
+ string.gsub!(/\]\s+/, "]")
33
+ string.gsub!(/<(\d*)>/) do
34
+ num_padding = $1.to_i
35
+ if num_padding > 0
36
+ result = WHITESPACE_BLOCK * num_padding
37
+ else
38
+ result = WHITESPACE_BLOCK
78
39
  end
79
- elsif escape
80
- escape = false
40
+ result
81
41
  end
82
- end
83
-
84
- return false unless open_br.length == close_br.length
85
- # make_tree(0)
86
- # return false if @tncnt.empty?
87
- # @tncnt.each do |key, value|
88
- # return false if key == ""
89
- # end
90
- return true
91
- end
92
-
93
- def parse
94
- make_tree(0);
95
- end
96
42
 
97
- def get_elementlist
98
- @elist;
99
- end
43
+ @data = string # Store it for later...
44
+ if @data.contains_cjk?
45
+ fontset[:normal] = fontset[:cjk]
46
+ end
47
+ @elist = ElementList.new # Initialize internal element list
48
+ @pos = 0 # Position in the sentence
49
+ @id = 1 # ID for the next element
50
+ @level = 0 # Level in the diagram
51
+ @fontset = fontset
52
+ @fontsize = fontsize
53
+ end
100
54
 
101
- def auto_subscript
102
- elements = @elist.get_elements
103
- tmpcnt = Hash.new
104
- elements.each do |element|
105
- if(element.type == ETYPE_NODE)
106
- count = 1
107
- content = element.content
55
+ def self.valid?(data)
56
+ if(data.length < 1)
57
+ raise RSTError, "Error: input text is empty"
58
+ end
108
59
 
109
- if @tncnt[content]
110
- count = @tncnt[content]
111
- end
60
+ if /\[\s*\]/m =~ data
61
+ raise RSTError, "Error: inside the brackets is empty"
62
+ end
112
63
 
113
- if(count > 1)
114
- if tmpcnt[content]
115
- tmpcnt[content] += 1
64
+ text = data.strip
65
+ text_r = text.split(//)
66
+ open_br, close_br = [], []
67
+ escape = false
68
+ text_r.each do |chr|
69
+ if chr == "\\"
70
+ if escape
71
+ escape = false
116
72
  else
117
- tmpcnt[content] = 1
73
+ escape = true
118
74
  end
75
+ next
76
+ end
119
77
 
120
- element.content += ("_" + tmpcnt[content].to_s)
78
+ if escape && /[\[\]]/ =~ chr
79
+ escape = false
80
+ next
81
+ elsif chr == '['
82
+ open_br.push(chr)
83
+ elsif chr == ']'
84
+ close_br.push(chr)
85
+ if open_br.length < close_br.length
86
+ break
87
+ end
121
88
  end
89
+ escape = false
90
+ end
122
91
 
92
+ if open_br.empty? && close_br.empty?
93
+ raise RSTError, "Error: input text does not contain paired brackets"
94
+ elsif open_br.length == close_br.length
95
+ return true
96
+ else
97
+ raise RSTError, "Error: open and close brackets do not match"
123
98
  end
124
- end
125
- @tncnt
126
- end
127
-
128
- def count_node(name)
129
- name = name.strip
130
- if @tncnt[name]
131
- @tncnt[name] += 1
132
- else
133
- @tncnt[name] = 1
134
99
  end
135
- end
136
100
 
137
- def get_next_token
138
- data = @data.split(//)
139
- gottoken = false
140
- token = ""
141
- i = 0
101
+ def parse
102
+ make_tree(0);
103
+ @elist.set_hierarchy
104
+ end
142
105
 
143
- if((@pos + 1) >= data.length)
144
- return ""
106
+ def get_elementlist
107
+ @elist;
145
108
  end
146
109
 
147
- escape = false
148
- while(((@pos + i) < data.length) && !gottoken)
149
- ch = data[@pos + i];
150
- case ch
151
- when "["
152
- if escape
153
- token += ch
154
- escape = false
155
- else
156
- if(i > 0)
110
+ def get_next_token
111
+ data = @data.split(//)
112
+ gottoken = false
113
+ token = ""
114
+ i = 0
115
+
116
+ if((@pos + 1) >= data.length)
117
+ return ""
118
+ end
119
+
120
+ escape = false
121
+ while(((@pos + i) < data.length) && !gottoken)
122
+ ch = data[@pos + i];
123
+ case ch
124
+ when "["
125
+ if escape
126
+ token += ch
127
+ escape = false
128
+ else
129
+ if(i > 0)
130
+ gottoken = true
131
+ else
132
+ token += ch
133
+ end
134
+ end
135
+ when "]"
136
+ if escape
137
+ token += ch
138
+ escape = false
139
+ else
140
+ if(i == 0 )
141
+ token += ch
142
+ end
157
143
  gottoken = true
144
+ end
145
+ when "\\"
146
+ if escape
147
+ token += '\\\\'
148
+ escape = false
149
+ else
150
+ escape = true
151
+ end
152
+ when " "
153
+ if escape
154
+ token += '\\n'
155
+ escape = false
158
156
  else
159
157
  token += ch
160
158
  end
161
- end
162
- when "]"
163
- if escape
164
- token += ch
165
- escape = false
166
- else
167
- if(i == 0 )
159
+ when /[n{}<>^+*_=~\|\-]/
160
+ if escape
161
+ token += '\\' + ch
162
+ escape = false
163
+ else
168
164
  token += ch
169
165
  end
170
- gottoken = true
171
- end
172
- when "\\"
173
- escape = true
174
- when "n", " ", "+", "-", "=", "~", "#", "*"
175
- if escape
176
- token += "\\#{ch}"
177
- escape = false
178
166
  else
179
- token += ch
167
+ if escape
168
+ token += ch
169
+ escape = false
170
+ else
171
+ token += ch
172
+ end
180
173
  end
181
- # when /[\n\r]/
182
- # gottoken = false # same as do nothing
183
- else
184
- token += ch
185
- escape = false if escape
174
+ i += 1
186
175
  end
187
- i += 1
188
- end
189
176
 
190
- if(i > 1)
191
- @pos += (i - 1)
192
- else
193
- @pos += 1
177
+ if(i > 1)
178
+ @pos += (i - 1)
179
+ else
180
+ @pos += 1
181
+ end
182
+ return token
194
183
  end
195
- return token
196
- end
197
184
 
198
- def make_tree(parent)
199
- token = get_next_token.strip
200
- parts = Array.new
201
-
202
- while(token != "" && token != "]" )
203
- token_r = token.split(//)
204
- case token_r[0]
205
- when "["
206
- tl = token_r.length
207
- token_r = token_r[1, tl - 1]
208
- spaceat = token_r.index(" ")
209
- newparent = -1
210
-
211
- if spaceat
212
- parts[0] = token_r[0, spaceat].join
213
- parts[0] = parts[0].gsub("<>", " ")
214
-
215
- tl =token_r.length
216
- parts[1] = token_r[spaceat, tl - spaceat].join
217
- parts[1] = parts[1].gsub("<>", " ")
218
-
219
- element = Element.new(@id, parent, parts[0], @level)
220
- @id += 1
221
- @elist.add(element)
222
- newparent = element.id
223
- count_node(parts[0])
224
-
225
- element = Element.new(@id, @id - 1, parts[1], @level + 1 )
226
- @id += 1
227
- @elist.add(element)
228
- else
229
- joined = token_r.join.gsub("<>", " ")
230
- element = Element.new(@id, parent, joined, @level)
231
- @id += 1
232
- newparent = element.id
233
- @elist.add(element)
234
- count_node(joined)
235
- end
185
+ def make_tree(parent)
186
+ token = get_next_token.strip
187
+ parts = Array.new
188
+
189
+ while(token != "" && token != "]" )
190
+ token_r = token.split(//)
191
+ case token_r[0]
192
+ when "["
193
+ tl = token_r.length
194
+ token_r = token_r[1, tl - 1]
195
+ spaceat = token_r.index(" ")
196
+ newparent = -1
197
+
198
+ if spaceat
199
+ parts[0] = token_r[0, spaceat].join
200
+
201
+ tl =token_r.length
202
+ parts[1] = token_r[spaceat, tl - spaceat].join
203
+
204
+ element = Element.new(@id, parent, parts[0], @level, @fontset, @fontsize)
205
+ @id += 1
206
+ @elist.add(element)
207
+ newparent = element.id
208
+
209
+ element = Element.new(@id, @id - 1, parts[1], @level + 1, @fontset, @fontsize)
210
+ @id += 1
211
+ @elist.add(element)
212
+ else
213
+ joined = token_r.join
214
+ element = Element.new(@id, parent, joined, @level, @fontset, @fontsize)
215
+ @id += 1
216
+ newparent = element.id
217
+ @elist.add(element)
218
+ end
236
219
 
237
- @level += 1
238
- make_tree(newparent)
220
+ @level += 1
221
+ make_tree(newparent)
239
222
 
240
- else
241
- if token.strip != ""
242
- element = Element.new(@id, parent, token, @level)
243
- @id += 1
244
- @elist.add(element)
245
- count_node(token)
223
+ else
224
+ if token.strip != ""
225
+ element = Element.new(@id, parent, token, @level, @fontset, @fontsize)
226
+ @id += 1
227
+ @elist.add(element)
228
+ end
246
229
  end
247
- end
248
230
 
249
- token = get_next_token
231
+ token = get_next_token
232
+ end
233
+ @level -= 1
250
234
  end
251
- @level -= 1
252
235
  end
253
236
  end
254
237