Как определить следующие шаблоны кода
У меня есть шаблон JS обещаний, которые я хочу определить для нескольких ключевых слов
Например, если я поставлю код как:
var deferred = Q.defer();
И в файле у меня также есть следующее соответствующее значение
deferred.reject(err);
deferred.resolve();
return deferred.promise;
Полный код
Пример 1
function writeError(errMessage) {
var deferred = Q.defer();
fs.writeFile("errors.log", errMessage, function (err) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve();
}
});
return deferred.promise;
}
И я хочу, чтобы, если я поместил большой файл кода (в виде строки), чтобы найти, что этот файл содержит шаблон
Другой пример
var d = Q.defer(); /* или $q.defer */
И в файле у вас также есть следующее соответствующее значение
d.resolve(val);
d.reject(err);
return d.promise;
Полный ПРИМЕР 2
function getStuffDone(param) {
var d = Q.defer(); /* or $q.defer */
Promise(function(resolve, reject) {
// or = new $.Deferred() etc.
myPromiseFn(param+1)
.then(function(val) { /* or .done */
d.resolve(val);
}).catch(function(err) { /* .fail */
d.reject(err);
});
return d.promise; /* or promise() */
}
Есть открытые источники, которые можно использовать для такого анализа (предоставьте шаблон, и он будет найден...)
Есть несколько более сложных шаблонов с childProcess, но пока это нормально:)
2 ответа
ОБНОВЛЕНИЕ: я сделал одну поправку к коду, т.е. изменил set[2]
в set[set.length - 1]
для размещения наборов запросов любого размера. Затем я применил точно такой же алгоритм к вашим двум примерам.
Предлагаемое мною решение следует некоторым правилам, которые, на мой взгляд, являются разумными для типа поиска, который вы предлагаете Предположим, вы ищете четыре строки, ABCD (без учета регистра, поэтому он найдет ABCD или abcd или aBcD):
- Несколько наборов совпадений можно найти в одном файле, то есть он найдет два набора в ABCDabcd.
- Регулярные выражения используются для отдельных строк, что означает, что могут быть включены варианты. (Как одно из следствий этого, не имеет значения, если у вас есть комментарий в конце соответствующей строки в вашем коде.)
- Запрашиваемые шаблоны всегда должны быть на разных линиях, например, A и B не могут быть на одной линии.
- Соответствующий набор должен быть полным, например, он не найдет ABC или ABD.
- Соответствующий набор должен быть непрерывным, то есть он ничего не найдет в ABCaD. (Важно, что это также означает, что is не найдет ничего в перекрывающихся наборах, например, ABCaDbcd. Вы можете утверждать, что это слишком ограничивает. Однако в этом примере, который должен быть найден, ABCD или abcd? Ответ произвольный, а произвол Сложно кодировать. Более того, основываясь на приведенных вами примерах, такое перекрытие обычно не ожидается, поэтому этот крайний случай кажется маловероятным, что делает данное ограничение разумным.)
- Соответствующий набор должен быть внутренне неповторяющимся, например, он не найдет ABbCD. Однако с AaBCD он найдет множество, то есть найдет aBCD.
- Разрешены встроенные наборы, но будет найден только внутренний, например, с ABabcdCD будет найден только abcd.
Фрагмент кода ниже показывает пример поиска. Это не демонстрирует все крайние случаи. Тем не менее, он показывает общую функциональность.
var queryRegexStrs = [
"I( really)? (like|adore) strawberry",
"I( really)? (like|adore) chocolate",
"I( really)? (like|adore) vanilla"
];
var codeStr =
"....\n" +
"Most people would say 'I like vanilla'\n" +
"....\n" +
"....\n" +
"....\n" +
"....\n" +
"Amir's taste profile:\n" +
"....\n" +
"I like strawberry\n" +
"....\n" +
"....\n" +
"I told Billy that I really adore chocolate a lot\n" +
"....\n" +
"I like vanilla most of the time\n" +
"....\n" +
"Let me emphasize that I like strawberry\n" +
"....\n" +
"....\n" +
"....\n" +
"....\n" +
"Juanita's taste profile:\n" +
"....\n" +
"I really adore strawberry\n" +
"I like vanilla\n" +
"....\n" +
"....\n" +
"....\n" +
"....\n" +
"Rachel's taste profile:\n" +
"I adore strawberry\n" +
"....\n" +
"Sometimes I like chocolate, I guess\n" +
"....\n" +
"I adore vanilla\n" +
"....\n" +
"....\n" +
"....\n" +
"....\n" +
"";
// allow for different types of end-of-line characters or character sequences
var endOfLineStr = "\n";
var matchSets = search(queryRegexStrs, codeStr, endOfLineStr);
function search(queryRegexStrs, codeStr, endOfLineStr) {
// break the large code string into an array of line strings
var codeLines = codeStr.split(endOfLineStr);
// remember the number of lines being sought
var numQueryLines = queryRegexStrs.length;
// convert the input regex strings into actual regex's in a parallel array
var queryRegexs = queryRegexStrs.map(function(queryRegexStr) {
return new RegExp(queryRegexStr);
});
// search the array for each query line
// to find complete, uninterrupted, non-repeating sets of matches
// make an array to hold potentially multiple match sets from the same file
var matchSets = [];
// prepare to try finding the next match set
var currMatchSet;
// keep track of which query line number is currently being sought
var idxOfCurrQuery = 0;
// whenever looking for a match set is (re-)initialized,
// start looking again for the first query,
// and forget any previous individual query matches that have been found
var resetCurrQuery = function() {
idxOfCurrQuery = 0;
currMatchSet = [];
};
// check each line of code...
codeLines.forEach(function(codeLine, codeLineNum, codeLines) {
// ...against each query line
queryRegexs.forEach(function(regex, regexNum, regexs) {
// check if this line of code is a match with this query line
var matchFound = regex.test(codeLine);
// if so, remember which query line it matched
if (matchFound) {
// if this code line matches the first query line,
// then reset the current query and continue
if (regexNum === 0) {
resetCurrQuery();
}
// if this most recent individual match is the one expected next, proceed
if (regexNum === idxOfCurrQuery) {
// temporarily remember the line number of this most recent individual match
currMatchSet.push(codeLineNum);
// prepare to find the next query in the sequence
idxOfCurrQuery += 1;
// if a whole query set has just been found, then permanently remember
// the corresponding code line numbers, and reset the search
if (idxOfCurrQuery === numQueryLines) {
matchSets.push(currMatchSet);
resetCurrQuery();
}
// if this most recent match is NOT the one expected next in the sequence,
// then start over in terms of starting to look again for the first query
} else {
resetCurrQuery();
}
}
});
});
return matchSets;
}
// report the results
document.write("<b>The code lines being sought:</b>");
document.write("<pre>" + JSON.stringify(queryRegexStrs, null, 2) + "</pre>");
document.write("<b>The code being searched:</b>");
document.write(
"<pre><ol start='0'><li>" +
codeStr.replace(new RegExp("\n", "g"), "</li><li>") +
"</li></ol></pre>"
);
document.write("<b>The code line numbers of query 'hits', grouped by query set:</b>");
document.write("<pre>" + JSON.stringify(matchSets) + "</pre>");
document.write("<b>One possible formatted output:</b>");
var str = "<p>(Note that line numbers are 0-based...easily changed to 1-based if desired)</p>";
str += "<pre>";
matchSets.forEach(function(set, setNum, arr) {
str += "Matching code block #" + (setNum + 1) + ": lines " + set[0] + "-" + set[set.length - 1] + "<br />";
});
str += "</pre>";
document.write(str);
Вот точно такой же алгоритм, просто используя ваши оригинальные примеры 1 и 2. Обратите внимание на пару вещей. Прежде всего, все, что требует экранирования в строках регулярных выражений, на самом деле нуждается в двойном экранировании, например, чтобы найти буквально открывающую скобку, необходимо включить "\\(" не просто "\(". Кроме того, регулярное выражение может показаться немного сложнее. У меня есть два комментария по этому поводу. Во-первых, многое из этого - просто нахождение буквальных периодов и скобок. Однако, во-вторых, и это важно: способность использовать сложные регулярные выражения является частью силы (читай "гибкость") весь этот подход, например, приведенные вами примеры требуют некоторого чередования, где, например, "a|b" означает "найти OR b".
var queryRegexStrs = [
"var deferred = Q\\.defer\\(\\);",
"deferred\\.reject\\(err\\);",
"deferred\\.resolve\\(\\);",
"return deferred\\.promise;"
];
var codeStr =
'function writeError(errMessage) {' + "\n" +
' var deferred = Q.defer();' + "\n" +
' fs.writeFile("errors.log", errMessage, function (err) {' + "\n" +
' if (err) {' + "\n" +
' deferred.reject(err);' + "\n" +
' } else {' + "\n" +
' deferred.resolve();' + "\n" +
' }' + "\n" +
' });' + "\n" +
' return deferred.promise;' + "\n" +
'}' + "\n" +
'';
// allow for different types of end-of-line characters or character sequences
var endOfLineStr = "\n";
var matchSets = search(queryRegexStrs, codeStr, endOfLineStr);
function search(queryRegexStrs, codeStr, endOfLineStr) {
// break the large code string into an array of line strings
var codeLines = codeStr.split(endOfLineStr);
// remember the number of lines being sought
var numQueryLines = queryRegexStrs.length;
// convert the input regex strings into actual regex's in a parallel array
var queryRegexs = queryRegexStrs.map(function(queryRegexStr) {
return new RegExp(queryRegexStr);
});
// search the array for each query line
// to find complete, uninterrupted, non-repeating sets of matches
// make an array to hold potentially multiple match sets from the same file
var matchSets = [];
// prepare to try finding the next match set
var currMatchSet;
// keep track of which query line number is currently being sought
var idxOfCurrQuery = 0;
// whenever looking for a match set is (re-)initialized,
// start looking again for the first query,
// and forget any previous individual query matches that have been found
var resetCurrQuery = function() {
idxOfCurrQuery = 0;
currMatchSet = [];
};
// check each line of code...
codeLines.forEach(function(codeLine, codeLineNum, codeLines) {
// ...against each query line
queryRegexs.forEach(function(regex, regexNum, regexs) {
// check if this line of code is a match with this query line
var matchFound = regex.test(codeLine);
// if so, remember which query line it matched
if (matchFound) {
// if this code line matches the first query line,
// then reset the current query and continue
if (regexNum === 0) {
resetCurrQuery();
}
// if this most recent individual match is the one expected next, proceed
if (regexNum === idxOfCurrQuery) {
// temporarily remember the line number of this most recent individual match
currMatchSet.push(codeLineNum);
// prepare to find the next query in the sequence
idxOfCurrQuery += 1;
// if a whole query set has just been found, then permanently remember
// the corresponding code line numbers, and reset the search
if (idxOfCurrQuery === numQueryLines) {
matchSets.push(currMatchSet);
resetCurrQuery();
}
// if this most recent match is NOT the one expected next in the sequence,
// then start over in terms of starting to look again for the first query
} else {
resetCurrQuery();
}
}
});
});
return matchSets;
}
// report the results
document.write("<b>The code lines being sought:</b>");
document.write("<pre>" + JSON.stringify(queryRegexStrs, null, 2) + "</pre>");
document.write("<b>The code being searched:</b>");
document.write(
"<pre><ol start='0'><li>" +
codeStr.replace(new RegExp("\n", "g"), "</li><li>") +
"</li></ol></pre>"
);
document.write("<b>The code line numbers of query 'hits', grouped by query set:</b>");
document.write("<pre>" + JSON.stringify(matchSets) + "</pre>");
document.write("<b>One possible formatted output:</b>");
var str = "<p>(Note that line numbers are 0-based...easily changed to 1-based if desired)</p>";
str += "<pre>";
matchSets.forEach(function(set, setNum, arr) {
str += "Matching code block #" + (setNum + 1) + ": lines " + set[0] + "-" + set[set.length - 1] + "<br />";
});
str += "</pre>";
document.write(str);
Вот точно такой же алгоритм, просто используя ваш оригинальный пример 2:
var queryRegexStrs = [
"var d = (Q\\.defer\\(\\)|\\$q\\.defer);",
"d\\.resolve\\(val\\);",
"d\\.reject\\(err\\);",
"return d\\.promise(\\(\\))?;"
];
var codeStr =
"...." + "\n" +
"...." + "\n" +
"...." + "\n" +
"function getStuffDone(param) {" + "\n" +
" var d = Q.defer();" + "\n" +
"" + "\n" +
" Promise(function(resolve, reject) {" + "\n" +
" // or = new $.Deferred() etc." + "\n" +
" myPromiseFn(param+1)" + "\n" +
" .then(function(val) { /* or .done */" + "\n" +
" d.resolve(val);" + "\n" +
" }).catch(function(err) { /* .fail */" + "\n" +
" d.reject(err);" + "\n" +
" });" + "\n" +
" return d.promise;" + "\n" +
"" + "\n" +
"}" + "\n" +
"...." + "\n" +
"...." + "\n" +
"...." + "\n" +
"function getStuffDone(param) {" + "\n" +
" var d = $q.defer;" + "\n" +
"" + "\n" +
" Promise(function(resolve, reject) {" + "\n" +
" // or = new $.Deferred() etc." + "\n" +
" myPromiseFn(param+1)" + "\n" +
" .then(function(val) { /* or .done */" + "\n" +
" d.resolve(val);" + "\n" +
" }).catch(function(err) { /* .fail */" + "\n" +
" d.reject(err);" + "\n" +
" });" + "\n" +
" return d.promise();" + "\n" +
"" + "\n" +
"}" + "\n" +
"...." + "\n" +
"...." + "\n" +
"...." + "\n" +
"";
// allow for different types of end-of-line characters or character sequences
var endOfLineStr = "\n";
var matchSets = search(queryRegexStrs, codeStr, endOfLineStr);
function search(queryRegexStrs, codeStr, endOfLineStr) {
// break the large code string into an array of line strings
var codeLines = codeStr.split(endOfLineStr);
// remember the number of lines being sought
var numQueryLines = queryRegexStrs.length;
// convert the input regex strings into actual regex's in a parallel array
var queryRegexs = queryRegexStrs.map(function(queryRegexStr) {
return new RegExp(queryRegexStr);
});
// search the array for each query line
// to find complete, uninterrupted, non-repeating sets of matches
// make an array to hold potentially multiple match sets from the same file
var matchSets = [];
// prepare to try finding the next match set
var currMatchSet;
// keep track of which query line number is currently being sought
var idxOfCurrQuery = 0;
// whenever looking for a match set is (re-)initialized,
// start looking again for the first query,
// and forget any previous individual query matches that have been found
var resetCurrQuery = function() {
idxOfCurrQuery = 0;
currMatchSet = [];
};
// check each line of code...
codeLines.forEach(function(codeLine, codeLineNum, codeLines) {
// ...against each query line
queryRegexs.forEach(function(regex, regexNum, regexs) {
// check if this line of code is a match with this query line
var matchFound = regex.test(codeLine);
// if so, remember which query line it matched
if (matchFound) {
// if this code line matches the first query line,
// then reset the current query and continue
if (regexNum === 0) {
resetCurrQuery();
}
// if this most recent individual match is the one expected next, proceed
if (regexNum === idxOfCurrQuery) {
// temporarily remember the line number of this most recent individual match
currMatchSet.push(codeLineNum);
// prepare to find the next query in the sequence
idxOfCurrQuery += 1;
// if a whole query set has just been found, then permanently remember
// the corresponding code line numbers, and reset the search
if (idxOfCurrQuery === numQueryLines) {
matchSets.push(currMatchSet);
resetCurrQuery();
}
// if this most recent match is NOT the one expected next in the sequence,
// then start over in terms of starting to look again for the first query
} else {
resetCurrQuery();
}
}
});
});
return matchSets;
}
// report the results
document.write("<b>The code lines being sought:</b>");
document.write("<pre>" + JSON.stringify(queryRegexStrs, null, 2) + "</pre>");
document.write("<b>The code being searched:</b>");
document.write(
"<pre><ol start='0'><li>" +
codeStr.replace(new RegExp("\n", "g"), "</li><li>") +
"</li></ol></pre>"
);
document.write("<b>The code line numbers of query 'hits', grouped by query set:</b>");
document.write("<pre>" + JSON.stringify(matchSets) + "</pre>");
document.write("<b>One possible formatted output:</b>");
var str = "<p>(Note that line numbers are 0-based...easily changed to 1-based if desired)</p>";
str += "<pre>";
matchSets.forEach(function(set, setNum, arr) {
str += "Matching code block #" + (setNum + 1) + ": lines " + set[0] + "-" + set[set.length - 1] + "<br />";
});
str += "</pre>";
document.write(str);
Следующее регулярное выражение может показаться немного пугающим, но оно было построено из простых концепций и допускает немного большую свободу действий, чем вы упомянули - например, дополнительный пробел, разные имена переменных, пропуск var
и т.д. Кажется, работает для обоих примеров - пожалуйста, посмотрите, соответствует ли он вашим потребностям.
([^\s\r\n]+)\s*=\s*(?:Q|\$q)\.defer\s*\(\s*\)\s*;(?:\r?\n|.)*(?:\s|\r?\n)(?:\1\.reject\(\w+\)\s*;(?:\r?\n|.)*(?:\s|\r?\n)\1\.resolve\(\s*\w*\)\s*;|\1\.resolve\(\s*\w*\)\s*;(?:\r?\n|.)*(?:\s|\r?\n)\1\.reject\(\w+\)\s*;)(?:\r?\n|.)*(?:\s|\r?\n)return\s+(?:\1\.)?promise\s*;