Pegjs: 添加跟踪节点位置的功能

创建于 2011-08-13  ·  15评论  ·  资料来源: pegjs/pegjs

PEG.js 生成的解析器当前不跟踪位置(行和列)。 我想添加此功能,因为它非常有用。

一种方法是将linecolumn属性添加到作为匹配结果返回的每个对象:

start = "a" b:"b" { return [b.line, b.column]; } // Returns [1, 2] on the input "ab".

另一种方法是让特殊的linecolumn变量在动作/谓词中可用,指的是当前规则开始的位置:

start = "a" "b" { return [line, column]; } // Returns [1, 1] on the input "ab".

第一种方式更灵活,但实际上可能不需要这种灵活性。 我不确定我将采用哪种方式。

两种方式都会损害性能。 为了防止在不需要位置跟踪的情况下发生这种情况,只有在生成解析器时将具有真值的trackPosition选项传递给PEG.buildParser时才应启用跟踪。

feature

最有用的评论

@tomitrescak这个功能在过去显然已经改变了几次。 查看更改日志以获取更多信息。 tl;dr 是您需要在语法中使用location()函数

所有15条评论

任何一种选择都会很棒。 对于我的需求,只需行,列就可以了。

我也在寻找解决此问题的方法。 目前,我已经基于 computeErrorPosition() 快速破解了一些东西,但它有几个问题。

从表面上看,第一种方法似乎比第二种方法更直观。

我也更喜欢第一种方法。

一种可能的效率是让 name.position 成为一个函数,因此可以延迟计算。 这可以返回一个由行和列组成的 2 项数组。 对于那些不想计算每个项目的位置的人来说,这只是一个想法。

事后看来,提供开始和结束字符位置似乎更好,然后在库中提供将位置转换为行/列的辅助函数。 因为在一般情况下,除非出现错误,否则您不需要行/列,并且一旦遇到错误,您通常只需要一次。

我目前正在通过滥用startPos0pos变量以一种骇人听闻的方式执行此操作,这感觉有些骇人听闻,但现在可以使用。

在正式修复之前,我会在我的语法顶部包含这个函数( computeErrorPosition的粗略副本)。 至少它会给我 _current_ 位置(虽然不是各个节点的位置):

  function computeCurrentPos() {
    /*
     * The first idea was to use |String.split| to break the input up to the
     * error position along newlines and derive the line and column from
     * there. However IE's |split| implementation is so broken that it was
     * enough to prevent it.
     */

    var line = 1;
    var column = 1;
    var seenCR = false;

    for (var i = 0; i < pos; i++) {
      var ch = input.charAt(i);
      if (ch === '\n') {
        if (!seenCR) { line++; }
        column = 1;
        seenCR = false;
      } else if (ch === '\r' | ch === '\u2028' || ch === '\u2029') {
        line++;
        column = 1;
        seenCR = true;
      } else {
        column++;
        seenCR = false;
      }
    }

    return { line: line, column: column, pos: pos };
  }

此问题已通过一系列提交修复。

您现在可以将trackLineAndColumn选项传递给PEG.buildParser函数:

var parser = PEG.buildParser(myGrammar, { trackLineAndColumn: true});

trackLineAndColumn设置为true会使两个新变量在动作和谓词中可见—— linecolumn 。 对于动作,这些变量表示动作表达式的开始位置,而在谓词中它们表示当前位置。 略有不同的行为是由预期的使用引起的。

行和列跟踪是可选的,因为它会损害性能(它会使解析器慢 3-4 倍)。 将来我可能会对此进行一些优化(我试图使其首先工作)。

在问题描述中,我提到了解决此问题的不同方法:在每个匹配结果中添加linecolumn属性。 虽然这个解决方案更简洁,并且受到用户的青睐,但我意识到不能为原始值设置属性,例如字符串、数字或布尔值(通常作为匹配结果返回)。 返回包装对象的实例(例如StringNumberBoolean )可能是一种解决方法,但它可能会导致用户创建的解析器出现细微的错误,因为这些包装器的行为与原语略有不同。 因此,我决定实施替代方案。

在线测试页面会更新以支持此功能吗?

@paulftw在线编辑器始终使用 PEG.js 的最新稳定版本(当前为 0.6.2)。 一旦我发布 PEG.js 0.7.0(将包含此功能),我将对其进行更新。 除非出现意外情况,否则这将在 4 月下半月发生。

注意网站源码在 GitHub 上,如果不耐烦可以自行运行修改。

刚刚意识到所有行和列都是从 1 开始的,而 JS 数组以及几乎所有现代语言和库都使用从 0 开始的索引。

有机会恢复那个设计决定吗?

@paulftw您能否举一个解析器生成器的示例,该解析器生成器将行和列报告为基于0的?

我的决定背后的想法是这些数字很可能会显示给用户(在错误消息中,作为节点位置等),因此基于 1 的索引比基于 0 的索引更有意义。 对于机器处理, offset变量可能最常用,它是从 0 开始的。

有机会恢复那个设计决定吗?

是的,如果我确信:-)

我使用 peg.js 在 Ace 编辑器中突出显示文本。 很自然,Ace 行号是从 0 开始的。
然而,在 Bison 中,位置似乎是从 1 开始的。
http://git.savannah.gnu.org/cgit/bison.git/tree/src/location.c#n73

如果真是这样,那我就不要争论了。

不知道你想看什么样的例子。
现在我使用以下代码来查找符号名称:

捕捉者
= "" { return new Position(line - 1, column - 1); }

象征
= start:captor name:IDENT end:captor S* { return new Symbol(name, new Range(start, end)); }

@paulftw我已将此问题拆分为单独的问题。 我会等待用户采用 0.7.0 并看看流行的用法是什么来决定。

我知道这早已关闭,但我怎样才能使这项工作? 在这方面我很菜鸟。 我用“trackLineAndColumn':true”标志编译了我的语法,但节点仍然不包含行和列。 还需要什么?

您提到以下内容,我只是不知道如何使用它是否记录在某处?

将 trackLineAndColumn 设置为 true 会使两个新变量在操作和谓词中可见 - 行和列。 对于动作,这些变量表示动作表达式的开始位置,而在谓词中它们表示当前位置。 略有不同的行为是由预期的使用引起的。

@tomitrescak这个功能在过去显然已经改变了几次。 查看更改日志以获取更多信息。 tl;dr 是您需要在语法中使用location()函数

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

dmajda picture dmajda  ·  20评论

futagoza picture futagoza  ·  13评论

futagoza picture futagoza  ·  6评论

StoneCypher picture StoneCypher  ·  6评论

ronjouch picture ronjouch  ·  3评论