PEG语法,以任何顺序接受三个可选元素

时间:2016-05-09 13:36:43

标签: parsing grammar antlr4 peg pegjs

假设我们有三个元素a bc

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

  1. 这三个元素中至少有一个必须存在。
  2. 所有三个元素都是可选的(只要存在其他两个元素中的至少一个,请参阅1)。
  3. 提供这三个元素的顺序并不重要。
  4. 是否有一种惯用的方式来编写满足这三个要求的PEG语法?

    我在http://pegjs.org/online玩了peg.js并解决了(1)(lookahead)和(2),但是(3)没有找到我。有什么建议吗?

    e = &(a / b / c) (a? b? c?) 
    
    a = 'a' _
    b = 'b' _
    c = 'c' _
    
    _ = [ \t]*
    

2 个答案:

答案 0 :(得分:1)

由于peg.js非常棒,如果元素列表s是某些元素集S的组合,那么提供一个返回true(并使用输入)的检查函数并不太难。{ 1}}(不允许重复)。基本思想是计算S的powerset并将s的每个元素映射到素数。 S的每个元素被映射到其对应元素的素数的乘积,即S的幂集的每个元素被映射到唯一的数字。集合sS中元素的组合,当且仅当s中相应素数的乘积属于从S计算的素数的乘积中时。 (我猜,执行此检查的方法不止一种:-))。以下是peg.js的解决方案,包含5个元素,我认为这些元素非常有效。 (使用& { predicate }时有点烦恼:使用arguments对象中的所有命名表达式调用javascript里面,因此(a / b /c /d /e)+必须有el:(a / b /c /d /e)+}之类的名称。

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

答案 1 :(得分:0)

真正唯一可能的是列出所有六个可能的订单,因为PEG没有&#34;无序排列&#34;运营商。 (传统的上下文无法使用语法,因此大致相同的程序是必要的。

例如,您可以使用:

npm install jasmine-expect --save-dev 

但构建大量替代品显然很乏味。

通常更容易解决a (b c? / c b?)? / b (a c? / c a?)? / c (a b? / b a?)? x,...的列表,无论如何重复,而且不会重复#34;通过接受yx,...的任意列表,然后检查语义动作中的重复,等等。这不仅使语法更容易编写,而且还允许更有意义的错误消息。