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,817 +1,814 @@
1
- # coding: utf-8
2
-
3
- module Jkf::Parser
4
- # CSA Parser
5
- class Csa < Base
6
- protected
7
-
8
- # kifu : csa2 | csa1
9
- def parse_root
10
- @input += "\n" unless @input[-1] =~ /\n|\r|,/ # FIXME
11
- s0 = parse_csa2
12
- s0 = parse_csa1 if s0 == :failed
13
- s0
14
- end
15
-
16
- # csa2 : version22 information? initialboard moves?
17
- def parse_csa2
18
- s0 = @current_pos
19
- if parse_version22 == :failed
20
- @current_pos = s0
21
- s0 = :failed
22
- else
23
- s1 = parse_information
24
- s1 = nil if s1 == :failed
25
- s2 = parse_initial_board
26
- if s2 == :failed
27
- @current_pos = s0
1
+ module Jkf
2
+ module Parser
3
+ # CSA Parser
4
+ class Csa < Base
5
+ protected
6
+
7
+ # kifu : csa2 | csa1
8
+ def parse_root
9
+ @scanner << "\n" unless @scanner.string[-1] =~ /\n|\r|,/ # FIXME
10
+ s0 = parse_csa2
11
+ s0 = parse_csa1 if s0 == :failed
12
+ s0
13
+ end
14
+
15
+ # csa2 : version22 information? initialboard moves?
16
+ def parse_csa2
17
+ s0 = @scanner.pos
18
+ if parse_version22 == :failed
19
+ @scanner.pos = s0
28
20
  s0 = :failed
29
21
  else
30
- s3 = parse_moves
31
- s3 = nil if s3 == :failed
32
- @reported_pos = s0
33
- s0 = -> (info, ini, ms) do
34
- ret = { "header" => info["header"], "initial" => ini, "moves" => ms }
35
- if info && info["players"]
36
- ret["header"]["先手"] = info["players"][0] if info["players"][0]
37
- ret["header"]["後手"] = info["players"][1] if info["players"][1]
38
- end
39
- ret
40
- end.call(s1, s2, s3)
22
+ s1 = parse_information
23
+ s1 = nil if s1 == :failed
24
+ s2 = parse_initial_board
25
+ if s2 == :failed
26
+ @scanner.pos = s0
27
+ s0 = :failed
28
+ else
29
+ s3 = parse_moves
30
+ s3 = nil if s3 == :failed
31
+ @reported_pos = s0
32
+ s0 = -> (info, ini, ms) do
33
+ ret = { 'header' => info['header'], 'initial' => ini, 'moves' => ms }
34
+ if info && info['players']
35
+ ret['header']['先手'] = info['players'][0] if info['players'][0]
36
+ ret['header']['後手'] = info['players'][1] if info['players'][1]
37
+ end
38
+ ret
39
+ end.call(s1, s2, s3)
40
+ end
41
41
  end
42
+ s0
42
43
  end
43
- s0
44
- end
45
44
 
46
- # version22 : comment* "V2.2" nl
47
- def parse_version22
48
- s0 = @current_pos
49
- s1 = parse_comments
50
- s2 = match_str("V2.2")
51
- if s2 == :failed
52
- @current_pos = s0
53
- s0 = :failed
54
- else
55
- s3 = parse_nl
56
- if s3 == :failed
57
- @current_pos = s0
45
+ # version22 : comment* "V2.2" nl
46
+ def parse_version22
47
+ s0 = @scanner.pos
48
+ s1 = parse_comments
49
+ s2 = match_str('V2.2')
50
+ if s2 == :failed
51
+ @scanner.pos = s0
58
52
  s0 = :failed
59
53
  else
60
- s0 = [s1, s2, s3]
54
+ s3 = parse_nl
55
+ if s3 == :failed
56
+ @scanner.pos = s0
57
+ s0 = :failed
58
+ else
59
+ s0 = [s1, s2, s3]
60
+ end
61
61
  end
62
+ s0
62
63
  end
63
- s0
64
- end
65
64
 
66
- # information : players? headers
67
- def parse_information
68
- s0 = @current_pos
69
- s1 = parse_players
70
- s1 = nil if s1 == :failed
71
- s2 = parse_headers
72
- if s2 == :failed
73
- @current_pos = s0
74
- s0 = :failed
75
- else
76
- @reported_pos = s0
77
- s0 = { "players" => s1, "header" => s2 }
65
+ # information : players? headers
66
+ def parse_information
67
+ s0 = @scanner.pos
68
+ s1 = parse_players
69
+ s1 = nil if s1 == :failed
70
+ s2 = parse_headers
71
+ if s2 == :failed
72
+ @scanner.pos = s0
73
+ s0 = :failed
74
+ else
75
+ @reported_pos = s0
76
+ s0 = { 'players' => s1, 'header' => s2 }
77
+ end
78
+ s0
78
79
  end
79
- s0
80
- end
81
80
 
82
- # headers : header*
83
- def parse_headers
84
- s0 = @current_pos
85
- s1 = []
86
- s2 = parse_header
87
- while s2 != :failed
88
- s1 << s2
81
+ # headers : header*
82
+ def parse_headers
83
+ s0 = @scanner.pos
84
+ s1 = []
89
85
  s2 = parse_header
90
- end
91
- @reported_pos = s0
92
- s0 = -> (header) do
93
- ret = {}
94
- header.each do |data|
95
- ret[normalize_header_key(data["k"])] = data["v"]
86
+ while s2 != :failed
87
+ s1 << s2
88
+ s2 = parse_header
96
89
  end
97
- ret
98
- end.call(s1)
99
- s0
100
- end
90
+ @reported_pos = s0
91
+ -> (header) do
92
+ ret = {}
93
+ header.each do |data|
94
+ ret[normalize_header_key(data['k'])] = data['v']
95
+ end
96
+ ret
97
+ end.call(s1)
98
+ end
101
99
 
102
- # header : comment* "$" [^:]+ ":" nonls nl
103
- def parse_header
104
- s0 = @current_pos
105
- parse_comments
106
- if match_str("$") == :failed
107
- @current_pos = s0
108
- s0 = :failed
109
- else
110
- s4 = match_regexp(/^[^:]/)
111
- if s4 == :failed
112
- s3 = :failed
100
+ # header : comment* "$" [^:]+ ":" nonls nl
101
+ def parse_header
102
+ s0 = @scanner.pos
103
+ parse_comments
104
+ if match_str('$') == :failed
105
+ @scanner.pos = s0
106
+ s0 = :failed
113
107
  else
