2011-12-13 73 views
1

我使用Whittle gem来解析模板语言,并希望匹配规则中未包含的任何内容。我非常了解其他模板引擎,但这更像是一次学术活动而不是生产案例。Whittle解析器条件规则激活

我遇到的问题是,分析器忽略的:id以上:raw的优先级,并仍在等待对{{: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) + 7foo = (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 

我一直在思考如何提供搭配不同状态中的不同令牌的能力,但我写警惕法/柔性的克隆,我觉得这是太混乱了,所以我试图想出一种方法,它使用块互相嵌套规则来传达状态如何相互关联;尽管创建一个容易理解的DSL并不容易;)我也想提供一个可选的DSL来隐藏用于重复的算法;也许提供一种转换成LR解析器的PEG层。这仍然是一个(非常)年轻的项目;)

+0

哇作者自己!我正在尽快尝试。谢谢 –

+0

经过一些调整,解决了我所有的问题。伟大的项目! –