3

我正在尝试为 Handlebars 编写一个 Ruby/Parslet 解析器,但我遇到了{{ else }}关键字。为了向那些不使用 Handlebars 的人解释一下,if/else 是这样写的:

{{#if my_condition}}
  show something
{{else}}
  show something else
{{/if}}

但它变得棘手,因为内联和帮助程序可以使用相同的语法,例如:

Name: {{ name }}
Address: {{ address }}

所以我首先制定了一个规则来识别替换:

rule(:identifier)  { match['a-zA-Z0-9_'].repeat(1) }
rule(:path)        { identifier >> (dot >> identifier).repeat }

rule(:replacement) { docurly >> space? >> path.as(:item) >> space? >> dccurly}

哪个匹配任何类似{{name}}or {{people.name}}。问题当然是也匹配{{ else }}块。这是我编写规则以匹配 if/else 块的方式:

rule(:else_kw) {str('else')}
rule(:if_block) {
  docurly >>
  str('#if') >>
  space >>
  path.as(:condition) >>
  space? >>
  dccurly >>
  block.as(:if_body) >>
  (
    docurly >>
    else_kw >>
    dccurly >>
    block.as(:else_body)
  ).maybe >>
  docurly >>
  str('/if') >>
  dccurly
}

(注意: docurly is {{, dccurly is}}和 block 或多或少可以是任何东西)

所以我现在需要重写 `identifier` 规则,使其匹配任何单词,但不匹配“else”。

提前致谢,文森特

4

2 回答 2

0

一种方法是使用absent?前瞻修饰符。foo.absent?如果原子或规则此时foo不匹配,则将匹配,并且不会消耗任何输入。

有了这个,你可以把identifier规则写成

rule(:identifier)
    { (else_kw >> dccurly).absent? >> match['a-zA-Z0-9_'].repeat(1) }
于 2015-03-05T19:39:23.200 回答
0

这取决于您尝试匹配的语法。如果你不在一个{{if}} {{/if}}pair里面应该{{else}}被当作一个有效的标识符还是一个语法错误?如果你有一条路径,a.else.b那应该是有效的吗?

如果a.else.b无效,您可以执行以下操作:

rule(:identifier)
    { (else_kw).absent? >> match['a-zA-Z0-9_'].repeat(1) | else_kw >> match['a-zA-Z0-9_'].repeat(1) }

它接受除“else”之外的所有字符串,方法是说“任何不以else开头的字符串,或者以else开头的至少还有一个字符的字符串”。

注意:这让我想“为什么else这么特别?” 我们应该在这里排除所有关键字吗?

如果a.else.b有效,则不能在标识符级别排除它。然后更准确地说你的pathcan't be "else"

如果你说:

rule(:path)        { else_kw.absent? >> (identifier >> (dot >> identifier).repeat) }

这将排除任何以“else”开头的标识符,例如“elsewise.option”

所以..absent?还需要匹配一些东西来显示你的块已经结束。

rule(:path)        { (else_kw >> dccurly).absent? >> (identifier >> (dot >> identifier).repeat) }

这里的问题是,我们现在正在将路径耦合到它以 a 结尾的想法,这dccurly不是严格正确的(并且不处理空格)。所以“路径”不是放置这些东西的正确位置。

如果我们试图阻止替换匹配else,那会更容易。

rule(:replacement) { docurly >> space? >> (else_kw >> space? >> dccurly).absent? >> path.as(:item) >> space? >> dccurly}

这将阻止替换匹配else,但将允许elsewise.something, 或else.something

如果你不想要“else.something”,那么你需要这样的东西:

rule(:replacement) { docurly >> space? >> (else_kw >> (space | dccurly | dot)).absent? >> path.as(:item) >> space? >> dccurly}

所以“其他”“其他”。和“else}}”都被阻止了。

于 2015-03-25T00:59:53.827 回答