jkf 0.4.3 → 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,426 +1,420 @@
1
- # coding: utf-8
1
+ module Jkf
2
+ module Parser
3
+ # KI2 Parser
4
+ class Ki2 < Base
5
+ include Kifuable
2
6
 
3
- module Jkf::Parser
4
- # KI2 Parser
5
- class Ki2 < Base
6
- include Kifuable
7
+ protected
7
8
 
8
- protected
9
-
10
- # kifu : header* initialboard? header* moves fork*
11
- def parse_root
12
- s0 = @current_pos
13
- s1 = []
14
- s2 = parse_header
15
- while s2 != :failed
16
- s1 << s2
9
+ # kifu : header* initialboard? header* moves fork*
10
+ def parse_root
11
+ s0 = @scanner.pos
12
+ s1 = []
17
13
  s2 = parse_header
18
- end
19
- if s1 != :failed
20
- s2 = parse_initialboard
21
- s2 = nil if s2 == :failed
22
- s3 = []
23
- s4 = parse_header
24
- while s4 != :failed
25
- s3 << s4
26
- s4 = parse_header
14
+ while s2 != :failed
15
+ s1 << s2
16
+ s2 = parse_header
27
17
  end
28
- s4 = parse_moves
29
- if s4 != :failed
30
- s5 = []
31
- s6 = parse_fork
32
- while s6 != :failed
33
- s5 << s6
18
+ if s1 == :failed
19
+ @scanner.pos = s0
20
+ s0 = :failed
21
+ else
22
+ s2 = parse_initialboard
23
+ s2 = nil if s2 == :failed
24
+ s3 = []
25
+ s4 = parse_header
26
+ while s4 != :failed
27
+ s3 << s4
28
+ s4 = parse_header
29
+ end
30
+ s4 = parse_moves
31
+ if s4 == :failed
32
+ @scanner.pos = s0
33
+ s0 = :failed
34
+ else
35
+ s5 = []
34
36
  s6 = parse_fork
37
+ while s6 != :failed
38
+ s5 << s6
39
+ s6 = parse_fork
40
+ end
41
+ @reported_pos = s0
42
+ s0 = transform_root(s1, s2, s3, s4, s5)
35
43
  end
36
- @reported_pos = s0
37
- s0 = transform_root(s1, s2, s3, s4, s5)
38
- else
39
- @current_pos = s0
40
- s0 = :failed
41
44
  end
42
- else
43
- @current_pos = s0
44
- s0 = :failed
45
+ s0
45
46
  end
46
- s0
47
- end
48
47
 
49
- # header : [^:\r\n]+ ":" nonls nl+ | header_teban
50
- def parse_header
51
- s0 = @current_pos
52
- s2 = match_regexp(/^[^*:\r\n]/)
53
- if s2 != :failed
54
- s1 = []
55
- while s2 != :failed
56
- s1 << s2
57
- s2 = match_regexp(/^[^:\r\n]/)
48
+ # header : [^:\r\n]+ ":" nonls nl+ | header_teban
49
+ def parse_header
50
+ s0 = @scanner.pos
51
+ s2 = match_regexp(/[^*:\r\n]/)
52
+ if s2 == :failed
53
+ s1 = :failed
54
+ else
55
+ s1 = []
56
+ while s2 != :failed
57
+ s1 << s2
58
+ s2 = match_regexp(/[^:\r\n]/)
59
+ end
58
60
  end
59
- else
60
- s1 = :failed
61
- end
62
- if s1 != :failed
63
- if match_str(":") != :failed
61
+ if s1 == :failed
62
+ @scanner.pos = s0
63
+ s0 = :failed
64
+ elsif match_str(':') != :failed
64
65
  s3 = parse_nonls
65
66
  s5 = parse_nl
66
- if s5 != :failed
67
+ if s5 == :failed
68
+ s4 = :failed
69
+ else
67
70
  s4 = []
68
71
  while s5 != :failed
69
72
  s4 << s5
70
73
  s5 = parse_nl
71
74
  end
72
- else
73
- s4 = :failed
74
75
  end
