【问题标题】:Finding parts of a JavaScript expression查找 JavaScript 表达式的一部分
【发布时间】:2014-03-24 17:23:33
【问题描述】:

假设我有一个像这样的字符串形式的 JavaScript 表达式:

string = ' collections.users[ currentUsers[ ids[1].id ] ] && users[ ids[1].id ]'

现在我需要得到这个表达式的某些部分,它们是我正在做的“依赖”

这是我需要从该字符串中提取的内容

result = [
         'collections.users[ currentUsers[ ids[1].id ] ]', 
          'currentUsers[ ids[1].id]', 'ids[1].id',
          'users[ ids[1].id ]', 'ids[1].id'
         ]

它基本上需要每个包含[*] 的“变量”

这是可能导致表达式改变的每个变量,我已经尝试这样做大约 3 天了,到目前为止我尝试过但失败的是

  1. 使用正则表达式查找所有依赖项(失败)

  2. 我曾尝试将字符串拆分为“如果括号打开做某事”之类的使用条件,但失败了。

    这真的很难弄清楚,我没有在代码中尝试过任何东西,因为我刚刚删除并重新开始。

我知道这可能有点“本地化”,但我已经在 J​​avaScript 聊天中询问了几天的建议,但没有得到任何建议,我别无选择。

任何人对如何实现这一点有任何建议,请记住这个字符串不是静态的并且可能有更深层次的条件?

有人为此建议使用 JSON,但我不知道它会如何工作,你怎么看?

由于问题的性质,我会给 50+ 赏金最佳答案/建议

【问题讨论】:

  • 我建议看看一个合适的 JS 语法解析器,比如esprima.org
  • @FelixKling 好的,我一直在思考编译器的问题,但是对于这样的事情会不会太过分了?我现在会研究一下,谢谢
  • 视情况而定。它肯定会工作:) 您可以在此处复制字符串的内容esprima.org/demo/parse.html 并查看 AST esprima 是否会返回,然后决定是否可以使用。这可能是最可靠的解决方案。
  • 好的,干杯@FelixKling
  • 哈,演示页面甚至可以让我创建一个唯一的 URL,所以仅供参考,这里是 esprima 将为上面的代码创建的 AST:esprima.org/demo/…

标签: javascript regex expression conditional-statements


【解决方案1】:

您可能需要使用递归函数并遍历每个字符。每次你碰到一个左括号,你就进入一个新的递归层;每次你击中一个右括号,你退出一个。

我相信这正是您想要的。

var toParse = 'collections.users[ currentUsers[ ids[1].id ] ] && users[ ids[1].id ]';
var operators = ['&','|','+','-','=','/','*'];
var answer = [];

parseBrackets(0);

function parseBrackets(start) {
    var openBrackets = 0;
    var currentString = '';
    for (var i = start; i < toParse.length; i++) {
        if (toParse[i] == '[') {  // open bracket
            if (openBrackets == 0) {
                parseBrackets(i + 1);
            }
            currentString += toParse[i];
            openBrackets++;
        } else if (toParse[i] == ']' && openBrackets-- == 0) { // close bracket
            answer.push(currentString);
            return;
        } else if (operators.indexOf(toParse[i]) >= 0) { // this is an expression operator, don't include it
            if (currentString != '') {
                answer.push(currentString);
            }
            currentString = '';
        } else { // add this character to the current string
            currentString += toParse[i];
        }
    }
    answer.push(currentString); // add the current string to the list of parsed 'dependencies'
}

console.log(answer);

在 jsfiddle 中:http://jsfiddle.net/W87m3/5/

这是输出:

 ["1", " ids[1].id ", " currentUsers[ ids[1].id ] ", "collections.users[ currentUsers[ ids[1].id ] ] ", "1", " ids[1].id ", " users[ ids[1].id ]"] 

【讨论】:

  • 我试过了,(有问题的第 2 个),但是说起来容易做起来难
  • 我喜欢这种方法,诀窍是从不平衡状态中恢复(并继续前进)。
  • @Connor 我编辑了帖子并添加了代码来执行我所描述的操作。我相信它可以解决您的需求。
  • 是的,不幸的是,当你尝试不同的表达方式时,这会失败,我在过去 10 个小时里尝试过延长它并让它工作,但这似乎不太可能
  • @Connor 你有一个表达式失败的例子吗?它符合您在问题中列出的标准。如果我有更多信息,也许我可以帮助您根据需要进行调整。
【解决方案2】:

正则表达式不适合这个问题,因为正则表达式不能“计数”。 IE。无论您的正则表达式有多复杂,都会有一定程度的括号嵌套不会被接受。

您真正需要的是解析器。有一些解析器生成器使用语法规则生成代码。以下是一些:Javascript parser generator

您的规则看起来很简单。像这样的:

Expression = AnythingExceptBrackets '[' Expression ']' AnythingExceptBrackets 
             | AnythingExceptBrackets 

AnythingExceptBrackets = [^\\[\\]]+ 

获得生成器后,剩下的唯一步骤就是将已识别的标记粘贴到结果数组中。

