CFG/PEG используется для завершения кода?
Мне интересно, можно ли использовать грамматику CFG или PEG в качестве основы для завершения кода напрямую без изменений. Я слышал, что завершение кода в IDE иногда обрабатывается и массируется или даже жестко закодировано, чтобы он работал лучше.
Я хочу скопировать код на небольшой DSL, поэтому я полностью понимаю, что грамматика не может помочь системе завершения кода со знанием библиотечных функций и т.д.
Насколько мне известно, сам синтаксический анализатор должен, по крайней мере, предоставить систему для запроса того, что ожидает следующего.
В частности, меня интересует решение для завершения JavaScript-кода, используя peg.js
или jison
Ответы
Ответ 1
Довольно легко создать редактор 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.
Ответ 2
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"
}
Говорят, что он разбирал символы 0-9 строки ( "FOO > 10" ), но затем обнаружил неожиданный токен на символе 10. И он дает вам список следующих токенов, которые он ожидал: FOO > 10 AND
, FOO > 10 ORDER BY
, FOO > 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;
}
Это довольно упрощенно: реальный автозаполнение будет соответствовать не только литералам, но и каким-то образом использовать контекст, недоступный для грамматики, например. список аргументов функции, вызываемой пользователем.