75
- if s4 != :failed
76
- @reported_pos = s0
77
- s0 = { "k" => s1.join, "v" => s3.join }
78
- else
79
- @current_pos = s0
76
+ if s4 == :failed
77
+ @scanner.pos = s0
80
78
  s0 = :failed
79
+ else
80
+ @reported_pos = s0
81
+ s0 = { 'k' => s1.join, 'v' => s3.join }
81
82
  end
82
83
  else
83
- @current_pos = s0
84
+ @scanner.pos = s0
84
85
  s0 = :failed
85
86
  end
86
- else
87
- @current_pos = s0
88
- s0 = :failed
87
+ s0 = parse_header_teban if s0 == :failed
88
+ s0
89
89
  end
90
- s0 = parse_header_teban if s0 == :failed
91
- s0
92
- end
93
90
 
94
- # header_teban : [先後上下] "手番" nl
95
- def parse_header_teban
96
- s0 = @current_pos
97
- s1 = parse_turn
98
- if s1 != :failed
99
- s2 = match_str("手番")
100
- if s2 != :failed
101
- s3 = parse_nl
102
- if s3 != :failed
103
- @reported_pos = s0
104
- { "k" => "手番", "v" => s1 }
105
- else
106
- @current_pos = s0
91
+ # header_teban : [先後上下] "手番" nl
92
+ def parse_header_teban
93
+ s0 = @scanner.pos
94
+ s1 = parse_turn
95
+ if s1 == :failed
96
+ @scanner.pos = s0
97
+ :failed
98
+ else
99
+ s2 = match_str('手番')
100
+ if s2 == :failed
101
+ @scanner.pos = s0
107
102
  :failed
103
+ else
104
+ s3 = parse_nl
105
+ if s3 == :failed
106
+ @scanner.pos = s0
107
+ :failed
108
+ else
109
+ @reported_pos = s0
110
+ { 'k' => '手番', 'v' => s1 }
111
+ end
108
112
  end
109
- else
110
- @current_pos = s0
111
- :failed
112
113
  end
113
- else
114
- @current_pos = s0
115
- :failed
116
114
  end
117
- end
118
115
 
119
- # moves : firstboard : move* result?
120
- def parse_moves
121
- s0 = @current_pos
122
- s1 = parse_firstboard
123
- if s1 != :failed
124
- s2 = []
125
- s3 = parse_move
126
- while s3 != :failed
127
- s2 << s3
116
+ # moves : firstboard : move* result?
117
+ def parse_moves
118
+ s0 = @scanner.pos
119
+ s1 = parse_firstboard
120
+ if s1 == :failed
121
+ @scanner.pos = s0
122
+ s0 = :failed
123
+ else
124
+ s2 = []
128
125
  s3 = parse_move
126
+ while s3 != :failed
127
+ s2 << s3
128
+ s3 = parse_move
129
+ end
130
+ s3 = parse_result
131
+ s3 = nil if s3 == :failed
132
+ @reported_pos = s0
133
+ s0 = -> (hd, tl, res) do
134
+ tl.unshift(hd)
135
+ tl << { 'special' => res } if res && !tl[tl.length - 1]['special']
136
+ tl
137
+ end.call(s1, s2, s3)
129
138
  end
130
- s3 = parse_result
131
- s3 = nil if s3 == :failed
132
- @reported_pos = s0
133
- s0 = -> (hd, tl, res) do
134
- tl.unshift(hd)
135
- tl << { "special" => res } if res && !tl[tl.length - 1]["special"]
136
- tl
137
- end.call(s1, s2, s3)
138
- else
139
- @current_pos = s0
140
- s0 = :failed
139
+ s0
141
140
  end
142
- s0
143
- end
144
141
 
145
- # firstboard : comment* pointer?
146
- def parse_firstboard
147
- s0 = @current_pos
148
- s1 = []
149
- s2 = parse_comment
150
- while s2 != :failed
151
- s1 << s2
142
+ # firstboard : comment* pointer?
143
+ def parse_firstboard
144
+ s0 = @scanner.pos
145
+ s1 = []
152
146
  s2 = parse_comment
