jkf 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,422 +1,420 @@
1
- # coding: utf-8
1
+ module Jkf
2
+ module Parser
3
+ # KI2 Parser
4
+ class Ki2 < Base
5
+ include Kifuable
2
6
 
3
- module Jkf::Parser
4
- # KI2 Parser
5
- class Ki2 < Base
6
- include Kifuable
7
+ protected
7
8
 
8
- protected
9
-
10
- # kifu : header* initialboard? header* moves fork*
11
- def parse_root
12
- s0 = @current_pos
13
- s1 = []
14
- s2 = parse_header
15
- while s2 != :failed
16
- s1 << s2
9
+ # kifu : header* initialboard? header* moves fork*
10
+ def parse_root
11
+ s0 = @scanner.pos
12
+ s1 = []
17
13
  s2 = parse_header
18
- end
19
- if s1 == :failed
20
- @current_pos = s0
21
- s0 = :failed
22
- else
23
- s2 = parse_initialboard
24
- s2 = nil if s2 == :failed
25
- s3 = []
26
- s4 = parse_header
27
- while s4 != :failed
28
- s3 << s4
29
- s4 = parse_header
14
+ while s2 != :failed
15
+ s1 << s2
16
+ s2 = parse_header
30
17
  end
31
- s4 = parse_moves
32
- if s4 == :failed
33
- @current_pos = s0
18
+ if s1 == :failed
19
+ @scanner.pos = s0
34
20
  s0 = :failed
35
21
  else
36
- s5 = []
37
- s6 = parse_fork
38
- while s6 != :failed
39
- s5 << s6
22
+ s2 = parse_initialboard
23
+ s2 = nil if s2 == :failed
24
+ s3 = []
25
+ s4 = parse_header
26
+ while s4 != :failed
27
+ s3 << s4
28
+ s4 = parse_header
29
+ end
30
+ s4 = parse_moves
31
+ if s4 == :failed
32
+ @scanner.pos = s0
33
+ s0 = :failed
34
+ else
35
+ s5 = []
40
36
  s6 = parse_fork
37
+ while s6 != :failed
38
+ s5 << s6
39
+ s6 = parse_fork
40
+ end
41
+ @reported_pos = s0
42
+ s0 = transform_root(s1, s2, s3, s4, s5)
41
43
  end
42
- @reported_pos = s0
43
- s0 = transform_root(s1, s2, s3, s4, s5)
44
44
  end
45
+ s0
45
46
  end
46
- s0
47
- end
48
47
 
49
- # header : [^:\r\n]+ ":" nonls nl+ | header_teban
50
- def parse_header
51
- s0 = @current_pos
52
- s2 = match_regexp(/^[^*:\r\n]/)
53
- if s2 == :failed
54
- s1 = :failed
55
- else
56
- s1 = []
57
- while s2 != :failed
58
- s1 << s2
59
- s2 = match_regexp(/^[^:\r\n]/)
60
- end
61
- end
62
- if s1 == :failed
63
- @current_pos = s0
64
- s0 = :failed
65
- elsif match_str(":") != :failed
66
- s3 = parse_nonls
67
- s5 = parse_nl
68
- if s5 == :failed
69
- s4 = :failed
48
+ # header : [^:\r\n]+ ":" nonls nl+ | header_teban
49
+ def parse_header
50
+ s0 = @scanner.pos
51
+ s2 = match_regexp(/[^*:\r\n]/)
52
+ if s2 == :failed
53
+ s1 = :failed
70
54
  else
71
- s4 = []
72
- while s5 != :failed
73
- s4 << s5
74
- s5 = parse_nl
55
+ s1 = []
56
+ while s2 != :failed
57
+ s1 << s2
58
+ s2 = match_regexp(/[^:\r\n]/)
75
59
  end
76
60
  end
77
- if s4 == :failed
78
- @current_pos = s0
61
+ if s1 == :failed
62
+ @scanner.pos = s0
79
63
  s0 = :failed
