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,620 +1,619 @@
1
- # coding: utf-8
1
+ module Jkf
2
+ module Parser
3
+ # KIF Parser
4
+ class Kif < Base
5
+ include Kifuable
2
6
 
3
- module Jkf::Parser
4
- # KIF Parser
5
- class Kif < Base
6
- include Kifuable
7
+ protected
7
8
 
8
- protected
9
+ # kifu : skipline* header* initialboard? header* split? moves fork* nl?
10
+ def parse_root
11
+ @scanner << "\n" unless @scanner.string.end_with?("\n")
9
12
 
10
- # kifu : skipline* header* initialboard? header* split? moves fork* nl?
11
- def parse_root
12
- @input += "\n" unless @input.end_with?("\n")
13
-
14
- s0 = @current_pos
15
- s1 = []
16
- s2 = parse_skipline
17
- while s2 != :failed
18
- s1 << s2
13
+ s0 = @scanner.pos
14
+ s1 = []
19
15
  s2 = parse_skipline
20
- end
16
+ while s2 != :failed
17
+ s1 << s2
18
+ s2 = parse_skipline
19
+ end
21
20
 
22
- s2 = []
23
- s3 = parse_header
24
- while s3 != :failed
25
- s2 << s3
21
+ s2 = []
26
22
  s3 = parse_header
27
- end
28
- s3 = parse_initialboard
29
- s3 = nil if s3 == :failed
30
- s4 = []
31
- s5 = parse_header
32
- while s5 != :failed
33
- s4 << s5
34
- s5 = parse_header
35
- end
36
- parse_split
37
- s6 = parse_moves
38
- if s6 == :failed
39
- @current_pos = s0
40
- s0 = :failed
41
- else
42
- s7 = []
43
- s8 = parse_fork
44
- while s8 != :failed
45
- s7 << s8
46
- s8 = parse_fork
23
+ while s3 != :failed
24
+ s2 << s3
25
+ s3 = parse_header
47
26
  end
48
- parse_nl
49
- @reported_pos = s0
50
- s0 = transform_root(s2, s3, s4, s6, s7)
51
- end
52
-
53
- s0
54
- end
55
-
56
- # header : [^:\r\n]+ ":" nonls nl
57
- # | turn "手番" nl
58
- # | "盤面回転" nl
59
- def parse_header
60
- s0 = @current_pos
61
- s2 = match_regexp(/^[^:\r\n]/)
62
- if s2 == :failed
63
- s1 = :failed
64
- else
65
- s1 = []
66
- while s2 != :failed
67
- s1 << s2
68
- s2 = match_regexp(/^[^:\r\n]/)
27
+ s3 = parse_initialboard
28
+ s3 = nil if s3 == :failed
29
+ s4 = []
30
+ s5 = parse_header
31
+ while s5 != :failed
32
+ s4 << s5
33
+ s5 = parse_header
69
34
  end
70
- end
71
- if s1 == :failed
72
- @current_pos = s0
73
- s0 = :failed
74
- elsif match_str(":") != :failed
75
- s3 = parse_nonls
76
- if parse_nl == :failed
77
- @current_pos = s0
35
+ parse_split
36
+ s6 = parse_moves
37
+ if s6 == :failed
38
+ @scanner.pos = s0
78
39
  s0 = :failed
79
40
  else
41
+ s7 = []
42
+ s8 = parse_fork
43
+ while s8 != :failed
44
+ s7 << s8
45
+ s8 = parse_fork
46
+ end
47
+ parse_nl
80
48
  @reported_pos = s0
81
- s1 = { "k" => s1.join, "v" => s3.join }
82
- s0 = s1
49
+ s0 = transform_root(s2, s3, s4, s6, s7)
83
50
  end
84
- else
85
- @current_pos = s0
86
- s0 = :failed
51
+
52
+ s0
87
53
  end
88
- if s0 == :failed
89
- s0 = @current_pos
90
- s1 = parse_turn
54
+
55
+ # header : [^:\r\n]+ ":" nonls nl
56
+ # | turn "手番" nl
57
+ # | "盤面回転" nl
58
+ def parse_header
59
+ s0 = @scanner.pos
60
+ s2 = match_regexp(/[^:\r\n]/)
61
+ if s2 == :failed
62
+ s1 = :failed
63
+ else
64
+ s1 = []
65
+ while s2 != :failed
66
+ s1 << s2
67
+ s2 = match_regexp(/[^:\r\n]/)
68
+ end
69
+ end
91
70
  if s1 == :failed
