jkf 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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