更新
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_NSとPNAME_LNのペア、そしてUnsinged、Negative、PositiveそれぞれのINTEGER、DECIMALそしてDOUBLEの組み合わせだ。これらがORで繋がるのはPrefixedNameとNumericLiteralUnsigned、NumericLiteralPositive、NumericLiteralNegativeである。常識的に考えて普通に実装した再帰下降型のパーサなら左(もしくは上)に書かれた選択肢から先読みの確認をしていくので、選択肢を最長一致になる物から並べればいい。つまりDECIMALは常にINTEGERより長くマッチし、DOUBLEはさらにそれより長いので、INTEGER|DECIMAL|DOUBLEをDOUBLE|DECIMAL|INTEGERに並び変えれば問題が解決する。PrefixedNameに関しては最初からそうなっているので、NumericLiteral系だけ並び替えればひとまずは正しく構文解析できるだろう。
加えてStringについてもLONG系を通常系の左に置く必要があるようだ