92
- @current_pos = s0
71
+ @scanner.pos = s0
93
72
  s0 = :failed
94
- elsif match_str("手番") != :failed
73
+ elsif match_str(':') != :failed
74
+ s3 = parse_nonls
95
75
  if parse_nl == :failed
96
- @current_pos = s0
76
+ @scanner.pos = s0
97
77
  s0 = :failed
98
78
  else
99
79
  @reported_pos = s0
100
- s0 = { "k" => "手番", "v" => s1 }
80
+ s1 = { 'k' => s1.join, 'v' => s3.join }
81
+ s0 = s1
101
82
  end
102
83
  else
103
- @current_pos = s0
84
+ @scanner.pos = s0
104
85
  s0 = :failed
105
86
  end
106
87
  if s0 == :failed
107
- s0 = @current_pos
108
- if match_str("盤面回転") == :failed
109
- @current_pos = s0
88
+ s0 = @scanner.pos
89
+ s1 = parse_turn
90
+ if s1 == :failed
91
+ @scanner.pos = s0
110
92
  s0 = :failed
111
- elsif parse_nl != :failed
112
- @reported_pos = s0
113
- s0 = nil
93
+ elsif match_str('手番') != :failed
94
+ if parse_nl == :failed
95
+ @scanner.pos = s0
96
+ s0 = :failed
97
+ else
98
+ @reported_pos = s0
99
+ s0 = { 'k' => '手番', 'v' => s1 }
100
+ end
114
101
  else
115
- @current_pos = s0
102
+ @scanner.pos = s0
116
103
  s0 = :failed
117
104
  end
105
+ if s0 == :failed
106
+ s0 = @scanner.pos
107
+ if match_str('盤面回転') == :failed
108
+ @scanner.pos = s0
109
+ s0 = :failed
110
+ elsif parse_nl != :failed
111
+ @reported_pos = s0
112
+ s0 = nil
113
+ else
114
+ @scanner.pos = s0
115
+ s0 = :failed
116
+ end
117
+ end
118
118
  end
119
- end
120
119
 
121
- s0
122
- end
120
+ s0
121
+ end
123
122
 
124
- # turn : [先後上下]
125
- def parse_turn
126
- match_regexp(/^[先後上下]/)
127
- end
123
+ # turn : [先後上下]
124
+ def parse_turn
125
+ match_regexp(/[先後上下]/)
126
+ end
128
127
 
129
- # split : "手数----指手--" "-------消費時間--"? nl
130
- def parse_split
131
- s0 = @current_pos
132
- s1 = match_str("手数----指手--")
133
- if s1 == :failed
134
- @current_pos = s0
135
- s0 = :failed
136
- else
137
- s2 = match_str("-------消費時間--")
138
- s2 = nil if s2 == :failed
139
- s3 = parse_nl
140
- if s3 == :failed
141
- @current_pos = s0
128
+ # split : "手数----指手--" "-------消費時間--"? nl
129
+ def parse_split
130
+ s0 = @scanner.pos
131
+ s1 = match_str('手数----指手--')
132
+ if s1 == :failed
133
+ @scanner.pos = s0
142
134
  s0 = :failed
143
135
  else
144
- s0 = [s1, s2, s3]
136
+ s2 = match_str('-------消費時間--')
137
+ s2 = nil if s2 == :failed
138
+ s3 = parse_nl
139
+ if s3 == :failed
140
+ @scanner.pos = s0
141
+ s0 = :failed
142
+ else
143
+ s0 = [s1, s2, s3]
144
+ end
145
145
  end
146
+ s0
146
147
  end
147
- s0
148
- end
149
148
 
150
- # moves : firstboard split? move* result?
151
- def parse_moves
152
- s0 = @current_pos
153
- s1 = parse_firstboard
154
- if s1 == :failed
155
- @current_pos = s0
156
- s0 = :failed
157
- else
158
- parse_split
159
- s2 = []
160
- s3 = parse_move
161
- while s3 != :failed
162
- s2 << s3
149
+ # moves : firstboard split? move* result?
150
+ def parse_moves
151
+ s0 = @scanner.pos
152
+ s1 = parse_firstboard
153
+ if s1 == :failed
154
+ @scanner.pos = s0
155
+ s0 = :failed
156
+ else
157
+ parse_split
158
+ s2 = []
163
159
  s3 = parse_move
160
+ while s3 != :failed
161
+ s2 << s3
162
+ s3 = parse_move
163
+ end
164
+ parse_result
165
+ @reported_pos = s0
166
+ s0 = s2.unshift(s1)
164
167
  end