64
+ elsif match_str(':') != :failed
65
+ s3 = parse_nonls
66
+ s5 = parse_nl
67
+ if s5 == :failed
68
+ s4 = :failed
69
+ else
70
+ s4 = []
71
+ while s5 != :failed
72
+ s4 << s5
73
+ s5 = parse_nl
74
+ end
75
+ end
76
+ if s4 == :failed
77
+ @scanner.pos = s0
78
+ s0 = :failed
79
+ else
80
+ @reported_pos = s0
81
+ s0 = { 'k' => s1.join, 'v' => s3.join }
82
+ end
80
83
  else
81
- @reported_pos = s0
82
- s0 = { "k" => s1.join, "v" => s3.join }
84
+ @scanner.pos = s0
85
+ s0 = :failed
83
86
  end
84
- else
85
- @current_pos = s0
86
- s0 = :failed
87
+ s0 = parse_header_teban if s0 == :failed
88
+ s0
87
89
  end
88
- s0 = parse_header_teban if s0 == :failed
89
- s0
90
- end
91
90
 
92
- # header_teban : [先後上下] "手番" nl
93
- def parse_header_teban
94
- s0 = @current_pos
95
- s1 = parse_turn
96
- if s1 == :failed
97
- @current_pos = s0
98
- :failed
99
- else
100
- s2 = match_str("手番")
101
- if s2 == :failed
102
- @current_pos = s0
91
+ # header_teban : [先後上下] "手番" nl
92
+ def parse_header_teban
93
+ s0 = @scanner.pos
94
+ s1 = parse_turn
95
+ if s1 == :failed
96
+ @scanner.pos = s0
103
97
  :failed
104
98
  else
105
- s3 = parse_nl
106
- if s3 == :failed
107
- @current_pos = s0
99
+ s2 = match_str('手番')
100
+ if s2 == :failed
101
+ @scanner.pos = s0
108
102
  :failed
109
103
  else
110
- @reported_pos = s0
111
- { "k" => "手番", "v" => s1 }
104
+ s3 = parse_nl
105
+ if s3 == :failed
106
+ @scanner.pos = s0
107
+ :failed
108
+ else
109
+ @reported_pos = s0
110
+ { 'k' => '手番', 'v' => s1 }
111
+ end
112
112
  end
113
113
  end
114
114
  end
115
- end
116
115
 
117
- # moves : firstboard : move* result?
118
- def parse_moves
119
- s0 = @current_pos
120
- s1 = parse_firstboard
121
- if s1 == :failed
122
- @current_pos = s0
123
- s0 = :failed
124
- else
125
- s2 = []
126
- s3 = parse_move
127
- while s3 != :failed
128
- s2 << s3
116
+ # moves : firstboard : move* result?
117
+ def parse_moves
118
+ s0 = @scanner.pos
119
+ s1 = parse_firstboard
120
+ if s1 == :failed
121
+ @scanner.pos = s0
122
+ s0 = :failed
123
+ else
124
+ s2 = []
129
125
  s3 = parse_move
126
+ while s3 != :failed
127
+ s2 << s3
128
+ s3 = parse_move
129
+ end
130
+ s3 = parse_result
131
+ s3 = nil if s3 == :failed
132
+ @reported_pos = s0
133
+ s0 = -> (hd, tl, res) do
134
+ tl.unshift(hd)
135
+ tl << { 'special' => res } if res && !tl[tl.length - 1]['special']
136
+ tl
137
+ end.call(s1, s2, s3)
130
138
  end
131
- s3 = parse_result
132
- s3 = nil if s3 == :failed
133
- @reported_pos = s0
134
- s0 = -> (hd, tl, res) do
135
- tl.unshift(hd)
136
- tl << { "special" => res } if res && !tl[tl.length - 1]["special"]
137
- tl
138
- end.call(s1, s2, s3)
139
+ s0
139
140
  end
140
- s0
141
- end
142
141
 
143
- # firstboard : comment* pointer?
144
- def parse_firstboard
145
- s0 = @current_pos
146
- s1 = []
147
- s2 = parse_comment
148
- while s2 != :failed
149
- s1 << s2
142
+ # firstboard : comment* pointer?
143
+ def parse_firstboard
144
+ s0 = @scanner.pos
145
+ s1 = []
150
146
  s2 = parse_comment
