Emacs 內建C語言函數實現機制

(寫的不好,歡迎善意拍磚)

轉載請註明出處:http://blog.csdn.net/fengningning/article/details/8214450

Emacs的歷史悠久和強大自不必多說,它內建了Elisp(Lisp的一種方言)的解釋引擎。它對很多後來的語言如Ruby(參見Ruby之父的How Emacs changed my life)產生了重大的影響。諸多計算機界的大拿對它青睞有加,如Donald Ervin Knuth。Coders at Work一書中有多處提到了Emacs。


Emacs是一個偉大的軟件! 


我在這裏進行源碼剖析的是GNU Emacs,而不是Emacs的其他實現(如XEmacs,它們一樣很強悍)。GNU Emacs的體系架構初探,可以參見 《程序員雜誌》 2003.9.1中的'GNU Emacs體系架構評論'一文,洪峯老師的評論非常犀利,見解獨到。


與硬件直接作用的GNU Emacs模塊(如顯示模塊)採用C語言編寫,而絕大多數文本編輯模塊則統統是利用Lisp語言來編寫,據統計,Lisp佔的代碼量有75%之多(不是太準確的數字)。從Lisp中可以調用C語言編寫的內建函數,我覺得所有C語言內建函數中最神祕的當屬'eval',不管認同與否,因爲eval幾乎就是Elisp的解釋引擎(解釋引擎先進行read,再進行eval)。因此,首先窺探一下eval函數,應該是意義重大的。


時間有限,我只能先提及eval的冰山一角,它的具體邏輯還得慢慢分析。不管怎麼樣,先將那些神祕的宏展開,露出它的本色,再品味一下涉及到的數據結構,也是一件很爽快的事。


  (注:其實我應該先說一下Lisp_Object的,但~~先來eval,我喜歡這個函數!)


// File: emacs-24.2/src/eval.c

DEFUN ("eval", Feval, Seval, 1, 2, 0,
       doc: /* Evaluate FORM and return its value.
If LEXICAL is t, evaluate using lexical scoping.  */)
  (Lisp_Object form, Lisp_Object lexical)
{
  int count = SPECPDL_INDEX ();
  specbind (Qinternal_interpreter_environment,
	    NILP (lexical) ? Qnil : Fcons (Qt, Qnil));
  return unbind_to (count, eval_sub (form));
}