114
- s3 = []
115
- while s4 != :failed
116
- s3 << s4
117
- s4 = match_regexp(/^[^:]/)
108
+ s4 = match_regexp(/[^:]/)
109
+ if s4 == :failed
110
+ s3 = :failed
111
+ else
112
+ s3 = []
113
+ while s4 != :failed
114
+ s3 << s4
115
+ s4 = match_regexp(/[^:]/)
116
+ end
118
117
  end
119
- end
120
- if s3 == :failed
121
- @current_pos = s0
122
- s0 = :failed
123
- elsif match_str(":") != :failed
124
- s4 = parse_nonls
125
- if parse_nl == :failed
126
- @current_pos = s0
118
+ if s3 == :failed
119
+ @scanner.pos = s0
127
120
  s0 = :failed
121
+ elsif match_str(':') != :failed
122
+ s4 = parse_nonls
123
+ if parse_nl == :failed
124
+ @scanner.pos = s0
125
+ s0 = :failed
126
+ else
127
+ @reported_pos = s0
128
+ s0 = { 'k' => s3.join, 'v' => s4.join }
129
+ end
128
130
  else
129
- @reported_pos = s0
130
- s0 = { "k" => s3.join, "v" => s4.join }
131
+ @scanner.pos = s0
132
+ s0 = :failed
131
133
  end
132
- else
133
- @current_pos = s0
134
- s0 = :failed
135
134
  end
135
+ s0
136
136
  end
137
- s0
138
- end
139
137
 
140
- # csa1 : players? initialboard? moves
141
- def parse_csa1
142
- s0 = @current_pos
143
- s1 = parse_players
144
- s1 = nil if s1 == :failed
145
- s2 = parse_initial_board
146
- s2 = nil if s2 == :failed
147
- s3 = parse_moves
148
- if s3 == :failed
149
- @current_pos = s0
150
- s0 = :failed
151
- else
152
- @reported_pos = s0
153
- s0 = -> (ply, ini, ms) do
154
- ret = { "header" => {}, "initial" => ini, "moves" => ms }
155
- if ply
156
- ret["header"]["先手"] = ply[0] if ply[0]
157
- ret["header"]["後手"] = ply[1] if ply[1]
158
- end
159
- ret
160
- end.call(s1, s2, s3)
138
+ # csa1 : players? initialboard? moves
139
+ def parse_csa1
140
+ s0 = @scanner.pos
141
+ s1 = parse_players
142
+ s1 = nil if s1 == :failed
143
+ s2 = parse_initial_board
144
+ s2 = nil if s2 == :failed
145
+ s3 = parse_moves
146
+ if s3 == :failed
147
+ @scanner.pos = s0
148
+ s0 = :failed
149
+ else
150
+ @reported_pos = s0
151
+ s0 = -> (ply, ini, ms) do
152
+ ret = { 'header' => {}, 'initial' => ini, 'moves' => ms }
153
+ if ply
154
+ ret['header']['先手'] = ply[0] if ply[0]
155
+ ret['header']['後手'] = ply[1] if ply[1]
156
+ end
157
+ ret
158
+ end.call(s1, s2, s3)
159
+ end
160
+ s0
161
161
  end
162
- s0
163
- end
164
162
 
165
- # players : comment* ("N+" nonls nl)? comment* ("N-" nonls nl)?
166
- def parse_players
167
- s0 = @current_pos
168
- parse_comments
169
- s2 = @current_pos
170
- if match_str("N+") == :failed
171
- @current_pos = s2
172
- s2 = :failed
173
- else
174
- s4 = parse_nonls
175
- if parse_nl == :failed
176
- @current_pos = s2
163
+ # players : comment* ("N+" nonls nl)? comment* ("N-" nonls nl)?
164
+ def parse_players
165
+ s0 = @scanner.pos
166
+ parse_comments
167
+ s2 = @scanner.pos
168
+ if match_str('N+') == :failed
169
+ @scanner.pos = s2
177
170
  s2 = :failed
178
171
  else
179
- @reported_pos = s2
180
- s2 = s4
181
- end
182
- end
183
- s2 = nil if s2 == :failed
184
- parse_comments
185
- s4 = @current_pos
186
- if match_str("N-") == :failed
187
- @current_pos = s4
188
- s4 = :failed
189
- else
190
- s6 = parse_nonls
191
- if parse_nl == :failed
192
- @current_pos = s4
172
+ s4 = parse_nonls
173
+ if parse_nl == :failed
174
+ @scanner.pos = s2
175
+ s2 = :failed
176
+ else
177
+ @reported_pos = s2
178
+ s2 = s4
179
+ end
180
+ end
181
+ s2 = nil if s2 == :failed
182
+ parse_comments
183
+ s4 = @scanner.pos
184
+ if match_str('N-') == :failed
185
+ @scanner.pos = s4
193
186
  s4 = :failed
194
187
  else
195
- @reported_pos = s4
196
- s4 = s6
188
+ s6 = parse_nonls
189
+ if parse_nl == :failed
190
+ @scanner.pos = s4
191
+ s4 = :failed
192
+ else
193
+ @reported_pos = s4
194
+ s4 = s6
195
+ end
197
196
  end
197
+ s4 = nil if s4 == :failed
198
+ @reported_pos = s0
199
+ [s2&.join, s4&.join]
198
200
  end
199
- s4 = nil if s4 == :failed
200
- @reported_pos = s0
201
- s0 = [(s2 ? s2.join : nil), (s4 ? s4.join : nil)]
202
- s0
203
- end
204
201
 
205
- # initialboard : comment* (hirate | ikkatsu | "") komabetsu comment* teban nl
206
- def parse_initial_board
207
- s0 = @current_pos
208
- parse_comments
209
- s2 = parse_hirate
210
- if s2 == :failed
211
- s2 = parse_ikkatsu
202
+ # initialboard : comment* (hirate | ikkatsu | "") komabetsu comment* teban nl
203
+ def parse_initial_board
204
+ s0 = @scanner.pos
205
+ parse_comments
206
+ s2 = parse_hirate
212
207
  if s2 == :failed
