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,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