jkf 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1536 @@
1
+ # coding: utf-8
2
+
3
+ module Jkf::Parser
4
+ class Kif < Base
5
+ def parse_root
6
+ @input += "\n" if @input[-1] != "\n"
7
+ s0 = @current_pos
8
+ s1 = []
9
+ s2 = parse_skipline
10
+ while s2 != :failed
11
+ s1 << s2
12
+ s2 = parse_skipline
13
+ end
14
+ if s1 != :failed
15
+ s2 = []
16
+ s3 = parse_header
17
+ while s3 != :failed
18
+ s2 << s3
19
+ s3 = parse_header
20
+ end
21
+ if s2 != :failed
22
+ s3 = parse_initial_board
23
+ s3 = nil if s3 == :failed
24
+ if s3 != :failed
25
+ s4 = []
26
+ s5 = parse_header
27
+ while s5 != :failed
28
+ s4 << s5
29
+ s5 = parse_header
30
+ end
31
+ if s4 != :failed
32
+ s5 = parse_split
33
+ s5 = nil if s5 == :failed
34
+ if s5 != :failed
35
+ s6 = parse_moves
36
+ if s6 != :failed
37
+ s7 = []
38
+ s8 = parse_fork
39
+ while s8 != :failed
40
+ s7 << s8
41
+ s8 = parse_fork
42
+ end
43
+ if s7 != :failed
44
+ s8 = parse_nl
45
+ s8 = nil if s8 == :failed
46
+ if s8 != :failed
47
+ @reported_pos = s0
48
+ s1 = -> (headers, ini, headers2, moves, forks) {
49
+ ret = { "header" => {}, "moves" => moves }
50
+ headers.compact.each { |h| ret["header"][h["k"]] = h["v"] }
51
+ headers2.compact.each { |h| ret["header"][h["k"]] = h["v"] }
52
+ if ini
53
+ ret["initial"] = ini
54
+ elsif ret["header"]["手合割"]
55
+ preset = preset2str(ret["header"]["手合割"])
56
+ ret["initial"] = { "preset" => preset } if preset && preset != "OTHER"
57
+ end
58
+ if ret["initial"] && ret["initial"]["data"]
59
+ if ret["header"]["手番"]
60
+ ret["initial"]["data"]["color"] = ("下先".index(ret["header"]["手番"]) >= 0 ? 0 : 1)
61
+ ret["header"].delete("手番")
62
+ else
63
+ ret["initial"]["data"]["color"] = 0
64
+ end
65
+ ret["initial"]["data"]["hands"] = [
66
+ make_hand(ret["header"]["先手の持駒"] || ret["header"]["下手の持駒"]),
67
+ make_hand(ret["header"]["後手の持駒"] || ret["header"]["上手の持駒"])
68
+ ]
69
+ %w(先手の持駒 下手の持駒 後手の持駒 上手の持駒).each do |key|
70
+ ret["header"].delete(key)
71
+ end
72
+ end
73
+ fork_stack = [{ "te" => 0, "moves" => moves }]
74
+ forks.each do |f|
75
+ now_fork = f
76
+ _fork = fork_stack.pop
77
+ _fork = fork_stack.pop while _fork["te"] > now_fork["te"]
78
+ move = _fork["moves"][now_fork["te"] - _fork["te"]]
79
+ move["forks"] ||= []
80
+ move["forks"] << now_fork["moves"]
81
+ fork_stack << _fork
82
+ fork_stack << now_fork
83
+ end
84
+ ret
85
+ }.call(s2, s3, s4, s6, s7)
86
+ s0 = s1
87
+ else
88
+ @current_pos = s0
89
+ s0 = :failed
90
+ end
91
+ else
92
+ @current_pos = s0
93
+ s0 = :failed
94
+ end
95
+ else
96
+ @current_pos = s0
97
+ s0 = :failed
98
+ end
99
+ else
100
+ @current_pos = s0
101
+ s0 = :failed
102
+ end
103
+ else
104
+ @current_pos = s0
105
+ s0 = :failed
106
+ end
107
+ else
108
+ @current_pos = s0
109
+ s0 = :failed
110
+ end
111
+ else
112
+ @current_pos = s0
113
+ s0 = :failed
114
+ end
115
+ else
116
+ @current_pos = s0
117
+ s0 = :failed
118
+ end
119
+
120
+ s0
121
+ end
122
+
123
+ def parse_header
124
+ s0 = @current_pos
125
+ s1 = []
126
+ s2 = match_regexp(/^[^:\r\n]/)
127
+ if s2 != :failed
128
+ while s2 != :failed
129
+ s1 << s2
130
+ s2 = match_regexp(/^[^:\r\n]/)
131
+ end
132
+ else
133
+ s1 = :failed
134
+ end
135
+ if s1 != :failed
136
+ s2 = match_str(":")
137
+ if s2 != :failed
138
+ s3 = []
139
+ s4 = parse_nonl
140
+ while s4 != :failed
141
+ s3 << s4
142
+ s4 = parse_nonl
143
+ end
144
+ if s3 != :failed
145
+ s4 = parse_nl
146
+ if s4 != :failed
147
+ @reported_pos = s0
148
+ s1 = { "k" => s1.join, "v" => s3.join }
149
+ s0 = s1
150
+ else
151
+ @current_pos = s0
152
+ s0 = :failed
153
+ end
154
+ else
155
+ @current_pos = s0
156
+ s0 = :failed
157
+ end
158
+ else
159
+ @current_pos = s0
160
+ s0 = :failed
161
+ end
162
+ else
163
+ @current_pos = s0
164
+ s0 = :failed
165
+ end
166
+ if s0 == :failed
167
+ s0 = @current_pos
168
+ s1 = parse_turn
169
+ if s1 != :failed
170
+ s2 = match_str("手番")
171
+ if s2 != :failed
172
+ s3 = parse_nl
173
+ if s3 != :failed
174
+ @reported_pos = s0
175
+ s0 = s1 = { "k" => "手番", "v" => s1 }
176
+ else
177
+ @current_pos = s0
178
+ s0 = :failed
179
+ end
180
+ else
181
+ @current_pos = s0
182
+ s0 = :failed
183
+ end
184
+ else
185
+ @current_pos = s0
186
+ s0 = :failed
187
+ end
188
+ if s0 == :failed
189
+ s0 = @current_pos
190
+ s1 = match_str("盤面回転")
191
+ if s1 != :failed
192
+ s2 = parse_nl
193
+ if s2 != :failed
194
+ @reported_pos = s0
195
+ s0 = s1 = nil
196
+ else
197
+ @current_pos = s0
198
+ s0 = :failed
199
+ end
200
+ else
201
+ @current_pos = s0
202
+ s0 = :failed
203
+ end
204
+ end
205
+ end
206
+
207
+ s0
208
+ end
209
+
210
+ def parse_turn
211
+ match_regexp(/[先後上下]/)
212
+ end
213
+
214
+ def parse_initial_board
215
+ s0 = s1 = @current_pos
216
+ s2 = match_str(" ")
217
+ if s2 != :failed
218
+ s3 = []
219
+ s4 = parse_nonl
220
+ while s4 != :failed
221
+ s3 << s4
222
+ s4 = parse_nonl
223
+ end
224
+ if s3 != :failed
225
+ s4 = parse_nl
226
+ if s4 != :failed
227
+ s1 = s2 = [s2, s3, s4]
228
+ else
229
+ @current_pos = s1
230
+ s1 = :failed
231
+ end
232
+ else
233
+ @current_pos = s1
234
+ s1 = :failed
235
+ end
236
+ else
237
+ @current_pos = s1
238
+ s1 = :failed
239
+ end
240
+ if s1 == :failed
241
+ s1 = nil
242
+ end
243
+ if s1 != :failed
244
+ s2 = @current_pos
245
+ s3 = match_str("+")
246
+ if s3 != :failed
247
+ s4 = []
248
+ s5 = parse_nonl
249
+ while s5 != :failed
250
+ s4 << s5
251
+ s5 = parse_nonl
252
+ end
253
+ if s4 != :failed
254
+ s5 = parse_nl
255
+ if s5 != :failed
256
+ s2 = s3 = [s3, s4, s5]
257
+ else
258
+ @current_pos = s2
259
+ s2 = :failed
260
+ end
261
+ else
262
+ @current_pos = s2
263
+ s2 = :failed
264
+ end
265
+ else
266
+ @current_pos = s2
267
+ s2 = :failed
268
+ end
269
+ s2 = nil if s2 == :failed
270
+ if s2 != :failed
271
+ s3 = []
272
+ s4 = parse_ikkatsu_line
273
+ if s4 != :failed
274
+ while s4 != :failed
275
+ s3 << s4
276
+ s4 = parse_ikkatsu_line
277
+ end
278
+ else
279
+ s3 = :failed
280
+ end
281
+ if s3 != :failed
282
+ s4 = @current_pos
283
+ s5 = match_str("+")
284
+ if s5 != :failed
285
+ s6 = []
286
+ s7 = parse_nonl
287
+ while s7 != :failed
288
+ s6 << s7
289
+ s7 = parse_nonl
290
+ end
291
+ if s6 != :failed
292
+ s7 = parse_nl
293
+ if s7 != :failed
294
+ s4 = s5 = [s5, s6, s7]
295
+ else
296
+ @current_pos = s4
297
+ s4 = :failed
298
+ end
299
+ else
300
+ @current_pos = s4
301
+ s4 = :failed
302
+ end
303
+ else
304
+ @current_pos = s4
305
+ s4 = :failed
306
+ end
307
+ s4 = nil if s4 == :failed
308
+ if s4 != :failed
309
+ @reported_pos = s0
310
+ s1 = -> (lines) {
311
+ ret = [];
312
+ 9.times { |i|
313
+ line = [];
314
+ 9.times { |j|
315
+ line << lines[j][8-i]
316
+ }
317
+ ret << line
318
+ }
319
+ { "preset" => "OTHER", "data" => { "board" => ret } }
320
+ }.call(s3)
321
+ s0 = s1
322
+ else
323
+ @current_pos = s0
324
+ s0 = :failed
325
+ end
326
+ else
327
+ @current_pos = s0
328
+ s0 = :failed
329
+ end
330
+ else
331
+ @current_pos = s0
332
+ s0 = :failed
333
+ end
334
+ else
335
+ @current_pos = s0
336
+ s0 = :failed
337
+ end
338
+
339
+ s0
340
+ end
341
+
342
+ def parse_ikkatsu_line
343
+ s0 = @current_pos
344
+ s1 = match_str("|")
345
+ if s1 != :failed
346
+ s2 = []
347
+ s3 = parse_masu
348
+ if s3 != :failed
349
+ while s3 != :failed
350
+ s2 << s3
351
+ s3 = parse_masu
352
+ end
353
+ else
354
+ s2 = :failed
355
+ end
356
+ if s2 != :failed
357
+ s3 = match_str("|")
358
+ if s3 != :failed
359
+ s4 = []
360
+ s5 = parse_nonl
361
+ if s5 != :failed
362
+ while s5 != :failed
363
+ s4 << s5
364
+ s5 = parse_nonl
365
+ end
366
+ else
367
+ s4 = :failed
368
+ end
369
+ if s4 != :failed
370
+ s5 = parse_nl
371
+ if s5 != :failed
372
+ @reported_pos = s0
373
+ s0 = s1 = s2
374
+ else
375
+ @current_pos = s0
376
+ s0 = :failed
377
+ end
378
+ else
379
+ @current_pos = s0
380
+ s0 = :failed
381
+ end
382
+ else
383
+ @current_pos = s0
384
+ s0 = :failed
385
+ end
386
+ else
387
+ @current_pos = s0
388
+ s0 = :failed
389
+ end
390
+ else
391
+ @current_pos = s0
392
+ s0 = :failed
393
+ end
394
+
395
+ s0
396
+ end
397
+
398
+ def parse_masu
399
+ s0 = @current_pos
400
+ s1 = parse_teban
401
+ if s1 != :failed
402
+ s2 = parse_piece
403
+ if s2 != :failed
404
+ @reported_pos = s0
405
+ s0 = s1 = { "color" => s1, "kind" => s2 }
406
+ else
407
+ @current_pos = s0
408
+ s0 = :failed
409
+ end
410
+ else
411
+ @current_pos = s0
412
+ s0 = :failed
413
+ end
414
+ if s0 == :failed
415
+ s0 = @current_pos
416
+ s1 = match_str(" ・")
417
+ if s1 != :failed
418
+ @reported_pos = s0
419
+ s1 = {}
420
+ end
421
+ s0 = s1
422
+ end
423
+
424
+ s0
425
+ end
426
+
427
+ def parse_teban
428
+ s0 = @current_pos
429
+ s1 = match_str(" ")
430
+ if s1 == :failed
431
+ s1 = match_str("+")
432
+ s1 = match_str("^") if s1 == :failed
433
+ end
434
+ if s1 != :failed
435
+ @reported_pos = s0
436
+ s1 = 0
437
+ end
438
+ s0 = s1
439
+ if s0 == :failed
440
+ s0 = @current_pos
441
+ s1 = match_str("v")
442
+ s1 = match_str("V") if s1 == :failed
443
+ if s1 != :failed
444
+ @reported_pos = s0
445
+ s1 = 1
446
+ end
447
+ s0 = s1
448
+ end
449
+ s0
450
+ end
451
+
452
+ def parse_split
453
+ s0 = @current_pos
454
+ s1 = match_str("手数----指手--")
455
+ if s1 != :failed
456
+ s2 = match_str("-------消費時間--")
457
+ s2 = nil if s2 == :failed
458
+ if s2 != :failed
459
+ s3 = parse_nl
460
+ if s3 != :failed
461
+ s0 = s1 = [s1, s2, s3]
462
+ else
463
+ @current_pos = s0
464
+ s0 = :failed
465
+ end
466
+ else
467
+ @current_pos = s0
468
+ s0 = :failed
469
+ end
470
+ else
471
+ @current_pos = s0
472
+ s0 = :failed
473
+ end
474
+ s0
475
+ end
476
+
477
+ def parse_moves
478
+ s0 = @current_pos
479
+ s1 = parse_firstboard
480
+ if s1 != :failed
481
+ s2 = parse_split
482
+ s2 = nil if s2 == :failed
483
+ if s2 != :failed
484
+ s3 = []
485
+ s4 = parse_move
486
+ while s4 != :failed
487
+ s3 << s4
488
+ s4 = parse_move
489
+ end
490
+ if s3 != :failed
491
+ s4 = parse_result
492
+ s4 = nil if s4 == :failed
493
+ if s4 != :failed
494
+ @reported_pos = s0
495
+ s0 = s1 = s3.unshift(s1)
496
+ else
497
+ @current_pos = s0
498
+ s0 = :failed
499
+ end
500
+ else
501
+ @current_pos = s0
502
+ s0 = :failed
503
+ end
504
+ else
505
+ @current_pos = s0
506
+ s0 = :failed
507
+ end
508
+ else
509
+ @current_pos = s0
510
+ s0 = :failed
511
+ end
512
+ s0
513
+ end
514
+
515
+ def parse_firstboard
516
+ s0 = @current_pos
517
+ s1 = []
518
+ s2 = parse_comment
519
+ while s2 != :failed
520
+ s1 << s2
521
+ s2 = parse_comment
522
+ end
523
+ if s1 != :failed
524
+ s2 = parse_pointer
525
+ if s2 == :failed
526
+ s2 = nil
527
+ end
528
+ if s2 != :failed
529
+ @reported_pos = s0
530
+ s0 = s1 = s1.length == 0 ? {} : { "comments" => s1 }
531
+ else
532
+ @current_pos = s0
533
+ s0 = :failed
534
+ end
535
+ else
536
+ @current_pos = s0
537
+ s0 = :failed
538
+ end
539
+ s0
540
+ end
541
+
542
+ def parse_move
543
+ s0 = @current_pos
544
+ s1 = parse_line
545
+ if s1 != :failed
546
+ s2 = []
547
+ s3 = parse_comment
548
+ while s3 != :failed
549
+ s2 << s3
550
+ s3 = parse_comment
551
+ end
552
+ if s2 != :failed
553
+ s3 = parse_pointer
554
+ s3 = nil if s3 == :failed
555
+ if s3 != :failed
556
+ @reported_pos = s0
557
+ s1 = -> (line, c) {
558
+ ret = {}
559
+ ret["comments"] = c if c.length > 0
560
+ if line["move"].is_a? Hash
561
+ ret["move"] = line["move"]
562
+ else
563
+ ret["special"] = special2csa(line["move"])
564
+ end
565
+ ret["time"] = line["time"] if line["time"]
566
+ ret
567
+ }.call(s1, s2)
568
+ s0 = s1
569
+ else
570
+ @current_pos = s0
571
+ s0 = :failed
572
+ end
573
+ else
574
+ @current_pos = s0
575
+ s0 = :failed
576
+ end
577
+ else
578
+ @current_pos = s0
579
+ s0 = :failed
580
+ end
581
+ s0
582
+ end
583
+
584
+ def parse_pointer
585
+ s0 = @current_pos
586
+ s1 = match_str("&")
587
+ if s1 != :failed
588
+ s2 = []
589
+ s3 = parse_nonl
590
+ while s3 != :failed
591
+ s2 << s3
592
+ s3 = parse_nonl
593
+ end
594
+ if s2 != :failed
595
+ s3 = parse_nl
596
+ if s3 != :failed
597
+ s0 = s1 = [s1, s2, s3]
598
+ else
599
+ @current_pos = s0
600
+ s0 = :failed
601
+ end
602
+ else
603
+ @current_pos = s0
604
+ s0 = :failed
605
+ end
606
+ else
607
+ @current_pos = s0
608
+ s0 = :failed
609
+ end
610
+ s0
611
+ end
612
+
613
+ def parse_line
614
+ s0 = @current_pos
615
+ s1 = []
616
+ s2 = match_str(" ")
617
+ while s2 != :failed
618
+ s1 << s2
619
+ s2 = match_str(" ")
620
+ end
621
+ if s1 != :failed
622
+ s2 = parse_te
623
+ if s2 != :failed
624
+ s3 = []
625
+ s4 = match_str(" ")
626
+ while s4 != :failed
627
+ s3 << s4
628
+ s4 = match_str(" ")
629
+ end
630
+ if s3 != :failed
631
+ s4 = @current_pos
632
+ s5 = parse_fugou
633
+ if s5 != :failed
634
+ s6 = parse_from
635
+ if s6 != :failed
636
+ @reported_pos = s4
637
+ s5 = -> (fugou, from) {
638
+ ret = { "piece" => fugou["piece"] }
639
+ if fugou["to"]
640
+ ret["to"] = fugou["to"]
641
+ else
642
+ ret["same"] = true
643
+ end
644
+ ret["promote"] = true if fugou["promote"]
645
+ ret["from"] = from if from
646
+ ret
647
+ }.call(s5, s6)
648
+ s4 = s5
649
+ else
650
+ @current_pos = s4
651
+ s4 = :failed
652
+ end
653
+ else
654
+ @current_pos = s4
655
+ s4 = :failed
656
+ end
657
+ if s4 == :failed
658
+ s4 = @current_pos
659
+ s5 = []
660
+ s6 = match_regexp(/^[^\r\n ]/)
661
+ while s6 != :failed
662
+ s5 << s6
663
+ s6 = match_regexp(/^[^\r\n ]/)
664
+ end
665
+ @reported_pos = s4
666
+ s4 = s5 = s5.join
667
+ end
668
+ if s4 != :failed
669
+ s5 = []
670
+ s6 = match_str(" ")
671
+ while s6 != :failed
672
+ s5 << s6
673
+ s6 = match_str(" ")
674
+ end
675
+ if s5 != :failed
676
+ s6 = parse_time
677
+ s6 = nil if s6 == :failed
678
+ if s6 != :failed
679
+ s7 = match_str("+")
680
+ s7 = nil if s7 == :failed
681
+ if s7 != :failed
682
+ s8 = parse_nl
683
+ if s8 != :failed
684
+ @reported_pos = s0
685
+ s0 = s1 = { "move" => s4, "time" => s6 }
686
+ else
687
+ @current_pos = s0
688
+ s0 = :failed
689
+ end
690
+ else
691
+ @current_pos = s0
692
+ s0 = :failed
693
+ end
694
+ else
695
+ @current_pos = s0
696
+ s0 = :failed
697
+ end
698
+ else
699
+ @current_pos = s0
700
+ s0 = :failed
701
+ end
702
+ else
703
+ @current_pos = s0
704
+ s0 = :failed
705
+ end
706
+ else
707
+ @current_pos = s0
708
+ s0 = :failed
709
+ end
710
+ else
711
+ @current_pos = s0
712
+ s0 = :failed
713
+ end
714
+ else
715
+ @current_pos = s0
716
+ s0 = :failed
717
+ end
718
+ s0
719
+ end
720
+
721
+ def parse_te
722
+ s0 = []
723
+ s1 = match_regexp(/^[0-9]/)
724
+ if s1 != :failed
725
+ while s1 != :failed
726
+ s0 << s1
727
+ s1 = match_regexp(/^[0-9]/)
728
+ end
729
+ else
730
+ s0 = :failed
731
+ end
732
+ s0
733
+ end
734
+
735
+ def parse_fugou
736
+ s0 = @current_pos
737
+ s1 = parse_place
738
+ if s1 != :failed
739
+ s2 = parse_piece
740
+ if s2 != :failed
741
+ s3 = match_str("成")
742
+ s3 = nil if s3 == :failed
743
+ if s3 != :failed
744
+ @reported_pos = s0
745
+ s0 = s1 = { "to" => s1, "piece" => s2, "promote" => !!s3 }
746
+ else
747
+ @current_pos = s0
748
+ s0 = :failed
749
+ end
750
+ else
751
+ @current_pos = s0
752
+ s0 = :failed
753
+ end
754
+ else
755
+ @current_pos = s0
756
+ s0 = :failed
757
+ end
758
+ s0
759
+ end
760
+
761
+ def parse_place
762
+ s0 = @current_pos
763
+ s1 = parse_num
764
+ if s1 != :failed
765
+ s2 = parse_numkan
766
+ if s2 != :failed
767
+ @reported_pos = s0
768
+ s0 = s1 = { "x" => s1, "y" => s2 }
769
+ else
770
+ @current_pos = s0
771
+ s0 = :failed
772
+ end
773
+ else
774
+ @current_pos = s0
775
+ s0 = :failed
776
+ end
777
+ if s0 == :failed
778
+ s0 = @current_pos
779
+ s1 = match_str("同 ")
780
+ if s1 != :failed
781
+ @reported_pos = s0
782
+ s1 = nil
783
+ end
784
+ s0 = s1
785
+ end
786
+ s0
787
+ end
788
+
789
+ def parse_num
790
+ s0 = @current_pos
791
+ s1 = match_regexp(/^[123456789]/)
792
+ if s1 != :failed
793
+ @reported_pos = s0
794
+ s1 = zen2n(s1)
795
+ end
796
+ s1
797
+ end
798
+
799
+ def parse_numkan
800
+ s0 = @current_pos
801
+ s1 = match_regexp(/^[一二三四五六七八九]/)
802
+ if s1 != :failed
803
+ @reported_pos = s0
804
+ s1 = kan2n(s1)
805
+ end
806
+ s1
807
+ end
808
+
809
+ def parse_piece
810
+ s0 = @current_pos
811
+ s1 = match_str("成")
812
+ s1 = "" if s1 == :failed
813
+ if s1 != :failed
814
+ s2 = match_regexp(/^[歩香桂銀金角飛王玉と杏圭全馬竜龍]/)
815
+ if s2 != :failed
816
+ @reported_pos = s0
817
+ s0 = s1 = kind2csa(s1+s2)
818
+ else
819
+ @current_pos = s0
820
+ s0 = :failed
821
+ end
822
+ else
823
+ @current_pos = s0
824
+ s0 = :failed
825
+ end
826
+ s0
827
+ end
828
+
829
+ def parse_from
830
+ s0 = @current_pos
831
+ s1 = match_str("打")
832
+ if s1 != :failed
833
+ @reported_pos = s0
834
+ s1 = nil
835
+ end
836
+ s0 = s1
837
+ if s0 == :failed
838
+ s0 = @current_pos
839
+ s1 = match_str("(")
840
+ if s1 != :failed
841
+ s2 = match_regexp(/^[1-9]/)
842
+ if s2 != :failed
843
+ s3 = match_regexp(/^[1-9]/)
844
+ if s3 != :failed
845
+ s4 = match_str(")")
846
+ if s4 != :failed
847
+ @reported_pos = s0
848
+ s0 = s1 = { "x" => s2.to_i, "y" => s3.to_i }
849
+ else
850
+ @current_pos = s0
851
+ s0 = :failed
852
+ end
853
+ else
854
+ @current_pos = s0
855
+ s0 = :failed
856
+ end
857
+ else
858
+ @current_pos = s0
859
+ s0 = :failed
860
+ end
861
+ else
862
+ @current_pos = s0
863
+ s0 = :failed
864
+ end
865
+ end
866
+ s0
867
+ end
868
+
869
+ def parse_time
870
+ s0 = @current_pos
871
+ s1 = match_str("(")
872
+ if s1 != :failed
873
+ s2 = []
874
+ s3 = match_str(" ")
875
+ while s3 != :failed
876
+ s2 << s3
877
+ s3 = match_str(" ")
878
+ end
879
+ if s2 != :failed
880
+ s3 = parse_ms
881
+ if s3 != :failed
882
+ s4 = match_str("/")
883
+ if s4 != :failed
884
+ s5 = parse_hms
885
+ if s5 != :failed
886
+ s6 = match_str(")")
887
+ if s6 != :failed
888
+ @reported_pos = s0
889
+ s0 = s1 = { "now" => s3, "total" => s5 }
890
+ else
891
+ @current_pos = s0
892
+ s0 = :failed
893
+ end
894
+ else
895
+ @current_pos = s0
896
+ s0 = :failed
897
+ end
898
+ else
899
+ @current_pos = s0
900
+ s0 = :failed
901
+ end
902
+ else
903
+ @current_pos = s0
904
+ s0 = :failed
905
+ end
906
+ else
907
+ @current_pos = s0
908
+ s0 = :failed
909
+ end
910
+ else
911
+ @current_pos = s0
912
+ s0 = :failed
913
+ end
914
+ s0
915
+ end
916
+
917
+ def parse_hms
918
+ s0 = @current_pos
919
+ s1 = []
920
+ s2 = match_regexp(/^[0-9]/)
921
+ if s2 != :failed
922
+ while s2 != :failed
923
+ s1 << s2
924
+ s2 = match_regexp(/^[0-9]/)
925
+ end
926
+ else
927
+ s1 = :failed
928
+ end
929
+
930
+ if s1 != :failed
931
+ s2 = match_str(":")
932
+ if s2 != :failed
933
+ s3 = []
934
+ s4 = match_regexp(/^[0-9]/)
935
+ if s4 != :failed
936
+ while s4 != :failed
937
+ s3 << s4
938
+ s4 = match_regexp(/^[0-9]/)
939
+ end
940
+ else
941
+ s3 = :failed
942
+ end
943
+ if s3 != :failed
944
+ s4 = match_str(":")
945
+ if s4 != :failed
946
+ s5 = []
947
+ s6 = match_regexp(/^[0-9]/)
948
+ if s6 != :failed
949
+ while s6 != :failed
950
+ s5 << s6
951
+ s6 = match_regexp(/^[0-9]/)
952
+ end
953
+ else
954
+ s5 = :failed
955
+ end
956
+ if s5 != :failed
957
+ @reported_pos = s0
958
+ s0 = s1 = { "h" => s1.join.to_i, "m" => s3.join.to_i, "s" => s5.join.to_i }
959
+ else
960
+ @current_pos = s0
961
+ s0 = :failed
962
+ end
963
+ else
964
+ @current_pos = s0
965
+ s0 = :failed
966
+ end
967
+ else
968
+ @current_pos = s0
969
+ s0 = :failed
970
+ end
971
+ else
972
+ @current_pos = s0
973
+ s0 = :failed
974
+ end
975
+ else
976
+ @current_pos = s0
977
+ s0 = :failed
978
+ end
979
+ s0
980
+ end
981
+
982
+ def parse_ms
983
+ s0 = @current_pos
984
+ s1 = []
985
+ s2 = match_regexp(/^[0-9]/)
986
+ if s2 != :failed
987
+ while s2 != :failed
988
+ s1 << s2
989
+ s2 = match_regexp(/^[0-9]/)
990
+ end
991
+ else
992
+ s1 = :failed
993
+ end
994
+ if s1 != :failed
995
+ s2 = match_str(":")
996
+ if s2 != :failed
997
+ s3 = []
998
+ s4 = match_regexp(/^[0-9]/)
999
+ if s4 != :failed
1000
+ while s4 != :failed
1001
+ s3 << s4
1002
+ s4 = match_regexp(/^[0-9]/)
1003
+ end
1004
+ else
1005
+ s3 = :failed
1006
+ end
1007
+ if s3 != :failed
1008
+ @reported_pos = s0
1009
+ s0 = s1 = { "m" => s1.join.to_i, "s" => s3.join.to_i }
1010
+ else
1011
+ @current_pos = s0
1012
+ s0 = :failed
1013
+ end
1014
+ else
1015
+ @current_pos = s0
1016
+ s0 = :failed
1017
+ end
1018
+ else
1019
+ @current_pos = s0
1020
+ s0 = :failed
1021
+ end
1022
+ s0
1023
+ end
1024
+
1025
+ def parse_comment
1026
+ s0 = @current_pos
1027
+ s1 = match_str("*")
1028
+ if s1 != :failed
1029
+ s2 = []
1030
+ s3 = parse_nonl
1031
+ while s3 != :failed
1032
+ s2 << s3
1033
+ s3 = parse_nonl
1034
+ end
1035
+ if s2 != :failed
1036
+ s3 = parse_nl
1037
+ if s3 != :failed
1038
+ @reported_pos = s0
1039
+ s1 = s2.join
1040
+ s0 = s1
1041
+ else
1042
+ @current_pos = s0
1043
+ s0 = :failed
1044
+ end
1045
+ else
1046
+ @current_pos = s0
1047
+ s0 = :failed
1048
+ end
1049
+ else
1050
+ @current_pos = s0
1051
+ s0 = :failed
1052
+ end
1053
+ if s0 == :failed
1054
+ s0 = @current_pos
1055
+ s1 = match_str("&")
1056
+ if s1 != :failed
1057
+ s2 = []
1058
+ s3 = parse_nonl
1059
+ while s3 != :failed
1060
+ s2 << s3
1061
+ s3 = parse_nonl
1062
+ end
1063
+
1064
+ if s2 != :failed
1065
+ s3 = parse_nl
1066
+ if s3 != :failed
1067
+ @reported_pos = s0
1068
+ s0 = s1 = "&" + s2.join
1069
+ else
1070
+ @current_pos = s0
1071
+ s0 = :failed
1072
+ end
1073
+ else
1074
+ @current_pos = s0
1075
+ s0 = :failed
1076
+ end
1077
+ else
1078
+ @current_pos = s0
1079
+ s0 = :failed
1080
+ end
1081
+ end
1082
+ s0
1083
+ end
1084
+
1085
+ def parse_result
1086
+ s0 = @current_pos
1087
+ s1 = match_str("まで")
1088
+
1089
+ if s1 != :failed
1090
+ s2 = []
1091
+ s3 = match_regexp(/^[0-9]/)
1092
+ if s3 != :failed
1093
+ while s3 != :failed
1094
+ s2 << s3
1095
+ s3 = match_regexp(/^[0-9]/)
1096
+ end
1097
+ else
1098
+ s2 = :failed
1099
+ end
1100
+ if s2 != :failed
1101
+ s3 = match_str("手")
1102
+ if s3 != :failed
1103
+ s4 = @current_pos
1104
+ s5 = match_str("で")
1105
+ if s5 != :failed
1106
+ s6 = parse_turn
1107
+ if s6 != :failed
1108
+ s7 = match_str("手の")
1109
+ if s7 != :failed
1110
+ s8 = @current_pos
1111
+ s9 = match_str("勝ち")
1112
+ if s9 != :failed
1113
+ @reported_pos = s8
1114
+ s9 = "TORYO"
1115
+ end
1116
+ s8 = s9
1117
+ if s8 == :failed
1118
+ s8 = @current_pos
1119
+ s9 = match_str("反則")
1120
+ if s9 != :failed
1121
+ s10 = @current_pos
1122
+ s11 = match_str("勝ち")
1123
+ if s11 != :failed
1124
+ @reported_pos = s10
1125
+ s11 = "ILLEGAL_ACTION"
1126
+ end
1127
+ s10 = s11
1128
+ if s10 == :failed
1129
+ s10 = @current_pos
1130
+ s11 = match_str("負け")
1131
+ if s11 != :failed
1132
+ @reported_pos = s10
1133
+ s11 = "ILLEGAL_MOVE"
1134
+ end
1135
+ s10 = s11
1136
+ end
1137
+ if s10 != :failed
1138
+ @reported_pos = s8
1139
+ s8 = s9 = s10
1140
+ else
1141
+ @current_pos = s8
1142
+ s8 = :failed
1143
+ end
1144
+ else
1145
+ @current_pos = s8
1146
+ s8 = :failed
1147
+ end
1148
+ end
1149
+ if s8 != :failed
1150
+ @reported_pos = s4
1151
+ s4 = s5 = s8
1152
+ else
1153
+ @current_pos = s4
1154
+ s4 = :failed
1155
+ end
1156
+ else
1157
+ @current_pos = s4
1158
+ s4 = :failed
1159
+ end
1160
+ else
1161
+ @current_pos = s4
1162
+ s4 = :failed
1163
+ end
1164
+ else
1165
+ @current_pos = s4
1166
+ s4 = :failed
1167
+ end
1168
+ if s4 == :failed
1169
+ s4 = @current_pos
1170
+ s5 = match_str("で時間切れにより")
1171
+ if s5 != :failed
1172
+ s6 = parse_turn
1173
+ if s6 != :failed
1174
+ s7 = match_str("手の勝ち")
1175
+ if s7 != :failed
1176
+ @reported_pos = s4
1177
+ s4 = s5 = "TIME_UP"
1178
+ else
1179
+ @current_pos = s4
1180
+ s4 = :failed
1181
+ end
1182
+ else
1183
+ @current_pos = s4
1184
+ s4 = :failed
1185
+ end
1186
+ else
1187
+ @current_pos = s4
1188
+ s4 = :failed
1189
+ end
1190
+ if s4 == :failed
1191
+ s4 = @current_pos
1192
+ s5 = match_str("で中断")
1193
+ if s5 != :failed
1194
+ @reported_pos = s4
1195
+ s5 = "CHUDAN"
1196
+ end
1197
+ s4 = s5
1198
+ if s4 == :failed
1199
+ s4 = @current_pos
1200
+ s5 = match_str("で持将棋")
1201
+ if s5 != :failed
1202
+ @reported_pos = s4
1203
+ s5 = "JISHOGI"
1204
+ end
1205
+ s4 = s5
1206
+ if s4 == :failed
1207
+ s4 = @current_pos
1208
+ s5 = match_str("で千日手")
1209
+ if s5 != :failed
1210
+ @reported_pos = s4
1211
+ s5 = "SENNICHITE"
1212
+ end
1213
+ s4 = s5
1214
+ if s4 == :failed
1215
+ s4 = @current_pos
1216
+ s5 = match_str("で")
1217
+ s5 = nil if s5 == :failed
1218
+ if s5 != :failed
1219
+ s6 = match_str("詰")
1220
+ if s6 != :failed
1221
+ s7 = match_str("み")
1222
+ s7 = nil if s7 == :failed
1223
+ if s7 != :failed
1224
+ @reported_pos = s4
1225
+ s4 = s5 = "TSUMI"
1226
+ else
1227
+ @current_pos = s4
1228
+ s4 = :failed
1229
+ end
1230
+ else
1231
+ @current_pos = s4
1232
+ s4 = :failed
1233
+ end
1234
+ else
1235
+ @current_pos = s4
1236
+ s4 = :failed
1237
+ end
1238
+ if s4 == :failed
1239
+ s4 = @current_pos
1240
+ s5 = match_str("で不詰")
1241
+ if s5 != :failed
1242
+ @reported_pos = s4
1243
+ s5 = "FUZUMI"
1244
+ end
1245
+ s4 = s5
1246
+ end
1247
+ end
1248
+ end
1249
+ end
1250
+ end
1251
+ end
1252
+ if s4 != :failed
1253
+ s5 = parse_nl
1254
+ if s5 != :failed
1255
+ @reported_pos = s0
1256
+ s0 = s1 = s4
1257
+ else
1258
+ @current_pos = s0
1259
+ s0 = :failed
1260
+ end
1261
+ else
1262
+ @current_pos = s0
1263
+ s0 = :failed
1264
+ end
1265
+ else
1266
+ @current_pos = s0
1267
+ s0 = :failed
1268
+ end
1269
+ else
1270
+ @current_pos = s0
1271
+ s0 = :failed
1272
+ end
1273
+ else
1274
+ @current_pos = s0
1275
+ s0 = :failed
1276
+ end
1277
+ s0
1278
+ end
1279
+
1280
+ def parse_fork
1281
+ s0 = @current_pos
1282
+ s1 = match_str("変化:")
1283
+ if s1 != :failed
1284
+ s2 = []
1285
+ s3 = match_str(" ")
1286
+ while s3 != :failed
1287
+ s2 << s3
1288
+ s3 = match_str(" ")
1289
+ end
1290
+ if s2 != :failed
1291
+ s3 = parse_te
1292
+ if s3 != :failed
1293
+ s4 = match_str("手")
1294
+ if s4 != :failed
1295
+ s5 = parse_nl
1296
+ if s5 != :failed
1297
+ s6 = parse_moves
1298
+ if s6 != :failed
1299
+ @reported_pos = s0
1300
+ s0 = s1 = { "te" => s3.join.to_i, "moves" => s6[1..-1] }
1301
+ else
1302
+ @current_pos = s0
1303
+ s0 = :failed
1304
+ end
1305
+ else
1306
+ @current_pos = s0
1307
+ s0 = :failed
1308
+ end
1309
+ else
1310
+ @current_pos = s0
1311
+ s0 = :failed
1312
+ end
1313
+ else
1314
+ @current_pos = s0
1315
+ s0 = :failed
1316
+ end
1317
+ else
1318
+ @current_pos = s0
1319
+ s0 = :failed
1320
+ end
1321
+ else
1322
+ @current_pos = s0
1323
+ s0 = :failed
1324
+ end
1325
+ s0
1326
+ end
1327
+
1328
+ def parse_nl
1329
+ s0 = @current_pos
1330
+ s1 = []
1331
+ s2 = parse_newline
1332
+ if s2 != :failed
1333
+ while (s2 != :failed)
1334
+ s1 << s2
1335
+ s2 = parse_newline
1336
+ end
1337
+ else
1338
+ s1 = :failed
1339
+ end
1340
+ if s1 != :failed
1341
+ s2 = []
1342
+ s3 = parse_skipline
1343
+ while s3 != :failed
1344
+ s2 << s3
1345
+ s3 = parse_skipline
1346
+ end
1347
+ if s2 != :failed
1348
+ s0 = s1 = [s1, s2]
1349
+ else
1350
+ @current_pos = s0
1351
+ s0 = :failed
1352
+ end
1353
+ else
1354
+ @current_pos = s0
1355
+ s0 = :failed
1356
+ end
1357
+
1358
+ s0
1359
+ end
1360
+
1361
+ def parse_skipline
1362
+ s0 = @current_pos
1363
+ s1 = match_str("#")
1364
+ if s1 != :failed
1365
+ s2 = []
1366
+ s3 = parse_nonl
1367
+ while s3 != :failed
1368
+ s2 << s3
1369
+ s3 = parse_nonl
1370
+ end
1371
+ if s2 != :failed
1372
+ s3 = parse_newline
1373
+ if s3 != :failed
1374
+ s0 = s1 = [s1, s2, s3]
1375
+ else
1376
+ @current_pos = s0
1377
+ s0 = :failed
1378
+ end
1379
+ else
1380
+ @current_pos = s0
1381
+ s0 = :failed
1382
+ end
1383
+ else
1384
+ @current_pos = s0
1385
+ s0 = :failed
1386
+ end
1387
+ s0
1388
+ end
1389
+
1390
+ def parse_whitespace
1391
+ match_regexp(/^[ \t]/)
1392
+ end
1393
+
1394
+ def parse_newline
1395
+ s0 = @current_pos
1396
+ s1 = []
1397
+ s2 = parse_whitespace
1398
+ while s2 != :failed
1399
+ s1 << s2
1400
+ s2 = parse_whitespace
1401
+ end
1402
+ if s1 != :failed
1403
+ s2 = match_str("\n")
1404
+ if s2 == :failed
1405
+ s2 = @current_pos
1406
+ s3 = match_str("\r")
1407
+ if s3 != :failed
1408
+ s4 = match_str("\n")
1409
+ s4 = nil if s4 == :failed
1410
+ if s4 != :failed
1411
+ s2 = s3 = [s3, s4]
1412
+ else
1413
+ @current_pos = s2
1414
+ s2 = :failed
1415
+ end
1416
+ else
1417
+ @current_pos = s2
1418
+ s2 = :failed
1419
+ end
1420
+ end
1421
+ if s2 != :failed
1422
+ s0 = s1 = [s1, s2]
1423
+ else
1424
+ @current_pos = s0
1425
+ s0 = :failed
1426
+ end
1427
+ else
1428
+ @current_pos = s0
1429
+ s0 = :failed
1430
+ end
1431
+ s0
1432
+ end
1433
+
1434
+ def parse_nonl
1435
+ match_regexp(/^[^\r\n]/)
1436
+ end
1437
+
1438
+ protected
1439
+
1440
+ def zen2n(s)
1441
+ "0123456789".index(s)
1442
+ end
1443
+
1444
+ def kan2n(s)
1445
+ "〇一二三四五六七八九".index(s)
1446
+ end
1447
+
1448
+ def kan2n2(s)
1449
+ case s.length
1450
+ when 1
1451
+ "〇一二三四五六七八九十".index(s)
1452
+ when 2
1453
+ "〇一二三四五六七八九十".index(s[1])+10
1454
+ else
1455
+ raise "21以上の数値に対応していません"
1456
+ end
1457
+ end
1458
+
1459
+ def kind2csa(kind)
1460
+ if kind[0] == "成"
1461
+ {
1462
+ "香" => "NY",
1463
+ "桂" => "NK",
1464
+ "銀" => "NG"
1465
+ }[kind[1]]
1466
+ else
1467
+ {
1468
+ "歩" => "FU",
1469
+ "香" => "KY",
1470
+ "桂" => "KE",
1471
+ "銀" => "GI",
1472
+ "金" => "KI",
1473
+ "角" => "KA",
1474
+ "飛" => "HI",
1475
+ "玉" => "OU",
1476
+ "王" => "OU",
1477
+ "と" => "TO",
1478
+ "杏" => "NY",
1479
+ "圭" => "NK",
1480
+ "全" => "NG",
1481
+ "馬" => "UM",
1482
+ "竜" => "RY",
1483
+ "龍" => "RY"
1484
+ }[kind]
1485
+ end
1486
+ end
1487
+
1488
+ def special2csa(str)
1489
+ {
1490
+ "中断" => "CHUDAN",
1491
+ "投了" => "TORYO",
1492
+ "持将棋" => "JISHOGI",
1493
+ "千日手" => "SENNICHITE",
1494
+ "詰み" => "TSUMI",
1495
+ "不詰" => "FUZUMI",
1496
+ "切れ負け" => "TIME_UP",
1497
+ "反則勝ち" => "ILLEGAL_ACTION", # 直前の手が反則(先頭に+か-で反則した側の情報を含める必要が有る)
1498
+ "反則負け" => "ILLEGAL_MOVE" # ここで手番側が反則,反則の内容はコメントで表現
1499
+ }[str]
1500
+ end
1501
+
1502
+ def preset2str(preset)
1503
+ {
1504
+ "平手" => "HIRATE",
1505
+ "香落ち" => "KY",
1506
+ "右香落ち" => "KY_R",
1507
+ "角落ち" => "KA",
1508
+ "飛車落ち" => "HI",
1509
+ "飛香落ち" => "HIKY",
1510
+ "二枚落ち" => "2",
1511
+ "三枚落ち" => "3",
1512
+ "四枚落ち" => "4",
1513
+ "五枚落ち" => "5",
1514
+ "左五枚落ち" => "5_L",
1515
+ "六枚落ち" => "6",
1516
+ "八枚落ち" => "8",
1517
+ "十枚落ち" => "10",
1518
+ "その他" => "OTHER",
1519
+ }[preset.gsub(/\s/, "")]
1520
+ end
1521
+
1522
+ def make_hand(str)
1523
+ # Kifu for iPhoneは半角スペース区切り
1524
+ kinds = str.split(/[  ]/)
1525
+ ret = { "FU" => 0, "KY" => 0, "KE" => 0, "GI" => 0, "KI" => 0, "KA" => 0, "HI" => 0 }
1526
+ return ret if str.empty?
1527
+
1528
+ kinds.each do |kind|
1529
+ next if kind.empty?
1530
+ ret[kind2csa(kind[0])] = kind.length == 1 ? 1 : kan2n2(kind[1..-1])
1531
+ end
1532
+
1533
+ ret
1534
+ end
1535
+ end
1536
+ end