165
- parse_result
166
- @reported_pos = s0
167
- s0 = s2.unshift(s1)
168
+ s0
168
169
  end
169
- s0
170
- end
171
170
 
172
- # firstboard : comment* pointer?
173
- def parse_firstboard
174
- s0 = @current_pos
175
- s1 = []
176
- s2 = parse_comment
177
- while s2 != :failed
178
- s1 << s2
171
+ # firstboard : comment* pointer?
172
+ def parse_firstboard
173
+ s0 = @scanner.pos
174
+ s1 = []
179
175
  s2 = parse_comment
176
+ while s2 != :failed
177
+ s1 << s2
178
+ s2 = parse_comment
179
+ end
180
+ parse_pointer
181
+ @reported_pos = s0
182
+ s1.empty? ? {} : { 'comments' => s1 }
180
183
  end
181
- parse_pointer
182
- @reported_pos = s0
183
- s0 = s1.empty? ? {} : { "comments" => s1 }
184
- s0
185
- end
186
184
 
187
- # move : line comment* pointer?
188
- def parse_move
189
- s0 = @current_pos
190
- s1 = parse_line
191
- if s1 == :failed
192
- @current_pos = s0
193
- s0 = :failed
194
- else
195
- s2 = []
196
- s3 = parse_comment
197
- while s3 != :failed
198
- s2 << s3
185
+ # move : line comment* pointer?
186
+ def parse_move
187
+ s0 = @scanner.pos
188
+ s1 = parse_line
189
+ if s1 == :failed
190
+ @scanner.pos = s0
191
+ s0 = :failed
192
+ else
193
+ s2 = []
199
194
  s3 = parse_comment
195
+ while s3 != :failed
196
+ s2 << s3
197
+ s3 = parse_comment
198
+ end
199
+ parse_pointer
200
+ @reported_pos = s0
201
+ s0 = transform_move(s1, s2)
200
202
  end
201
- parse_pointer
202
- @reported_pos = s0
203
- s0 = transform_move(s1, s2)
203
+ s0
204
204
  end
205
- s0
206
- end
207
205
 
208
- # line : " "* te " "* (fugou from | [^\r\n ]*) " "* time? "+"? nl
209
- def parse_line
210
- s0 = @current_pos
211
- match_spaces
212
- s2 = parse_te
213
- if s2 == :failed
214
- @current_pos = s0
215
- s0 = :failed
216
- else
206
+ # line : " "* te " "* (fugou from | [^\r\n ]*) " "* time? "+"? nl
207
+ def parse_line
208
+ s0 = @scanner.pos
217
209
  match_spaces
218
- s4 = @current_pos
219
- s5 = parse_fugou
220
- if s5 == :failed
221
- @current_pos = s4
222
- s4 = :failed
210
+ s2 = parse_te
211
+ if s2 == :failed
212
+ @scanner.pos = s0
213
+ s0 = :failed
223
214
  else
224
- s6 = parse_from
225
- if s6 == :failed
226
- @current_pos = s4
215
+ match_spaces
216
+ s4 = @scanner.pos
217
+ s5 = parse_fugou
218
+ if s5 == :failed
219
+ @scanner.pos = s4
227
220
  s4 = :failed
228
221
  else
229
- @reported_pos = s4
230
- s4 = transform_teban_fugou_from(s2, s5, s6)
222
+ s6 = parse_from
223
+ if s6 == :failed
224
+ @scanner.pos = s4
225
+ s4 = :failed
226
+ else
227
+ @reported_pos = s4
228
+ s4 = transform_teban_fugou_from(s2, s5, s6)
229
+ end
231
230
  end
232
- end
233
- if s4 == :failed
234
- s4 = @current_pos
235
- s5 = []
236
- s6 = match_regexp(/^[^\r\n ]/)
237
- while s6 != :failed
238
- s5 << s6
239
- s6 = match_regexp(/^[^\r\n ]/)
231
+ if s4 == :failed
232
+ s4 = @scanner.pos
233
+ s5 = []
234
+ s6 = match_regexp(/[^\r\n ]/)
235
+ while s6 != :failed
236
+ s5 << s6
237
+ s6 = match_regexp(/[^\r\n ]/)
238
+ end
239
+ @reported_pos = s4
240
+ s4 = s5.join
240
241
  end
241
- @reported_pos = s4
242
- s4 = s5.join
243
- end
244
- if s4 == :failed
245
- @current_pos = s0
246
- s0 = :failed
247
- else
248
- match_spaces
249
- s6 = parse_time
250
- s6 = nil if s6 == :failed
251
- match_str("+")
252
- if parse_nl == :failed
253
- @current_pos = s0
242
+ if s4 == :failed
243
+ @scanner.pos = s0
254
244
  s0 = :failed