147
+ while s2 != :failed
148
+ s1 << s2
149
+ s2 = parse_comment
150
+ end
151
+ parse_pointer
152
+ @reported_pos = s0
153
+ s1.empty? ? {} : { 'comments' => s1 }
151
154
  end
152
- parse_pointer
153
- @reported_pos = s0
154
- s0 = s1.empty? ? {} : { "comments" => s1 }
155
- s0
156
- end
157
155
 
158
- # move : line comment* pointer? (nl | " ")*
159
- def parse_move
160
- s0 = @current_pos
161
- s1 = parse_line
162
- if s1 == :failed
163
- @current_pos = s0
164
- s0 = :failed
165
- else
166
- s2 = []
167
- s3 = parse_comment
168
- while s3 != :failed
169
- s2 << s3
156
+ # move : line comment* pointer? (nl | " ")*
157
+ def parse_move
158
+ s0 = @scanner.pos
159
+ s1 = parse_line
160
+ if s1 == :failed
161
+ @scanner.pos = s0
162
+ s0 = :failed
163
+ else
164
+ s2 = []
170
165
  s3 = parse_comment
171
- end
172
- parse_pointer
173
- s4 = []
174
- s5 = parse_nl
175
- s5 = match_space if s5 == :failed
176
- while s5 != :failed
177
- s4 << s5
166
+ while s3 != :failed
167
+ s2 << s3
168
+ s3 = parse_comment
169
+ end
170
+ parse_pointer
171
+ s4 = []
178
172
  s5 = parse_nl
179
173
  s5 = match_space if s5 == :failed
174
+ while s5 != :failed
175
+ s4 << s5
176
+ s5 = parse_nl
177
+ s5 = match_space if s5 == :failed
178
+ end
179
+ @reported_pos = s0
180
+ s0 = -> (line, c) do
181
+ ret = { 'move' => line }
182
+ ret['comments'] = c unless c.empty?
183
+ ret
184
+ end.call(s1, s2)
180
185
  end
181
- @reported_pos = s0
182
- s0 = -> (line, c) do
183
- ret = { "move" => line }
184
- ret["comments"] = c if !c.empty?
185
- ret
186
- end.call(s1, s2)
187
- end
188
186
 
189
- s0
190
- end
187
+ s0
188
+ end
191
189
 
192
- # line : [▲△] fugou (nl / " ")*
193
- def parse_line
194
- s0 = @current_pos
195
- s1 = match_regexp(/^[▲△]/)
196
- if s1 == :failed
197
- @current_pos = s0
198
- s0 = :failed
199
- else
200
- s1 = if s1 == "▲"
201
- { "color" => 0 }
202
- else
203
- { "color" => 1 }
204
- end
205
- s2 = parse_fugou
206
- if s2 == :failed
207
- @current_pos = s0
190
+ # line : [▲△] fugou (nl / " ")*
191
+ def parse_line
192
+ s0 = @scanner.pos
193
+ s1 = match_regexp(/[▲△]/)
194
+ if s1 == :failed
195
+ @scanner.pos = s0
208
196
  s0 = :failed
209
197
  else
210
- s3 = []
211
- s4 = parse_nl
212
- s4 = match_space if s4 == :failed
213
- while s4 != :failed
214
- s3 << s4
198
+ s1 = if s1 == '▲'
199
+ { 'color' => 0 }
200
+ else
201
+ { 'color' => 1 }
202
+ end
203
+ s2 = parse_fugou
204
+ if s2 == :failed
205
+ @scanner.pos = s0
206
+ s0 = :failed
207
+ else
208
+ s3 = []
215
209
  s4 = parse_nl
216
210
  s4 = match_space if s4 == :failed
211
+ while s4 != :failed
212
+ s3 << s4
213
+ s4 = parse_nl
214
+ s4 = match_space if s4 == :failed
215
+ end
216
+ @reported_pos = s0
217
+ s0 = s2.merge(s1)
217
218
  end
