javascript 解析 lisp 表达式

问题

项目中需要前端解析出 lisp 表达式中的内容后,渲染到页面上:

(or
  (==
    (latest "template:zebra_status" "#1")
    1
  )
  (==
    (latest "template:ospfd_status" "#1")
    1
  )
  (==
    (latest "template:keepalived_status" "#1")
    1
  )
)

解决

同事离职前实现了一版,思路是使用正则表达式捕获住对应的内容,缺点也是显而易见的:不够灵活。我参考了 (How to Write a (Lisp) Interpreter (in Python)) 中的实现,完成了一版 javascript 的实现。

函数 tokenize 用于将字符串预处理成统一的形式:

function tokenize(lisp_str) {
    // 表达式中的双引号也需要被初始化成()
    var index = 0,
    q = ['(', ')'],
    pre_token = lisp_str.replace(/\"/g, function() {
        var ret = q[index];
        index = 1 - index;
        return ret;
    });

    return pre_token
        .replace(/\( +/g, "(") // 删除 "(" 之后的空格
        .replace(/ +\)/g, ")") // 删除 ")" 之前的空格
        .replace(/ +/g, ' ')   // 多个连续空格合并成一个空格
        .replace(/\(/g, "( ")  // "(" 之后需要添加空格
        .replace(/\)/g, " )")  // ")" 之前需要添加空格
        .split(" ");           // 拆分成数组
}

函数 read_from_tokens 负责将字符串解析成对应的数据:

function read_from_tokens(tokens) {
    if (tokens.length == 0) {
        throw "unexpected EOF while reading";
    }
    token = tokens.shift();
    if (token == "(") {
        var L = [];
        while(tokens[0] != ')') {
            L.push(read_from_tokens(tokens));
        }
        tokens.shift();
        return L;
    }
    else if (token == ")") {
        throw "unexpected )";
    }
    else {
        return atom(token);
    }
}

工具函数 atom 负责处理变量类型相关的工作:

function atom(token) {
    if (Number(token)===token && token%1===0) {
        return parseInt(token);
    }
    else if (token===Number(token) && token%1!==0) {
        return parseFloat(token);
    }
    else {
        return token;
    }
}

看下效果,还不错:

Leave a Reply

Your email address will not be published. Required fields are marked *

*