255
245
  else
256
- @reported_pos = s0
257
- s0 = { "move" => s4, "time" => s6 }
246
+ match_spaces
247
+ s6 = parse_time
248
+ s6 = nil if s6 == :failed
249
+ match_str('+')
250
+ if parse_nl == :failed
251
+ @scanner.pos = s0
252
+ s0 = :failed
253
+ else
254
+ @reported_pos = s0
255
+ s0 = { 'move' => s4, 'time' => s6 }
256
+ end
258
257
  end
259
258
  end
259
+ s0
260
260
  end
261
- s0
262
- end
263
261
 
264
- # te : [0-9]+
265
- def parse_te
266
- match_digits!
267
- end
262
+ # te : [0-9]+
263
+ def parse_te
264
+ match_digits!
265
+ end
268
266
 
269
- # fugou : place piece "成"?
270
- def parse_fugou
271
- s0 = @current_pos
272
- s1 = parse_place
273
- if s1 == :failed
274
- @current_pos = s0
275
- s0 = :failed
276
- else
277
- s2 = parse_piece
278
- if s2 == :failed
279
- @current_pos = s0
267
+ # fugou : place piece "成"?
268
+ def parse_fugou
269
+ s0 = @scanner.pos
270
+ s1 = parse_place
271
+ if s1 == :failed
272
+ @scanner.pos = s0
280
273
  s0 = :failed
281
274
  else
282
- s3 = match_str("成")
283
- s3 = nil if s3 == :failed
284
- @reported_pos = s0
285
- s0 = { "to" => s1, "piece" => s2, "promote" => !!s3 }
275
+ s2 = parse_piece
276
+ if s2 == :failed
277
+ @scanner.pos = s0
278
+ s0 = :failed
279
+ else
280
+ s3 = match_str('成')
281
+ s3 = nil if s3 == :failed
282
+ @reported_pos = s0
283
+ s0 = { 'to' => s1, 'piece' => s2, 'promote' => !!s3 }
284
+ end
286
285
  end
286
+ s0
287
287
  end
288
- s0
289
- end
290
288
 
291
- # place : num numkan | "同 "
292
- def parse_place
293
- s0 = @current_pos
294
- s1 = parse_num
295
- if s1 == :failed
296
- @current_pos = s0
297
- s0 = :failed
298
- else
299
- s2 = parse_numkan
300
- if s2 == :failed
301
- @current_pos = s0
289
+ # place : num numkan | "同 "
290
+ def parse_place
291
+ s0 = @scanner.pos
292
+ s1 = parse_num
293
+ if s1 == :failed
294
+ @scanner.pos = s0
302
295
  s0 = :failed
303
296
  else
304
- @reported_pos = s0
305
- s0 = { "x" => s1, "y" => s2 }
297
+ s2 = parse_numkan
298
+ if s2 == :failed
299
+ @scanner.pos = s0
300
+ s0 = :failed
301
+ else
302
+ @reported_pos = s0
303
+ s0 = { 'x' => s1, 'y' => s2 }
304
+ end
306
305
  end
306
+ if s0 == :failed
307
+ s0 = @scanner.pos
308
+ s1 = match_str('同 ')
309
+ if s1 != :failed
310
+ @reported_pos = s0
311
+ s1 = nil
312
+ end
313
+ s0 = s1
314
+ end
315
+ s0
307
316
  end
308
- if s0 == :failed
309
- s0 = @current_pos
310
- s1 = match_str("同 ")
317
+
318
+ # from : "打" | "(" [1-9] [1-9] ")"
319
+ def parse_from
320
+ s0 = @scanner.pos
321
+ s1 = match_str('打')
311
322
  if s1 != :failed
312
323
  @reported_pos = s0
313
324
  s1 = nil
314
325
  end
315
326
  s0 = s1
316
- end
317
- s0
318
- end
319
-
320
- # from : "打" | "(" [1-9] [1-9] ")"
321
- def parse_from
322
- s0 = @current_pos
323
- s1 = match_str("打")
324
- if s1 != :failed
325
- @reported_pos = s0
326
- s1 = nil
327
- end
328
- s0 = s1
329
- if s0 == :failed
330
- s0 = @current_pos
331
- if match_str("(") == :failed
332
- @current_pos = s0
333
- s0 = :failed
334
- else
335
- s2 = match_regexp(/^[1-9]/)
336
- if s2 == :failed
337
- @current_pos = s0
327
+ if s0 == :failed
328
+ s0 = @scanner.pos
329
+ if match_str('(') == :failed
330
+ @scanner.pos = s0
338
331
  s0 = :failed
