以前、パーサーを雑に比較した。
e-tipsmemo.hatenablog.com
結局Left-recursionは書き方で何とか対応できる範囲だと思われるので
Introducing pest into glsl and hindsight about nom vs. pest (part 1) – phaazon.net
(それ以外はどうしようもない)
インデントセンシティブなパース方法についてテストしていく。
pestでindent sensitiveな文法(pythonのような)をハンドルするには。
pest/lists.pest at master · pest-parser/pest · GitHub
PEEK_ALLとPUSHとDROPを使えばいいらしい。
まずはindentをスペースかタブが連続したものとして定義しておく。
(あとは適当なものを定義する)
indentation = _{(" " | " ")+}
id = {ASCII_ALPHANUMERIC+}
oneline = { (!" " ~ ANY)* }今回の場合、同じインデントの深さを持つ連続するonelineが1つのブロックとして認識されてほしいのでLeft-recursiveの書き換えと組み合わせて
oneline_first = _{oneline}
oneline_continue = _{PEEK_ALL~oneline}
onelines = _{oneline_first~(" "~oneline_continue)*}
oneline_children = {PEEK_ALL~PUSH(indentation)~onelines~DROP}oneline_firstが1個とoneline_continueが複数並ぶと書き換えることで、left-recursiveを避ける。
同じ要領で、インデントされた連続するmoduleを定義する。
moduleはonline_childrenを持つ。
circuit = {"circuit "~id~" :"~"\n"~module_children~"\n"?~EOI}
module = {"module "~id~" :"~("\n"~oneline_children)?}
modules = _{module_first~("\n"~module_continue)*}
module_first = _{module}
module_continue = _{PEEK_ALL~module}
module_children = {PEEK_ALL~PUSH(indentation)~modules~DROP}これに
circuit hoge : module piyo : foo bar module baz : one two
こんな入力をいれたら
よさそう?

pest. The Elegant Parser
まだこれだけではwhen ~ else ~のようなインデントの位置が戻るが、1つのブロックとなっている文法には対応できていないがそれはまた後でやる。
インデントスコープを区切る言語/設定ファイルは作る側にとっては楽かもしれんが、パースすることを考えたら面倒くさいんじゃないかなと思う。が、割と溢れている気がする。
とにかく自分の使いたいことを実現できそうなパーサーがあってよかった。