jkf 0.4.3 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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