339
332
  else
340
- s3 = match_regexp(/^[1-9]/)
341
- if s3 == :failed
342
- @current_pos = s0
333
+ s2 = match_regexp(/[1-9]/)
334
+ if s2 == :failed
335
+ @scanner.pos = s0
343
336
  s0 = :failed
344
- elsif match_str(")") != :failed
345
- @reported_pos = s0
346
- s0 = { "x" => s2.to_i, "y" => s3.to_i }
347
337
  else
348
- @current_pos = s0
349
- s0 = :failed
338
+ s3 = match_regexp(/[1-9]/)
339
+ if s3 == :failed
340
+ @scanner.pos = s0
341
+ s0 = :failed
342
+ elsif match_str(')') != :failed
343
+ @reported_pos = s0
344
+ s0 = { 'x' => s2.to_i, 'y' => s3.to_i }
345
+ else
346
+ @scanner.pos = s0
347
+ s0 = :failed
348
+ end
350
349
  end
351
350
  end
352
351
  end
352
+ s0
353
353
  end
354
- s0
355
- end
356
354
 
357
- # time : "(" " "* ms " "* "/" " "* (hms | ms) " "* ")"
358
- def parse_time
359
- s0 = @current_pos
360
- if match_str("(") == :failed
361
- @current_pos = s0
362
- s0 = :failed
363
- else
364
- match_spaces
365
- s3 = parse_ms
366
- if s3 == :failed
367
- @current_pos = s0
355
+ # time : "(" " "* ms " "* "/" " "* (hms | ms) " "* ")"
356
+ def parse_time
357
+ s0 = @scanner.pos
358
+ if match_str('(') == :failed
359
+ @scanner.pos = s0
368
360
  s0 = :failed
369
361
  else
370
362
  match_spaces
371
- if match_str("/") == :failed
372
- @current_pos = s0
363
+ s3 = parse_ms
364
+ if s3 == :failed
365
+ @scanner.pos = s0
373
366
  s0 = :failed
374
367
  else
375
368
  match_spaces
376
- s5 = parse_hms
377
- s5 = parse_ms(with_hour: true) if s5 == :failed
378
- if s5 == :failed
379
- @current_pos = s0
369
+ if match_str('/') == :failed
370
+ @scanner.pos = s0
380
371
  s0 = :failed
381
372
  else
382
373
  match_spaces
383
- if match_str(")") == :failed
384
- @current_pos = s0
374
+ s5 = parse_hms
375
+ s5 = parse_ms(with_hour: true) if s5 == :failed
376
+ if s5 == :failed
377
+ @scanner.pos = s0
385
378
  s0 = :failed
386
379
  else
387
- @reported_pos = s0
388
- s0 = { "now" => s3, "total" => s5 }
380
+ match_spaces
381
+ if match_str(')') == :failed
382
+ @scanner.pos = s0
383
+ s0 = :failed
384
+ else
385
+ @reported_pos = s0
386
+ s0 = { 'now' => s3, 'total' => s5 }
387
+ end
389
388
  end
390
389
  end
391
390
  end
392
391
  end
392
+ s0
393
393
  end
394
- s0
395
- end
396
394
 
397
- # hms : [0-9]+ ":" [0-9]+ ":" [0-9]+
398
- def parse_hms
399
- s0 = @current_pos
400
- s1 = match_digits!
401
-
402
- if s1 == :failed
403
- @current_pos = s0
404
- s0 = :failed
405
- elsif match_str(":") != :failed
406
- s3 = match_digits!
407
- if s3 == :failed
408
- @current_pos = s0
395
+ # hms : [0-9]+ ":" [0-9]+ ":" [0-9]+
396
+ def parse_hms
397
+ s0 = @scanner.pos
398
+ s1 = match_digits!
399
+
400
+ if s1 == :failed
401
+ @scanner.pos = s0
409
402
  s0 = :failed
410
- elsif match_str(":") != :failed
411
- s5 = match_digits!
412
- if s5 == :failed
413
- @current_pos = s0
403
+ elsif match_str(':') != :failed
404
+ s3 = match_digits!
405
+ if s3 == :failed
406
+ @scanner.pos = s0
414
407
  s0 = :failed
