2021/9/5 parslet
onk.icon
Parslet とは
PEG parser
類似
業務で Scala プロダクトから使っていた
目的
markdown ではない 独自言語 (Wiki 記法のような) を扱いたい
とりあえず markdown をある程度パースできたらゴールかな。あとは微修正でいけるだろう
始め方
Get Started
ルールを作って
code:ruby
require "parslet"
class Mini < Parslet::Parser
rule(:integer) { match("0-9").repeat(1) } root(:integer)
end
パースする
code:ruby
Mini.new.parse("132432")
# => "132432"@0
これは Parslet::Slice Class のインスタンス
code:ruby
Mini.new.parse("132432").class
# => Parslet::Slice
対応していない入力を投げるとエラー
code:ruby
Mini.new.parse("puts(1)")
/Users/onaka/.bundle/ruby/3.1.0/gems/parslet-2.0.0/lib/parslet/cause.rb:70:in `raise': Expected at least 1 of 0-9 at line 1 char 1. (Parslet::ParseFailed) from /Users/onaka/.bundle/ruby/3.1.0/gems/parslet-2.0.0/lib/parslet/atoms/base.rb:49:in `parse'
ここまでで2つの定義を書いた。
code:ruby
rule(:integer) { match("0-9").repeat(1) } :integer のルールは「[0-9] が 1 つ以上」
code:ruby
root(:integer)
パースを :integer から始める
Addition
定義を追加する
code:ruby
rule(:space) { match("\s").repeat(1) }
rule(:space?) { space.maybe }
ルールの中から他のルールを呼べる
maybe というものがある
repeat(0, 1) と同じ。
いきなり完成形出てきた
code:ruby
class Mini < Parslet::Parser
rule(:integer) { match("0-9").repeat(1) >> space? } rule(:space) { match("\s").repeat(1) }
rule(:space?) { space.maybe }
rule(:operator) { match("+") >> space? } rule(:sum) { integer >> operator >> expression }
rule(:expression) { sum | integer }
root(:expression)
end
def parse(str)
mini = Mini.new
mini.parse(str)
rescue Parslet::ParseFailed => failure
puts failure.parse_failure_cause.ascii_tree
end
:integer の定義が「後ろに space を含んでも良い」に変わってる
:expression は :sum または :integer
:sum は :integer, :operator, :expression
なるほど、こういう再帰
:operator は [+] w/space?
実行する
code:ruby
parse("1 + 2 + 3") # => "1 + 2 + 3"@0
code:ruby
parse("a + 2")
|- Failed to match sequence (INTEGER OPERATOR EXPRESSION) at line 1 char 1.
| `- Failed to match sequence (0-9{1, } SPACE?) at line 1 char 1. | `- Expected at least 1 of 0-9 at line 1 char 1. | `- Failed to match 0-9 at line 1 char 1. `- Failed to match sequence (0-9{1, } SPACE?) at line 1 char 1. `- Expected at least 1 of 0-9 at line 1 char 1. `- Failed to match 0-9 at line 1 char 1. => nil
エラー見やすい
[SUM, INTEGER] に引っかからなかった
SUM 側
(INTEGER OPERATOR EXPRESSION) に引っかからなかった
([0-9]{1, } SPACE?) に引っかからなかった
at least 1 of [0-9] を期待しているが違う入力が来た
[0-9] に引っかからなかった
INTEGER 側
([0-9]{1, } SPACE?) に引っかからなかった
at least 1 of [0-9] を期待しているが違う入力が来た
[0-9] に引っかからなかった
ここまでで分かったこと
match, maybe, repeat 辺りを知る
ルールの合成の仕方
space? をうまく使う
エラーの読み方
Making the parser complete
パースした結果は Hash, Array, String でしかない、というのが書いてある
GET STARTED を終わったら
GitHub で検索しまくって実例を探す
この辺ヨサソウ?
sunaot さんの最高の記事があった
テスト
公式の RSpec Matcher がある