Parsing 以任意顺序接受三个可选元素的PEG语法
假设我们有三个元素Parsing 以任意顺序接受三个可选元素的PEG语法,parsing,grammar,antlr4,peg,pegjs,Parsing,Grammar,Antlr4,Peg,Pegjs,假设我们有三个元素ab和c 有效的表达式使用这三个元素(以及可选的空格) 这三个要素中至少有一个必须存在 所有三个元素都是可选的(只要其他两个元素中至少有一个存在,请参见1) 这三个元素的供应顺序并不重要 有没有一种惯用的方法来编写符合这三个要求的PEG语法 我在玩peg.js时解决了(1)(前瞻)和(2),但(3)没有解决。有什么建议吗 e = &(a / b / c) (a? b? c?) a = 'a' _ b = 'b' _ c = 'c' _ _ = [ \t]* 实际
a
b
和c
有效的表达式使用这三个元素(以及可选的空格)
e = &(a / b / c) (a? b? c?)
a = 'a' _
b = 'b' _
c = 'c' _
_ = [ \t]*
实际上,唯一的可能是列出所有六种可能的顺序,因为PEG没有“无序排列”操作符。(传统的上下文无关语法也不例外,因此需要大致相同的过程 例如,您可以使用:
a (b c? / c b?)? / b (a c? / c a?)? / c (a b? / b a?)?
但对于大量的备选方案来说,这显然是一件乏味的事情
通常更容易解决“以任何顺序但不重复的
x
,y
,…列表”通过接受任意的x
,y
,…,然后检查语义动作中的重复。这不仅使语法更容易编写,还允许出现更有意义的错误消息。由于peg.js的强大功能,提供返回true的检查函数并不难(并使用输入)如果元素列表s
是某组元素的组合s
(不允许重复)。基本思想是计算S
的幂集,并将S
的每个元素映射到一个素数。S
的每个元素映射到其相应元素的素数的乘积,即S
幂集的每个元素映射到一个唯一的数字。S
集是<>代码> S/<代码>当且仅当在<代码> S/<代码>中的对应素数的乘积是从<代码> s>代码>中计算的素数的乘积中。(我猜,有不止一种方式来执行这个检查:-))下面是一个PEG .js的解决方案,它具有5个我认为相当有效的元素。(使用<代码> {谓词}时有点困难。:内部的javascript使用arguments对象中的所有命名表达式进行调用,因此(a/b/c/d/e)+
必须有一个名称,例如el:(a/b/c/d/e)+
)
{
//元素数组(表达式)
var数据=['a'、'b'、'c'、'd'、'e'];
//将元素映射到素数
变量素数映射={
答:2,,
b:3,
c:5,
d:7,
e:11
};
//阵列的功率集
功能动力装置(arr){
var ps=[[]];
对于(变量i=0;i
生产和(a/b/c)(a?b?c?)满足第3点的要求未满足:由于PEG的贪婪性,提供的语法检测abc
,abc
,acc
,b
,bc
和c
,但不检测cb
等安排。它与贪婪性无关。它与cb
不匹配,因为(abc?)
要求b
在c
之前出现。无论PEG是否贪婪,这都是事实;也就是说,非PEG回溯匹配器也是如此。谢谢你直截了当地说出来!谢谢,rici。我已经感觉到这在语法中是做不到的。我在PEG.js中找到了一些解决方法(请参阅我自己问题的答案。)多亏了这段代码,我知道如何使用&{}
,但为什么不使用对象或映射并计算出现次数,比如:?我认为它比至少2个循环和一些像您使用的.ps那样的函数调用要快。我不确定代码中的ifs
{
// array of elements (expressions)
var data = ['a','b','c', 'd', 'e'];
// map elements to primes
var primemap = {
a: 2,
b: 3,
c: 5,
d: 7,
e: 11
};
// powerset of an array
function powerset(arr) {
var ps = [ [] ];
for (var i=0; i < arr.length; i++) {
for (var j = 0, len = ps.length; j < len; j++) {
ps.push(ps[j].concat(arr[i]));
}
}
return ps;
}
// compute the product of primes corresponding to each element of an array arr
function primeprod(arr) {
return arr.reduce( function(p,c) { return p * primemap[c] }, 1 );
}
// compute powerset and remove empty set at index 0 of the powerset
var ps = powerset(data);
ps.splice(0,1);
// map elements of powerset to products of primes
var prods = ps.map( function(el) { return primeprod(el); });
// returns true if an arr is a combination of the elements
function isCombination(arr) {
return prods.indexOf(primeprod(arr)) !== -1
}
}
expr = exp / blankline;
exp = (el:(a / b / c / d / e)+ &{ return isCombination(Array.prototype.slice.call(arguments)[0]); } {return el; } ) rest*
a = _ a:'a' {return a; }
b = _ b:'b' {return b; }
c = _ c:'c' {return c; }
d = _ d:'d' {return d; }
e = _ e:'e' {return e; }
rest = [^abcde]
blankline =
[ \t]* ("\n" / eof) { return []; }
_ = [ \t]*
eof = !.