我现在正在使用这个。它可能不完整,也可能不是最佳速度,但它完成了我想要的:获取一个表达式并返回 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"}]
任何建议都非常受欢迎!