(ຊՈXerox൛ͷ ’92ͷarchive) ɾgithub.com/sbcl/sbcl/tree/master/src/pcl/ Authors: Gregor Kiczales, Jim des Rivieres (AMOPຊAOPͷݯྲྀ) গͳ͔Β͵CLॲཧܥͷ“CLOS෦”ͷίʔυϕʔε including cmucl, sbcl, ecl, … 4
nodeͷtreeͱ͔)ͩͬͨΓ ݹయతLisp༻walker(ؚPCL’s)ɺ SࣜίʔυΛࠪͯ͠Δ So, what is a code walker? ͜Ε͕ code walking ͷग़͔ࣗͱࢥ͍·͢ (ASTͷࠪࣗମ ίϯύΠϥͷsemantic analysis͕ී௨ʹߦ͍ͬͯΔ͜ͱͰ͢) 6
modified) (defun sb-cltl2:macroexpand-all (form &optional env) (flet ((walk-fn (subform context env2) (aif (and (eq context :eval) (listp subform) (symbolp (car subform)) (get (car subform) :partial-macroexpander)) ;; The partial expander, only for QUASIQUOTE as of Aug 2019, ;; must return T as its 2nd value to stop the walk (funcall it subform env2) subform)) ;; just call WALK-FORM after binding *walk-…-macros-p* to t (let ((*WALK-FORM-EXPAND-MACROS-P* t )) (WALK-FORM form env #’walk-fn)))) ɾ walk-fnɺquasiquoteͷͱ͖Ҏ֎ ୯ʹsubformࣗ(ͱnil)Λฦ͢ ɾ ଈͪ *walk-…-macros-p* Λม͚͑ͨͩͷ४ඪ४ಈ࡞͕ɺϚΫϩશల։ 20
(eq (VAR-LEXICAL-P var env1) (VAR-LEXICAL-P var env2))) ;; lambda bodyͷڥͱ lambda formͷஔ͔ΕͨڥͰɺมΛൺֱ͍ͯ͠Δ (defun aggregate (lmd-form lmd-env &aux (pool '())) (WALK-FORM lmd-f lmd-e (lambda (f c e) (when (and (eq c :eval) f (symbolp f) (same-var-p f e lmd-env)) (pushnew f pool)) f)) pool) (defun main (&optional (form '(let (x y z) (lambda (y) (foo x y z))) env)) (WALK-FORM form env (lambda (f c e) (when (lambda-form-p f) (return-from main (aggregate f e))) f))) ;; (main) => (Z X) -- Ϋϩʔδϟม͕(ͦ͏Ͱͳ͍yΛআ͍ͯ)ूΊΒΕΔ Examples ʙ aggregating closure variables in a lambda form ʙ 25
that macro can’t The main difference between a macro and a code walker is coverage. A macro can change only the parts of the program that call the macro (*1). Whereas a code walker can change the whole program. *1: One reason macros can’t walk over a whole program is the runtime system doesn’t let them. … ɾSBCLʹ͍ͬͯΔ walk-formͷݺग़Օॴʹɺmulti-levelͰͳ͍͚Ͳ ෳͷsubformղੳΛdispatch͍ͯ͠Δͱ͜Ζ(boot.lisp)͕͋Γ·͢ Examples ʙ transforming across multi-level forms (3/3) ʙ 30
for the syntactic convenience they provide, but because they are programs that manipulate programs, which has always been a central theme in the Lisp community. If FORTRAN is the language that pushes numbers around, and C is the language that pushes characters and pointers around, then Lisp is the language that pushes programs around. ˞ underline mine ˞ ਂொ͞Μͷϒϩά ʹ ༁ͱڞʹࡌ͍ͬͯ·͢ Finally... ʙ A quote from “The Evolution of Lisp” ʙ 40
July 1989 (an intro. for PCL code walker) [2] B. v. Melle, Implementation of an Iteration Macro, Lisp Pointers, ACM SIGPLAN, 3(2-4):29-40, Apr 1990 [3] R. C. Waters, Macroexpand-All: An example of a Simple Lisp Code Walker, Lisp Pointers, ACM SIGPLAN, 6(1):25-32, Jan 1993 [4] J. Fremlin, Portable Common Lisp code walking with macroexpand-dammit, Jul 2009 (g000001, a Japanese intro.) [5] Hacker News, news.ycombinator.com/item?id=3607707, Feb 2012 [6] Z. Beane, The tree-walkers of CL, CL Tips, Feb 2013 [7] @quek, Using hu.dwim.walker (in Japanese), May 2013 References (1/2) 42
satisfy (in Japanese), Feb 2014 [9] C. Rhodes, naive vs proper code-walking, Sep 2014 [10] C. Rhodes, code walking for pipe sequencing, Sep 2014 (showing examples using sbcl code walker, so does [9]) [11] What Code Walkers can do that macros can’t, Aug 2016 [12] M. Raskin, Writing a best-effort portable code walker in Common Lisp, Euro Lisp Symp. (gitlab repo.), Apr 2017 [13] A. Gutev, cl-environments (github repo.), Sep 2017 [14] github.com/sbcl/sbcl/blob/master/src/pcl/walk.lisp [15] @nfunato, companion code for this presentation (gist) 43