在(3)中说到对子表达式求值的步骤如下:
1、 输入子表达式字符串,如"div:last-child","ul","li.class",
2、 对字符串预处理,除去两端空格,除去二元运算符两边多余的空格,
3、 获得第一项非二元组表达式组,上述为:"div:last-child"
4、 将非二元组分解为表达式,上述为"div",":last-child"
5、 求出符合非二元组的元素集合ret
6、将剩余的字符串构造为二元组集合binExpr,即为二元运算符加非二元表达式组,也就是符合语法规则 binExpr = expr1 + op + expr2,因为expr1前面我们已经求过值了,所以现在就是根据op和expr2来过滤掉不符合条件的元素,而expr2可以重复利用第4步骤进行分解存储。
6、遍历二元组集合binExpr,根据运算符和expr2筛选符合条件的元素
7、返回ret
并且已经给出了XX.selector的实现,其中步骤1,2需要的函数都已实现,现在需要实现步骤3,4所需要的关键函数XX.getExprGroup,XX.getUnaryExprObj,同时考虑到扩展性,额外定义了三个对象表XX.UNAOP,XX.BINOP,XX.PSEOP,XX.ATTOP分别用来保存各个运算符的功能函数,在表达式对象中filter以及calculate方法都会委托到这些表中,大体框架如下:
/*一元运算符功能函数表*/
XX.UNAOP = {
//ID
'#':{
calculate: function(){},
filter: function(){}
},
//Class
'.': {
calculate: function(){},
filter: function(){}
},
//Tag
'':{
calculate: function(){},
filter: function(){}
}
};
/*二元运算符功能函数表,二元运算符没有calculate,无法单独求出值*/
XX.BINOP = {
'>' : {
filter : function() {}
},
'+' : {
filter : function() {}
},
' ' : {
filter : function() {}
}
};
//属性运算功能函数表
XX.ATTOP = {
'':{
filter:function(){},
calculate:function(){}
},
'^=':{
filter:function(){},
calculate:function(){}
}
//待添加
};
//伪类运算功能函数表
XX.PSEOP = {
'last-child':{
filter:function(){},
calculate:function(){}
},
'first-child':{
filter:function(){},
calculate:function(){}
}
//待添加
};
下面给出 XX.getExprGroup的实现,利用到了上面的一些表来进行操作符判断:主要思路已经在注释中:
/*顺序获得一组一元表达式字符串
*参数selector:输入的字符串表达式
* start: 开始位置
* 返回值:表达式组字符串
* */
XX.getExprGroup = function(selector, start){
//str存储一元表达式字符串
var str = '', len, curChar;
start = start || 0;
//逐字符扫描
for( len = selector.length; start < len; ++start) {
curChar = selector.charAt(start);
//若遇到二元运算符,则一组一元表达式已经获得,直接返回,如"#id.class div [type]"
if(XX.BINOP[curChar]) {//二元运算
return str;
} else if(curChar === '[') {// 属性运算
//则需要截取整个"[type]"出来拼接
var end = selector.indexOf(']', start);
str += selector.substring(start, end + 1);
start = end;
} else { //否则直接拼接
str += curChar;
}
}
return str;
};
对于XX.getUnaryExprObj函数,这里就直接返回构造后的表达式对象。这里对表达式组进行了逆向扫描,主要考虑到,在表达式组由两个以上的表达式组成时,最后一个表达式不可能是标签运算,这样先计算得出的元素集合更小,方便后续过滤。
/*
* 逆向扫描,获取一个表达式对象
* 参数selector:选择符字符串
* 参数curIndex:开始的搜索的索引位置
* 返回值:无表达式则为null,否则返回一个对象,{expr:表达式对象, index:运算符的位置}
* */
XX.getExprObj = function(selector, curIndex) {
var str = '',
curChar = selector.charAt(curIndex),
end,
expr = null;
for(; curIndex > -1; --curIndex) {
curChar = selector.charAt(curIndex);
if(']' === curChar) {//属性运算符
end = selector.lastIndexOf('[', curIndex);
expr = new XX.AttExpr(selector.substring(end+1, curIndex));
return {expr:expr, index: end};
}
if(XX.UNAOP[curChar]){ //一元运算符
return {expr: new XX.unaryExpr(curChar, str), index: curIndex};
}
if(':' === curChar ){ //伪类运算符
return {expr: new XX.PseExpr(str), index: curIndex};
}
//标识符拼接
str = curChar + str;
}
//边界情况,对应如:"div#id",扫描到div时,循环已经退出,这里需要判断是否第一个表达式为标签
if(str != ''){
return { expr : new XX.unaryExpr('', str), index : curIndex};
}
return null;
};