213
- s2 = @current_pos
214
- s3 = match_str("")
215
- if s3 != :failed
216
- @reported_pos = s2
217
- s3 = "NO"
208
+ s2 = parse_ikkatsu
209
+ if s2 == :failed
210
+ s2 = @scanner.pos
211
+ s3 = match_str('')
212
+ if s3 != :failed
213
+ @reported_pos = s2
214
+ s3 = 'NO'
215
+ end
216
+ s2 = s3
218
217
  end
219
- s2 = s3
220
218
  end
221
- end
222
- if s2 == :failed
223
- @current_pos = s0
224
- :failed
225
- else
226
- s3 = parse_komabetsu
227
- if s3 == :failed
228
- @current_pos = s0
219
+ if s2 == :failed
220
+ @scanner.pos = s0
229
221
  :failed
230
222
  else
231
- parse_comments
232
- s5 = parse_teban
233
- if s5 == :failed
234
- @current_pos = s0
223
+ s3 = parse_komabetsu
224
+ if s3 == :failed
225
+ @scanner.pos = s0
235
226
  :failed
236
- elsif parse_nl != :failed
237
- @reported_pos = s0
238
- -> (data, koma, teban) do
239
- if data == "NO"
240
- data = koma
241
- else
242
- data["data"]["hands"] = koma["data"]["hands"]
243
- end
244
- data["data"]["color"] = teban
245
- data
246
- end.call(s2, s3, s5)
247
227
  else
248
- @current_pos = s0
249
- :failed
228
+ parse_comments
229
+ s5 = parse_teban
230
+ if s5 == :failed
231
+ @scanner.pos = s0
232
+ :failed
233
+ elsif parse_nl != :failed
234
+ @reported_pos = s0
235
+ -> (data, koma, teban) do
236
+ if data == 'NO'
237
+ data = koma
238
+ else
239
+ data['data']['hands'] = koma['data']['hands']
240
+ end
241
+ data['data']['color'] = teban
242
+ data
243
+ end.call(s2, s3, s5)
244
+ else
245
+ @scanner.pos = s0
246
+ :failed
247
+ end
250
248
  end
251
249
  end
252
250
  end
253
- end
254
251
 
255
- # hirate : "PI" xypiece* nl
256
- def parse_hirate
257
- s0 = @current_pos
258
- if match_str("PI") == :failed
259
- @current_pos = s0
260
- s0 = :failed
261
- else
262
- s2 = []
263
- s3 = parse_xy_piece
264
- while s3 != :failed
265
- s2 << s3
266
- s3 = parse_xy_piece
267
- end
268
- if parse_nl == :failed
269
- @current_pos = s0
252
+ # hirate : "PI" xypiece* nl
253
+ def parse_hirate
254
+ s0 = @scanner.pos
255
+ if match_str('PI') == :failed
256
+ @scanner.pos = s0
270
257
  s0 = :failed
271
258
  else
272
- @reported_pos = s0
273
- s0 = -> (ps) do
274
- ret = { "preset" => "OTHER", "data" => { "board" => get_hirate } }
275
- ps.each do |piece|
276
- ret["data"]["board"][piece["xy"]["x"] - 1][piece["xy"]["y"] - 1] = {}
277
- end
278
- ret
279
- end.call(s2)
280
- end
281
- end
282
- s0
283
- end
284
-
285
- # ikkatsu : ikkatsuline+
286
- def parse_ikkatsu
287
- s0 = @current_pos
288
- s2 = parse_ikkatsu_line
289
- if s2 == :failed
290
- s1 = :failed
291
- else
292
- s1 = []
293
- while s2 != :failed
294
- s1 << s2
295
- s2 = parse_ikkatsu_line
296
- end
297
- end
298
- if s1 != :failed
299
- @reported_pos = s0
300
- s1 = -> (lines) do
301
- board = []
302
- 9.times do |i|
303
- line = []
304
- 9.times do |j|
305
- line << lines[j][8 - i]
306
- end
307
- board << line
308
- end
309
- { "preset" => "OTHER", "data" => { "board" => board } }
310
- end.call(s1)
311
- end
312
- s0 = s1
313
- s0
314
- end
315
-
316
- # ikkatsuline : "P" [1-9] masu+ nl
317
- def parse_ikkatsu_line
318
- s0 = @current_pos
319
- if match_str("P") == :failed
320
- @current_pos = s0
321
- s0 = :failed
322
- elsif match_digit != :failed
323
- s4 = parse_masu
324
- if s4 == :failed
325
- s3 = :failed
326
- else
327
- s3 = []
328
- while s4 != :failed
329
- s3 << s4
330
- s4 = parse_masu
259
+ s2 = []
260
+ s3 = parse_xy_piece
261
+ while s3 != :failed
262
+ s2 << s3
263
+ s3 = parse_xy_piece
331
264
  end
332
- end
333
- if s3 == :failed
334
- @current_pos = s0
335
- s0 = :failed
336
- else
337
- s4 = parse_nl
338
- if s4 == :failed
339
- @current_pos = s0
265
+ if parse_nl == :failed
266
+ @scanner.pos = s0
340
267
  s0 = :failed
341
268
  else
342
269
  @reported_pos = s0
343
- s0 = s3
270
+ s0 = -> (ps) do
271
+ ret = { 'preset' => 'OTHER', 'data' => { 'board' => hirate } }
272
+ ps.each do |piece|
273
+ ret['data']['board'][piece['xy']['x'] - 1][piece['xy']['y'] - 1] = {}
274
+ end
275
+ ret
276
+ end.call(s2)
344
277
  end
345
278
  end
346
- else
347
- @current_pos = s0
348
- s0 = :failed
279
+ s0
349
280
  end
350
- s0
351
- end
352
281
 
353
- # masu : teban piece | " * "
354
- def parse_masu
355
- s0 = @current_pos
356
- s1 = parse_teban
357
- if s1 == :failed
358
- @current_pos = s0
359
- s0 = :failed
360
- else
361
- s2 = parse_piece
282
+ # ikkatsu : ikkatsuline+
283
+ def parse_ikkatsu
284
+ s0 = @scanner.pos
285
+ s2 = parse_ikkatsu_line
362
286
  if s2 == :failed
363
- @current_pos = s0
364
- s0 = :failed
287
+ s1 = :failed
365
288
  else