147
+ while s2 != :failed
148
+ s1 << s2
149
+ s2 = parse_comment
150
+ end
151
+ parse_pointer
152
+ @reported_pos = s0
153
+ s1.empty? ? {} : { 'comments' => s1 }
153
154
  end
154
- parse_pointer
155
- @reported_pos = s0
156
- s0 = s1.empty? ? {} : { "comments" => s1 }
157
- s0
158
- end
159
155
 
160
- # move : line comment* pointer? (nl | " ")*
161
- def parse_move
162
- s0 = @current_pos
163
- s1 = parse_line
164
- if s1 != :failed
165
- s2 = []
166
- s3 = parse_comment
167
- while s3 != :failed
168
- s2 << s3
156
+ # move : line comment* pointer? (nl | " ")*
157
+ def parse_move
158
+ s0 = @scanner.pos
159
+ s1 = parse_line
160
+ if s1 == :failed
161
+ @scanner.pos = s0
162
+ s0 = :failed
163
+ else
164
+ s2 = []
169
165
  s3 = parse_comment
170
- end
171
- parse_pointer
172
- s4 = []
173
- s5 = parse_nl
174
- s5 = match_space if s5 == :failed
175
- while s5 != :failed
176
- s4 << s5
166
+ while s3 != :failed
167
+ s2 << s3
168
+ s3 = parse_comment
169
+ end
170
+ parse_pointer
171
+ s4 = []
177
172
  s5 = parse_nl
178
173
  s5 = match_space if s5 == :failed
174
+ while s5 != :failed
175
+ s4 << s5
176
+ s5 = parse_nl
177
+ s5 = match_space if s5 == :failed
178
+ end
179
+ @reported_pos = s0
180
+ s0 = -> (line, c) do
181
+ ret = { 'move' => line }
182
+ ret['comments'] = c unless c.empty?
183
+ ret
184
+ end.call(s1, s2)
179
185
  end
180
- @reported_pos = s0
181
- s0 = -> (line, c) do
182
- ret = { "move" => line }
183
- ret["comments"] = c if !c.empty?
184
- ret
185
- end.call(s1, s2)
186
- else
187
- @current_pos = s0
188
- s0 = :failed
189
- end
190
186
 
191
- s0
192
- end
187
+ s0
188
+ end
193
189
 
194
- # line : [▲△] fugou (nl / " ")*
195
- def parse_line
196
- s0 = @current_pos
197
- s1 = match_regexp(/^[▲△]/)
198
- if s1 != :failed
199
- s1 = if s1 == "▲"
200
- { "color" => 0 }
201
- else
202
- { "color" => 1 }
203
- end
204
- s2 = parse_fugou
205
- if s2 != :failed
206
- s3 = []
207
- s4 = parse_nl
208
- s4 = match_space if s4 == :failed
209
- while s4 != :failed
210
- s3 << s4
190
+ # line : [▲△] fugou (nl / " ")*
191
+ def parse_line
192
+ s0 = @scanner.pos
193
+ s1 = match_regexp(/[▲△]/)
194
+ if s1 == :failed
195
+ @scanner.pos = s0
196
+ s0 = :failed
197
+ else
198
+ s1 = if s1 == '▲'
199
+ { 'color' => 0 }
200
+ else
201
+ { 'color' => 1 }
202
+ end
203
+ s2 = parse_fugou
204
+ if s2 == :failed
205
+ @scanner.pos = s0
206
+ s0 = :failed
207
+ else
208
+ s3 = []
211
209
  s4 = parse_nl
212
210
  s4 = match_space if s4 == :failed
211
+ while s4 != :failed
212
+ s3 << s4
213
+ s4 = parse_nl
214
+ s4 = match_space if s4 == :failed
215
+ end
216
+ @reported_pos = s0
217
+ s0 = s2.merge(s1)
213
218
  end
214
- @reported_pos = s0
215
- s0 = s2.merge(s1)
216
- else
217
- @current_pos = s0
218
- s0 = :failed
219
219
  end
