有人知道一种从javascript表达式中提取路径的方法,以便在像https://github.com/polymer/observe-js这样的PathObserver中使用吗?
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).
我的目标是观察可能影响表达结果的所有路径(低'观察'表达式)。我希望有一个简单的正则表达式,但使用像Esprima或Shift这样的js解析器的代码也很好。
这是一个实际的例子:输入是
'if (count(body.arms)/numlegs==1) head[0]=eyes[symmetry]'
和输出将是
["body.arms","numlegs","head[0]","eyes","symmetry"]
快速胜过完美; PathObserver最终会告诉我路径是否未验证。
我现在正在使用它。它可能不完整,并且它可能不是最佳速度,但它可以实现我想要的:获取表达式并返回Observe.js(https://github.com/polymer/observe-js)的可观察路径。它使用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"}]
任何建议都非常欢迎!