366
- @reported_pos = s0
367
- s0 = { "color" => s1, "kind" => s2 }
289
+ s1 = []
290
+ while s2 != :failed
291
+ s1 << s2
292
+ s2 = parse_ikkatsu_line
293
+ end
368
294
  end
369
- end
370
- if s0 == :failed
371
- s0 = @current_pos
372
- if match_str(" * ") != :failed
295
+ if s1 != :failed
373
296
  @reported_pos = s0
374
- s1 = {}
297
+ s1 = -> (lines) do
298
+ board = []
299
+ 9.times do |i|
300
+ line = []
301
+ 9.times do |j|
302
+ line << lines[j][8 - i]
303
+ end
304
+ board << line
305
+ end
306
+ { 'preset' => 'OTHER', 'data' => { 'board' => board } }
307
+ end.call(s1)
375
308
  end
376
- s0 = s1
377
- end
378
- s0
379
- end
380
-
381
- # komabetsu : komabetsuline*
382
- def parse_komabetsu
383
- s0 = @current_pos
384
- s1 = []
385
- s2 = parse_komabetsu_line
386
- while s2 != :failed
387
- s1 << s2
388
- s2 = parse_komabetsu_line
309
+ s1
389
310
  end
390
- @reported_pos = s0
391
- transform_komabetsu_lines(s1)
392
- end
393
311
 
394
- # komabetsuline : "P" teban xypiece+ nl
395
- def parse_komabetsu_line
396
- s0 = @current_pos
397
- if match_str("P") == :failed
398
- @current_pos = s0
399
- s0 = :failed
400
- else
401
- s2 = parse_teban
402
- if s2 == :failed
403
- @current_pos = s0
312
+ # ikkatsuline : "P" [1-9] masu+ nl
313
+ def parse_ikkatsu_line
314
+ s0 = @scanner.pos
315
+ if match_str('P') == :failed
316
+ @scanner.pos = s0
404
317
  s0 = :failed
405
- else
406
- s4 = parse_xy_piece
318
+ elsif match_digit != :failed
319
+ s4 = parse_masu
407
320
  if s4 == :failed
408
321
  s3 = :failed
409
322
  else
410
323
  s3 = []
411
324
  while s4 != :failed
412
325
  s3 << s4
413
- s4 = parse_xy_piece
326
+ s4 = parse_masu
414
327
  end
415
328
  end
416
329
  if s3 == :failed
417
- @current_pos = s0
330
+ @scanner.pos = s0
418
331
  s0 = :failed
419
- elsif parse_nl != :failed
420
- @reported_pos = s0
421
- s0 = { "teban" => s2, "pieces" => s3 }
422
332
  else
423
- @current_pos = s0
424
- s0 = :failed
333
+ s4 = parse_nl
334
+ if s4 == :failed
335
+ @scanner.pos = s0
336
+ s0 = :failed
337
+ else
338
+ @reported_pos = s0
339
+ s0 = s3
340
+ end
425
341
  end
342
+ else
343
+ @scanner.pos = s0
344
+ s0 = :failed
426
345
  end
346
+ s0
427
347
  end
428
- s0
429
- end
430
348
 
431
- # moves : firstboard move* comment*
432
- def parse_moves
433
- s0 = @current_pos
434
- s1 = parse_firstboard
435
- if s1 == :failed
436
- @current_pos = s0
437
- s0 = :failed
438
- else
439
- s2 = []
440
- s3 = parse_move
441
- while s3 != :failed
442
- s2 << s3
443
- s3 = parse_move
349
+ # masu : teban piece | " * "
350
+ def parse_masu
351
+ s0 = @scanner.pos
352
+ s1 = parse_teban
353
+ if s1 == :failed
354
+ @scanner.pos = s0
355
+ s0 = :failed
356
+ else
357
+ s2 = parse_piece
358
+ if s2 == :failed
359
+ @scanner.pos = s0
360
+ s0 = :failed
361
+ else
362
+ @reported_pos = s0
363
+ s0 = { 'color' => s1, 'kind' => s2 }
364
+ end
444
365
  end
445
- parse_comments
446
- @reported_pos = s0
447
- s0 = s2.unshift(s1)
366
+ if s0 == :failed
367
+ s0 = @scanner.pos
368
+ if match_str(' * ') != :failed
369
+ @reported_pos = s0
370
+ s1 = {}
371
+ end
372
+ s0 = s1
373
+ end
374
+ s0
448
375
  end
449
- s0
450
- end
451
376
 
452
- # firstboard : comment*
453
- def parse_firstboard
454
- s0 = @current_pos
455
- s1 = parse_comments
456
- @reported_pos = s0
457
- s1.empty? ? {} : { "comments" => s1 }
458
- end
459
-
460
- # move : (normalmove | specialmove) time? comment*
461
- def parse_move
462
- s0 = @current_pos
463
- s1 = parse_normal_move
464
- s1 = parse_special_move if s1 == :failed
465
- if s1 == :failed
466
- @current_pos = s0
467
- s0 = :failed
468
- else
469
- s2 = parse_time
470
- s2 = nil if s2 == :failed
471
- s3 = parse_comments
377
+ # komabetsu : komabetsuline*
378
+ def parse_komabetsu
379
+ s0 = @scanner.pos
380
+ s1 = []
381
+ s2 = parse_komabetsu_line
382
+ while s2 != :failed
383
+ s1 << s2
384
+ s2 = parse_komabetsu_line
385
+ end
472
386
  @reported_pos = s0
473
- s0 = -> (move, time, comments) do
474
- ret = {}
475
- ret["comments"] = comments if !comments.empty?
476
- ret["time"] = time if time
477
- if move["special"]
478
- ret["special"] = move["special"]
479
- else
480
- ret["move"] = move
481
- end
482
- ret
483
- end.call(s1, s2, s3)
387
+ transform_komabetsu_lines(s1)
484
388
  end
485
- s0
486
- end
487
389
 
488
- # normalmove : teban xy xy piece nl
489
- def parse_normal_move
490
- s0 = @current_pos
491
- s1 = parse_teban
492
- if s1 == :failed
493
- @current_pos = s0
494
- s0 = :failed
495
- else
496
- s2 = parse_xy
497
- if s2 == :failed
498
- @current_pos = s0
390
+ # komabetsuline : "P" teban xypiece+ nl
391
+ def parse_komabetsu_line
392
+ s0 = @scanner.pos
393
+ if match_str('P') == :failed
394
+ @scanner.pos = s0
499
395
  s0 = :failed
