FreeBSDユーザーな自分のための棋譜検討ソフト

WindowsMacであればいろんなソフトが揃っていて困らないんでしょうけど、私のようなFreeBSDユーザーにとってはwineとかmono使ってwindowsアプリを動かすしか方法がなく、それもなあということで渋々(?)自作することになった。

まずは、CLIで操作する簡単なものを作成した。

black(80/180)? s
#80/180 white: 8d7e (132)
9 8 7 6 5 4 3 2 1
. n . . . . . n l  a
. r . . . g k . .  b
. . . . s p . p .  c
. . P . p s p . p  d
l . g p . . b . .  e
p P R . P . . . .  f
. . N . . P P . P  g
. . G . S . S K .  h
L . . . . G . N L  i
black: Px2
white: Px3 Bx1

black(80/180)?

github.com

しかし、さすがにasciiテキストではわかりにくいし、将棋わかるけどコンピュータには詳しくない人に局面を伝えるには無理がある。
ということで、次はPythontkinterを使って、多少見栄えのあるフロントエンドを作ることにした。
f:id:paleman:20190309154603p:plain
github.com

PlayOKのPSNYahoo!モバゲーの独自のKIFが読めるように適当に雑なパーサーを書いた。
それから、2chとかに貼られているKI2を読めるようにするかということで、本格的にPEGを利用。

PEGもArpeggioだとかTatSuだとかいろいろ選択肢がある中、なぜか自作。
PEGのオペレータの名前は、and, notが予約語で使えないので、RubyのParsletに倣ってpresent, absentという単語を使うことにした。

NezというPEGの文法そのものを拡張してアドホックなセマンティックアクションを減らす方法とかも考えたが、そこまでやらなくてよいことがわかった。
棋譜のパースという特殊な事情を考えれば、ASTの構築はdefaultで全部構築すればよく、optional, zero-or-moreの場合にマッチしなかった場合には、あえてNoneや空のリストをそのまま返すといった単純な方が都合がよいことがわかった。

例として、以下の文法があったとする。

e1 e2? e3

入力が e1 e3だった場合に、e2が省略されたことをセマンティックアクション側で知るには、e2の位置にe2がくるのかe3がくるのかを判定しなければいけない。さらにe3の処理を行う場合にはe2があった場合と省略された場合で処理対象のASTを1つずらす必要が出てくる。

そこで、e2?でe2にマッチしなかった場合に、Noneを返すようにする。

[e1, None, e3]

そうすると、e2の位置にあるオブジェクトはNoneかe2になるので、セマンティックアクションを実行する関数は固定位置にパースしたASTがあるかないかを判定するだけで済む。ASTにTag付けして識別する必要がなくなる。

zero-or-moreも同様に0マッチの場合に空listになるようにする。

e1 e2* e3
[e1, [], e3]

この方法はメリットもあるが、当然デメリットもある。
逆にASTを構築したくない場合に、いちいちflattenしないといけなくなる。
特にzero-or-moreの場合、必要以上に構造が入れ子になってしまい、ただ単にリニアなASTのノードの羅列だけ欲しい場合に邪魔になってしまう。

どちらの方がよいかは対象のドメインによって変わってくる。
どのドメインにも適用できる汎用PEGパーサー(コンビネータ or ジェネレータ)というのは難しい。