【问题标题】:PEG grammar which accepts three optional elements in any order以任意顺序接受三个可选元素的 PEG 语法
【发布时间】:2016-05-09 13:36:43
【问题描述】:

假设我们有三个元素 a bc

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

  1. 必须存在这三个元素中的至少一个。
  2. 所有三个元素都是可选的(只要存在其他两个元素中的至少一个,请参见 1)。
  3. 这三个元素的提供顺序并不重要。

有没有一种惯用的方式写出满足这三个要求的 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]*

【问题讨论】:

  • 生产&(a / b / c) (a? b? c?)满足第3点,不是吗?
  • 不,不满足规则 (3):由于 PEG 的贪婪,提供的语法检测到 a b ca ba cbb cc 但不是c bc b a 等安排。
  • 这与贪婪无关。它与c b 不匹配,因为(a? b? c?) 要求b 位于c 之前。无论 PEG 是否贪婪,这都是正确的;也就是说,非 PEG 回溯匹配器也是如此。
  • 感谢您直言不讳!

标签: parsing grammar antlr4 peg pegjs


【解决方案1】:

感谢 peg.js 的强大功能,如果元素列表 s 是一组元素 S 的组合,则提供一个返回 true(并使用输入)的检查函数并不难(不允许重复)。基本思想是计算S 的幂集并将s 的每个元素映射到一个素数。 S 的每个元素都映射到其对应元素的素数乘积,即S 的幂集的每个元素都映射到一个唯一的数字。集合sS 中元素的组合当且仅当s 中相应素数的乘积是从S 计算的素数乘积中。 (我想,执行此检查的方法不止一种 :-))。下面是 peg.js 的一个解决方案,它有 5 个我认为非常有效的元素。 (使用 & { predicate } 时的一个小问题:调用内部的 javascript 时会使用 arguments 对象中的所有命名表达式,因此 (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 = !.

【讨论】:

  • 感谢该代码,我知道如何使用&amp;{},但您为什么不使用对象或地图并计算出现次数,例如:gist.github.com/nedzadarek/b0bf9aaaefd084be4f411a6767eee7fa?我认为它会比您使用的至少 2 个循环和一些函数调用更快。附言。我不确定我的代码中是否有 ifs
【解决方案2】:

真正唯一的可能是列出所有六个可能的顺序,因为 PEG 没有“无序排列”运算符。 (传统的上下文无关语法也不行,因此需要大致相同的过程。

例如,您可以使用:

a (b c? / c b?)? / b (a c? / c a?)? / c (a b? / b a?)?

但是对于大量的替代方案,这显然是乏味的。

通过接受x, y, ... ,然后检查语义动作中的重复。这不仅使语法更容易编写,还允许出现更有意义的错误消息。

【讨论】:

  • 谢谢,里奇。我已经有一种感觉,这在语法中是做不到的。我在 peg.js 中找到了一些解决方法(请参阅我自己问题的答案。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-09
  • 1970-01-01
相关资源
最近更新 更多