使用Narcissus解析JavaScript代码
2010-11-17 18:08 by 老赵, 4272 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代码。
有点意思。。