500
396
  else
501
- s3 = parse_xy
502
- if s3 == :failed
503
- @current_pos = s0
397
+ s2 = parse_teban
398
+ if s2 == :failed
399
+ @scanner.pos = s0
504
400
  s0 = :failed
505
401
  else
506
- s4 = parse_piece
402
+ s4 = parse_xy_piece
507
403
  if s4 == :failed
508
- @current_pos = s0
404
+ s3 = :failed
405
+ else
406
+ s3 = []
407
+ while s4 != :failed
408
+ s3 << s4
409
+ s4 = parse_xy_piece
410
+ end
411
+ end
412
+ if s3 == :failed
413
+ @scanner.pos = s0
509
414
  s0 = :failed
510
415
  elsif parse_nl != :failed
511
416
  @reported_pos = s0
512
- s0 = -> (color, from, to, piece) do
513
- ret = { "color" => color, "to" => to, "piece" => piece }
514
- ret["from"] = from if from["x"] != 0
515
- ret
516
- end.call(s1, s2, s3, s4)
417
+ s0 = { 'teban' => s2, 'pieces' => s3 }
517
418
  else
518
- @current_pos = s0
419
+ @scanner.pos = s0
519
420
  s0 = :failed
520
421
  end
521
422
  end
522
423
  end
424
+ s0
523
425
  end
524
- s0
525
- end
526
426
 
527
- # specialmove : "%" [-+_A-Z]+ nl
528
- def parse_special_move
529
- s0 = @current_pos
530
- s1 = match_str("%")
531
- if s1 == :failed
532
- @current_pos = s0
533
- s0 = :failed
534
- else
535
- s3 = match_regexp(/^[\-+_A-Z]/)
536
- if s3 == :failed
537
- s2 = :failed
427
+ # moves : firstboard move* comment*
428
+ def parse_moves
429
+ s0 = @scanner.pos
430
+ s1 = parse_firstboard
431
+ if s1 == :failed
432
+ @scanner.pos = s0
433
+ s0 = :failed
538
434
  else
539
435
  s2 = []
436
+ s3 = parse_move
540
437
  while s3 != :failed
541
438
  s2 << s3
542
- s3 = match_regexp(/^[\-+_A-Z]/)
439
+ s3 = parse_move
543
440
  end
441
+ parse_comments
442
+ @reported_pos = s0
443
+ s0 = s2.unshift(s1)
544
444
  end
545
- if s2 == :failed
546
- @current_pos = s0
445
+ s0
446
+ end
447
+
448
+ # firstboard : comment*
449
+ def parse_firstboard
450
+ s0 = @scanner.pos
451
+ s1 = parse_comments
452
+ @reported_pos = s0
453
+ s1.empty? ? {} : { 'comments' => s1 }
454
+ end
455
+
456
+ # move : (normalmove | specialmove) time? comment*
457
+ def parse_move
458
+ s0 = @scanner.pos
459
+ s1 = parse_normal_move
460
+ s1 = parse_special_move if s1 == :failed
461
+ if s1 == :failed
462
+ @scanner.pos = s0
547
463
  s0 = :failed
548
- elsif parse_nl != :failed
549
- @reported_pos = s0
550
- s0 = { "special" => s2.join }
551
464
  else
552
- @current_pos = s0
465
+ s2 = parse_time
466
+ s2 = nil if s2 == :failed
467
+ s3 = parse_comments
468
+ @reported_pos = s0
469
+ s0 = -> (move, time, comments) do
470
+ ret = {}
471
+ ret['comments'] = comments unless comments.empty?
472
+ ret['time'] = time if time
473
+ if move['special']
474
+ ret['special'] = move['special']
475
+ else
476
+ ret['move'] = move
477
+ end
478
+ ret
479
+ end.call(s1, s2, s3)
480
+ end
481
+ s0
482
+ end
483
+
484
+ # normalmove : teban xy xy piece nl
485
+ def parse_normal_move
486
+ s0 = @scanner.pos
487
+ s1 = parse_teban
488
+ if s1 == :failed
489
+ @scanner.pos = s0
553
490
  s0 = :failed
491
+ else
492
+ s2 = parse_xy
493
+ if s2 == :failed
494
+ @scanner.pos = s0
495
+ s0 = :failed
496
+ else
497
+ s3 = parse_xy
498
+ if s3 == :failed
499
+ @scanner.pos = s0
500
+ s0 = :failed
501
+ else
502
+ s4 = parse_piece
503
+ if s4 == :failed
504
+ @scanner.pos = s0
505
+ s0 = :failed
506
+ elsif parse_nl != :failed
507
+ @reported_pos = s0
508
+ s0 = -> (color, from, to, piece) do
509
+ ret = { 'color' => color, 'to' => to, 'piece' => piece }
510
+ ret['from'] = from if from['x'] != 0
511
+ ret
512
+ end.call(s1, s2, s3, s4)
513
+ else
514
+ @scanner.pos = s0
515
+ s0 = :failed
516
+ end
517
+ end
518
+ end
554
519
  end
520
+ s0
555
521
  end
556
- s0
557
- end
558
522
 
559
- # teban : "+" | "-"
560
- def parse_teban
561
- s0 = @current_pos
562
- s1 = match_str("+")
563
- if s1 != :failed
564
- @reported_pos = s0
565
- s1 = 0
523
+ # specialmove : "%" [-+_A-Z]+ nl
524
+ def parse_special_move
525
+ s0 = @scanner.pos
526
+ s1 = match_str('%')
527
+ if s1 == :failed
528
+ @scanner.pos = s0
529
+ s0 = :failed
530
+ else
531
+ s3 = match_regexp(/[-+_A-Z]/)
532
+ if s3 == :failed
533
+ s2 = :failed
534
+ else
535
+ s2 = []
536
+ while s3 != :failed
537
+ s2 << s3
538
+ s3 = match_regexp(/[-+_A-Z]/)
539
+ end
540
+ end
541
+ if s2 == :failed
542
+ @scanner.pos = s0
543
+ s0 = :failed
544
+ elsif parse_nl != :failed
545
+ @reported_pos = s0
546
+ s0 = { 'special' => s2.join }
547
+ else
548
+ @scanner.pos = s0
549
+ s0 = :failed
550
+ end
551
+ end
552
+ s0
566
553
  end
