jkf 0.2.2

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.
@@ -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