oz grep源码分析
今天在笔记本上折腾ubuntu18,真是很好玩。
配置低的本上,装ubuntu,真划算。原来4G跑win10总是慢得不行,现在跑linux,感觉还是很快的。
我在本子上读regex.c这个文件,一个地方卡住了。
for (p = pat; *p; p++) {
lp = mp;
switch(*p) {
case '.': /* match any char.. */
store(ANY);
break;
case '^': /* match beginning.. */
if (p == pat)
store(BOL);
else {
store(CHR);
store(*p);
}
break;
case '$': /* match endofline.. */
if (!*(p+1))
store(EOL);
else {
store(CHR);
store(*p);
}
break;
case '[': /* match char class..*/
store(CCL);
if (*++p == '^') {
mask = 0377;
//0377==11 111 111,与它异或,按位取反。:w
p++;
}
else
mask = 0;
if (*p == '-') /* real dash */
chset(*p++);
if (*p == ']') /* real brac */
chset(*p++);
while (*p && *p != ']') {
if (*p == '-' && *(p+1) && *(p+1) != ']') {
p++;
c1 = *(p-2) + 1;
c2 = *p++;
while (c1 <= c2)
chset((CHAR)c1++);
}
#ifdef EXTEND
else if (*p == '\\' && *(p+1)) {
p++;
chset(*p++);
}
#endif
else
chset(*p++);
}
if (!*p)
return badpat("Missing ]");
for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
store(mask ^ bittab[n]);
break;
这是在re_comp中进行编译,把正则表达式翻译成中间码。此处,把'[1-9]'翻译成
'123456789',但你看呀,
如果p指向[,此时,先存储“CCL”,接着,判断
*++p,此时,p指向1,所以下面两个判断不用做,因为不是-,也不是],那就来到了
while (*p && *p != ']') {
if (*p == '-' && *(p+1) && *(p+1) != ']') {
p++;
c1 = *(p-2) + 1;
c2 = *p++;
while (c1 <= c2)
chset((CHAR)c1++);
}
#ifdef EXTEND
else if (*p == '\\' && *(p+1)) {
p++;
chset(*p++);
}
#endif
else
chset(*p++);
}
此时,p指向'[1-9]'中的1,所以执行循环中第三分支
else
chset(*p++);
把1保存进去。接着p++,此时p指向-
那好,接着循环,因为*p=='-'所以执行第一分支
if (*p == '-' && *(p+1) && *(p+1) != ']') {
p++; //p指向'-',++后指向9
c1 = *(p-2) + 1;//c1指向1
c2 = *p++; //p指向9,c2也指向9,但p++后指向]
while (c1 <= c2)
chset((CHAR)c1++);
}
此分支,完成的是就是把23456789这几个字符保存好。
此时,用话不好说,请看代码中注释。这个分支做完后,p指向],因此,要跳出循环。
做下一步处理
for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
store(mask ^ bittab[n]);
也就是把16字节的位置保存到中间代码数组中去。
此时,p指向],那程序如何处理]的呢?
程序只有如下分支
case .
case $
case [
case *
case +
....
如果不是这些字符,它是:
default : /* an ordinary char */
store(CHR);
store(*p);
break;
可是,中间代码编译结果中根据没有存储]呀,况且存储了也没意义。你看:
yang@DESKTOP-V9HS3B6:~/grep$ echo "12345" | ./ogrep '[1-9]+'
pattern: [1-9]+
nfacode:
CCL [123456789]
CLOSURE CCL [123456789]
看到没,正则表达式翻译的结果中根据没有】
我想呀想,不停地翻代码。总是不得劲。
后来,忽然想,不对,这里有个break,就是
case [
把[1-9]变成ccl 123456789保存到中间码,此时p指向],
break
结束本次循环后,到哪了?
for (p = pat; *p; p++) {//此处有p++,哈哈,我明白了。原来在这里!!!
lp = mp;
switch(*p) {
case '.':
看到我的注释没?p本来指向],这是没错的,但break后,p++,就跳过了]
通过读程序,发现自己的程序功底还是有欠缺。
同样的错误犯了两次。前一次,看代码
for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
store(mask ^ bittab[n]);
这里,是把16字节的位图存储到中间代码结果中去。当时我曾想,你把bittab[]保存进去了,你下次使用是,要初始化吧,那在哪儿初始化呢???
我找呀找!
你肯定要初始化的,不然如果这样写正则表达式'[a-z'] hello [1-9]'那如何搞?
前面使用的位置如果不初始化,后面且不是变成了[a-z1-9]。当时是用kindle读代码,想呀想,后来明白了。
for (n = 0; n < BITBLK; bittab[n++] = (char) 0)//此处有bittab[n++] = (char)
store(mask ^ bittab[n]);
作者在for的最后做了。
代码要经常读,太好了。