567
- s0 = s1
568
- if s0 == :failed
569
- s0 = @current_pos
570
- s1 = match_str("-")
554
+
555
+ # teban : "+" | "-"
556
+ def parse_teban
557
+ s0 = @scanner.pos
558
+ s1 = match_str('+')
571
559
  if s1 != :failed
572
560
  @reported_pos = s0
573
- s1 = 1
561
+ s1 = 0
574
562
  end
575
563
  s0 = s1
564
+ if s0 == :failed
565
+ s0 = @scanner.pos
566
+ s1 = match_str('-')
567
+ if s1 != :failed
568
+ @reported_pos = s0
569
+ s1 = 1
570
+ end
571
+ s0 = s1
572
+ end
573
+ s0
576
574
  end
577
- s0
578
- end
579
575
 
580
- # comment : "'" nonls nl
581
- def parse_comment
582
- s0 = @current_pos
583
- if match_str("'") == :failed
584
- @current_pos = s0
585
- :failed
586
- else
587
- s2 = parse_nonls
588
- if parse_nl == :failed
589
- @current_pos = s0
576
+ # comment : "'" nonls nl
577
+ def parse_comment
578
+ s0 = @scanner.pos
579
+ if match_str("'") == :failed
580
+ @scanner.pos = s0
590
581
  :failed
591
582
  else
592
- @reported_pos = s0
593
- s2.join
583
+ s2 = parse_nonls
584
+ if parse_nl == :failed
585
+ @scanner.pos = s0
586
+ :failed
587
+ else
588
+ @reported_pos = s0
589
+ s2.join
590
+ end
594
591
  end
595
592
  end
596
- end
597
593
 
598
- # comments : comment*
599
- def parse_comments
600
- stack = []
601
- matched = parse_comment
602
- while matched != :failed
603
- stack << matched
594
+ # comments : comment*
595
+ def parse_comments
596
+ stack = []
604
597
  matched = parse_comment
598
+ while matched != :failed
599
+ stack << matched
600
+ matched = parse_comment
601
+ end
602
+ stack
605
603
  end
606
- stack
607
- end
608
604
 
609
- # time : "T" [0-9]* nl
610
- def parse_time
611
- s0 = @current_pos
612
- if match_str("T") == :failed
613
- @current_pos = s0
614
- s0 = :failed
615
- else
616
- s2 = match_digits
617
- if parse_nl == :failed
618
- @current_pos = s0
605
+ # time : "T" [0-9]* nl
606
+ def parse_time
607
+ s0 = @scanner.pos
608
+ if match_str('T') == :failed
609
+ @scanner.pos = s0
619
610
  s0 = :failed
620
611
  else
621
- @reported_pos = s0
622
- s0 = { "now" => sec2time(s2.join.to_i) }
612
+ s2 = match_digits
613
+ if parse_nl == :failed
614
+ @scanner.pos = s0
615
+ s0 = :failed
616
+ else
617
+ @reported_pos = s0
618
+ s0 = { 'now' => sec2time(s2.join.to_i) }
619
+ end
623
620
  end
621
+ s0
624
622
  end
625
- s0
626
- end
627
623
 
628
- # xy : [0-9] [0-9]
629
- def parse_xy
630
- s0 = @current_pos
631
- s1 = match_digit
632
- if s1 == :failed
633
- @current_pos = s0
634
- s0 = :failed
635
- else
636
- s2 = match_digit
637
- if s2 == :failed
638
- @current_pos = s0
624
+ # xy : [0-9] [0-9]
625
+ def parse_xy
626
+ s0 = @scanner.pos
627
+ s1 = match_digit
628
+ if s1 == :failed
629
+ @scanner.pos = s0
639
630
  s0 = :failed
640
631
  else
641
- @reported_pos = s0
642
- s0 = { "x" => s1.to_i, "y" => s2.to_i }
632
+ s2 = match_digit
633
+ if s2 == :failed
634
+ @scanner.pos = s0
635
+ s0 = :failed
636
+ else
637
+ @reported_pos = s0
638
+ s0 = { 'x' => s1.to_i, 'y' => s2.to_i }
639
+ end
643
640
  end
641
+ s0
644
642
  end
645
- s0
646
- end
647
643
 
648
- # piece : [A-Z] [A-Z]
649
- def parse_piece
650
- s0 = @current_pos
651
- s1 = match_regexp(/^[A-Z]/)
652
- if s1 == :failed
653
- @current_pos = s0
654
- s0 = :failed
655
- else
656
- s2 = match_regexp(/^[A-Z]/)
657
- if s2 == :failed
658
- @current_pos = s0
644
+ # piece : [A-Z] [A-Z]
645
+ def parse_piece
646
+ s0 = @scanner.pos
647
+ s1 = match_regexp(/[A-Z]/)
648
+ if s1 == :failed
649
+ @scanner.pos = s0
659
650
  s0 = :failed
660
651
  else
661
- @reported_pos = s0
662
- s0 = s1 + s2
652
+ s2 = match_regexp(/[A-Z]/)
653
+ if s2 == :failed
654
+ @scanner.pos = s0
655
+ s0 = :failed
656
+ else
657
+ @reported_pos = s0
658
+ s0 = s1 + s2
659
+ end
663
660
  end
661
+ s0
664
662
  end
665
- s0
666
- end
667
663
 
668
- # xypiece : xy piece
669
- def parse_xy_piece
670
- s0 = @current_pos
671
- s1 = parse_xy
672
- if s1 == :failed
673
- @current_pos = s0
674
- s0 = :failed
675
- else
676
- s2 = parse_piece
677
- if s2 == :failed
678
- @current_pos = s0
664
+ # xypiece : xy piece
665
+ def parse_xy_piece
666
+ s0 = @scanner.pos
667
+ s1 = parse_xy
668
+ if s1 == :failed
669
+ @scanner.pos = s0
679
670
  s0 = :failed
680
671
  else