218
- @reported_pos = s0
219
- s0 = s2.merge(s1)
220
219
  end
220
+ s0
221
221
  end
222
- s0
223
- end
224
222
 
225
- # fugou : place piece soutai? dousa? ("成" | "不成")? "打"?
226
- def parse_fugou
227
- s0 = @current_pos
228
- s1 = parse_place
229
- if s1 == :failed
230
- @current_pos = s0
231
- :failed
232
- else
233
- s2 = parse_piece
234
- if s2 == :failed
235
- @current_pos = s0
223
+ # fugou : place piece soutai? dousa? ("成" | "不成")? "打"?
224
+ def parse_fugou
225
+ s0 = @scanner.pos
226
+ s1 = parse_place
227
+ if s1 == :failed
228
+ @scanner.pos = s0
236
229
  :failed
237
230
  else
238
- s3 = parse_soutai
239
- s3 = nil if s3 == :failed
240
- s4 = parse_dousa
241
- s4 = nil if s4 == :failed
242
- s5 = match_str("成")
243
- s5 = match_str("不成") if s5 == :failed
244
- s5 = nil if s5 == :failed
245
- s6 = match_str("打")
246
- s6 = nil if s6 == :failed
247
- @reported_pos = s0
248
- transform_fugou(s1, s2, s3, s4, s5, s6)
231
+ s2 = parse_piece
232
+ if s2 == :failed
233
+ @scanner.pos = s0
234
+ :failed
235
+ else
236
+ s3 = parse_soutai
237
+ s3 = nil if s3 == :failed
238
+ s4 = parse_dousa
239
+ s4 = nil if s4 == :failed
240
+ s5 = match_str('成')
241
+ s5 = match_str('不成') if s5 == :failed
242
+ s5 = nil if s5 == :failed
243
+ s6 = match_str('打')
244
+ s6 = nil if s6 == :failed
245
+ @reported_pos = s0
246
+ transform_fugou(s1, s2, s3, s4, s5, s6)
247
+ end
249
248
  end
250
249
  end
251
- end
252
250
 
253
- # place : num numkan
254
- def parse_place
255
- s0 = @current_pos
256
- s1 = parse_num
257
- if s1 == :failed
258
- @current_pos = s0
259
- s0 = :failed
260
- else
261
- s2 = parse_numkan
262
- if s2 == :failed
263
- @current_pos = s0
251
+ # place : num numkan
252
+ def parse_place
253
+ s0 = @scanner.pos
254
+ s1 = parse_num
255
+ if s1 == :failed
256
+ @scanner.pos = s0
264
257
  s0 = :failed
265
258
  else
266
- @reported_pos = s0
267
- s0 = { "x" => s1, "y" => s2 }
259
+ s2 = parse_numkan
260
+ if s2 == :failed
261
+ @scanner.pos = s0
262
+ s0 = :failed
263
+ else
264
+ @reported_pos = s0
265
+ s0 = { 'x' => s1, 'y' => s2 }
266
+ end
268
267
  end
269
- end
270
- if s0 == :failed
271
- s0 = @current_pos
272
- if match_regexp("同") == :failed
273
- @current_pos = s0
274
- s0 = :failed
275
- else
276
- match_str(" ")
277
- @reported_pos = s0
278
- s0 = { "same" => true }
268
+ if s0 == :failed
269
+ s0 = @scanner.pos
270
+ if match_regexp('同') == :failed
271
+ @scanner.pos = s0
272
+ s0 = :failed
273
+ else
274
+ match_str(' ')
275
+ @reported_pos = s0
276
+ s0 = { 'same' => true }
277
+ end
279
278
  end
279
+ s0
280
280
  end
281
- s0
282
- end
283
281
 
284
- # soutai : [左直右]
285
- def parse_soutai
286
- match_regexp(/^[左直右]/)
287
- end
282
+ # soutai : [左直右]
283
+ def parse_soutai
284
+ match_regexp(/[左直右]/)
285
+ end
288
286
 
