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

有效的表达式使用这三个元素(以及可选的空格)

  • 这三个要素中至少有一个必须存在
  • 所有三个元素都是可选的(只要其他两个元素中至少有一个存在,请参见1)
  • 这三个元素的供应顺序并不重要
  • 有没有一种惯用的方法来编写符合这三个要求的PEG语法

    我在玩peg.js时解决了(1)(前瞻)和(2),但(3)没有解决。有什么建议吗

    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 = !.