220
- else
221
- @current_pos = s0
222
- s0 = :failed
220
+ s0
223
221
  end
224
- s0
225
- end
226
222
 
227
- # fugou : place piece soutai? dousa? ("成" | "不成")? "打"?
228
- def parse_fugou
229
- s0 = @current_pos
230
- s1 = parse_place
231
- if s1 != :failed
232
- s2 = parse_piece
233
- if s2 != :failed
234
- s3 = parse_soutai
235
- s3 = nil if s3 == :failed
236
- s4 = parse_dousa
237
- s4 = nil if s4 == :failed
238
- s5 = match_str("成")
239
- s5 = match_str("不成") if s5 == :failed
240
- s5 = nil if s5 == :failed
241
- s6 = match_str("打")
242
- s6 = nil if s6 == :failed
243
- @reported_pos = s0
244
- transform_fugou(s1, s2, s3, s4, s5, s6)
245
- else
246
- @current_pos = s0
223
+ # fugou : place piece soutai? dousa? ("成" | "不成")? "打"?
224
+ def parse_fugou
225
+ s0 = @scanner.pos
226
+ s1 = parse_place
227
+ if s1 == :failed
228
+ @scanner.pos = s0
247
229
  :failed
230
+ else
231
+ s2 = parse_piece
232
+ if s2 == :failed
233
+ @scanner.pos = s0
234
+ :failed
235
+ else
236
+ s3 = parse_soutai
237
+ s3 = nil if s3 == :failed
238
+ s4 = parse_dousa
239
+ s4 = nil if s4 == :failed
240
+ s5 = match_str('成')
241
+ s5 = match_str('不成') if s5 == :failed
242
+ s5 = nil if s5 == :failed
243
+ s6 = match_str('打')
244
+ s6 = nil if s6 == :failed
245
+ @reported_pos = s0
246
+ transform_fugou(s1, s2, s3, s4, s5, s6)
247
+ end
248
248
  end
249
- else
250
- @current_pos = s0
251
- :failed
252
249
  end
253
- end
254
250
 
255
- # place : num numkan
256
- def parse_place
257
- s0 = @current_pos
258
- s1 = parse_num
259
- if s1 != :failed
260
- s2 = parse_numkan
261
- if s2 != :failed
262
- @reported_pos = s0
263
- s0 = { "x" => s1, "y" => s2 }
264
- else
265
- @current_pos = s0
251
+ # place : num numkan
252
+ def parse_place
253
+ s0 = @scanner.pos
254
+ s1 = parse_num
255
+ if s1 == :failed
256
+ @scanner.pos = s0
266
257
  s0 = :failed
267
- end
268
- else
269
- @current_pos = s0
270
- s0 = :failed
271
- end
272
- if s0 == :failed
273
- s0 = @current_pos
274
- if match_regexp("同") != :failed
275
- match_str(" ")
276
- @reported_pos = s0
277
- s0 = { "same" => true }
278
258
  else
279
- @current_pos = s0
280
- s0 = :failed
259
+ s2 = parse_numkan
260
+ if s2 == :failed
261
+ @scanner.pos = s0
262
+ s0 = :failed
263
+ else
264
+ @reported_pos = s0
265
+ s0 = { 'x' => s1, 'y' => s2 }
266
+ end
281
267
  end
268
+ if s0 == :failed
269
+ s0 = @scanner.pos
270
+ if match_regexp('同') == :failed
271
+ @scanner.pos = s0
272
+ s0 = :failed
273
+ else
274
+ match_str(' ')
275
+ @reported_pos = s0
276
+ s0 = { 'same' => true }
277
+ end
278
+ end
279
+ s0
282
280
  end
283
- s0
284
- end
285
281
 
286
- # soutai : [左直右]
287
- def parse_soutai
288
- match_regexp(/^[左直右]/)
289
- end
282
+ # soutai : [左直右]
283
+ def parse_soutai
284
+ match_regexp(/[左直右]/)
285
+ end
290
286
 
