在JavaScript中构建AST

时间:2015-10-26 01:24:33

标签: javascript parsing recursion abstract-syntax-tree bbcode

我正在尝试在JavaScript中为BBCode构建一个解析器,它允许我将带有BBCode的字符串转换为带有HTML的字符串。我脑子里想知道它是如何工作的,我甚至已经构建了两个解析器步骤。

现在,解析器的整个过程可以描述为

  1. 获取输入
  2. 将输入分解为令牌(tokenize)
  3. 添加有关令牌(lex)的信息
  4. 从代币构建AST(解析)
  5. 根据语法规则(清洁)清理AST
  6. 评估AST并转换为HTML(评估)
  7. 返回HTML字符串
  8. 除了第四步,我总体上知道如何在脑海中完成所有这些工作。

    当我到达第四步时,我在构建AST时遇到了问题。问题是如何递归地构建这棵树。我过去以递归方式构建了二维数组,但是变深度树超出了我的能力范围。

    在我脑海中,我认为树应该是这样的:

    // Hello, [b]World![/b]
    {
        "text": "Hello, ",
        "tag": {
            "type": "b",
            "text": "World!"
        }
    }
    

    但是当试图生成这个时,我有一个问题,递归地构建它。

    更复杂的例子如下:

    // [c=red]Hello Tom, [/c][b][c=green]how are you?[/c][/b]
    {
        "tag": {
            type: "c",
            "parameters": "red",
            "text": "Hello Tom, "
            "tag": {
                "type": "b",
                "tag": {
                    "type": "c",
                    "parameters": "green",
                    "text": "how are you?"
                }
            }
        }
    }
    

    我遇到的主要问题是在建筑物的同时保持我的位置,而不会意外地覆盖整棵树。

    目前我使用的代码是:

    var bbcode = {};
    
    bbcode._tokens = {
        'TO_DEL': '[',
        'TC_DEL': ']',
        'TE_DEL': '/',
        'EQ_DEL': '='
    };
    
    bbcode._tags = ['c', 'b'];
    
    bbcode.parse = function(bbcode) {
        var tokens = this._tokenize(bbcode);
            tokens = this._lex(tokens);
    
        var ast = this._parse(tokens);
    
        console.log(JSON.stringify(ast, null, 4));
        //return tokens;
    };
    
    bbcode._isToken = function(token) {
        for (var k in this._tokens) {
            if (this._tokens[k] === token) {
                return true;
            }
        }
    
        return false;
    };
    
    bbcode._isTag = function(token) {
        return (this._tags.indexOf(token) > -1) ? true : false;
    };
    
    bbcode._getType = function(token) {
        for (var k in this._tokens) {
            if (this._tokens[k] === token) {
                return k;
            }
        }   
    };
    
    bbcode._next = function(tokens, curr) {
        return tokens[curr + 1][0];
    };
    
    bbcode._previous = function(tokens, curr) {
        return tokens[curr - 1][0];
    };
    
    bbcode._tokenize = function(bbcode) {
        var tree = [];
        var temp = '';
    
        for (var i = 0; i < bbcode.length; i++) {
            if (this._isToken(bbcode[i])) {
                if (temp.length > 0) {
                    tree.push(temp);
                    temp = '';
                }
    
                tree.push(bbcode[i]);
            } else {
                temp += bbcode[i];
            }
        }
    
        return tree;
    };
    
    bbcode._lex = function(tokens) {
        var tree = [];
    
        for (var i = 0; i < tokens.length; i++) {
            if (this._isToken(tokens[i])) {
                tree.push([this._getType(tokens[i]), tokens[i]]);
            } else if (this._isTag(tokens[i])) {
                tree.push(['BB_TAG', tokens[i]]);
            } else {
                tree.push(['BB_STRING', tokens[i]]);
            }
        }
    
        return tree;
    };
    
    /*****************************************************************************/
    /* I need help with the block below                                          */
    /*****************************************************************************/
    
    bbcode._parse = function(tokens) {
        var tree = {};
    
        for (var i = 0; i < tokens.length; i++) {
            if (tokens[i][0] === 'BB_STRING') {
                if (tree['text']) {
                    tree['text'] += tokens[i][1];
                } else {
                    tree['text'] = tokens[i][1];
                }
            } else if (tokens[i][0] === 'TO_DEL') {
                if (this._next(tokens, i) === 'BB_TAG') {
                    tree['tag'] = {};
                } else {
                    if (tree['text']) {
                        tree['text'] += tokens[i][1];
                    } else {
                        tree['text'] = tokens[i][1];
                    }
                }
            }
        }
    
        return tree;
    };
    
    /*****************************************************************************/
    

0 个答案:

没有答案