为了支持
JSI包定义中的模式匹配(方便某些懒人)。我需要一个查找脚本全局变量的脚本。
一直没有勇气编写JS的完整语法解析程序,所以,只能走一些旁门左道。
/*
* JavaScript Integration Framework
* License LGPL(您可以在任何地方免费使用,但请不要吝啬您对框架本身的改进)
* http://www.xidea.org/project/jsi/
* @author jindw
* @version $Id: fn.js,v 1.5 2008/02/24 08:58:15 jindw Exp $
*/
/**
*
* @param <String> source 脚本源文件
* @return <Array> 改脚本中的定层申明变量(包括函数)
*/
function findGlobals(source){
source = replaceSpecialEntry(source.replace(/^\s*#.*/,''));
//简单的实现,还以为考虑的问题很多很多:
var varFlagMap = {};
var scopePattern = /\b(function\b[^\(]*)[^{]+\{|\{|\}|\[|\]/mg;//|{\s*(?:[\$\w\d]+\s*\:\s*(?:for|while|do)\b|""\:)
//找到办法不用判断了,省心了。。。。
//var objectPattern = /\{\s*(?:[\$\w\d]+|"")\:/mg
var varPattern = /\b(var|function|,)\b\s*([\w\$]+)\s*/mg;
//var lineParrern = /([\$\w]+|[^\$\w])\s*[\r\n]+\s*([\$\w]+|[^\$\w])/g
var buf = [];
var fnDepth = 0;
var arrayDepth = 0;
var begin = 0;
var match;
while(match = scopePattern.exec(source)){
switch(match[0] ){
//array
case '[':
if(!fnDepth){
if(!arrayDepth){
buf.push(source.substring(begin,match.index),'[]');
}
arrayDepth ++;
}
break;
case ']':
if(!fnDepth){
arrayDepth --;
if(!arrayDepth){
begin = match.index+1;
}
}
break;
//function
case '{':
if(!arrayDepth && fnDepth){//in function
fnDepth++;
}
break;
case '}':
if(!arrayDepth && fnDepth){//in function
fnDepth--;
if(fnDepth == 0){
begin = match.index+1;
}
}
break;
default://function..
if(!arrayDepth){
if(!fnDepth){
buf.push(source.substring(begin,match.index),match[1],'}');
}
fnDepth++;
}
break;
}
}
buf.push(source.substr(begin))
source=buf.join('');
source = source.replace(/([\w\$\]])\s*\([\w\$\d,]*\)/m,'$1()');
begin = 0;
while(match = varPattern.exec(source)){
switch(match[1]){
case 'var':
begin = match.index;
case 'function':
varFlagMap[match[2]] = 1;
default://,
var next = source.charAt(match.index + match[0].length);
if(next!=':'){
var temp = source.indexOf(';',begin);
if(temp>0 && temp<match.index){
continue;
}
try{
//不知道是不是还有什么问题
temp = source.substring(begin,match.index);
//if(/var|if|else/.test(temp)){continue;}
temp = temp.replace(/[\r\n]/g,' ');
new Function(temp+',a;')
}catch(e){
continue;
}
varFlagMap[match[2]] = 1;
}
}
}
var result = [];
for(match in varFlagMap){
result.push(match)
}
return result;
}
/**
* java 接口
* @param <String>source 脚本源码
* @return java.util.Collection 返回全局id集合
*/
function findGlobalsAsList(source){
var result = findGlobals(source)
var list = new java.util.ArrayList();
for (var i = 0; i < result.length; i++) {
list.add(result[i]);
}
return list;
}
var specialRegExp = new RegExp([
//muti-comment
'/\\*(?:[^\\*]|\\*[^/])*\\*/',
//single-comment
'//.*$',
//string
'"(?:\\\\(?:.|\\r|\\n|\\r\\n)|[^"\\n\\r])*"',
"'(?:\\\\(?:.|\\r|\\n|\\r\\n)|[^'\\n\\r])*'",
'/.*/'
].join('|'),'m');
function replaceSpecialEntry(source){
var head = '';
var tail = source;
var p1
outer:
while(p1 = specialRegExp.exec(tail)){
var p2 = p1.index + p1[0].length;
var p1 = p1.index;
if(tail.charAt(p1) == '/'){
switch(tail.charAt(p1 + 1)){
case '/':
case '*':
head += tail.substr(0,p1);
tail = tail.substr(p2+1);
continue outer;
}
try{//试探正则
new Function(head+tail.replace(specialRegExp,"/\\$&"));
//是正则
p2 = p1;
while((p2 = tail.indexOf('/',p2)+1)>p1){
//println([p1,p2]);//,tail.substring(p1,p2)
try{
var text = tail.substring(p1,p2);
if(/.*/.test(text)){//有效正则
new Function(text);
}
head += tail.substr(0,p1)+"/./";
tail = tail.substr(p2);
continue outer;
}catch(e){
//无效,继续探测
}
}
throw new Error("怎么可能??^_^");
}catch(e){
//只是一个除号:(
head += tail.substr(0,p1+1);
tail = tail.substr(p1+1);
continue outer;
}
}else{
head += tail.substr(0,p1)+'""';
tail = tail.substr(p2+1);
continue outer;
}
}
return head + tail;
}
在自己机器上测试了 213个脚本文件。与Rhino的解析结果对比。测试通过。
应该比较可靠了。
这个脚本好早就写过了,一直没有满意的结果,现在算比较满意了。