更新
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系を通常系の左に置く必要があるようだ