CFG/PEG用于代码补全?

13

我想知道是否有可能直接使用CFG或PEG语法作为代码补全的基础,而不需要进行修改。据我所知,IDE中的代码补全有时会被操纵、调整甚至硬编码,以使其性能更好。

我想在一个小DSL上完成代码,因此我充分理解语法无法帮助代码补全系统了解库函数等知识。

就我所知,解析器本身至少需要提供一种查询它下一个期望值的系统。

特别是我对使用peg.jsjison的JavaScript代码补全解决方案感兴趣。


1
确实可以使用带有Packrat的PEG进行代码补全 - 我曾在Emacs和Visual Studio中使用过。诀窍是在解析器的最右失败位置存储已尝试的“标记”列表 - 然后可以将它们用于补全。如果期望标识符,则解析器还可以给出提示。 - SK-logic
2个回答

14
使用PEG语法很容易构建带有代码完成的Javascript编辑器。我将描述如何使用PEG.js实现。您需要通过一些宽松的解析规则扩展语法,以便在上一个语句出错时提供建议。这些宽松规则需要有条件地处理,否则您需要两个单独的语法——一个用于解析源代码,另一个用于代码完成。您可以使用Javascript谓词(在PEG.js中可用)来维护一个语法。它看起来像&{return laxParsing},并且当laxParsing标志为true时,会导致整个规则被处理。您可以通过设置解析器的内部标志轻松地在宽松和严格解析之间切换。
为了方便向用户提供建议,您必须略微修改生成的PEG.js解析器(版本0.5),以在解析错误结构中接收位置(除列和行之外)和期望列表(除错误消息之外)。您可以从https://gist.github.com/1281239复制准备好的片段。
当您有解析器后,就可以在编辑器中附加它,例如在CTRL+SPACE键按下时。当在文本源中按下这些键时,您需要在光标的位置放置一个特殊的不可解析符号(以导致解析错误)并在宽松模式下启动解析器。然后,您将收到带有建议列表的错误。
一些建议不仅是语法,还定义了引用(例如实体、变量)。当找到特定期望(例如VariableName)时,您可以触发搜索这些建议。您可以通过在不同的宽松解析模式下解析相同的源代码(仅过滤变量名)来提供列表。
有关此方法的工作示例和源代码,请查看https://github.com/mstefaniuk/Concrete-Freetext

5

PEG.js在生成SyntaxError时会提供相当多的上下文信息。例如,如果您有一个针对SQL的语法,并将以下内容输入:

FOO > 10 A

那么PEG.js将返回这个结果:
{
  "message": "Expected \"AND\", \"ORDER BY\" or end of input but \"A\" found.",
  "expected": [
    {
      "type": "literal",
      "value": "AND",
      "description": "\"AND\""
    },
    {
      "type": "literal",
      "value": "ORDER BY",
      "description": "\"ORDER BY\""
    },
    {
      "type": "end",
      "description": "end of input"
    }
  ],
  "found": "A",
  "offset": 9,
  "line": 1,
  "column": 10,
  "name": "SyntaxError"
}

它的意思是解析了字符串("FOO > 10 ")的0-9个字符,但在第10个字符处遇到了一个意外的标记。它会给你一个期望的下一个标记列表:FOO > 10 ANDFOO > 10 ORDER BYFOO > 10。如果你将这些附加到查询的有效部分,你将得到一组可能的完整性:
function getCompletions(pegParse, text) {
  var parsedText = pegParse(text);
  var completions = [];
  if (parsedText.expected) {
    var start = text.substr(0, parsedText.offset);
    parsedText.expected.forEach(function(expected) {
      if (expected.type != 'literal') return;
      var completion = start + expected.value;
      if (completion.substr(0, text.length) == text) {
        completions.push(completion);
      }
    });
  }
  return completions;
}

这很简单 -- 真正的自动完成需要匹配更多内容,而且需要一种方法来利用语法无法获取的上下文信息,例如用户正在调用的函数参数列表。


嘿,你是怎么得到结构化的错误信息的?我没有看到除了 .parse 之外的任何公共 API。 - Ray Wu
我发现可以通过 try { p.parse(...); } catch (e) { console.log(e); } 获取它。 - Ray Wu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接