如何从 javascript 表达式中提取键路径
How to extract keypaths from a javascript expression
有人知道从 javascript 表达式中提取路径以便在 PathObserver 中使用的方法,例如 https://github.com/polymer/observe-js 吗?
Polymer 通过
定义路径
an ECMAScript expression consisting only of identifiers (myVal),
member accesses (foo.bar) and key lookup with literal values
(arr[0] obj['str-value'].bar.baz).
我的目标是观察所有可能影响表达式结果的路径(low 'observe' 表达式)。我希望有一个简单的正则表达式,但是使用像 Esprima 或 Shift 这样的 js 解析器的代码也很好。
所以一个实际的例子:输入是
'if (count(body.arms)/numlegs==1) head[0]=eyes[symmetry]'
输出为
["body.arms","numlegs","head[0]","eyes","symmetry"]
快速胜于完美;如果路径未通过验证,PathObserver 最终会告诉我。
我现在正在用这个。它可能不完整,也可能不是最快速的,但它做了我想要的:采用表达式和 return 可观察路径 Observe.js (https://github.com/polymer/observe-js) . It uses esprima (http://esprima.org) 来解析表达式。
Parser = {
// requires esprima.
// @see http://esprima.org/demo/parse.html
outerscope : 'window',
getObservablePaths : function(expression) {
console.log('Parser.getPaths',expression);
var ast = esprima.parse(expression);
if (ast) {
console.log('Parser.getPaths',ast);
var paths = new Array();
this.recurseObservablePaths(ast,paths);
return paths;
} else return false;
},
recurseObservablePaths : function(tree,paths,path) {
if (!tree || !paths) return false;
if (tree.type =='Identifier') {
// some sort of global
console.log('Parser.recurseObservablePaths','adding identifier '+tree.name);
paths.push({object:this.outerscope,path:tree.name});
} else if (tree.type =='MemberExpression') {
// member expression
if (tree.property.type=='Identifier' || tree.property.type=='Literal') {
// like foo[bar][24].quz ; the property is 'quz'
// dabble down the object to get the path
if (tree.property.type=='Identifier') {
path = (path)?'.'+tree.property.name+path:'.'+tree.property.name;
} else {
path = (path)?'['+tree.property.raw+']'+path:'['+tree.property.raw+']';
}
if (tree.object.type=='Identifier') {
// like foo.bar ; were done with this path - push !
console.log('Parser.recurseObservablePaths','adding path '+tree.object.name+path);
if (path.indexOf('.')===0) {
paths.push({object:tree.object.name,path:path.substring(1)});
} else {
paths.push({object:this.outerscope,path:tree.object.name+path});
}
} else {
if (tree.object.type=='MemberExpression') {
// like foo.bar.quz ; recurse the object
console.log('Parser.recurseObservablePaths','recursing member expression ..');
this.recurseObservablePaths(tree.object,paths,path);
} else {
// like foo(bar).quz ; the object is something weird.
// ignore the property .. but recurse the object
this.recurseObservablePaths(tree.object,paths);
}
}
} else {
// the property is some sort of thing itself:
if (tree.object.type=='Identifier') {
// like foo[bar.quz] - push the object, recurse the property
console.log('Parser.recurseObservablePaths','adding identifier '+tree.object.name);
paths.push({object:this.outerscope,path:tree.object.name});
this.recurseObservablePaths(tree.property);
} else {
// like foo.bar[quz(raz)] ; recurse both
console.log('Parser.recurseObservablePaths','recursing member expression ..');
this.recurseObservablePaths(tree.object,paths);
this.recurseObservablePaths(tree.property,paths);
}
}
} else if (tree.type=="CallExpression") {
// like foo.bar(quz.baz) ; we only want the arguments
this.recurseObservablePaths(tree.arguments,paths);
} else if (tree.type=="AssignmentExpression") {
// like foo.bar=baz*quz ; we only want the right hand
this.recurseObservablePaths(tree.right,paths);
} else {
// unknown garbage. dig deeper.
var props = Object.getOwnPropertyNames(tree);
for (var pc=0; pc<props.length; pc++) {
var key = props[pc];
if (typeof tree[key] == 'object') {
if (Array.isArray(tree[key])) {
for (var kc=0;kc<tree[key].length;kc++) {
console.log('Parser.recurseObservablePaths','recursing '+key+':'+kc);
this.recurseObservablePaths(tree[key][kc],paths);
}
} else {
console.log('Parser.recurseObservablePaths','recursing '+key);
this.recurseObservablePaths(tree[key],paths);
}
} else {
console.log('Parser.recurseObservablePaths','ignoring '+key);
}
}
}
}
}
试试吧..
Parser.getObservablePaths('alert(life.and[42].the); universe=everything.else')
[{"object":"life","path":"and[42]"},{"object":"everything","path":"else"}]
Parser.getObservablePaths('if (count(body.arms)/numlegs==1) head[0]=eyes[symmetry]')
[{"object":"body","path":"arms"},{"object":"window","path":"numlegs"},{"object":"eyes","path":"symmetry"}]
欢迎提出任何建议!
有人知道从 javascript 表达式中提取路径以便在 PathObserver 中使用的方法,例如 https://github.com/polymer/observe-js 吗?
Polymer 通过
定义路径an ECMAScript expression consisting only of identifiers (myVal),
member accesses (foo.bar) and key lookup with literal values
(arr[0] obj['str-value'].bar.baz).
我的目标是观察所有可能影响表达式结果的路径(low 'observe' 表达式)。我希望有一个简单的正则表达式,但是使用像 Esprima 或 Shift 这样的 js 解析器的代码也很好。
所以一个实际的例子:输入是
'if (count(body.arms)/numlegs==1) head[0]=eyes[symmetry]'
输出为
["body.arms","numlegs","head[0]","eyes","symmetry"]
快速胜于完美;如果路径未通过验证,PathObserver 最终会告诉我。
我现在正在用这个。它可能不完整,也可能不是最快速的,但它做了我想要的:采用表达式和 return 可观察路径 Observe.js (https://github.com/polymer/observe-js) . It uses esprima (http://esprima.org) 来解析表达式。
Parser = {
// requires esprima.
// @see http://esprima.org/demo/parse.html
outerscope : 'window',
getObservablePaths : function(expression) {
console.log('Parser.getPaths',expression);
var ast = esprima.parse(expression);
if (ast) {
console.log('Parser.getPaths',ast);
var paths = new Array();
this.recurseObservablePaths(ast,paths);
return paths;
} else return false;
},
recurseObservablePaths : function(tree,paths,path) {
if (!tree || !paths) return false;
if (tree.type =='Identifier') {
// some sort of global
console.log('Parser.recurseObservablePaths','adding identifier '+tree.name);
paths.push({object:this.outerscope,path:tree.name});
} else if (tree.type =='MemberExpression') {
// member expression
if (tree.property.type=='Identifier' || tree.property.type=='Literal') {
// like foo[bar][24].quz ; the property is 'quz'
// dabble down the object to get the path
if (tree.property.type=='Identifier') {
path = (path)?'.'+tree.property.name+path:'.'+tree.property.name;
} else {
path = (path)?'['+tree.property.raw+']'+path:'['+tree.property.raw+']';
}
if (tree.object.type=='Identifier') {
// like foo.bar ; were done with this path - push !
console.log('Parser.recurseObservablePaths','adding path '+tree.object.name+path);
if (path.indexOf('.')===0) {
paths.push({object:tree.object.name,path:path.substring(1)});
} else {
paths.push({object:this.outerscope,path:tree.object.name+path});
}
} else {
if (tree.object.type=='MemberExpression') {
// like foo.bar.quz ; recurse the object
console.log('Parser.recurseObservablePaths','recursing member expression ..');
this.recurseObservablePaths(tree.object,paths,path);
} else {
// like foo(bar).quz ; the object is something weird.
// ignore the property .. but recurse the object
this.recurseObservablePaths(tree.object,paths);
}
}
} else {
// the property is some sort of thing itself:
if (tree.object.type=='Identifier') {
// like foo[bar.quz] - push the object, recurse the property
console.log('Parser.recurseObservablePaths','adding identifier '+tree.object.name);
paths.push({object:this.outerscope,path:tree.object.name});
this.recurseObservablePaths(tree.property);
} else {
// like foo.bar[quz(raz)] ; recurse both
console.log('Parser.recurseObservablePaths','recursing member expression ..');
this.recurseObservablePaths(tree.object,paths);
this.recurseObservablePaths(tree.property,paths);
}
}
} else if (tree.type=="CallExpression") {
// like foo.bar(quz.baz) ; we only want the arguments
this.recurseObservablePaths(tree.arguments,paths);
} else if (tree.type=="AssignmentExpression") {
// like foo.bar=baz*quz ; we only want the right hand
this.recurseObservablePaths(tree.right,paths);
} else {
// unknown garbage. dig deeper.
var props = Object.getOwnPropertyNames(tree);
for (var pc=0; pc<props.length; pc++) {
var key = props[pc];
if (typeof tree[key] == 'object') {
if (Array.isArray(tree[key])) {
for (var kc=0;kc<tree[key].length;kc++) {
console.log('Parser.recurseObservablePaths','recursing '+key+':'+kc);
this.recurseObservablePaths(tree[key][kc],paths);
}
} else {
console.log('Parser.recurseObservablePaths','recursing '+key);
this.recurseObservablePaths(tree[key],paths);
}
} else {
console.log('Parser.recurseObservablePaths','ignoring '+key);
}
}
}
}
}
试试吧..
Parser.getObservablePaths('alert(life.and[42].the); universe=everything.else')
[{"object":"life","path":"and[42]"},{"object":"everything","path":"else"}]
Parser.getObservablePaths('if (count(body.arms)/numlegs==1) head[0]=eyes[symmetry]')
[{"object":"body","path":"arms"},{"object":"window","path":"numlegs"},{"object":"eyes","path":"symmetry"}]
欢迎提出任何建议!