408
+ elsif match_str(':') != :failed
409
+ s5 = match_digits!
410
+ if s5 == :failed
411
+ @scanner.pos = s0
412
+ s0 = :failed
413
+ else
414
+ @reported_pos = s0
415
+ s0 = { 'h' => s1.join.to_i, 'm' => s3.join.to_i, 's' => s5.join.to_i }
416
+ end
415
417
  else
416
- @reported_pos = s0
417
- s0 = { "h" => s1.join.to_i, "m" => s3.join.to_i, "s" => s5.join.to_i }
418
+ @scanner.pos = s0
419
+ s0 = :failed
418
420
  end
419
421
  else
420
- @current_pos = s0
422
+ @scanner.pos = s0
421
423
  s0 = :failed
422
424
  end
423
- else
424
- @current_pos = s0
425
- s0 = :failed
425
+ s0
426
426
  end
427
- s0
428
- end
429
427
 
430
- # ms : [0-9]+ ":" [0-9]+
431
- def parse_ms(with_hour: false)
432
- s0 = @current_pos
433
- s1 = match_digits!
434
- if s1 == :failed
435
- @current_pos = s0
436
- s0 = :failed
437
- elsif match_str(":") != :failed
438
- s3 = match_digits!
439
- if s3 == :failed
440
- @current_pos = s0
428
+ # ms : [0-9]+ ":" [0-9]+
429
+ def parse_ms(with_hour: false)
430
+ s0 = @scanner.pos
431
+ s1 = match_digits!
432
+ if s1 == :failed
433
+ @scanner.pos = s0
441
434
  s0 = :failed
442
- else
443
- @reported_pos = s0
444
- m = s1.join.to_i
445
- s = s3.join.to_i
446
- if with_hour
447
- h = m / 60
448
- m = m % 60
449
- s0 = { "h" => h, "m" => m, "s" => s }
435
+ elsif match_str(':') != :failed
436
+ s3 = match_digits!
437
+ if s3 == :failed
438
+ @scanner.pos = s0
439
+ s0 = :failed
450
440
  else
451
- s0 = { "m" => m, "s" => s }
441
+ @reported_pos = s0
442
+ m = s1.join.to_i
443
+ s = s3.join.to_i
444
+ if with_hour
445
+ h = m / 60
446
+ m = m % 60
447
+ s0 = { 'h' => h, 'm' => m, 's' => s }
448
+ else
449
+ s0 = { 'm' => m, 's' => s }
450
+ end
452
451
  end
453
- end
454
- else
455
- @current_pos = s0
456
- s0 = :failed
457
- end
458
- s0
459
- end
460
-
461
- # comment : "*" nonls nl | "&" nonls nl
462
- def parse_comment
463
- s0 = @current_pos
464
- if match_str("*") == :failed
465
- @current_pos = s0
466
- s0 = :failed
467
- else
468
- s2 = parse_nonls
469
- if parse_nl == :failed
470
- @current_pos = s0
471
- s0 = :failed
472
452
  else
473
- @reported_pos = s0
474
- s0 = s2.join
453
+ @scanner.pos = s0
454
+ s0 = :failed
475
455
  end
456
+ s0
476
457
  end
477
- if s0 == :failed
478
- s0 = @current_pos
479
- s1 = match_str("&")
480
- if s1 == :failed
481
- @current_pos = s0
458
+
459
+ # comment : "*" nonls nl | "&" nonls nl
460
+ def parse_comment
461
+ s0 = @scanner.pos
462
+ if match_str('*') == :failed
463
+ @scanner.pos = s0
482
464
  s0 = :failed
483
465
  else
484
466
  s2 = parse_nonls
485
467
  if parse_nl == :failed
486
- @current_pos = s0
468
+ @scanner.pos = s0
487
469
  s0 = :failed
488
470
  else
489
471
  @reported_pos = s0
490
- s0 = "&" + s2.join
472
+ s0 = s2.join
491
473
  end
492
474
  end
493
- end
494
- s0
495
- end
496
-
497
- # fork : "変化:" " "* [0-9]+ "手" nl moves
498
- def parse_fork
499
- s0 = @current_pos
500
- if match_str("変化:") == :failed
501
- @current_pos = s0
502
- s0 = :failed
503
- else
504
- match_spaces
505
- s3 = parse_te
506
- if s3 == :failed
507
- @current_pos = s0
508
- s0 = :failed
509
- elsif match_str("手") != :failed
510
- if parse_nl == :failed
511
- @current_pos = s0
475
+ if s0 == :failed
476
+ s0 = @scanner.pos
477
+ s1 = match_str('&')
478
+ if s1 == :failed
479
+ @scanner.pos = s0
512
480
  s0 = :failed