// 先來看一下DEFUN的定義
#define DEFUN(lname, fnname, sname, minargs, maxargs, intspec, doc)	\
   Lisp_Object fnname DEFUN_ARGS_ ## maxargs ;				\
   static DECL_ALIGN (struct Lisp_Subr, sname) =			\
     { PVEC_SUBR,							\
      { .a ## maxargs = fnname },					\
       minargs, maxargs, lname, intspec, 0};				\
   Lisp_Object fnname
#endif

// 再看一下DEFUN是怎麼被調用的
DEFUN ("eval", Feval, Seval, 1, 2, 0,
       doc: /* Evaluate FORM and return its value.
If LEXICAL is t, evaluate using lexical scoping.  */)

// 展開之後得到
Lisp_Object Feval DEFUN_ARGS_2 ;
static DECL_ALIGN (struct Lisp_Subr, Seval) =
{
    PVEC_SUBR,
    { .a2 = Feval},
    1, 2, "eval", 0, 0
};


Lisp_Object Feval 

// 後面緊跟着函數體,由此可見這個宏雖然做了很多其他工作,但最基本的,它定義了 fnname爲名稱的函數
// 展開後的代碼的第一行,究竟做了什麼?
Lisp_Object Feval DEFUN_ARGS_2 ;

// 還是先看一下 DEFUN_ARGS_2 這個宏
#define DEFUN_ARGS_2	(Lisp_Object, Lisp_Object)

// 繼續展開
Lisp_Object Feval (Lisp_Object, Lisp_Object) ;

// 終於明白了,原來這行只是做了函數的聲明而已


// 下面這個語句塊似乎有點複雜,不急,我們先把不太直觀的DECL_ALIGN給解決掉

static DECL_ALIGN (struct Lisp_Subr, Seval) =
{
    PVEC_SUBR,
    { .a2 = Feval},
    1, 2, "eval", 0, 0
};

#   define DECL_ALIGN(type, var) \
     type __attribute__ ((__aligned__ (1 << GCTYPEBITS))) var


// 我想,對於宏的展開我們已經輕車熟路了,只需要懂得語法,小心行事即可
static struct Lisp_Subr __attribute__ ((__aligned__ (1 << GCTYPEBITS))) Seval
{
    PVEC_SUBR,
    { .a2 = Feval},
    1, 2, "eval", 0, 0
};


// 原來,DECL_ALIGN真的只是做了字節對齊的操作,雖然字節對齊在C編程中非常重要,如果不注意會惹出許多亂子,但我們現在太關注細節只會讓自己迷失方向。忽略它!
static struct Lisp_Subr Seval
{
    PVEC_SUBR,
    { .a2 = Feval},
    1, 2, "eval", 0, 0
};


// 現在清爽多了,這個語句的作用是定義一個變量,這個變量是struct Lisp_Subr類型
// PVEC_SUBR 是一個枚舉變量,其值爲 0x4000,類似的還有PVEC_CHAR_TABLE,暫且把它當做是一個flag,丟在一邊先不管。


// 還有一個叫做 Lisp_Subr的結構體沒弄清楚
/* This structure describes a built-in function.
   It is generated by the DEFUN macro only.
   defsubr makes it into a Lisp object.


   This type is treated in most respects as a pseudovector,
   but since we never dynamically allocate or free them,
   we don't need a struct vectorlike_header and its 'next' field.  */


struct Lisp_Subr
  {
    EMACS_INT size;
    union {
      Lisp_Object (*a0) (void);
      Lisp_Object (*a1) (Lisp_Object);
      Lisp_Object (*a2) (Lisp_Object, Lisp_Object);
      Lisp_Object (*a3) (Lisp_Object, Lisp_Object, Lisp_Object);
      Lisp_Object (*a4) (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object);
      Lisp_Object (*a5) (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object);
      Lisp_Object (*a6) (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object);
      Lisp_Object (*a7) (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object);
      Lisp_Object (*a8) (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object);
      Lisp_Object (*aUNEVALLED) (Lisp_Object args);
      Lisp_Object (*aMANY) (ptrdiff_t, Lisp_Object *);
    } function;
    short min_args, max_args;
    const char *symbol_name;
    const char *intspec;
    const char *doc;
  };
// 一堆的註釋,一堆放在聯合體裏面的函數指針,還有幾個貌似眼熟的字段。
// 從註釋中可以看出,這個結構體代表了一個Emacs中用C語言實現的內部函數,裏面的信息包含了Lisp調用時需要的信息(如果不是全部的話)
// 回過頭來看一下DEFUN的定義
#define DEFUN(lname, fnname, sname, minargs, maxargs, intspec, doc)

// 可以找到如下的對應關係
/*
  sname -> struct Lisp_Subr結構體變量名
  lname -> Lisp_Subr.symbol_name
  fnname -> Lisp_Subr.function
  minargs -> Lisp_Subr.min_args
  maxargs -> Lisp_Subr.max_args
  intspec -> Lisp_Subr.intspec
  doc -> Lisp_Subr.intspec
*/

// 看來各個參數都已經歸位了


// 最後,我們似乎反了一個嚴重的錯誤,沒有仔細閱讀 DEFUN這個宏的代碼註釋。其實Emacs的源代碼是有着非常好的註釋的。不過現在看還來得及:
/* Define a built-in function for calling from Lisp.
 `lname' should be the name to give the function in Lisp,
    as a null-terminated C string.
 `fnname' should be the name of the function in C.
    By convention, it starts with F.
 `sname' should be the name for the C constant structure
    that records information on this function for internal use.
    By convention, it should be the same as `fnname' but with S instead of F.
    It's too bad that C macros can't compute this from `fnname'.
 `minargs' should be a number, the minimum number of arguments allowed.
 `maxargs' should be a number, the maximum number of arguments allowed,
    or else MANY or UNEVALLED.
    MANY means pass a vector of evaluated arguments,
	 in the form of an integer number-of-arguments
	 followed by the address of a vector of Lisp_Objects
	 which contains the argument values.
    UNEVALLED means pass the list of unevaluated arguments
 `intspec' says how interactive arguments are to be fetched.
    If the string starts with a `(', `intspec' is evaluated and the resulting
    list is the list of arguments.
    If it's a string that doesn't start with `(', the value should follow
    the one of the doc string for `interactive'.
    A null string means call interactively with no arguments.
 `doc' is documentation for the user.  */


/* This version of DEFUN declares a function prototype with the right
   arguments, so we can catch errors with maxargs at compile-time.  */


未完待續

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章