291
- # dousa : [上寄引]
292
- def parse_dousa
293
- match_regexp(/^[上寄引]/)
294
- end
287
+ # dousa : [上寄引]
288
+ def parse_dousa
289
+ match_regexp(/[上寄引]/)
290
+ end
295
291
 
296
- # "*" nonls nl
297
- def parse_comment
298
- s0 = @current_pos
299
- if match_str("*") != :failed
300
- s2 = parse_nonls
301
- if parse_nl != :failed
302
- @reported_pos = s0
303
- s0 = s2.join
304
- else
305
- @current_pos = s0
292
+ # "*" nonls nl
293
+ def parse_comment
294
+ s0 = @scanner.pos
295
+ if match_str('*') == :failed
296
+ @scanner.pos = s0
306
297
  s0 = :failed
298
+ else
299
+ s2 = parse_nonls
300
+ if parse_nl == :failed
301
+ @scanner.pos = s0
302
+ s0 = :failed
303
+ else
304
+ @reported_pos = s0
305
+ s0 = s2.join
306
+ end
307
307
  end
308
- else
309
- @current_pos = s0
310
- s0 = :failed
308
+ s0
311
309
  end
312
- s0
313
- end
314
310
 
315
- # fork : "変化:" " "* [0-9]+ "手" nl moves
316
- def parse_fork
317
- s0 = @current_pos
318
- if match_str("変化:") != :failed
319
- match_spaces
320
- s3 = match_digits!
321
- if s3 != :failed
322
- if match_str("手") != :failed
323
- if parse_nl != :failed
311
+ # fork : "変化:" " "* [0-9]+ "手" nl moves
312
+ def parse_fork
313
+ s0 = @scanner.pos
314
+ if match_str('変化:') == :failed
315
+ @scanner.pos = s0
316
+ s0 = :failed
317
+ else
318
+ match_spaces
319
+ s3 = match_digits!
320
+ if s3 == :failed
321
+ @scanner.pos = s0
322
+ s0 = :failed
323
+ elsif match_str('手') != :failed
324
+ if parse_nl == :failed
325
+ @scanner.pos = s0
326
+ s0 = :failed
327
+ else
324
328
  s6 = parse_moves
325
- if s6 != :failed
326
- @reported_pos = s0
327
- s0 = { "te" => s3.join.to_i, "moves" => s6[1..-1] }
328
- else
329
- @current_pos = s0
329
+ if s6 == :failed
330
+ @scanner.pos = s0
330
331
  s0 = :failed
332
+ else
333
+ @reported_pos = s0
334
+ s0 = { 'te' => s3.join.to_i, 'moves' => s6[1..-1] }
331
335
  end
332
- else
333
- @current_pos = s0
334
- s0 = :failed
335
336
  end
336
337
  else
337
- @current_pos = s0
338
+ @scanner.pos = s0
338
339
  s0 = :failed
339
340
  end
340
- else
341
- @current_pos = s0
342
- s0 = :failed
343
341
  end
344
- else
345
- @current_pos = s0
346
- s0 = :failed
342
+ s0
347
343
  end
348
- s0
349
- end
350
344
 
351
- # turn : [先後上下]
352
- def parse_turn
353
- match_regexp(/^[先後上下]/)
354
- end
345
+ # turn : [先後上下]
346
+ def parse_turn
347
+ match_regexp(/[先後上下]/)
348
+ end
355
349
 
356
- # transform to jkf
357
- def transform_root(headers, ini, headers2, moves, forks)
358
- ret = { "header" => {}, "moves" => moves }
359
- headers.compact.each { |h| ret["header"][h["k"]] = h["v"] }
360
- headers2.compact.each { |h| ret["header"][h["k"]] = h["v"] }
361
- if ini
362
- ret["initial"] = ini
363
- elsif ret["header"]["手合割"]
364
- preset = preset2str(ret["header"]["手合割"])
365
- ret["initial"] = { "preset" => preset } if preset != "OTHER"
350
+ # transform to jkf
351
+ def transform_root(headers, ini, headers2, moves, forks)
352
+ ret = { 'header' => {}, 'moves' => moves }
353
+ headers.compact.each { |h| ret['header'][h['k']] = h['v'] }
354
+ headers2.compact.each { |h| ret['header'][h['k']] = h['v'] }
355
+ if ini
356
+ ret['initial'] = ini
357
+ elsif ret['header']['手合割']
358
+ preset = preset2str(ret['header']['手合割'])
359
+ ret['initial'] = { 'preset' => preset } if preset != 'OTHER'
360
+ end
361
+ transform_root_header_data(ret) if ret['initial'] && ret['initial']['data']
362
+ transform_root_forks(forks, moves)
363
+ ret
366
364
  end