513
481
  else
514
- s6 = parse_moves
515
- if s6 == :failed
516
- @current_pos = s0
482
+ s2 = parse_nonls
483
+ if parse_nl == :failed
484
+ @scanner.pos = s0
517
485
  s0 = :failed
518
486
  else
519
487
  @reported_pos = s0
520
- s0 = { "te" => s3.join.to_i, "moves" => s6[1..-1] }
488
+ s0 = '&' + s2.join
521
489
  end
522
490
  end
523
- else
524
- @current_pos = s0
491
+ end
492
+ s0
493
+ end
494
+
495
+ # fork : "変化:" " "* [0-9]+ "手" nl moves
496
+ def parse_fork
497
+ s0 = @scanner.pos
498
+ if match_str('変化:') == :failed
499
+ @scanner.pos = s0
525
500
  s0 = :failed
501
+ else
502
+ match_spaces
503
+ s3 = parse_te
504
+ if s3 == :failed
505
+ @scanner.pos = s0
506
+ s0 = :failed
507
+ elsif match_str('手') != :failed
508
+ if parse_nl == :failed
509
+ @scanner.pos = s0
510
+ s0 = :failed
511
+ else
512
+ s6 = parse_moves
513
+ if s6 == :failed
514
+ @scanner.pos = s0
515
+ s0 = :failed
516
+ else
517
+ @reported_pos = s0
518
+ s0 = { 'te' => s3.join.to_i, 'moves' => s6[1..-1] }
519
+ end
520
+ end
521
+ else
522
+ @scanner.pos = s0
523
+ s0 = :failed
524
+ end
526
525
  end
526
+ s0
527
527
  end
528
- s0
529
- end
530
528
 
531
- # transfrom to jkf
532
- def transform_root(headers, ini, headers2, moves, forks)
533
- ret = { "header" => {}, "moves" => moves }
534
- headers.compact.each { |h| ret["header"][h["k"]] = h["v"] }
535
- headers2.compact.each { |h| ret["header"][h["k"]] = h["v"] }
536
- if ini
537
- ret["initial"] = ini
538
- elsif ret["header"]["手合割"]
539
- preset = preset2str(ret["header"]["手合割"])
540
- ret["initial"] = { "preset" => preset } if preset && preset != "OTHER"
529
+ # transfrom to jkf
530
+ def transform_root(headers, ini, headers2, moves, forks)
531
+ ret = { 'header' => {}, 'moves' => moves }
532
+ headers.compact.each { |h| ret['header'][h['k']] = h['v'] }
533
+ headers2.compact.each { |h| ret['header'][h['k']] = h['v'] }
534
+ if ini
535
+ ret['initial'] = ini
536
+ elsif ret['header']['手合割']
537
+ preset = preset2str(ret['header']['手合割'])
538
+ ret['initial'] = { 'preset' => preset } if preset && preset != 'OTHER'
539
+ end
540
+ transform_root_header_data(ret) if ret['initial'] && ret['initial']['data']
541
+ transform_root_forks(forks, moves)
542
+ if ret['initial'] && ret['initial']['data'] && ret['initial']['data']['color'] == 1
543
+ reverse_color(ret['moves'])
544
+ end
545
+ ret
541
546
  end
542
- transform_root_header_data(ret) if ret["initial"] && ret["initial"]["data"]
543
- transform_root_forks(forks, moves)
544
- if ret["initial"] && ret["initial"]["data"] && ret["initial"]["data"]["color"] == 1
545
- reverse_color(ret["moves"])
547
+
548
+ # transform move to jkf
549
+ def transform_move(line, c)
550
+ ret = {}
551
+ ret['comments'] = c unless c.empty?
552
+ if line['move'].is_a? Hash
553
+ ret['move'] = line['move']
554
+ else
555
+ ret['special'] = special2csa(line['move'])
556
+ end
557
+ ret['time'] = line['time'] if line['time']
558
+ ret
546
559
  end
547
- ret
548
- end
549
560
 
550
- # transform move to jkf
551
- def transform_move(line, c)
552
- ret = {}
553
- ret["comments"] = c if !c.empty?
554
- if line["move"].is_a? Hash
555
- ret["move"] = line["move"]
556
- else
557
- ret["special"] = special2csa(line["move"])
561
+ # transform teban-fugou-from to jkf
562
+ def transform_teban_fugou_from(teban, fugou, from)
563
+ ret = { 'color' => teban2color(teban.join), 'piece' => fugou['piece'] }
564
+ if fugou['to']
565
+ ret['to'] = fugou['to']
566
+ else
567
+ ret['same'] = true
568
+ end
569
+ ret['promote'] = true if fugou['promote']
570
+ ret['from'] = from if from
571
+ ret
558
572
  end
