Parslet:识别给定关键字以外的任何内容
Parslet: recognise anything but a given keyword
我正在尝试为 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}}
或 {{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 是 {{
,dccurly 是 }}
,block 可以是或多或少的任何东西)
所以我现在需要重写 `identifier`` 规则,使其匹配任何单词但不匹配 "else"。
提前致谢,
文森特
一种方法是使用 absent?
前瞻修饰符。如果原子或规则 foo
此时 不 匹配,并且不消耗任何输入,foo.absent?
将匹配。
有了这个,您可以将 identifier
规则写成
rule(:identifier)
{ (else_kw >> dccurly).absent? >> match['a-zA-Z0-9_'].repeat(1) }
这取决于您要匹配的语法。如果您不在 {{if}}
{{/if}}
对中,应该将 {{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) }
通过说 "any string not starting with else, OR strings starting with else that have at least one more character".
接受除 "else" 之外的所有字符串
注意:这让我觉得 "Why is else
so special?" 我们应该在此处排除所有关键字吗?
如果 a.else.b
有效,则不能在标识符级别排除它。然后更准确地说你的 path
不能是 "else"
.
如果你说:
rule(:path) { else_kw.absent? >> (identifier >> (dot >> identifier).repeat) }
这将排除任何以 'else' 开头的标识符,例如"elsewise.option"
所以.. absent?
也需要匹配一些东西来显示你的块已经结束。
rule(:path) { (else_kw >> dccurly).absent? >> (identifier >> (dot >> identifier).repeat) }
这里的问题是我们现在将路径与它以 dccurly
结尾的想法结合起来,这并不严格正确(并且不处理空格)。所以 "path" 不是放置这些东西的正确位置。
如果我们试图阻止替换匹配 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 " "else." 和 "else}}" 都被阻止了。
我正在尝试为 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}}
或 {{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 是 {{
,dccurly 是 }}
,block 可以是或多或少的任何东西)
所以我现在需要重写 `identifier`` 规则,使其匹配任何单词但不匹配 "else"。
提前致谢, 文森特
一种方法是使用 absent?
前瞻修饰符。如果原子或规则 foo
此时 不 匹配,并且不消耗任何输入,foo.absent?
将匹配。
有了这个,您可以将 identifier
规则写成
rule(:identifier)
{ (else_kw >> dccurly).absent? >> match['a-zA-Z0-9_'].repeat(1) }
这取决于您要匹配的语法。如果您不在 {{if}}
{{/if}}
对中,应该将 {{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) }
通过说 "any string not starting with else, OR strings starting with else that have at least one more character".
接受除 "else" 之外的所有字符串注意:这让我觉得 "Why is else
so special?" 我们应该在此处排除所有关键字吗?
如果 a.else.b
有效,则不能在标识符级别排除它。然后更准确地说你的 path
不能是 "else"
.
如果你说:
rule(:path) { else_kw.absent? >> (identifier >> (dot >> identifier).repeat) }
这将排除任何以 'else' 开头的标识符,例如"elsewise.option"
所以.. absent?
也需要匹配一些东西来显示你的块已经结束。
rule(:path) { (else_kw >> dccurly).absent? >> (identifier >> (dot >> identifier).repeat) }
这里的问题是我们现在将路径与它以 dccurly
结尾的想法结合起来,这并不严格正确(并且不处理空格)。所以 "path" 不是放置这些东西的正确位置。
如果我们试图阻止替换匹配 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 " "else." 和 "else}}" 都被阻止了。