-
Notifications
You must be signed in to change notification settings - Fork 0
yet another emacs lisp compiler / interpreter written in javascript / nodejs
License
jawatech/elisp4js
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
#+AUTHOR: Sigmund Tzeng
* synopsis
#+BEGIN_SRC sh
node js/temacs-24.5.js js/loadup_1.el # first list; working!!
node js/temacs-24.5.js js/loadup.el # benchmark; shall work finally
#+END_SRC
以上指令將載入 emacs 預載的 loadup.el 並進行 sanity check ,驗證 temacs 的完成度。
接下來的工作,將分為兩個方向進行。 從 mal 方找尋 emacs 方的對應元素,以及從 emacs 的執行期行為找尋對應的程式以決定實作的優先順序。
* 從 emacs 的執行期行為找尋對應的程式碼
程式執行開頭頭有顯示一個 splash 畫面,我們將從此畫面的元素著手,得到初始載入順序相關資訊。
startup.el 是第一個被載入執行的 lisp 檔案。 經分析後發現此一行為由 Makefile.in 所指定。
** building
*** bare emacs: Emacs Lisp interpreter and I/O routines
[[bookmark:temacs][Bookmark: temacs]]
在建置 emacs 的過程中,第一步為產生此一空殼 temacs
*** dumped (pure) Emacs: set up the normal Emacs editing environment
[[bookmark:dumped%20Emacs][Bookmark: dumped Emacs]]
c.f. 末傾印,只進行 "拔靴法(Bootstrap)"
[[bookmark:bootstrap-emacs][Bookmark: bootstrap-emacs]]
在建置 emacs 的過程中,第二步為產生此一 "dumped Emacs" 的預載映象檔;其 --load 參數指定了 loadup.el 這個預載檔案;其中 loadup.el 會再指定預載檔案
**** load "startup"
[[bookmark:load%20"startup"][Bookmark: load "startup"]]
在此檔末端並指定執行 startup.el 中的 top-level 函數,也就是 normal-top-level。
startup.el 有特定的字串,似乎與視窗介面的建立有關。
其它載入檔案如下:
(load "emacs-lisp/byte-run")
(load "emacs-lisp/backquote")
(load "subr")
(load "version")
(load "widget")
(load "custom")
(load "emacs-lisp/map-ynp")
(load "international/mule")
(load "international/mule-conf")
(load "env")
(load "format")
(load "bindings")
(load "cus-start")
(load "window") ; Needed here for `replace-buffer-in-windows'.
(load "files")
(load "emacs-lisp/macroexp")
(load "emacs-lisp/pcase"))
(load "emacs-lisp/macroexp"))
(load "cus-face")
(load "faces") ; after here, `defface' may be used.
(load "button")
(load "startup")
(load "loaddefs.el")
(file-error (load "ldefs-boot.el")))
(load "emacs-lisp/nadvice")
(load "minibuffer")
(load "abbrev") ;lisp-mode.el and simple.el use define-abbrev-table.
(load "simple")
(load "help")
(load "jka-cmpr-hook")
(load "epa-hook")
(load "international/mule-cmds")
(load "case-table")
(load "international/charprop.el" t)
(load "international/characters")
(load "composite")
(load "language/chinese")
(load "language/cyrillic")
(load "language/indian")
(load "language/sinhala")
(load "language/english")
(load "language/ethiopic")
(load "language/european")
(load "language/czech")
(load "language/slovak")
(load "language/romanian")
(load "language/greek")
(load "language/hebrew")
(load "language/japanese")
(load "language/korean")
(load "language/lao")
(load "language/tai-viet")
(load "language/thai")
(load "language/tibetan")
(load "language/vietnamese")
(load "language/misc-lang")
(load "language/utf-8-lang")
(load "language/georgian")
(load "language/khmer")
(load "language/burmese")
(load "language/cham")
(load "indent")
(load "frame")
(load "term/tty-colors")
(load "font-core")
(load "facemenu")
(load "emacs-lisp/syntax")
(load "font-lock")
(load "jit-lock")
(load "mouse")
(load "scroll-bar"))
(load "select")))
(load "emacs-lisp/timer")
(load "isearch")
(load "rfn-eshadow")
(load "menu-bar")
(load "emacs-lisp/lisp")
(load "textmodes/page")
(load "register")
(load "textmodes/paragraphs")
(load "progmodes/prog-mode")
(load "emacs-lisp/lisp-mode")
(load "textmodes/text-mode")
(load "textmodes/fill")
(load "newcomment")
(load "replace")
(load "emacs-lisp/tabulated-list")
(load "buff-menu")
(load "fringe")
(load "emacs-lisp/regexp-opt")
(load "image")
(load "international/fontset")
(load "dnd")
(load "tool-bar")))
(load "dynamic-setting"))
(load "x-dnd")
(load "term/common-win")
(load "term/x-win")))
(load "term/common-win")
(load "w32-vars")
(load "term/w32-win")
(load "disp-table")
(load "w32-common-fns")
(load "w32-fns")
(load "ls-lisp")
(load "dos-w32"))))
(load "dos-w32")
(load "dos-fns")
(load "dos-vars")
(load "term/internal")
(load "term/pc-win")
(load "ls-lisp")
(load "disp-table"))) ; needed to setup ibm-pc char set, see internal.el
(load "term/common-win")
(load "term/ns-win")))
(load "mwheel"))
(load "emacs-lisp/float-sup")
(load "vc/vc-hooks")
(load "vc/ediff-hook")
(load "uniquify")
(load "electric")
(if (not (eq system-type 'ms-dos)) (load "tooltip"))
(load "leim/leim-list.el" t)
(load "site-load" t)
(load "site-init" t)
*** daily emacs, finally
[[bookmark:Unless%20next%20switch%20is%20-nl,%20load%20"loadup.el"%20first%20thing.][Bookmark: Unless next switch is -nl, load "loadup.el" first thing.]]
Unless next switch is -nl, load "loadup.el" first thing.
** running
*** 初始化@main()@emacs.c: initXXX & syms_of_XXX (defsubr, DEFVAR & DEFSYM)
**** init_obarray (object array = Vobarray)
[[bookmark:init_obarray%20(void)][Bookmark: init_obarray (void)]]
obarray 就是 object array 的意思,object 的型態都是 Lisp_Object
initial_obarray 是最初始的物件陣列,包含了最開始的三個常量: Qt, Qnil, Qunbound
最陽春的符號就是 Qt ,其定義代表最低要求 intern_c_string & SET_SYMBOL_VAL
***** Qt: "t"
#+BEGIN_SRC C
Qt = intern_c_string ("t");
SET_SYMBOL_VAL (XSYMBOL (Qt), Qt);
XSYMBOL (Qt)->constant = 1;
#+END_SRC
***** Qnil: "nil"
#+BEGIN_SRC C
/* Set temporary dummy values to Qnil and Vpurify_flag to satisfy the
NILP (Vpurify_flag) check in intern_c_string. */
Qnil = make_number (-1); Vpurify_flag = make_number (1);
Qnil = intern_c_string ("nil");
SET_SYMBOL_VAL (XSYMBOL (Qnil), Qnil);
XSYMBOL (Qnil)->constant = 1;
XSYMBOL (Qnil)->declared_special = 1;
set_symbol_plist (Qnil, Qnil);
set_symbol_function (Qnil, Qnil);
XSYMBOL (Qnil)->declared_special = 1;
#+END_SRC
***** Qunbound: "unbound"
#+BEGIN_SRC C
Qunbound = Fmake_symbol (build_pure_c_string ("unbound"));
/* Fmake_symbol inits fields of new symbols with Qunbound and Qnil,
so those two need to be fixed manually. */
SET_SYMBOL_VAL (XSYMBOL (Qunbound), Qunbound);
set_symbol_function (Qunbound, Qnil);
set_symbol_plist (Qunbound, Qnil);
#+END_SRC
**** init_XXX: 以 Vcommand_line_args 為例
[[bookmark:init_cmdargs%20(argc,%20argv,%20skip_args,%20original_pwd);][Bookmark: init_cmdargs (argc, argv, skip_args, original_pwd);]]
[[bookmark:init_cmdargs%20(int%20argc,%20char%20**argv,%20int%20skip_args,%20char%20*original_pwd)][Bookmark: init_cmdargs (int argc, char **argv, int skip_args, char *original_pwd)]]
[[bookmark:Fcons%20(build_unibyte_string%20(argv%5Bi%5D),%20Vcommand_line_args);][Bookmark: Fcons (build_unibyte_string (argv{i}), Vcommand_line_args);]]
#+BEGIN_SRC C
Vcommand_line_args = Qnil;
for (i = argc - 1; i >= 0; i--)
{
if (i == 0 || i > skip_args)
/* For the moment, we keep arguments as is in unibyte strings.
They are decoded in the function command-line after we know
locale-coding-system. */
Vcommand_line_args
= Fcons (build_unibyte_string (argv[i]), Vcommand_line_args);
}
#+END_SRC
**** DEFVAR in syms_of_emacs ()
[[bookmark:syms_of_emacs%20();][Bookmark: syms_of_emacs ();]]
[[bookmark:void%20syms_of_emacs%20(void)][Bookmark: void syms_of_emacs (void)]]
[[bookmark:DEFVAR_LISP%20("command-line-args",%20Vcommand_line_args,][Bookmark: DEFVAR_LISP ("command-line-args", Vcommand_line_args,]]
#+BEGIN_SRC C
DEFVAR_LISP ("command-line-args", Vcommand_line_args,
doc: /* Args passed by shell to Emacs, as a list of strings.
Many arguments are deleted from the list as they are processed. */);
#+END_SRC
DEFVAR 的作用似乎是直接在 global 的 symbol table 中寫入一筆資料,作用比較單純
**** DEFVAR
***** DEFVAR_LISP @ lread.c
[[bookmark:DEFVAR_LISP%20("command-line-args",%20Vcommand_line_args,][Bookmark: DEFVAR_LISP ("command-line-args", Vcommand_line_args,]]
[[bookmark:#define%20DEFVAR_LISP(lname,%20vname,%20doc)][Bookmark: #define DEFVAR_LISP(lname, vname, doc)]]
#+BEGIN_SRC C
/* Macros we use to define forwarded Lisp variables.
These are used in the syms_of_FILENAME functions.
An ordinary (not in buffer_defaults, per-buffer, or per-keyboard)
lisp variable is actually a field in `struct emacs_globals'. The
field's name begins with "f_", which is a convention enforced by
these macros. Each such global has a corresponding #define in
globals.h; the plain name should be used in the code.
E.g., the global "cons_cells_consed" is declared as "int
f_cons_cells_consed" in globals.h, but there is a define:
#define cons_cells_consed globals.f_cons_cells_consed
All C code uses the `cons_cells_consed' name. This is all done
this way to support indirection for multi-threaded Emacs. */
#define DEFVAR_LISP(lname, vname, doc) \
do { \
static struct Lisp_Objfwd o_fwd; \
defvar_lisp (&o_fwd, lname, &globals.f_ ## vname); \
} while (false)
#+END_SRC
在執行最開始的 make 的過程中,所有 DEFVAR 的 vname 會被收集到 globals.h 當中, 成為 emacs_globasl 的一個欄位, 名稱會加上前綴 f_ , 然後又被巨集宣告為與 lname 同名的變數。
***** defvar_lisp @ lread.c
由以下程式碼可看出,這些變數會存到 staticvec 這個陣列中, 這個部分與 gargabe collection 有關, 在此不詳述。 與 defsubr 的作法類似, 此變數的名稱會被註冊為一符號,並以 obarray 存放,方便以變數名查詢符號。 符號再經 SET_SYMBOL_FWD 存放此一變數的指標。
[[bookmark:defvar_lisp%20(struct%20Lisp_Objfwd%20*o_fwd,][Bookmark: defvar_lisp (struct Lisp_Objfwd *o_fwd,]]
#+BEGIN_SRC C
/* Similar but define a variable whose value is the Lisp Object stored
at address. Two versions: with and without gc-marking of the C
variable. The nopro version is used when that variable will be
gc-marked for some other reason, since marking the same slot twice
can cause trouble with strings. */
void
defvar_lisp_nopro (struct Lisp_Objfwd *o_fwd,
const char *namestring, Lisp_Object *address)
{
Lisp_Object sym;
sym = intern_c_string (namestring);
o_fwd->type = Lisp_Fwd_Obj;
o_fwd->objvar = address;
XSYMBOL (sym)->declared_special = 1;
XSYMBOL (sym)->redirect = SYMBOL_FORWARDED;
SET_SYMBOL_FWD (XSYMBOL (sym), (union Lisp_Fwd *)o_fwd);
}
void
defvar_lisp (struct Lisp_Objfwd *o_fwd,
const char *namestring, Lisp_Object *address)
{
defvar_lisp_nopro (o_fwd, namestring, address);
staticpro (address);
}
#+END_SRC
***** staticpro: garbage collection
[[bookmark:staticpro%20(Lisp_Object%20*varaddress)][Bookmark: staticpro (Lisp_Object *varaddress)]]
#+BEGIN_SRC C
/***********************************************************************
Protection from GC
***********************************************************************/
/* Put an entry in staticvec, pointing at the variable with address
VARADDRESS. */
void
staticpro (Lisp_Object *varaddress)
{
if (staticidx >= NSTATICS)
fatal ("NSTATICS too small; try increasing and recompiling Emacs.");
staticvec[staticidx++] = varaddress;
}
#+END_SRC
***** SET_SYMBOL_FWD
[[bookmark:SET_SYMBOL_FWD%20(struct%20Lisp_Symbol%20*sym,%20union%20Lisp_Fwd%20*v)][Bookmark: SET_SYMBOL_FWD (struct Lisp_Symbol *sym, union Lisp_Fwd *v)]]
#+BEGIN_SRC C
INLINE void
SET_SYMBOL_FWD (struct Lisp_Symbol *sym, union Lisp_Fwd *v)
{
eassert (sym->redirect == SYMBOL_FORWARDED);
sym->val.fwd = v;
}
#+END_SRC
***** SYMBOL_FORWARDED 影響到的函數
****** specbind
特重要!!! lisp 中的這些函數也需要它: let, 以及因為實質上使用了 let 而以 C 改寫的函數
****** 其它
******* buffer_local_value_1
#+BEGIN_SRC C
/* Return the value of VARIABLE in BUFFER.
If VARIABLE does not have a buffer-local binding in BUFFER, the value
is the default binding of the variable. */
#+END_SRC
******* store_frame_param
******* mark_object
***** staticvec: 與 GC 有關,不重要
**** 兼論 expand-file-name
[[bookmark:EMACSLOADPATH=$(CURDIR)/../lisp][Bookmark: EMACSLOADPATH=$(CURDIR)/../lisp]]
#+BEGIN_SRC C
DEFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0,
#+END_SRC
**** 小結: 復刻 emacs 初始化行為
*** C / Lisp 兩用函數的宣告/定義及登錄: DEFUN (& defsubr) @syms_of_XXX()
以 if 這個函數來作為範例,它的宣告/定義是 DEFUN ("if", Fif, Sif, 2, UNEVALLED, 0 …
前面三個參數是最重要的, "if" 是它在 lisp 程式中的名稱, Fif 是在 C 程式碼中的名稱, 至於 Sif 可以看做是紀錄相關資訊的結構 (struct) 的名稱。
[[bookmark:#define%20DEFUN(lname,%20fnname,%20sname,%20minargs,%20maxargs,%20intspec,%20doc)][Bookmark: #define DEFUN(lname, fnname, sname, minargs, maxargs, intspec, doc)]]
#+BEGIN_SRC C
# define DEFUN_FUNCTION_INIT(fnname, maxargs) .a ## maxargs = fnname
#define DEFUN(lname, fnname, sname, minargs, maxargs, intspec, doc) \
Lisp_Object fnname DEFUN_ARGS_ ## maxargs ; \
static struct Lisp_Subr alignas (GCALIGNMENT) sname = \
{ { PVEC_SUBR << PSEUDOVECTOR_AREA_BITS }, \
{ DEFUN_FUNCTION_INIT (fnname, maxargs) }, \
minargs, maxargs, lname, intspec, 0}; \
Lisp_Object fnname
#+END_SRC
sname 此一結構的 an 成員 (0<=n<=9) 即為 FXXX 形式的函數(指標?)
至於定義後要如何被 lisp 的程式碼找到並呼叫呢? 這就需要以 defsubr 來登錄 Sif 結構了。
**** defsubr (&SXXX); 將名稱加到 Vobarray 中、登錄 SXXX 結構
[[bookmark:defsubr][Bookmark: defsubr]]
***** intern_c_string
此函數首先呼叫了 intern_c_string 以登錄函數名稱為符號,並加到 Vobarray 中
[[bookmark:intern_c_string%20(const%20char%20*str)][Bookmark: intern_c_string (const char *str)]]
[[bookmark:intern_c_string_1%20(const%20char%20*str,%20ptrdiff_t%20len)][Bookmark: intern_c_string_1 (const char *str, ptrdiff_t len)]]
#+BEGIN_SRC C
/* Intern the C string STR: return a symbol with that name,
interned in the current Vobarray. */
typedef EMACS_INT Lisp_Object;
#+END_SRC
***** XSETSUBR (tem, sname)
#+BEGIN_SRC C
#define XSETSUBR(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_SUBR))
#+END_SRC
***** set_symbol_function
然後呼叫 set_symbol_function 把符號和函數連結起來
[[bookmark:set_symbol_function%20(Lisp_Object%20sym,%20Lisp_Object%20function)][Bookmark: set_symbol_function (Lisp_Object sym, Lisp_Object function)]]
*** 為何沒有DEFCONST?DEFSYM的作用?
https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Symbols.html#Creating-Symbols
**** func, subst, alias, macro
**** var, const, custom
*** REPL
在 main 函數中,呼叫了 Frecursive-edit ,這是事件處理迴圈的入口
[[bookmark:Frecursive_edit%20();][Bookmark: Frecursive_edit ();]]
[[bookmark:DEFUN%20("recursive-edit",%20Frecursive_edit,%20Srecursive_edit,%200,%200,%20"",][DEFUN ("recursive-edit", Frecursive_edit, Srecursive_edit, 0, 0, "",]]
[[bookmark:command_loop%20(void)][Bookmark: command_loop (void)]]
**** load "loadup.el" first thing
在進入 Frecursive-edit 之前,已經塞了指令 load loadup.el 給最上層的環境:
[[bookmark:Vtop_level%20=%20list2%20(intern_c_string%20("load"),][Bookmark: Vtop_level = list2 (intern_c_string ("load"),]]
因此先討論整個 eval 的入口
[[bookmark:internal_catch%20(Qtop_level,%20top_level_1,%20Qnil);][Bookmark: internal_catch (Qtop_level, top_level_1, Qnil);]]
[[bookmark:top_level_1%20(Lisp_Object%20ignore)][Bookmark: top_level_1 (Lisp_Object ignore)]]
[[bookmark:internal_condition_case%20(top_level_2,%20Qerror,%20cmd_error);][Bookmark: internal_condition_case (top_level_2, Qerror, cmd_error);]]
[[bookmark:top_level_2%20(void)][Bookmark: top_level_2 (void)]]
[[bookmark:return%20Feval%20(Vtop_level,%20Qnil);][Bookmark: return Feval (Vtop_level, Qnil);]]
[[bookmark:DEFUN%20("eval",%20Feval,%20Seval,%201,%202,%200,][Bookmark: DEFUN ("eval", Feval, Seval, 1, 2, 0,]]
***** eval_sub
[[bookmark:return%20unbind_to%20(count,%20eval_sub%20(form));][Bookmark: return unbind_to (count, eval_sub (form));]]
[[bookmark:eval_sub%20(Lisp_Object%20form)][Bookmark: eval_sub (Lisp_Object form)]]
此處呼叫了 list 的函數以進行求值
#+BEGIN_SRC C
switch (i)
{
case 0:
val = (XSUBR (fun)->function.a0 ());
break;
case 1:
val = (XSUBR (fun)->function.a1 (argvals[0]));
break;
case 2:
val = (XSUBR (fun)->function.a2 (argvals[0], argvals[1]));
break;
case 3:
val = (XSUBR (fun)->function.a3
(argvals[0], argvals[1], argvals[2]));
break;
case 4:
val = (XSUBR (fun)->function.a4
(argvals[0], argvals[1], argvals[2], argvals[3]));
break;
case 5:
val = (XSUBR (fun)->function.a5
(argvals[0], argvals[1], argvals[2], argvals[3],
argvals[4]));
break;
case 6:
val = (XSUBR (fun)->function.a6
(argvals[0], argvals[1], argvals[2], argvals[3],
argvals[4], argvals[5]));
break;
case 7:
val = (XSUBR (fun)->function.a7
(argvals[0], argvals[1], argvals[2], argvals[3],
argvals[4], argvals[5], argvals[6]));
break;
case 8:
val = (XSUBR (fun)->function.a8
(argvals[0], argvals[1], argvals[2], argvals[3],
argvals[4], argvals[5], argvals[6], argvals[7]));
break;
default:
/* Someone has created a subr that takes more arguments than
is supported by this code. We need to either rewrite the
subr to use a different argument protocol, or add more
cases to this switch. */
emacs_abort ();
}
#+END_SRC
***** Fload(): Execute a file of Lisp code named FILE.
Fload() 雖然不是初級的語法元素,但是了解它的流程對測試有相當的幫助,因此以下說明它的執行流程
[[bookmark:DEFUN%20("load",%20Fload,%20Sload,%201,%205,%200,][Bookmark: DEFUN ("load", Fload, Sload, 1, 5, 0,]]
****** readevalloop
[[bookmark:readevalloop%20(Qget_file_char,%20stream,%20hist_file_name,][Bookmark: readevalloop (Qget_file_char, stream, hist_file_name,]]
[[bookmark:readevalloop%20(Lisp_Object%20readcharfun,][Bookmark: readevalloop (Lisp_Object readcharfun,]]
即然名為 ReadEval(Print)Loop = RE(P)L ,應該就會有個迴圈,進行讀取->求值。
參數 stream 為所讀取檔案的 handle , 若為 nil 時表示由 stdin 讀取。
迴圈中以 READCHAR 來預讀一個字元,以判斷接下來的語法元素,並調用對應的函式
除了 read_list 以外,其它函數都還滿 trivial 的,因此以下集中討論 read_list
******* READCHAR
[[bookmark:c%20=%20READCHAR;][Bookmark: c = READCHAR;]]
#define READCHAR readchar (readcharfun, NULL)
[[bookmark:static%20int%20readchar%20(Lisp_Object%20readcharfun,%20bool%20*multibyte)][Bookmark: static int readchar (Lisp_Object readcharfun, bool *multibyte)]]
/* When READCHARFUN is Qget_file_char, Qget_emacs_mule_file_char,
Qlambda, or a cons, we use this to keep an unread character because
a file stream can't handle multibyte-char unreading. The value -1
means that there's no unread character. */
******* read_list
[[bookmark:val%20=%20read_list%20(0,%20readcharfun);][Bookmark: val = read_list (0, readcharfun);]]
[[bookmark:static%20Lisp_Object%20read_list%20(bool%20flag,%20Lisp_Object%20readcharfun)][Bookmark: static Lisp_Object read_list (bool flag, Lisp_Object readcharfun)]]
******** read1
[[bookmark:elt%20=%20read1%20(readcharfun,%20&ch,%20first_in_list);][Bookmark: elt = read1 (readcharfun, &ch, first_in_list);]]
[[bookmark:static%20Lisp_Object%20read1%20(Lisp_Object%20readcharfun,%20int%20*pch,%20bool%20first_in_list)][Bookmark: static Lisp_Object read1 (Lisp_Object readcharfun, int *pch, bool first_in_list)]]
******** Fintern
[[bookmark:result%20=%20(uninterned_symbol%20?%20Fmake_symbol%20(name)%20:%20Fintern%20(name,%20Qnil));][Bookmark: result = (uninterned_symbol ? Fmake_symbol (name) : Fintern (name, Qnil));]]
[[bookmark:DEFUN%20("intern",%20Fintern,%20Sintern,%201,%202,%200,][Bookmark: DEFUN ("intern", Fintern, Sintern, 1, 2, 0,]]
/* Return the canonical symbol whose name is STRING.
If there is none, one is created by this function and returned.
A second optional argument specifies the obarray to use;
it defaults to the value of `Vobarray'. */
此一函數做的事基本上就跟 intern_c_string_1/intern_c_string 一樣,查詢此一字串是否已定義為符號,並傳回之
******* DEFUN ("macroexpand", Fmacroexpand, Smacroexpand, 1, 2, 0,
[[bookmark:DEFUN%20("macroexpand",%20Fmacroexpand,%20Smacroexpand,%201,%202,%200,][Bookmark: DEFUN ("macroexpand", Fmacroexpand, Smacroexpand, 1, 2, 0,]]
這個部分我滿懷疑是不是寫錯了,似乎永遠不會執行到??
******* eval_sub
[[bookmark:val%20=%20eval_sub%20(val);][Bookmark: val = eval_sub (val);]]
此後由於 read_list 已經進行了實質上的 scanning ,而 emacs lisp 實質上就是 AST ,所以不需要 parsing
因此可以直接進行求值。
***** emacs lisp key syntax elements in eval.c
[[bookmark:syms_of_eval%20(void)][Bookmark: syms_of_eval (void)]]
****** conditionals
defsubr (&Sor);
defsubr (&Sand);
defsubr (&Sif);
defsubr (&Scond);
****** blocks
defsubr (&Sprogn);
defsubr (&Sprog1);
defsubr (&Sprog2);
****** var
defsubr (&Ssetq);
defsubr (&Squote);
defsubr (&Sfunction);
defsubr (&Sdefault_toplevel_value);
defsubr (&Sset_default_toplevel_value);
defsubr (&Sdefvar);
defsubr (&Sdefvaralias);
defsubr (&Sdefconst);
defsubr (&Smake_var_non_special);
defsubr (&Slet);
defsubr (&SletX);
****** macro
defsubr (&Swhile);
defsubr (&Smacroexpand);
****** exception
defsubr (&Scatch);
defsubr (&Sthrow);
defsubr (&Sunwind_protect);
****** flow
defsubr (&Scondition_case);
defsubr (&Ssignal);
defsubr (&Scommandp);
defsubr (&Sautoload);
defsubr (&Sautoload_do_load);
defsubr (&Seval);
defsubr (&Sapply);
defsubr (&Sfuncall);
****** misc
defsubr (&Srun_hooks);
defsubr (&Srun_hook_with_args);
defsubr (&Srun_hook_with_args_until_success);
defsubr (&Srun_hook_with_args_until_failure);
defsubr (&Srun_hook_wrapped);
defsubr (&Sfetch_bytecode);
defsubr (&Sbacktrace_debug);
defsubr (&Sbacktrace);
defsubr (&Sbacktrace_frame);
defsubr (&Sbacktrace_eval);
defsubr (&Sbacktrace__locals);
defsubr (&Sspecial_variable_p);
defsubr (&Sfunctionp);
**** UI
以下部分屬於使用者互動,在 repl 的層級暫不討論
[[bookmark:command_loop_2%20(Lisp_Object%20ignore)][Bookmark: command_loop_2 (Lisp_Object ignore)]]
[[bookmark:internal_condition_case%20(command_loop_1,%20Qerror,%20cmd_error);][Bookmark: internal_condition_case (command_loop_1, Qerror, cmd_error);]]
* 從 mal 方找尋 emacs 方的對應元素
** repl_env => Vobarray
mal 在初始化的過程中,把 core.ns 的內容複製到了 repl_env 當中,因此 repl_env 才是真正意義上存放所有 symboe 的物件,core.ns 則類似於 initial_obarray
** equal
嘗試載入 loadup.el 的過程中,第一個遇到的問題就是找不到這個算子。這個問題倒是好解決,因為 mal 本身已經定義了 "=" 算子;不過這倒是提供我們再一次審視 scanning / parsing 的流程
*** emacs implementation
fns.c 中有如下定義:
[[bookmark:DEFUN%20("equal",%20Fequal,%20Sequal,%202,%202,%200,][Bookmark: DEFUN ("equal", Fequal, Sequal, 2, 2, 0,]]
[[bookmark:%20%20defsubr%20(&Sequal);][Bookmark: defsubr (&Sequal);]]
*** mal implementation
core.js 中有如下定義:
[[bookmark:var%20ns%20=%20{'type':%20types._obj_type,%20'=':%20types._equal_Q,][Bookmark: var ns = {'type': types._obj_type, '=': types._equal_Q,]]
因此符號的代表字串 '=' ,以及對應的函數 _equal_Q ,做為 hash 的一筆資料而存在於 ns 中
stepA_mal.js 中有如下定義:
[[bookmark:for%20(var%20n%20in%20core.ns)%20{%20repl_env.set(types._symbol(n),%20core.ns%5Bn%5D);%20}][Bookmark: for (var n in core.ns) { repl_env.set(types._symbol(n), core.ns{n}); }]]
ns 的資料再被逐筆的複製到 repl_env 中
*** 小結: DEFUN/defsubr -> core.ns
** let* / let
Note that there are plenty of ways to make (GLOBAL) bindings: ‘defconst’, ‘defun’, ‘defvar’, 'let', ‘flet’, ‘labels’, ‘prog’, etc.
根據 [[https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding][此文]] , emacs 只有 dynamic binding ,也就是說所有符號共用一個資料結構來存放 , 使得內層的函數/迴圈可以影響外層符號,不管這個符號背後是變數或函數。
根據這樣的邏輯,dynamic binding (ie emacs lisp) 在局部變數宣告時, 會先查找環境是否有同名的符號,若然則直接使用它,不在自己的 scope 再定義自己的符號; lexical binding (scheme?) 則無論如何都直接在自己的環境中, 為局部變數定義符號,並且優先查找。
let* 與 let 的不同之處在於,let 的 value 會在與 key 作 binding 前先求值, 因此當 local / global 有同名變數時, 會優先取 global 的值。
更精確的來說, let 會比 let* 多一個迴圈,先對所有的 value 進行 eval ; 後面的部分就都差不多
*** emacs:
**** let*
[[bookmark:DEFUN%20("let*",%20FletX,%20SletX,%201,%20UNEVALLED,%200,][Bookmark: DEFUN ("let*", FletX, SletX, 1, UNEVALLED, 0,]]
**** let
[[bookmark:DEFUN%20("let",%20Flet,%20Slet,%201,%20UNEVALLED,%200,][Bookmark: DEFUN ("let", Flet, Slet, 1, UNEVALLED, 0,]]
*** mal:
最外層的環境為 repl_env ,也就是 DEFUN / DEFVAR / DEFCONST 的作用域
*** 小結: DEFVAR => init_XXXX
共 738 個, 其初始化常發生在 init_XXXX 函數中,必需忠實呈現在 js 端
** load-path 的初始化
*** emacs
[[bookmark:init_lread%20(void)][Bookmark: init_lread (void)]]
Vpurify_flag 在 loadup.el 執行期間,其值為真; EMACSLOADPATH 則通常未指定,暫時忽略它
[[bookmark:load_path_default%20(void)][Bookmark: load_path_default (void)]]
[[bookmark:EMACSLOADPATH=$(CURDIR)/../lisp][Bookmark: EMACSLOADPATH=$(CURDIR)/../lisp]]
因此目前暫時將此變數初始化為 ../lisp
*** mal
** or
這部分 mal 有實作,而且是以 self-hosting 的方式,省下寫 js 的工夫
*** mal implementation
[[bookmark:case%20"or":][Bookmark: case "or":]]
*** emacs implementation
[[bookmark:DEFUN%20("or",%20For,%20Sor,%200,%20UNEVALLED,%200,][Bookmark: DEFUN ("or", For, Sor, 0, UNEVALLED, 0,]]
[[bookmark:defsubr%20(&Sor);][Bookmark: defsubr (&Sor);]]
** def! / set / setq
** if
*** mal implementation
[[bookmark:case%20"if":][Bookmark: case "if":]]
*** emacs implementation
[[bookmark:DEFUN%20("if",%20Fif,%20Sif,%202,%20UNEVALLED,%200,][Bookmark: DEFUN ("if", Fif, Sif, 2, UNEVALLED, 0,]]
[[bookmark:defsubr%20(&Sif);][Bookmark: defsubr (&Sif);]]
** macroexpand
macroexpand 是一個特殊的算子
mal 實作如下:
[[bookmark:mal/macroexpand][mal/macroexpand]]
#+BEGIN_SRC C
case 'macroexpand':
return macroexpand(a1, env);
#+END_SRC
[[bookmark:mal/function%20macroexpand][mal/function macroexpand]]
#+BEGIN_SRC C
function macroexpand(ast, env) {
while (is_macro_call(ast, env)) {
var mac = env.get(ast[0]);
ast = mac.apply(mac, ast.slice(1));
}
return ast;
}
#+END_SRC
emacs 的實作如下:
[[bookmark:eval/macroexpand][Bookmark: eval/macroexpand]]
#+BEGIN_SRC C
DEFUN ("macroexpand", Fmacroexpand, Smacroexpand, 1, 2, 0,
doc: /* Return result of expanding macros at top level of FORM.
If FORM is not a macro call, it is returned unchanged.
Otherwise, the macro is expanded and the expansion is considered
in place of FORM. When a non-macro-call results, it is returned.
The second optional arg ENVIRONMENT specifies an environment of macro
definitions to shadow the loaded ones for use in file byte-compilation. */)
(Lisp_Object form, Lisp_Object environment)
{
/* With cleanups from Hallvard Furuseth. */
register Lisp_Object expander, sym, def, tem;
while (1)
{
/* Come back here each time we expand a macro call,
in case it expands into another macro call. */
if (!CONSP (form))
break;
/* Set SYM, give DEF and TEM right values in case SYM is not a symbol. */
def = sym = XCAR (form);
tem = Qnil;
/* Trace symbols aliases to other symbols
until we get a symbol that is not an alias. */
while (SYMBOLP (def))
{
QUIT;
sym = def;
tem = Fassq (sym, environment);
if (NILP (tem))
{
def = XSYMBOL (sym)->function;
if (!NILP (def))
continue;
}
break;
}
/* Right now TEM is the result from SYM in ENVIRONMENT,
and if TEM is nil then DEF is SYM's function definition. */
if (NILP (tem))
{
/* SYM is not mentioned in ENVIRONMENT.
Look at its function definition. */
struct gcpro gcpro1;
GCPRO1 (form);
def = Fautoload_do_load (def, sym, Qmacro);
UNGCPRO;
if (!CONSP (def))
/* Not defined or definition not suitable. */
break;
if (!EQ (XCAR (def), Qmacro))
break;
else expander = XCDR (def);
}
else
{
expander = XCDR (tem);
if (NILP (expander))
break;
}
{
Lisp_Object newform = apply1 (expander, XCDR (form));
if (EQ (form, newform))
break;
else
form = newform;
}
}
return form;
}
#+END_SRC
** try*/catch*
catch 是一個特殊的算子, mal 中使用另一個名字: try*/catch*
mal 實作如下:
[[bookmark:try%20in%20mal][Bookmark: try in mal]]
#+BEGIN_SRC C
case "try*":
try {
return EVAL(a1, env);
} catch (exc) {
if (a2 && a2[0].value === "catch*") {
if (exc instanceof Error) { exc = exc.message; }
return EVAL(a2[2], new Env(env, [a2[1]], [exc]));
} else {
throw exc;
}
}
#+END_SRC
emacs 的實作如下:
[[bookmark:eval.c/catch][Bookmark: eval.c/catch]]
#+BEGIN_SRC C
DEFUN ("catch", Fcatch, Scatch, 1, UNEVALLED, 0,
doc: /* Eval BODY allowing nonlocal exits using `throw'.
TAG is evalled to get the tag to use; it must not be nil.
Then the BODY is executed.
Within BODY, a call to `throw' with the same TAG exits BODY and this `catch'.
If no throw happens, `catch' returns the value of the last BODY form.
If a throw happens, it specifies the value to return from `catch'.
usage: (catch TAG BODY...) */)
(Lisp_Object args)
{
register Lisp_Object tag;
struct gcpro gcpro1;
GCPRO1 (args);
tag = eval_sub (XCAR (args));
UNGCPRO;
return internal_catch (tag, Fprogn, XCDR (args));
}
#+END_SRC
* $(RUN_TEMACS) --batch --load loadup bootstrap
** set-buffer
[[bookmark:DEFUN%20("set-buffer",%20Fset_buffer,%20Sset_buffer,%201,%201,%200,][Bookmark: DEFUN ("set-buffer", Fset_buffer, Sset_buffer, 1, 1, 0,]]
** load
[[bookmark:DEFUN%20("load",%20Fload,%20Sload,%201,%205,%200,][Bookmark: DEFUN ("load", Fload, Sload, 1, 5, 0,]]
*** Execute a file of Lisp code named FILE.
*** `load-history'
Loading a file records its definitions, and its `provide' and
`require' calls, in an element of `load-history' whose
car is the file name loaded. See `load-history'.
*** `load-in-progress' / `load-file-name'
While the file is in the process of being loaded, the variable
`load-in-progress' is non-nil and the variable `load-file-name'
is bound to the file's name.
Check if we're stuck in a recursive load cycle.
Vloads_in_progress = Fcons (found, Vloads_in_progress);
/* Get the name for load-history. */
hist_file_name = (! NILP (Vpurify_flag)
? concat2 (Ffile_name_directory (file),
Ffile_name_nondirectory (found))
: found) ;
version = -1;
*** return
Return t if the file exists and loads successfully.
* 推估應預載模組
(load "emacs-lisp/byte-run")
(load "emacs-lisp/backquote")
(load "emacs-lisp/map-ynp")
(load "emacs-lisp/macroexp")
(load "emacs-lisp/pcase"))
(load "emacs-lisp/macroexp"))
(load "emacs-lisp/nadvice")
(load "emacs-lisp/syntax")
(load "emacs-lisp/timer")
(load "emacs-lisp/lisp")
(load "emacs-lisp/lisp-mode")
(load "emacs-lisp/tabulated-list")
(load "emacs-lisp/regexp-opt")
(load "emacs-lisp/float-sup")
About
yet another emacs lisp compiler / interpreter written in javascript / nodejs
Resources
License
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published