559
- ret["time"] = line["time"] if line["time"]
560
- ret
561
- end
562
573
 
563
- # transform teban-fugou-from to jkf
564
- def transform_teban_fugou_from(teban, fugou, from)
565
- ret = { "color" => teban2color(teban.join), "piece" => fugou["piece"] }
566
- if fugou["to"]
567
- ret["to"] = fugou["to"]
568
- else
569
- ret["same"] = true
574
+ # special string to csa
575
+ def special2csa(str)
576
+ {
577
+ '中断' => 'CHUDAN',
578
+ '投了' => 'TORYO',
579
+ '持将棋' => 'JISHOGI',
580
+ '千日手' => 'SENNICHITE',
581
+ '詰み' => 'TSUMI',
582
+ '不詰' => 'FUZUMI',
583
+ '切れ負け' => 'TIME_UP',
584
+ '反則勝ち' => 'ILLEGAL_ACTION', # 直前の手が反則(先頭に+か-で反則した側の情報を含める必要が有る)
585
+ '反則負け' => 'ILLEGAL_MOVE' # ここで手番側が反則,反則の内容はコメントで表現
586
+ }[str] || (raise ParseError)
570
587
  end
571
- ret["promote"] = true if fugou["promote"]
572
- ret["from"] = from if from
573
- ret
574
- end
575
588
 
576
- # special string to csa
577
- def special2csa(str)
578
- {
579
- "中断" => "CHUDAN",
580
- "投了" => "TORYO",
581
- "持将棋" => "JISHOGI",
582
- "千日手" => "SENNICHITE",
583
- "詰み" => "TSUMI",
584
- "不詰" => "FUZUMI",
585
- "切れ負け" => "TIME_UP",
586
- "反則勝ち" => "ILLEGAL_ACTION", # 直前の手が反則(先頭に+か-で反則した側の情報を含める必要が有る)
587
- "反則負け" => "ILLEGAL_MOVE" # ここで手番側が反則,反則の内容はコメントで表現
588
- }[str] || (raise ParseError)
589
- end
589
+ # teban to color
590
+ def teban2color(teban)
591
+ teban = teban.to_i unless teban.is_a? Integer
592
+ (teban + 1) % 2
593
+ end
590
594
 
591
- # teban to color
592
- def teban2color(teban)
593
- teban = teban.to_i unless teban.is_a? Integer
594
- (teban + 1) % 2
595
- end
595
+ # generate motigoma
596
+ def make_hand(str)
597
+ # Kifu for iPhoneは半角スペース区切り
598
+ ret = { 'FU' => 0, 'KY' => 0, 'KE' => 0, 'GI' => 0, 'KI' => 0, 'KA' => 0, 'HI' => 0 }
599
+ return ret if str.empty?
596
600
 
597
- # generate motigoma
598
- def make_hand(str)
599
- # Kifu for iPhoneは半角スペース区切り
600
- ret = { "FU" => 0, "KY" => 0, "KE" => 0, "GI" => 0, "KI" => 0, "KA" => 0, "HI" => 0 }
601
- return ret if str.empty?
601
+ str.split(/[  ]/).each do |kind|
602
+ next if kind.empty?
603
+ ret[kind2csa(kind[0])] = kind.length == 1 ? 1 : kan2n2(kind[1..-1])
604
+ end
602
605
 
603
- str.split(/[  ]/).each do |kind|
604
- next if kind.empty?
605
- ret[kind2csa(kind[0])] = kind.length == 1 ? 1 : kan2n2(kind[1..-1])
606
+ ret
606
607
  end
607
608
 
608
- ret
609
- end
610
-
611
- # exchange sente gote
612
- def reverse_color(moves)
613
- moves.each do |move|
614
- if move["move"] && move["move"]["color"]
615
- move["move"]["color"] = (move["move"]["color"] + 1) % 2
609
+ # exchange sente gote
610
+ def reverse_color(moves)
611
+ moves.each do |move|
612
+ if move['move'] && move['move']['color']
613
+ move['move']['color'] = (move['move']['color'] + 1) % 2
614
+ end
615
+ move['forks']&.each { |fork| reverse_color(fork) }
616
616
  end
617
- move["forks"].each { |_fork| reverse_color(_fork) } if move["forks"]
618
617
  end
619
618
  end
620
619
  end