【讨论】:

    【解决方案3】:

    为了成功解析短语,必须知道短语的完整结构。从给出的一个例子中确定所有可能的结构是不可能的。然而,做一些假设,我已经使用扩展巴科斯-瑙尔形式 (EBNF) 定义了一个可能的结构。

    假设每个数组元素可以有一个整数、一个变量或另一个数组元素作为索引,并且变量和数组元素可以具有使用点符号编写的属性。数组元素列表之间可以有运算符。

    然后您可以根据需要更改和扩展此结构。

    扩展巴科斯-瑙尔形式表示法

    { } 0 个或更多

    [ ] 1 个或多个

    ( ) 组

    字母 = "a"|"b"|"c....."|"x"|"y"|"z"|"A"|"B"|"....."| "Y"|"Z"

    数字 = 0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9

    整数 = [数字]

    术语 = [字符|数字]

    标识符 = ($|_|char){term}

    属性 = .char{term}

    变量 = 标识符{属性}

    索引=数字|变量|数字

    数组 = 变量"["索引"]"{属性}

    运算符 = "&&"|"||"|+"|"-"|"*"|"/"

    短语 = 数组{运算符数组}:

    有一个 fiddle 解析一个短语并为给定示例生成结果

    代码是

    letter = /[a-zA-Z]/;
    digit = /[0-9]/;
    join = /[&|+\-*]/;  //possible operators
    
    
    var phrase=' collections.users[ currentUsers[ ids[1].id ] ] && users[ ids[1].id ]';
    var ptr=0;
    var stack=[];
    
    phrase=phrase.replace(/\s/g,''); //remove extraneous white space
    phrase+=':'; //add : as terminator
    
    var crctr=readNext();
    
    getArray();         
    document.getElementById('results').innerHTML=setResults();
    
    function getArray() {
        var L=new List();
        L.name=getVariable();
        L.index=getIndex();
        crctr=readNext();
        if(crctr=='.') {
            crctr=readNext();
            L.property=getProperty();
        }   
        stack.push(L);
        if(crctr !=':') { //not end of phrase
            if (join.test(crctr)) {     
                readJoin();         
                getArray();
            }
        }
    }
    
    function getIndex() {
        var indx='';
        if(crctr=="[") {
            crctr=readNext();   
            if(digit.test(crctr))
            {
                indx=getInteger();
                if(crctr==']') {
                    return indx;
                }
            }
            else {
                indx=getVariable(); 
                if(crctr=="]"){
                    return indx;
                }
                else if(crctr=="[") {                   
                    var L=new List();
                    L.name=indx;
                    L.index=getIndex();
                    crctr=readNext();
                    if(crctr=='.') {
                        crctr=readNext();
                        L.property=getProperty();
                    }
                    stack.push(L);          
                    return L;
                }
            }
        }
        else {
            throw 'index error';
        }
    }
    
    
    
    function getVariable() {
        var v="";   
        if(crctr=="$" || crctr=="_" || letter.test(crctr)) {
            v+=crctr;
            crctr=readNext();
            v+=getIdentifier();
            if (crctr==".") {
                v+=crctr;
                crctr=readNext();
                v+=getProperty();
            }
            return v;   
        }
        else {
            throw 'variable error';
        }
    }
    
    function getIdentifier() {
        var id="";
        if(crctr=="$" || crctr=="_" || letter.test(crctr)) {
            id=crctr;
            crctr=readNext();
            id+=getTerm();
            return id;  
        }
        else {
            throw 'Identifier error';
        }
    }
    
    function getTerm() {
        var t="";
        while(letter.test(crctr) || digit.test(crctr)) {
            t+=crctr;
            crctr=readNext();
        }
        return t;
    }
    
    function getProperty() {
        var p='';
        if(letter.test(crctr)) {
            p=crctr;
            crctr=readNext();
            while(letter.test(crctr) || digit.test(crctr)) {
                p+=crctr;
                crctr=readNext();
            }
            if(crctr=='.') {
                p+=crctr;
                crctr=readNext();
                p+=getProperty();
            }
            else {
                return p;
            }
        }
        else {
            throw 'property error';
        }
    }
    
    function getInteger() {
        var i='';
        while(digit.test(crctr)) {
            i+=crctr;
            crctr=readNext();
        }
        return i;
    }
    
    function readJoin() {
        if(crctr=="&" || crctr=="|") {
            crctr=readNext();
        }
        crctr=readNext();
    }
    
    function readNext(){
        var crctr=phrase.charAt(ptr);
        ptr+=1;
        return crctr;
    }
    
    function List() {
        this.name='';
        this.index='';
        this.property='';
    }
    
    function setResults() {
        var r='results = [';
        while(stack.length>1) {
            var A=stack.pop();
            r+='<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'+listArrays(A)+',';
        }
        var A=stack.pop();
        r+='<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'+listArrays(A);
        return r+='<br>]';
    }
    
    function listArrays(A) {        
        if(A.index.name) { //A.index is array   
            var name=A.name;
            var indx=A.index;
            var prp='';
            if(A.property !='') {
                prp='.'+A.property;
            }
            return A.name+'['+listArrays(A.index)+']'+prp;
        }
        else {
            var name=A.name;
            var indx=A.index;
            var prp='';
            if(A.property !='') {
                prp='.'+A.property;
            }
            return A.name+'['+A.index+']'+prp;
        }
    }
    

    这会将结果放在一个 div 中 &lt;div id='results'&gt;&lt;/div&gt;

    代码需要在您拥有的其他示例上进行测试。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      • 1970-01-01
      • 2017-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多