Emacs字符串搜索
EMACSw为搜索暴露了很多接口
re-search-forwardre-search-backwardsearch-forward...
本文来探究Emacs的这些接口的实现与算法. 它们定义于search.c
Emacs内部会维护一个全局的match data 底层为current_thread->m_search_regs 保存最近一次成功搜索的结果.
我们主要研究这两个函数
re-search-forwardsearch-forward
DEFUN ("re-search-forward", Fre_search_forward, Sre_search_forward, 1, 4,
"sRE search: ",
doc: /* Search forward from point for regular expression REGEXP.
Set point to the end of the occurrence found, and return point.
The optional second argument BOUND is a buffer position that bounds
the search. The match found must not end after that position. A
value of nil means search to the end of the accessible portion of
the buffer.
The optional third argument NOERROR indicates how errors are handled
when the search fails: if it is nil or omitted, emit an error; if
it is t, simply return nil and do nothing; if it is neither nil nor
t, move to the limit of search and return nil.
The optional fourth argument COUNT is a number that indicates the
search direction and the number of occurrences to search for. If it
is positive, search forward for COUNT successive occurrences; if it
is negative, search backward, instead of forward, for -COUNT
occurrences. A value of nil means the same as 1.
With COUNT positive/negative, the match found is the COUNTth/-COUNTth
one in the buffer located entirely after/before the origin of the
search.
Search case-sensitivity is determined by the value of the variable
`case-fold-search', which see.
See also the functions `match-beginning', `match-end', `match-string',
and `replace-match'. */)
(Lisp_Object regexp, Lisp_Object bound, Lisp_Object noerror, Lisp_Object count)
{
return search_command (regexp, bound, noerror, count, 1, true, false);
}
DEFUN ("search-forward", Fsearch_forward, Ssearch_forward, 1, 4, "MSearch: ",
doc: /* Search forward from point for STRING.
Set point to the end of the occurrence found, and return point.
The optional second argument BOUND is a buffer position that bounds
the search. The match found must not end after that position. A
value of nil means search to the end of the accessible portion of
the buffer.
The optional third argument NOERROR indicates how errors are handled
when the search fails: if it is nil or omitted, emit an error; if
it is t, simply return nil and do nothing; if it is neither nil nor
t, move to the limit of search and return nil.
The optional fourth argument COUNT is a number that indicates the
search direction and the number of occurrences to search for. If it
is positive, search forward for COUNT successive occurrences; if it
is negative, search backward, instead of forward, for -COUNT
occurrences. A value of nil means the same as 1.
With COUNT positive/negative, the match found is the COUNTth/-COUNTth
one in the buffer located entirely after/before the origin of the
search.
Search case-sensitivity is determined by the value of the variable
`case-fold-search', which see.
See also the functions `match-beginning', `match-end', `match-string',
and `replace-match'. */)
(Lisp_Object string, Lisp_Object bound, Lisp_Object noerror, Lisp_Object count)
{
return search_command (string, bound, noerror, count, 1, false, false);
}
我们发现它们都调用的search_command函数.
search_command
值得注意的是 在目前emacs30版本 这个函数仍然是包含1994当年Richard Stallman和92年Jim的提交
static Lisp_Object
search_command (Lisp_Object string, Lisp_Object bound, Lisp_Object noerror,
Lisp_Object count, int direction, bool RE, bool posix)
{
// 最终搜索结果 即point跳转位置
EMACS_INT np;
EMACS_INT lim;
ptrdiff_t lim_byte;
EMACS_INT n = direction;
if (!NILP (count))
{
CHECK_FIXNUM (count);
n *= XFIXNUM (count);
}
CHECK_STRING (string);
if (NILP (bound))
{
// 正向搜索
if (n > 0)
lim = ZV, lim_byte = ZV_BYTE;
// 反向搜索
else
lim = BEGV, lim_byte = BEGV_BYTE;
}
else
{
lim = fix_position (bound);
// 正向搜索必须 point <= bound, 反向搜索必须bound <= point
if (n > 0 ? lim < PT : lim > PT)
error ("Invalid search bound (wrong side of point)");
if (lim > ZV)
lim = ZV, lim_byte = ZV_BYTE;
else if (lim < BEGV)
lim = BEGV, lim_byte = BEGV_BYTE;
else
lim_byte = CHAR_TO_BYTE (lim);
}
/* This is so set_image_of_range_1 in regex-emacs.c can find the EQV
table. */
// 准备等价字符表 因为emacs默认情况下 abc也会匹配ABC aBC ..
set_char_table_extras (BVAR (current_buffer, case_canon_table), 2,
BVAR (current_buffer, case_eqv_table));
np = search_buffer (string, PT, PT_BYTE, lim, lim_byte, n, RE,
(!NILP (Vcase_fold_search)
? BVAR (current_buffer, case_canon_table)
: Qnil),
(!NILP (Vcase_fold_search)
? BVAR (current_buffer, case_eqv_table)
: Qnil),
posix);
if (np <= 0)
{
if (NILP (noerror))
xsignal1 (Qsearch_failed, string);
if (!EQ (noerror, Qt))
{
eassert (BEGV <= lim && lim <= ZV);
SET_PT_BOTH (lim, lim_byte);
return Qnil;
#if 0 /* This would be clean, but maybe programs depend on
a value of nil here. */
np = lim;
#endif
}
else
return Qnil;
}
eassert (BEGV <= np && np <= ZV);
SET_PT (np);
return make_fixnum (np);
}