681
- @reported_pos = s0
682
- s0 = { "xy" => s1, "piece" => s2 }
672
+ s2 = parse_piece
673
+ if s2 == :failed
674
+ @scanner.pos = s0
675
+ s0 = :failed
676
+ else
677
+ @reported_pos = s0
678
+ s0 = { 'xy' => s1, 'piece' => s2 }
679
+ end
683
680
  end
681
+ s0
684
682
  end
685
- s0
686
- end
687
683
 
688
- # nl : ("\r"? "\n") | " "* ","
689
- def parse_nl
690
- s0 = @current_pos
691
- s1 = match_str("\r")
692
- s1 = nil if s1 == :failed
693
- s2 = match_str("\n")
694
- if s2 == :failed
695
- @current_pos = s0
696
- s0 = :failed
697
- else
698
- s0 = [s1, s2]
699
- end
700
- if s0 == :failed
701
- s0 = @current_pos
702
- s1 = match_spaces
703
- s2 = match_str(",")
684
+ # nl : ("\r"? "\n") | " "* ","
685
+ def parse_nl
686
+ s0 = @scanner.pos
687
+ s1 = match_str("\r")
688
+ s1 = nil if s1 == :failed
689
+ s2 = match_str("\n")
704
690
  if s2 == :failed
705
- @current_pos = s0
691
+ @scanner.pos = s0
706
692
  s0 = :failed
707
693
  else
708
694
  s0 = [s1, s2]
709
695
  end
696
+ if s0 == :failed
697
+ s0 = @scanner.pos
698
+ s1 = match_spaces
699
+ s2 = match_str(',')
700
+ if s2 == :failed
701
+ @scanner.pos = s0
702
+ s0 = :failed
703
+ else
704
+ s0 = [s1, s2]
705
+ end
706
+ end
707
+ s0
710
708
  end
711
- s0
712
- end
713
709
 
714
- # nonl : [^\r\n]
715
- def parse_nonl
716
- match_regexp(/^[^\r\n]/)
717
- end
718
-
719
- # nonls : nonl*
720
- def parse_nonls
721
- stack = []
722
- matched = parse_nonl
723
- while matched != :failed
724
- stack << matched
725
- matched = parse_nonl
710
+ # nonl : [^\r\n]
711
+ def parse_nonl
712
+ match_regexp(/[^\r\n]/)
726
713
  end
727
- stack
728
- end
729
714
 
730
- # lines to jkf
731
- def transform_komabetsu_lines(lines)
732
- board = generate_empty_board
733
- hands = [
734
- { "FU" => 0, "KY" => 0, "KE" => 0, "GI" => 0, "KI" => 0, "KA" => 0, "HI" => 0 },
735
- { "FU" => 0, "KY" => 0, "KE" => 0, "GI" => 0, "KI" => 0, "KA" => 0, "HI" => 0 }
736
- ]
737
- all = { "FU" => 18, "KY" => 4, "KE" => 4, "GI" => 4, "KI" => 4, "KA" => 2, "HI" => 2 }
738
-
739
- lines.each do |line|
740
- line["pieces"].each do |piece|
741
- xy = piece["xy"]
742
- if xy["x"] == 0
743
- if piece["piece"] == "AL"
744
- hands[line["teban"]] = all
745
- return { "preset" => "OTHER", "data" => { "board" => board, "hands" => hands } }
715
+ # nonls : nonl*
716
+ def parse_nonls
717
+ stack = []
718
+ matched = parse_nonl
719
+ while matched != :failed
720
+ stack << matched
721
+ matched = parse_nonl
722
+ end
723
+ stack
724
+ end
725
+
726
+ # lines to jkf
727
+ def transform_komabetsu_lines(lines)
728
+ board = generate_empty_board
729
+ hands = [
730
+ { 'FU' => 0, 'KY' => 0, 'KE' => 0, 'GI' => 0, 'KI' => 0, 'KA' => 0, 'HI' => 0 },
731
+ { 'FU' => 0, 'KY' => 0, 'KE' => 0, 'GI' => 0, 'KI' => 0, 'KA' => 0, 'HI' => 0 }
732
+ ]
733
+ all = { 'FU' => 18, 'KY' => 4, 'KE' => 4, 'GI' => 4, 'KI' => 4, 'KA' => 2, 'HI' => 2 }
734
+
735
+ lines.each do |line|
736
+ line['pieces'].each do |piece|
737
+ xy = piece['xy']
738
+ if xy['x'] == 0
739
+ if piece['piece'] == 'AL'
740
+ hands[line['teban']] = all
741
+ return { 'preset' => 'OTHER', 'data' => { 'board' => board, 'hands' => hands } }
742
+ end
743
+ obj = hands[line['teban']]
744
+ obj[piece['piece']] += 1
745
+ else
746
+ board[xy['x'] - 1][xy['y'] - 1] = { 'color' => line['teban'],
747
+ 'kind' => piece['piece'] }
746
748
  end
747
- obj = hands[line["teban"]]
748
- obj[piece["piece"]] += 1
749
- else
750
- board[xy["x"] - 1][xy["y"] - 1] = { "color" => line["teban"],
751
- "kind" => piece["piece"] }
749
+ all[piece['piece']] -= 1 if piece['piece'] != 'OU'
752
750
  end
753
- all[piece["piece"]] -= 1 if piece["piece"] != "OU"
754
751
  end
755
- end
756
752
 
757
- { "preset" => "OTHER", "data" => { "board" => board, "hands" => hands } }
758
- end
753
+ { 'preset' => 'OTHER', 'data' => { 'board' => board, 'hands' => hands } }
754
+ end
759
755
 
760
- # return empty board jkf
761
- def generate_empty_board
762
- board = []
763
- 9.times do |_i|
764
- line = []
765
- 9.times do |_j|
766
- line << {}
756
+ # return empty board jkf
757
+ def generate_empty_board
758
+ board = []
759
+ 9.times do |_i|
760
+ line = []
761
+ 9.times do |_j|
762
+ line << {}
763
+ end
764
+ board << line
767
765
  end
