Hello World
Spiga

使用Narcissus解析JavaScript代码

2010-11-17 18:08 by 老赵, 4210 visits

最近在做一个有关JavaScript的实验,需要在客户端将JavaScript代码解析为一棵语法树。换句话说,就是一个用JavaScript实现的JavaScript解析器。这方面的选择有很多,常见的yacc、lex或是bison等等都有JavaScript的版本,使用ANTLR也可以将生成目标设为JavaScript。不过我不想在这方面耗费太多时间,自然想找个现成的工具,于是最终我将目标放在了Narcissus上。

Narcissus是一个JavaScript引擎,完全使用JavaScript编写,不过利用了SpiderMonkey的一些扩展,因此无法直接在仅仅实现了ECMAScript 3的引擎上执行(例如各浏览器)。从它的Wikipedia页面上得知,Narcissus由SpiderMonkey的作者Brendan Eich开发,名称来源于希腊神话中爱上自己倒影的人物,和“JavaScript编写的JavaScript引擎”的概念契合(真是太有文化了)。此外,Firefox有一个Zaphod插件,可以将浏览器的JavaScript引擎替换为Narcissus。

Narcissus是个十分简单的JavaScript引擎,可以用来做一些JavaScript语言新特性的探索工作。它几乎不做任何优化,因此不能与其他引擎比拼性能,但很显然它包含完整的JavaScript分析器,正好为我所用。首先,从Github上下载它的源代码,其中包括六个文件,而我只需要其中的三个:

  • jsdef.js:包含了Narcissus.definitions组件,各种Token定义等等。
  • jslex.js:包含了Narcissus.lexer组件,分词器。
  • jsparse.js:包含了Narcissus.parser,分析器。

之前提到过,Narcissus不能直接在浏览器上运行,因此我们还必须对它进行修改。首先,是在jsdefs.js文件中,我们需要将开头的一段利用Object.create方法的定义:

(function() {
    var builderTypes = Object.create(null, {
        ...
    });

    ...

    var narcissus = {
        ...
    };

    Narcissus = narcissus;
})();

替换成直接的声明:

var Narcissus = { };

其次还是在jsdefs.js中,我们要改变defineProperty和defineGetter的实现:

function defineGetter(obj, prop, fn, dontDelete, dontEnum) {
    Object.defineProperty(...);
}

function defineProperty(obj, prop, val, dontDelete, readOnly, dontEnum) {
    Object.defineProperty(...);
}

Object的defineProperty和defineGetter方法也是SpiderMonkey的扩展,我们要把它们修改为“直接赋值”的版本:

function defineGetter(obj, prop, fn, dontDelete, dontEnum) {
    obj[prop] = fn;
}

function defineProperty(obj, prop, val, dontDelete, readOnly, dontEnum) {
    obj[prop] = val;
}

当然,这么做与之前的效果并不等价,不过并不影响代码的使用。您可以从jsparse.js文件中找到使用了这两个方法的地方。

现在您就可以在一个页面里引入这三个JavaScript文件,并Narcissus.parser分析JavaScript代码了。Narcissus几乎没有说明文档,不过从代码中找到它的使用方法并不困难。例如:

function parseSelf() { 
    var builder = new Narcissus.parser.DefaultBuilder();
    return Narcissus.parser.parse(builder, parseSelf.toString(), "temp", 1);
}

document.write("<pre>" + parseSelf() + "</pre>");

在JavaScript中调用一个函数的toString方法会得到它的代码,于是执行上面这段代码会打印出parseSelf方法的语法树。剩下的我就不多说了,爱玩的同学自然知道可以做什么。

补:经过实验,Narcissus还是过于依赖SpiderMonkey引擎的特性,如果要在IE上运行还是需要修改更多内容。此外,最新的Narcissus源码还有一些bug,如果您想要使用合适的实现,不妨参考NarrativeJS中旧版的Narcissus代码。

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

5 条回复

  1. zc
    114.246.179.*
    链接

    zc 2010-11-17 20:04:19

    有点意思。。

  2. E.T
    113.108.133.*
    链接

    E.T 2010-11-17 21:31:39

    是不是意味着可以捣鼓个 (lisp) macro 出来啊 O_O

  3. 老赵
    admin
    链接

    老赵 2010-11-17 21:44:34

    @E.T

    关键还是JavaScript函数可以直接toString出来内容,所以真的是nb大发了……

  4. 链接

    Ivony 2010-11-21 22:50:05

    看来你真的打算玩黑魔法了。

  5. 老赵
    admin
    链接

    老赵 2010-11-22 00:05:19

    @Ivony

    今天写了一下午代码,已经搞出原型来了,效果还不错。

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我