如何从javascript表达式中提取关键路径

问题描述 投票:0回答:1

有人知道一种从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最终会告诉我路径是否未验证。

javascript regex parsing expression keypaths
1个回答
1
投票

我现在正在使用它。它可能不完整,并且它可能不是最佳速度,但它可以实现我想要的:获取表达式并返回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"}]

任何建议都非常欢迎!

© www.soinside.com 2019 - 2024. All rights reserved.