誰かがすでに Parslet について言及していましたが、私はそれがいかに簡単かをデモしようと思いました。
require 'parslet'
require 'pp'
slides = <<EOS
title: Ruby Programming
subtitle: A simple introduction
* First bullet
* Second bullet
EOS
#Best to read the parser from the bottom up.
class SlidesParser < Parslet::Parser
rule(:eol) { str("\n") | any.absent? }
rule(:ws?) { match('[\s\t]').repeat(0) }
rule(:rest_of_line) { ws? >> (str("\n").absent? >> any).repeat(1).as(:text) }
rule(:title) { ws? >> str("title:")>> rest_of_line.as(:title) >> eol }
rule(:subtitle) { ws? >> str("subtitle:")>> rest_of_line.as(:subtitle) >> eol }
rule(:bullet) { ws? >> str("*") >> rest_of_line >> eol }
rule(:bullet_list) { bullet.repeat(1).as(:bullets) }
rule(:slide) { (title >> subtitle >> bullet_list).as(:slide) }
root(:slide)
end
# Note: parts can be made optional by adding a ".maybe" eg. => subtitle.maybe
result = SlidesParser.new.parse(slides)
pp result
#{:slide=>
# {:title=>{:text=>"Ruby Programming"@9},
# :subtitle=>{:text=>"A simple introduction"@38},
# :bullets=>[{:text=>"First bullet"@64}, {:text=>"Second bullet"@81}]}}
Parslet では、パーサーはテキストをルビ構造に変換するだけなので、仕事の一部にすぎません。
次に、変換を使用してツリー ノードを一致/置換し、必要な構造を作成します。
# You can do lots of things here.. I am just replacing the 'text' element with their value
# You can use transforms to build your custom AST from the raw ruby tree
class SlidesTransform < Parslet::Transform
rule(:text => simple(:str)) { str }
# rule(
# :title => simple(:title),
# :subtitle => simple(:subtitle),
# :bullets => sequence(:bullets)) { Slide.new(title, subtitle, bullets) }
end
pp SlidesTransform.new.apply(result)
#{:slide=>
# {:title=>"Ruby Programming"@9,
# :subtitle=>"A simple introduction"@38,
# :bullets=>["First bullet"@64, "Second bullet"@81]}}