需要从规则中或带括号的子规则内的非终结符之一返回值是很常见的。 例如:
varDecl = type:type id:ID init:( EQ e:expr {return e} )?
{ return scopedAST('VARDECL', {type, id, init}) }
在这种情况下,我需要在expr
init
括号级别内标记为e
的init
用于语言中的可选短语。 我不需要“噪音词” EQ
作为返回值的一部分。
如果 PEGjs 语言有一个符号用于标记像上面的expr
这样的终端,这样它们,并且只有它们,是从语法规则或子规则返回的值,这种情况会更简单。
重写我上面的例子:
varDecl = type:type id:ID init:( EQ ^expr )?
{ return scopedAST('VARDECL', {type, id, init}) }
请注意使用^
来标记init
括号内的可选短语子规则中的expr
值,以指定绑定到init
。 这简化了本示例中显示的带括号子规则和不带括号子规则的许多情况。
感谢您制作出如此简单、优雅且功能强大的工具。 我爱 PEGjs! :微笑:
喜欢这个主意! ^
也非常直观。
这也适用于非嵌套规则:
WhiteSpacedIdentifier = WhiteSpace? identifier:Identifier WhiteSpace {return identifier;}
// becomes
WhiteSpacedIdentifier = WhiteSpace? ^Identifier WhiteSpace?
很有可读性! 据推测,使用多个^
也可以,例如:
a = ^b c ^d e
会返回[b, d]
吗? 似乎有道理。
同样,如果与命名捕获混合使用, ^
规则将被忽略,这似乎是有道理的,所以
x = a ^b foo:c { return foo; }
只会返回 c.
哦,倍数的想法很棒。 与命名捕获混合应该是
一个错误。
2016 年 7 月 5 日,星期二,01:07 Graham Wakefield通知@ github.com
写道:
很有可读性! 据推测,使用多个 ^ 也可以,例如:
a = ^bc ^de
会返回 [b, d] 吗? 似乎有道理。
同样,如果与命名捕获混合,^
规则被忽略:x = a ^b foo:c { 返回 foo; }
—
您收到此消息是因为您创作了该线程。直接回复本邮件,在GitHub上查看
https://github.com/pegjs/pegjs/issues/427#issuecomment -230413015,或者静音
线程
https://github.com/notifications/unsubscribe/ABC26k8v0DIzuWUlkoDZGm2ep10Y5bcMks5qShDAgaJpZM4IkuA9
.
我完全同意所描述的模式很常见。 有一种无需行动即可表达的方式是有道理的。
我对提议的解决方案不太确定( ^
运算符)。 使用意义不是很明显的特殊字符总是有问题并且会增加学习曲线。 也有可能该角色更适合用于其他目的。 最后但并非最不重要的一点是,我不喜欢将不会直接影响解析的东西放入表达式中的想法。 有人可能会争辩说已经有这样的一个实例—— $
运算符——我同意。 但我不确定添加$
是否不是(小)错误。 如果是这样,我想避免再次发生。
我会在 1.0.0 之后更深入地思考这个问题。
一些更多的思考:由于^
和标记的表达式有点冲突( @grrrwaaa建议忽略^
),例如,可以标记 _ignored_ 表达式而不是标记结果如何(语法建议!)通过提供一个空标签:
WhiteSpacedIdentifier = WhiteSpace? identifier:Identifier WhiteSpace {return identifier;}
// becomes
WhiteSpacedIdentifier = :WhiteSpace? Identifier :WhiteSpace?
在那里,没有新的语法(我们已经有了:
),只有一些语义上的扩展:
在这种情况下,更一致的将用空“标签”标记那些需要作为结果返回的表达式。 顺便说一句,它不打破现有的语义:标签存在,但它是未命名的; 由于引入了标签以访问结果,因此未命名的标签自动成为结果是非常合乎逻辑的。 禁止自动标签和具体标签同时存在。 如果只存在一个自动标签,则必须返回单个结果,而不是一个包含一个元素的数组,因为这种行为更需要。
@Mingun
为什么不直接返回任何标签?
start = "{" :expr "}" // return expr
start = "{" label:expr "}" // return label
我认为这是有道理的,如果你“标记”某物,那么你想用它做一些事情(例如返回它)。
另一方面,为什么像start = ex:expr :expr
这样的规则会引发错误?
也许它应该做一些类似于 javascript 函数的参数变量的事情? 例如start = ex:expr :expr
应该返回[ex, expr]
。 当你有一个动作时,应该有标签 & arguments
变量( start = ex:expr :expr { return [ex, arguments[0], ex] }
)
@alanmimms我喜欢这个主意。 我们不必为了返回简单值而创建名称(变量/标签)。
我认为未命名的标签( :expr
)会比^expr
为什么不直接返回任何标签?
@nedzadarek因为如果你给表达式一个名字,你更有可能不会在一些重要的表达式中使用它。 至少,名字对你来说很重要,否则你不会给它,真的吗? 此外,混合命名和未命名的标签比有意识的行为更可能是错误的,因此如果禁止它会更安全。 如果你给出一个名字,为什么不提供另一个?
不幸的是,有必要在@opatut提供的那种外观中识别自动标签,这是不可能实现的,因为它会造成语法上的歧义。 基本示例:
start = a :b;// `a` - it is rule reference or label?
a = .;
b = .;
因此,为此需要选择另一个字符。 目前有一个选择: ~
, (backslash)
, @
, #
, %
, ^
、 -
、 |
、 \
和,
。
另一个解决方案——引入一些伪动作——创建简单返回函数的快捷方式,例如, {=>[]}
可以表示_“从序列中收集标记的结果并将它们返回到数组中”_ , 和{=>{}}
-- 相同,但返回一个对象,键等于标签名称。 但是这种行为的实现不需要语法的扩展,完全可以通过插件来实现。 我什至会说通过插件来实现这样的实现更可取:
start1 = a:'a' b c d:. {=>[]};// returns ['a', <d value>]
start2 = a:'a' b c d:. {=>{}};// returns { a: 'a', d: <d value> }
@Mingun
因为如果你给表达式一个名字,你更有可能不会在一些重要的表达式中使用它。 至少,名字对你来说很重要,否则你不会给它,真的吗?
是的,名字很重要 => 我想使用它 => 我想归还它。
非平凡表达式有什么问题?
不幸的是,有必要在@opatut提供的那种外观中识别自动标签,这是不可能实现的,因为它会造成语法上的歧义。 基本示例:
是的。
我猜::expression
也很混乱? @dmajda
作为#235 的副本关闭
编辑:在 OP 对 #235 的评论中添加了注释,引用了这个问题