更新

SPECに記載されているSPARQLの文法がLL(1)か怪しい件(2)

前回の件についてEditorのAndyに聞いてみたところ、SPECの文法はARQ用のパーサをJavaCCで作ったものだと教えてくれた。JavaCCには余り詳しくないが、ざっと調べてみたところ、たしかにJavaCCはLL(k)の構文解析器を標準で使うトップダウン型のパーサーらしい。

が、JavaCCは単純な再帰下降型のパーサーではない。お手軽再帰下降型パーサが構文解析と字句解析を平行して行うのに対し、JavaCCは字句解析を予め行い、入力をトークンに分解してから再帰下降な構文解析を行う。そしてどうやら字句解析器(Token Manager)が最長一致でトークンの切り出しを行うらしい。FAQに以下の項目があった。

Then the longest prefix is used. That is, the token's image will be the longest prefix of the input that matches the chosen regular expression.

というわけで、JavaCCのような字句解析を先行して行う場合は、SPECのEBNFは役に立つかもしれない。しかしLL(1)文法だと思って字句解析を平行して行うパーサーを作ると痛い目を見るので注意が必要だ。

なお、SPARQLのEBNFの場合、チート的な解決策がある。uppercasedなTerminalルールのうち、実際に問題を起こしうるのは(把握している範囲では)PNAME_NSPNAME_LNのペア、そしてUnsinged、Negative、PositiveそれぞれのINTEGERDECIMALそしてDOUBLEの組み合わせだ。これらがORで繋がるのはPrefixedNameNumericLiteralUnsignedNumericLiteralPositiveNumericLiteralNegativeである。常識的に考えて普通に実装した再帰下降型のパーサなら左(もしくは上)に書かれた選択肢から先読みの確認をしていくので、選択肢を最長一致になる物から並べればいい。つまりDECIMALは常にINTEGERより長くマッチし、DOUBLEはさらにそれより長いので、INTEGER|DECIMAL|DOUBLEDOUBLE|DECIMAL|INTEGERに並び変えれば問題が解決する。PrefixedNameに関しては最初からそうなっているので、NumericLiteral系だけ並び替えればひとまずは正しく構文解析できるだろう。

加えてStringについてもLONG系を通常系の左に置く必要があるようだ