289
- # dousa : [上寄引]
290
- def parse_dousa
291
- match_regexp(/^[上寄引]/)
292
- end
287
+ # dousa : [上寄引]
288
+ def parse_dousa
289
+ match_regexp(/[上寄引]/)
290
+ end
293
291
 
294
- # "*" nonls nl
295
- def parse_comment
296
- s0 = @current_pos
297
- if match_str("*") == :failed
298
- @current_pos = s0
299
- s0 = :failed
300
- else
301
- s2 = parse_nonls
302
- if parse_nl == :failed
303
- @current_pos = s0
292
+ # "*" nonls nl
293
+ def parse_comment
294
+ s0 = @scanner.pos
295
+ if match_str('*') == :failed
296
+ @scanner.pos = s0
304
297
  s0 = :failed
305
298
  else
306
- @reported_pos = s0
307
- s0 = s2.join
299
+ s2 = parse_nonls
300
+ if parse_nl == :failed
301
+ @scanner.pos = s0
302
+ s0 = :failed
303
+ else
304
+ @reported_pos = s0
305
+ s0 = s2.join
306
+ end
308
307
  end
308
+ s0
309
309
  end
310
- s0
311
- end
312
310
 
313
- # fork : "変化:" " "* [0-9]+ "手" nl moves
314
- def parse_fork
315
- s0 = @current_pos
316
- if match_str("変化:") == :failed
317
- @current_pos = s0
318
- s0 = :failed
319
- else
320
- match_spaces
321
- s3 = match_digits!
322
- if s3 == :failed
323
- @current_pos = s0
311
+ # fork : "変化:" " "* [0-9]+ "手" nl moves
312
+ def parse_fork
313
+ s0 = @scanner.pos
314
+ if match_str('変化:') == :failed
315
+ @scanner.pos = s0
324
316
  s0 = :failed
325
- elsif match_str("手") != :failed
326
- if parse_nl == :failed
327
- @current_pos = s0
317
+ else
318
+ match_spaces
319
+ s3 = match_digits!
320
+ if s3 == :failed
321
+ @scanner.pos = s0
328
322
  s0 = :failed
329
- else
330
- s6 = parse_moves
331
- if s6 == :failed
332
- @current_pos = s0
323
+ elsif match_str('手') != :failed
324
+ if parse_nl == :failed
325
+ @scanner.pos = s0
333
326
  s0 = :failed
334
327
  else
335
- @reported_pos = s0
336
- s0 = { "te" => s3.join.to_i, "moves" => s6[1..-1] }
328
+ s6 = parse_moves
329
+ if s6 == :failed
330
+ @scanner.pos = s0
331
+ s0 = :failed
332
+ else
333
+ @reported_pos = s0
334
+ s0 = { 'te' => s3.join.to_i, 'moves' => s6[1..-1] }
335
+ end
337
336
  end
337
+ else
338
+ @scanner.pos = s0
339
+ s0 = :failed
338
340
  end
339
- else
340
- @current_pos = s0
341
- s0 = :failed
342
341
  end
342
+ s0
343
343
  end
344
- s0
345
- end
346
344
 
347
- # turn : [先後上下]
348
- def parse_turn
349
- match_regexp(/^[先後上下]/)
350
- end
345
+ # turn : [先後上下]
346
+ def parse_turn
347
+ match_regexp(/[先後上下]/)
348
+ end
351
349
 
352
- # transform to jkf
353
- def transform_root(headers, ini, headers2, moves, forks)
354
- ret = { "header" => {}, "moves" => moves }
355
- headers.compact.each { |h| ret["header"][h["k"]] = h["v"] }
356
- headers2.compact.each { |h| ret["header"][h["k"]] = h["v"] }
357
- if ini
358
- ret["initial"] = ini
359
- elsif ret["header"]["手合割"]
360
- preset = preset2str(ret["header"]["手合割"])
361
- ret["initial"] = { "preset" => preset } if preset != "OTHER"
350
+ # transform to jkf
351
+ def transform_root(headers, ini, headers2, moves, forks)
352
+ ret = { 'header' => {}, 'moves' => moves }
353
+ headers.compact.each { |h| ret['header'][h['k']] = h['v'] }
354
+ headers2.compact.each { |h| ret['header'][h['k']] = h['v'] }
355
+ if ini
356
+ ret['initial'] = ini
357
+ elsif ret['header']['手合割']
358
+ preset = preset2str(ret['header']['手合割'])
359
+ ret['initial'] = { 'preset' => preset } if preset != 'OTHER'
360
+ end
361
+ transform_root_header_data(ret) if ret['initial'] && ret['initial']['data']
362
+ transform_root_forks(forks, moves)
363
+ ret
362
364
  end
