28.06.2017, 10:52
|
|
CacheVar
|
|
Регистрация: 19.08.2010
Сообщений: 14,214
|
|
Сообщение от Teamur
|
Поучил строку:
"function MyFn(a,b=1,c=7){}"
Какую надо придумать регулярку, чтобы:
|
Уже лучше...
Сообщение от Teamur
|
1) получить всё что после первого пробела (после "function ")
|
var str="function MyFn(a,b=1,c=7){}";
alert(str.substr(9));
Сообщение от Teamur
|
2) отсечь всё что после первой закрывающей скобки
|
var str="function MyFn(a,b=1,c=7){}";
alert(str.split(')')[0]+')');
Сообщение от Teamur
|
3) разбить "MyFn(a,b=1,c=7)" на две части: "MyFn" и "(a,b=1,c=7)"
|
var str="MyFn(a,b=1,c=7)";
var a=str.split('(');
alert(a[0]+' и ('+a[1]);
Сообщение от Teamur
|
4) из второй части убрать скобки, нап. так: "(a,b=1,c=7)".slice(1,-1)
|
var str="(a,b=1,c=7)";
alert(str.match(/\((.+)\)/)[1]);
Сообщение от Teamur
|
5) вернуть массив ["MyFn","a,b=1,c=7"] или объект {fn:"MyFn",args:"a,b=1,c=7"}
|
var str="MyFn(a,b=1,c=7)";
var o={};
alert(str.match(/.*(?=\()/));
o.fn=str.match(/.*(?=\()/);
alert(str.match(/\((.+)\)/)[1]);
o.args=str.match(/\((.+)\)/)[1];
|
|
28.06.2017, 11:02
|
|
Профессор
|
|
Регистрация: 30.04.2012
Сообщений: 3,018
|
|
На деле таких задач никогда не должно возникать, а если и возникают - значит Вы делаете что-то не так.
|
|
28.06.2017, 20:22
|
Профессор
|
|
Регистрация: 08.06.2015
Сообщений: 206
|
|
ksa,
обалдеть, спасибо!
Ruslan_xDD,
если только вы не проводите различные опыты )
|
|
28.06.2017, 20:43
|
Профессор
|
|
Регистрация: 08.06.2015
Сообщений: 206
|
|
ksa,
'(a,b=1,c=7)'.match(/\((.+)\)/)[1]
Это самый короткий способ получить регуляркой то, что внутри скобок?
Добавлю, что все функции, которые отправляются в MyFn, будут предварительно сжаты, следовательно обработку пробелов в регулярке можно исключить.
Конечная цель получить функцию, которая бы разбивала входящие данные на куски по типам: имя_функции, группа_параметров, свойство, метод. В итоге для каждой полученной функции должен быть создан объект с разложенными по типу кусками.
По сути речь идет о Деконструкторе. Вместо функции можно передать любой валидный код и Деконструктор должен разложить ее на части: свойства, методы, циклы и тд. Останется потом только обернуть в теги и раскрасить.
Отправляем строку коду, а на выходе красивая сборка из типовых блоков кода.
|
|
28.06.2017, 21:08
|
|
Профессор
|
|
Регистрация: 15.01.2015
Сообщений: 622
|
|
Teamur,
Рекомендую изучить исходный код готово парсера js в AST. https://github.com/babel/babylon например.
'(a =")", bc)'.replace(/^\((.*)\)$/, '$1') // a =")", bc
Более безопасный способ, чтобы у вас код не упал с ошибкой, когда пришла строка без скобок.
Последний раз редактировалось EmperioAf, 28.06.2017 в 21:19.
|
|
29.06.2017, 08:28
|
|
CacheVar
|
|
Регистрация: 19.08.2010
Сообщений: 14,214
|
|
Сообщение от Teamur
|
Это самый короткий способ получить регуляркой то, что внутри скобок?
|
Можно и не регуляркой...
var str="(a,b=1,c=7)";
alert(str.substr(1,str.length-2));
|
|
29.06.2017, 18:44
|
Профессор
|
|
Регистрация: 08.06.2015
Сообщений: 206
|
|
ksa,
я думаю через слайс будет короче и проще:
var result = "(lorem ipsum dolor)".slice(1,-1)
А какая самая простая и короткая регулярка для этого?
|
|
30.06.2017, 08:50
|
|
CacheVar
|
|
Регистрация: 19.08.2010
Сообщений: 14,214
|
|
Сообщение от Teamur
|
А какая самая простая и короткая регулярка для этого?
|
Так я же показал ее выше
Сообщение от ksa
|
alert(str.match(/\((.+)\)/)[1]);
|
|
|
30.06.2017, 18:43
|
Профессор
|
|
Регистрация: 08.06.2015
Сообщений: 206
|
|
ksa,
ага, понял, поверю, спасибо! Удачи!
|
|
01.07.2017, 05:44
|
|
Профессор
|
|
Регистрация: 20.12.2009
Сообщений: 1,714
|
|
Имя функций, которая не является ни геттером ни сеттером и не является результатом функций bind можно узнать вызвав свойство name у функций.
Что же касается способов получения имени функций, и её параметров при помощи регулярного выражения, которые описаны выше в этой теме, то там не учтены некоторые особенности объекта функция (и в частности её метода toString) Поэтому чудовищно хромают.
Для начала нужно узнать, является ли объект object функцией. Для этого подойдёт...
typeof object === "function" или
object instanceof Function
function getFunctionData(object) {
if(object instanceof Function === false) throw new TypeError("First argument to getFunctionData function must be an Function");
// удаляем из начала имени указания того, что это сеттер,
// геттер или получено из функций bind
// имя функций объявленное при помощи символа будет в квадратных скобках
let name = object.name.replace(/^(?:set|get)\s|(?:bound\s+)+/, "");
return {
name
}
}
// помощник
function test(fn) {
let functionData = getFunctionData(fn);
console.group(`Функция — ${functionData.name ? functionData.name : "безымянная"}`);
console.log(fn);
console.groupEnd();
}
// примеры (результаты видны в консоли, F12)
test(Function);
test(function Awesome() {});
test(class PolyRange extends HTMLInputElement {});
test(new Function("/* I'm really anonymous */"));
test(({ id: a }) => () => a);
test(async (url) => await fetch(url).then(response => response.json()));
test(function* counter() {});
test(class { baz(...nums) { return [...new Set(nums)]; } });
test((class { baz(...nums) { return [...new Set(nums)]; } }).prototype.baz);
test(((x = "function you have got confused() {}") => ({[x](x=x){}})[x])());
test(Object.getOwnPropertyDescriptor({set foo({mode} = { mode: "normal" }) {}}, "foo").set);
test(function* range({ mapper = function(x) { return x }, start = 0, end = Infinity, step = 1 } = {}) { let value = start; while(end > value) yield mapper(value += step); });
test((function save() {}).bind({}));
test((function save() {}).bind({}).bind({}).bind({}));
test(({[Symbol.for("Hello")](x){}})[Symbol.for("Hello")]);
Теперь давайте получим имена всех параметров функций. При помощи какого-то одного регулярного выражений это не получится сделать, поскольку невозможно отследить правильный порядок скобок.
И аргументы и тело могут содержать последовательности символов "=>", ")", "{" или "}". Например, вот какие строки может возвращать метод toString у функций:
function() {}
(a = a => a) => {}
function* (...args) {}
class {}
(){} // получается путем ({[""](){}})[""];
async *function({url, options: { mode }}) {}
function (callback = function(callback = async function(callback) {}) {}) {}
Можно использовать парсер, например, Esprima — стандартно-совместимый анализатор ECMAScript — чтобы найти все имена параметров, которые будут доступны после того, как функция будет вызвана.
Вот окончательный вариант функций getFunctionData, которая возвращает имя и имена параметров функций...
<!doctype html>
<html lang="en" style="background: #f06; ">
<head>
<meta charset="utf-8">
</head>
<body>
<script src="https://unpkg.com/esprima@4.0.0/dist/esprima.js"></script>
<script>
function getFunctionData(object) {
if(object instanceof Function === false) throw new TypeError("First argument to getFunctionData function must be an Function");
// удаляем из начала имени указания того, что это сеттер,
// геттер или получено из функций bind
// имя функций объявленное при помощи символа будет в квадратных скобках
let name = object.name.replace(/^(?:set|get)\s|(?:bound\s+)+/, "");
// теперь используем Esprima
// встроенные функций и wasm всегда возвратят []
// поскольку имеют вид "function() { [native code] }"
// и поскольку можно `Function.prototype.toString = () => "function() { [native code] }";`
// таким образом не гарантируется получение имён параметров, если toString у
// функций был переопределён
// вместо этого можно таким же образом разобрать исходный код...
let body = object.toString();
let ast = tryThese(
() => esprima.parse("(" + body + ");"),
() => esprima.parse("(function " + body + ");"),
() => esprima.parse("(function " + body.replace(/^function set /, "") + ");"),
() => esprima.parse("(function " + body.replace(/^function get /, "") + ");"),
() => esprima.parse("(" + body.replace("[native code]", "") + ");"),
);
let firstBody = ast.body[0] || {};
let params = firstBody.type === "ExpressionStatement" ? firstBody.expression.type === "ClassExpression" ? firstBody.expression.body.body : firstBody.expression.params : firstBody.params;
// возвращает массив всех переменных, которые будут
// доступны, когда функция будет вызвана
function getParams(ctx, paramNames = []) {
if(!ctx) return paramNames;
if("type" in ctx === false) {
ctx.forEach(param => getParams(param, paramNames));
} else {
// рекурсивный обход дерева — поиск узлов типа "Identifier"
switch(ctx.type) {
case "AssignmentPattern":
paramNames.push(...getParams(ctx.left));
break;
case "ArrayPattern":
paramNames.push(...getParams(ctx.elements));
break;
case "FunctionExpression":
paramNames.push(...getParams(ctx.params));
break;
case "Identifier":
paramNames.push(ctx.name);
break;
case "ObjectPattern":
paramNames.push(...getParams(ctx.properties));
break;
case "MethodDefinition":
if(ctx.kind !== "constructor") break;
case "Property":
paramNames.push(...getParams(ctx.value));
break;
case "RestElement":
paramNames.push(...getParams(ctx.argument));
break;
}
}
return paramNames;
}
// возвращает результат той функций, которая не улетает
function tryThese(...fns) {
let fn, error;
while(fn = fns.shift()) {
try {
return fn();
} catch (_) {
error = _;
continue;
}
}
throw error;
}
return {
name,
params: getParams(params)
}
}
// помощник
function test(fn) {
let functionData = getFunctionData(fn);
console.group(`Функция — ${functionData.name ? functionData.name : "безымянная"}`);
console.log(fn);
console.log("Параметры: ", JSON.stringify(functionData.params));
console.groupEnd();
}
// примеры (результаты видны в консоли, F12)
test(Function);
test(function Awesome() {});
test(function Point(x, y) {});
test(class Point { constructor(x, y) {}});
test(class PolyRange extends HTMLInputElement {});
test(new Function("[a, b = \"\", ...c]", "/* I'm really anonymous */"));
test(({ id: a }) => () => a);
test(async (url) => await fetch(url).then(response => response.json()));
test(function* counter() {});
test(class { baz(...nums) { return [...new Set(nums)]; } });
test((class { baz(...nums) { return [...new Set(nums)]; } }).prototype.baz);
test(((x = "function you have got confused() {}") => ({[x](x){}})[x])());
test(class extends (class extends (class extends (class extends (function ({ href } = location) {}) {}) {}) {}) {});
test(function whois({age, displayName: displayName, fullName: {firstName: name}}){});
test(Object.getOwnPropertyDescriptor({set foo({mode} = { mode: "normal" }) {}}, "foo").set);
test(Object.getOwnPropertyDescriptor({get foo() {}}, "foo").get);
test(function* range({ mapper = function(x) { return x }, start = 0, end = Infinity, step = 1 } = {}) { let value = start; while(end > value) yield mapper(value += step); });
test((function save() {}).bind({}));
test((function save() {}).bind({}).bind({}).bind({}));
test(({[Symbol.for("Hello")](x){}})[Symbol.for("Hello")]);
test(({a1: a2 = a3 => a4}) => {});
</script>
<p style="font: 2.5em / 1.5 Menlo, monospace; text-align: center; text-shadow: 0 0.1em 0.1em #7b3755; ">результаты видны в консоли, F12</p>
</body>
</html>
Esprima http://esprima.org/demo/parse.html Конечно же Вы можете анализировать всё, что угодно, а не только параметры функции...
|
|
|
|