2013-09-06 更新
上一版本的分析器包含两个BUG,导致模板逻辑语法中出现地雷:
- 无法正确处理特殊情况下的字符串
"\""
或者 '\''
- 无法解析$号开头且后面为数字的变量,如
$0
以下是新的解析器实现,完善字符串与数字判断的正则,解决上述两个BUG:
// 静态分析模板变量
var KEYWORDS =
// 关键字
'break,case,catch,continue,debugger,default,delete,do,else,false'
+ ',finally,for,function,if,in,instanceof,new,null,return,switch,this'
+ ',throw,true,try,typeof,var,void,while,with'
// 保留字
+ ',abstract,boolean,byte,char,class,const,double,enum,export,extends'
+ ',final,float,goto,implements,import,int,interface,long,native'
+ ',package,private,protected,public,short,static,super,synchronized'
+ ',throws,transient,volatile'
// ECMA 5 - use strict
+ ',arguments,let,yield'
+ ',undefined';
var REMOVE_RE = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g;
var SPLIT_RE = /[^\w$]+/g;
var KEYWORDS_RE = new RegExp(["\\b" + KEYWORDS.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g');
var NUMBER_RE = /^\d[^,]*|,\d[^,]*/g;
var BOUNDARY_RE = /^,+|,+$/g;
var getVariable = function (code) {
code = code
.replace(REMOVE_RE, '')
.replace(SPLIT_RE, ',')
.replace(KEYWORDS_RE, '')
.replace(NUMBER_RE, '')
.replace(BOUNDARY_RE, '');
code = code ? code.split(/,+/) : [];
return code;
};
测试代码:
// test
var test = function () {
/*tang bin*/
// hello
var $1, $1$, a$0, a$$, a0;
var str = c$0 + 'fdsf\'hello';//word
var $ = 7;
var x = {};
x.toString();
var _me; _2, _;
var $test = _2 > 399 || _3 < -235;
return false;
}.toString();
console.log(getVariable(test))
算法采用过滤的思路实现:
- 删除注释、字符串、方法名,这一步是为了排除干扰
- 删除可组成变量的字符,只保留字母、数字、美元符号与下划线,并进行分组
- 删除分组中的 js 关键字与保留字成员
- 删除分组中的数字成员
经过四轮过滤后,剩下来的就是模板变量列表了,且不会有漏网之鱼,从而避免编译后的函数出现未定义的变量。当然,出于使用场景考虑,忽略了下面的两个问题:
- 没有排除对象字面量
- 没有排除正则表达式
关于第2点,有个现实的问题是是:要完美区分正则与除法在不使用语法分析的情况下是无法实现的,引入 ast 成本过大。
最后,默念一遍:没有完美的方案,只有最适合的方案。如有问题欢迎提出
question