0

私は現在、インスタンス eval を使用するスライドを作成するための Ruby ベースの DSL を持っています。

# slides.rb
slide {
  title 'Ruby Programming'
  subtitle 'A simple introduction'
  bullet 'First bullet'
  bullet 'Second bullet'
}

# implementation:
class DSL
  class Slide
    def title(title)
      @title = title
    end
    # ...etc...
  end

  def slide(&block)
    @slides << Slide.new.instance_eval(&block)
  end
end

dsl = DSL.new
dsl.instance_eval(File.read('slides.rb'))

次のような結果になります。


Ruby プログラミング

簡単な紹介

  • 最初の弾丸
  • 2 番目のブレット

Ruby 構文を使用しない DSL を作成することで、これを次のレベルに引き上げたいと考えています。たぶん、YAML や Markdown のようなもの:

title: Ruby Programming
subtitle: A simple introduction
* First bullet
* Second bullet

このタイプの構文の DSL/パーサーを作成するにはどうすればよいですか?

4

4 に答える 4

3

誰かがすでに 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]}}
于 2013-09-16T23:22:27.313 に答える
1

おそらく、現在のオープンソースの実装をいくつか見てみる価値があります。

しかし、私は尋ねなければなりません - なぜあなたはあなた自身のものを作っているのですか? すでに利用可能なものを使用しないのはなぜですか?TOMLは素晴らしいです。

Ruby パーサーの実装: https://github.com/jm/toml

于 2013-08-19T21:37:59.127 に答える