768
- board << line
766
+ board
767
+ end
768
+
769
+ # sec to time(m, s)
770
+ def sec2time(sec)
771
+ s = sec % 60
772
+ m = (sec - s) / 60
773
+ { 'm' => m, 's' => s }
774
+ end
775
+
776
+ # return hirate board jkf
777
+ def hirate
778
+ [
779
+ [{ 'color' => 1, 'kind' => 'KY' }, {}, { 'color' => 1, 'kind' => 'FU' }, {}, {}, {},
780
+ { 'color' => 0, 'kind' => 'FU' }, {}, { 'color' => 0, 'kind' => 'KY' }],
781
+ [{ 'color' => 1, 'kind' => 'KE' }, { 'color' => 1, 'kind' => 'KA' },
782
+ { 'color' => 1, 'kind' => 'FU' }, {}, {}, {}, { 'color' => 0, 'kind' => 'FU' },
783
+ { 'color' => 0, 'kind' => 'HI' }, { 'color' => 0, 'kind' => 'KE' }],
784
+ [{ 'color' => 1, 'kind' => 'GI' }, {}, { 'color' => 1, 'kind' => 'FU' }, {}, {}, {},
785
+ { 'color' => 0, 'kind' => 'FU' }, {}, { 'color' => 0, 'kind' => 'GI' }],
786
+ [{ 'color' => 1, 'kind' => 'KI' }, {}, { 'color' => 1, 'kind' => 'FU' }, {}, {}, {},
787
+ { 'color' => 0, 'kind' => 'FU' }, {}, { 'color' => 0, 'kind' => 'KI' }],
788
+ [{ 'color' => 1, 'kind' => 'OU' }, {}, { 'color' => 1, 'kind' => 'FU' }, {}, {}, {},
789
+ { 'color' => 0, 'kind' => 'FU' }, {}, { 'color' => 0, 'kind' => 'OU' }],
790
+ [{ 'color' => 1, 'kind' => 'KI' }, {}, { 'color' => 1, 'kind' => 'FU' }, {}, {}, {},
791
+ { 'color' => 0, 'kind' => 'FU' }, {}, { 'color' => 0, 'kind' => 'KI' }],
792
+ [{ 'color' => 1, 'kind' => 'GI' }, {}, { 'color' => 1, 'kind' => 'FU' }, {}, {}, {},
793
+ { 'color' => 0, 'kind' => 'FU' }, {}, { 'color' => 0, 'kind' => 'GI' }],
794
+ [{ 'color' => 1, 'kind' => 'KE' }, { 'color' => 1, 'kind' => 'HI' },
795
+ { 'color' => 1, 'kind' => 'FU' }, {}, {}, {}, { 'color' => 0, 'kind' => 'FU' },
796
+ { 'color' => 0, 'kind' => 'KA' }, { 'color' => 0, 'kind' => 'KE' }],
797
+ [{ 'color' => 1, 'kind' => 'KY' }, {}, { 'color' => 1, 'kind' => 'FU' }, {}, {}, {},
798
+ { 'color' => 0, 'kind' => 'FU' }, {}, { 'color' => 0, 'kind' => 'KY' }]
799
+ ]
800
+ end
801
+
802
+ # normalize header key
803
+ def normalize_header_key(key)
804
+ {
805
+ 'EVENT' => '棋戦',
806
+ 'SITE' => '場所',
807
+ 'START_TIME' => '開始日時',
808
+ 'END_TIME' => '終了日時',
809
+ 'TIME_LIMIT' => '持ち時間'
810
+ }[key] || key
769
811
  end
770
- board
771
- end
772
-
773
- # sec to time(m, s)
774
- def sec2time(sec)
775
- s = sec % 60
776
- m = (sec - s) / 60
777
- { "m" => m, "s" => s }
778
- end
779
-
780
- # return hirate board jkf
781
- def get_hirate
782
- [
783
- [{ "color" => 1, "kind" => "KY" }, {}, { "color" => 1, "kind" => "FU" }, {}, {}, {},
784
- { "color" => 0, "kind" => "FU" }, {}, { "color" => 0, "kind" => "KY" }],
785
- [{ "color" => 1, "kind" => "KE" }, { "color" => 1, "kind" => "KA" },
786
- { "color" => 1, "kind" => "FU" }, {}, {}, {}, { "color" => 0, "kind" => "FU" },
787
- { "color" => 0, "kind" => "HI" }, { "color" => 0, "kind" => "KE" }],
788
- [{ "color" => 1, "kind" => "GI" }, {}, { "color" => 1, "kind" => "FU" }, {}, {}, {},
789
- { "color" => 0, "kind" => "FU" }, {}, { "color" => 0, "kind" => "GI" }],
790
- [{ "color" => 1, "kind" => "KI" }, {}, { "color" => 1, "kind" => "FU" }, {}, {}, {},
791
- { "color" => 0, "kind" => "FU" }, {}, { "color" => 0, "kind" => "KI" }],
792
- [{ "color" => 1, "kind" => "OU" }, {}, { "color" => 1, "kind" => "FU" }, {}, {}, {},
793
- { "color" => 0, "kind" => "FU" }, {}, { "color" => 0, "kind" => "OU" }],
794
- [{ "color" => 1, "kind" => "KI" }, {}, { "color" => 1, "kind" => "FU" }, {}, {}, {},
795
- { "color" => 0, "kind" => "FU" }, {}, { "color" => 0, "kind" => "KI" }],
796
- [{ "color" => 1, "kind" => "GI" }, {}, { "color" => 1, "kind" => "FU" }, {}, {}, {},
797
- { "color" => 0, "kind" => "FU" }, {}, { "color" => 0, "kind" => "GI" }],
798
- [{ "color" => 1, "kind" => "KE" }, { "color" => 1, "kind" => "HI" },
799
- { "color" => 1, "kind" => "FU" }, {}, {}, {}, { "color" => 0, "kind" => "FU" },
800
- { "color" => 0, "kind" => "KA" }, { "color" => 0, "kind" => "KE" }],
801
- [{ "color" => 1, "kind" => "KY" }, {}, { "color" => 1, "kind" => "FU" }, {}, {}, {},
802
- { "color" => 0, "kind" => "FU" }, {}, { "color" => 0, "kind" => "KY" }]
803
- ]
804
- end
805
-
806
- # normalize header key
807
- def normalize_header_key(key)
808
- {
809
- "EVENT" => "棋戦",
810
- "SITE" => "場所",
811
- "START_TIME" => "開始日時",
812
- "END_TIME" => "終了日時",
813
- "TIME_LIMIT" => "持ち時間"
814
- }[key] || key
815
812
  end
816
813
  end
817
814
  end