Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

yet another emacs lisp compiler / interpreter written in javascript / nodejs

License

Notifications You must be signed in to change notification settings

jawatech/elisp4js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

19 Commits

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

No packages published

Contributors 2

AltStyle によって変換されたページ (->オリジナル) /