363
- transform_root_header_data(ret) if ret["initial"] && ret["initial"]["data"]
364
- transform_root_forks(forks, moves)
365
- ret
366
- end
367
365
 
368
- # transfrom fugou to jkf
369
- def transform_fugou(pl, pi, sou, dou, pro, da)
370
- ret = { "piece" => pi }
371
- if pl["same"]
372
- ret["same"] = true
373
- else
374
- ret["to"] = pl
366
+ # transfrom fugou to jkf
367
+ def transform_fugou(pl, pi, sou, dou, pro, da)
368
+ ret = { 'piece' => pi }
369
+ if pl['same']
370
+ ret['same'] = true
371
+ else
372
+ ret['to'] = pl
373
+ end
374
+ ret['promote'] = (pro == '成') if pro
375
+ if da
376
+ ret['relative'] = 'H'
377
+ else
378
+ rel = soutai2relative(sou) + dousa2relative(dou)
379
+ ret['relative'] = rel unless rel.empty?
380
+ end
381
+ ret
375
382
  end
376
- ret["promote"] = (pro == "成") if pro
377
- if da
378
- ret["relative"] = "H"
379
- else
380
- rel = soutai2relative(sou) + dousa2relative(dou)
381
- ret["relative"] = rel unless rel.empty?
383
+
384
+ # relative string to jkf
385
+ def soutai2relative(str)
386
+ {
387
+ '左' => 'L',
388
+ '直' => 'C',
389
+ '右' => 'R'
390
+ }[str] || ''
382
391
  end
383
- ret
384
- end
385
392
 
386
- # relative string to jkf
387
- def soutai2relative(str)
388
- {
389
- "左" => "L",
390
- "直" => "C",
391
- "右" => "R"
392
- }[str] || ""
393
- end
393
+ # movement string to jkf
394
+ def dousa2relative(str)
395
+ {
396
+ '上' => 'U',
397
+ '寄' => 'M',
398
+ '引' => 'D'
399
+ }[str] || ''
400
+ end
394
401
 
395
- # movement string to jkf
396
- def dousa2relative(str)
397
- {
398
- "上" => "U",
399
- "寄" => "M",
400
- "引" => "D"
401
- }[str] || ""
402
- end
402
+ # generate motigoma
403
+ def make_hand(str)
404
+ ret = { 'FU' => 0, 'KY' => 0, 'KE' => 0, 'GI' => 0, 'KI' => 0, 'KA' => 0, 'HI' => 0 }
405
+ return ret if str.empty?
403
406
 
404
- # generate motigoma
405
- def make_hand(str)
406
- ret = { "FU" => 0, "KY" => 0, "KE" => 0, "GI" => 0, "KI" => 0, "KA" => 0, "HI" => 0 }
407
- return ret if str.empty?
407
+ str.gsub(/ $/, '').split(' ').each do |kind|
408
+ next if kind.empty?
409
+ ret[kind2csa(kind[0])] = kind.length == 1 ? 1 : kan2n2(kind[1..-1])
410
+ end
408
411
 
409
- str.gsub(/ $/, "").split(" ").each do |kind|
410
- next if kind.empty?
411
- ret[kind2csa(kind[0])] = kind.length == 1 ? 1 : kan2n2(kind[1..-1])
412
+ ret
412
413
  end
413
414
 
414
- ret
415
- end
416
-
417
- # check eos
418
- def eos?
419
- @input[@current_pos].nil?
415
+ def eos?
416
+ @scanner.eos?
417
+ end
420
418
  end
421
419
  end
422
420
  end