367
- transform_root_header_data(ret) if ret["initial"] && ret["initial"]["data"]
368
- transform_root_forks(forks, moves)
369
- ret
370
- end
371
365
 
372
- # transfrom fugou to jkf
373
- def transform_fugou(pl, pi, sou, dou, pro, da)
374
- ret = { "piece" => pi }
375
- if pl["same"]
376
- ret["same"] = true
377
- else
378
- ret["to"] = pl
366
+ # transfrom fugou to jkf
367
+ def transform_fugou(pl, pi, sou, dou, pro, da)
368
+ ret = { 'piece' => pi }
369
+ if pl['same']
370
+ ret['same'] = true
371
+ else
372
+ ret['to'] = pl
373
+ end
374
+ ret['promote'] = (pro == '成') if pro
375
+ if da
376
+ ret['relative'] = 'H'
377
+ else
378
+ rel = soutai2relative(sou) + dousa2relative(dou)
379
+ ret['relative'] = rel unless rel.empty?
380
+ end
381
+ ret
379
382
  end
380
- ret["promote"] = (pro == "成") if pro
381
- if da
382
- ret["relative"] = "H"
383
- else
384
- rel = soutai2relative(sou) + dousa2relative(dou)
385
- ret["relative"] = rel unless rel.empty?
383
+
384
+ # relative string to jkf
385
+ def soutai2relative(str)
386
+ {
387
+ '左' => 'L',
388
+ '直' => 'C',
389
+ '右' => 'R'
390
+ }[str] || ''
386
391
  end
387
- ret
388
- end
389
392
 
390
- # relative string to jkf
391
- def soutai2relative(str)
392
- {
393
- "左" => "L",
394
- "直" => "C",
395
- "右" => "R"
396
- }[str] || ""
397
- end
393
+ # movement string to jkf
394
+ def dousa2relative(str)
395
+ {
396
+ '上' => 'U',
397
+ '寄' => 'M',
398
+ '引' => 'D'
399
+ }[str] || ''
400
+ end
398
401
 
399
- # movement string to jkf
400
- def dousa2relative(str)
401
- {
402
- "上" => "U",
403
- "寄" => "M",
404
- "引" => "D"
405
- }[str] || ""
406
- end
402
+ # generate motigoma
403
+ def make_hand(str)
404
+ ret = { 'FU' => 0, 'KY' => 0, 'KE' => 0, 'GI' => 0, 'KI' => 0, 'KA' => 0, 'HI' => 0 }
405
+ return ret if str.empty?
407
406
 
408
- # generate motigoma
409
- def make_hand(str)
410
- ret = { "FU" => 0, "KY" => 0, "KE" => 0, "GI" => 0, "KI" => 0, "KA" => 0, "HI" => 0 }
411
- return ret if str.empty?
407
+ str.gsub(/ $/, '').split(' ').each do |kind|
408
+ next if kind.empty?
409
+ ret[kind2csa(kind[0])] = kind.length == 1 ? 1 : kan2n2(kind[1..-1])
410
+ end
412
411
 
413
- str.gsub(/ $/, "").split(" ").each do |kind|
414
- next if kind.empty?
415
- ret[kind2csa(kind[0])] = kind.length == 1 ? 1 : kan2n2(kind[1..-1])
412
+ ret
416
413
  end
417
414
 
418
- ret
419
- end
420
-
421
- # check eos
422
- def eos?
423
- @input[@current_pos].nil?
415
+ def eos?
416
+ @scanner.eos?
417
+ end
424
418
  end
425
419
  end
426
420
  end