2011-12-13 9 views
1

私はWhittle gemを使用してテンプレート言語を解析し、ルールに含まれていないものと一致させたいと考えました。私はそこに他のテンプレートエンジンをよく知っていますが、これは生産上の事例よりも学問的なものです。whittleパーサー条件付きルールアクティブ化

私が遭遇した問題は、パーサーが:raw上記:idの優先順位を無視し、まだ{{:rawタグに待っていることです。

:rawルールを式の中に適用し、式の中にのみ:spcルールを適用することは許可されていません。

パーサコード

class Parser < Whittle::Parser 
    # Skip whitespaces (should not apply in :raw) 
    rule(:spc => /\s+/).skip! 

    # Various delimiters 
    rule("{{")^4 
    rule("}}")^4 
    rule("{%")^4 
    rule("%}")^4 
    rule("|")^4 
    rule("end")^4 

    # Defines an id (very large match) 
    rule(:id => /[a-zA-Z_.$<>=!:]+(\((\w+|\s+|,|")+\))?/)^2 

    # inline tag 
    rule(:inline) do |r| 
     r["{{", :inline_head, "}}"].as { |_,id,_| Tag::Inline.new(id) } 
    end 
    # inline tag contents 
    # allows "|" chaining 
    rule(:inline_head) do |r| 
     r[:inline_head, "|", :id].as { |head, _, id| head << id } 
     r[:id].as { |id| [id] } 
     r[].as { [] } 
    end 

    # block tag 
    rule(:block) do |r| 
     r["{%", :block_head, "%}", :all, "{%", "end", "%}"].as { |_,head,_,tags,_,_,_| 
      Tag::Block.new(head, tags) 
     } 
    end 
    # block tag heading 
    # separates all the keywords 
    rule(:block_head) do |r| 
     r[:block_head, :id].as { |head, id| head << id } 
     #r[:id].as { |id| [id] } 
     r[].as { [] } 
    end 

    # one rule to match them all 
    rule(:all) do |r| 
     r[:all,:inline].as { |all, inline| all << inline } 
     r[:all, :block].as { |all, block| all << block } 
     r[:all, :raw].as { |all, raw| all << raw } 
     r[].as { [] } 
    end 

    # the everything but tags rule 
    rule(:raw => /[^\{\}%]+/).as { |text| Tag::Raw.new(text) }^1 

    # starting rule 
    start(:all) 
end 

そして、入力されたテキストは以下のようになり、出力はオブジェクトによって表される抽象構文木(彼らは単に今のオブジェクトのようにハッシュされている)です。

<html> 
    <head> 
     <title>{{ title|capitalize }}</title> 
    </head> 
    <body> 
     <div class="news"> 
      {% for news in articles %} 
       {{ news.title }} 
       {{ news.body | limit(100) }} 
       {{ tags | join(",", name) }} 
      {% end %} 
     </div> 
    </body> 
</html> 

答えて

1

私はオペレータ優先順位のサポートがここで役割を果たすとは思わない。演算子の優先順位は、foo = 6 + 7のような式のあいまいさを解消する場合にのみ発生します。式は(foo = 6) + 7またはfoo = (6 + 7)と解釈されます。非オペレータに優先順位を与えることは、実際には何の目的にも役立たない。

おそらくパーサが実際に何をするのかは不明です。基本的には繰り返しループし、すべての入力規則と一致します。見つかったものについては、それは最も長いものを取り、それが適合する現在の状態でルールを見つけることを試みる。したがって、パーサーは常にあなたの空白を見つけてそれを破棄します。これはあなたの文法の最初の規則です。

あなたの文法では重要なので、実際には空白をスキップしたくないと思います。あなたはそれを許すあなたのルールにそれを含めることを望みます。あなたの文法はより冗長になりますが、(現在)やむを得ないことです。

ので:rawは、単一の文字列にすべての空白と非構文トー​​クンを飲み込む、次のようなものになり:

rule(:raw => /[^\s\{\}%]+/) 

rule(:text) do |r| 
    r[:text, :raw].as { |text, raw| text << raw } 
    r[:text, :spc].as { |text, spc| text << spc } 
    r[:spc] 
    r[:raw] 
end 

次に、あなたの:allルールで、あなたのASTの一部にそのテキストを回す(あなたは可能性があり上記のルールでも実際にこれを行いますが、私はあなたのクラス定義について何も知らない)。

rule(:all) do |r| 
    # ... snip ... 
    r[:all, :text].as { |all, text| all << Tag::Raw.new(text) } 
    # ... snip ... 
end 

私は異なる状態内の異なるトークンを照合する機能を提供する方法について考えてきたが、私は、私は希望があまりにも混乱すると思うLEX /フレックスのクローンを、書き込みの警戒ので、私ブロックが互いの中でどのように関係しているかを伝えるために、ルールを入れ子にするアプローチを思いついています。これを行うDSLを理解するのは簡単ではありませんが)また、繰り返し用のアルゴリズムを隠すオプションのDSLを提供したいと思っています。多分LRパーサに変換する一種のPEG層を提供するかもしれない。これはまだ(非常に)若いプロジェクトです;)

+0

著者自身からのWoah!私はこれをできるだけ早く試みている。ありがとうございました –

+0

作業し、いくつかの調整後にすべての私の問題を解決しました。素晴らしいプロジェクト! –