GaucheのPEGが地味に難しい
moshの0.2.7に入れたGauche由来のPEGパーサは便利なんだけど、なかなか慣れるのが難しい。
スペースで区切られた"hoge fuga hogehoge"の文字列から、'("hoge" "fuga" "hogehoge")のリストを受け取るようなパーサを考える。
(define name ($do [n ($many letter)] [($c #\space)] ;; ← 区切り文字も消費する必要がある。 ($return (list->string n)))) (define names ($many name)) (write (peg-parse-string names "hoge fuga hogehoge ")) ;; ← 最後にスペース
区切り文字の消費を忘れると、$manyパーサは無限ループになる。(この指定の仕方だと0個にもマッチするから)
よって正解は、
(define name ($do [n ($many letter 1)] [($optional ($c #\space))] ($return (list->string n)))) (define names ($many name)) (write (peg-parse-string names "hoge fuga hogehoge"))
のようになる。
要するに、パーサが何を消費するのかを常に把握しないといけない。$orを書くときに$tryを忘れがち。たとえば、"ab"にマッチするパーサと"ac"にマッチするパーサの両方をorでつなぐ時、
(import (rnrs) (yuni text peg)) (define a ($c #\a)) (define ab ($seq a ($c #\b))) (define ac ($seq a ($c #\c))) ;(define pat ($or ab ac)) ;; ← まちがい (define pat ($or ($try ab) ($try ac))) (write (peg-parse-string ($many pat) "abacabac"))
abとacを単純にorで連結してしまうと、abがダメだったときにバックトラックが起こらなくて悲しいことになる。