Skip to content

Instantly share code, notes, and snippets.

@guicho271828
Created February 14, 2014 13:35
Show Gist options
  • Save guicho271828/9001076 to your computer and use it in GitHub Desktop.
Save guicho271828/9001076 to your computer and use it in GitHub Desktop.
Common Lisp is still hard to satisfy. ref: http://qiita.com/guicho271828/items/2852196ddf4c48ff0e3c
(defmacro name lambda-list &body body)
(macrolet bindigs &body body)
(eval form)
(compile name definition)
(macroexpand form &optional env)
(macroexpand-1 form &optional env)
*macroexpand-hook*
AUGMENT-ENVIRONMENT
COMPILER-LET
DECLARATION-INFORMATION
DEFINE-DECLARATION
ENCLOSE
FUNCTION-INFORMATION
PARSE-MACRO
VARIABLE-INFORMATION
(defun %%eval (exp env)
(cond
((symbolp exp)
;; CLHS 3.1.2.1.1 Symbols as Forms
(multiple-value-bind (value kind) (get-variable exp env)
(ecase kind
(:variable value)
(:expansion (%eval value env)))))
;; CLHS 3.1.2.1.3 Self-Evaluating Objects
((atom exp) exp)
;; CLHS 3.1.2.1.2 Conses as Forms
((consp exp)
(case (car exp)
;; CLHS 3.1.2.1.2.1 Special Forms
((block) (eval-block (cdr exp) env))
((catch) (eval-catch (cdr exp) env))
;; special form が20個ぐらい続く
;;
;; (略)
(t
(let ((dispatcher (getf *eval-dispatch-functions* (car exp))))
(cond
(dispatcher ; cltl2:compiler-let のための処理
(funcall dispatcher exp env))
;; CLHS 3.1.2.1.2.4 Lambda Forms
((and (consp (car exp)) (eq (caar exp) 'lambda))
(interpreted-apply (eval-function (list (car exp)) env)
(eval-args (cdr exp) env)))
(t
(multiple-value-bind (function kind) (get-function (car exp) env)
(ecase kind
;; CLHS 3.1.2.1.2.3 Function Forms
(:function (%apply function (eval-args (cdr exp) env)))
;; CLHS 3.1.2.1.2.2 Macro Forms
(:macro
;; **ここからがキモ**
;; **ここからがキモ**
)))))))))))
(let ((hook *macroexpand-hook*))
(%eval (funcall hook ; 展開
function
exp
(env-native-lexenv env))
env))
;; eval するコード
(let ((x 5))
(my-unless (plusp x)
(print :minus)))
;; 展開してできるコード
(let ((x 5))
(IF (NOT (PLUSP X))
(PROGN (PRINT :MINUS))
NIL))
;; コールスタック
(eval ; let
(eval ; my-unless
(macroexpand) ; my-unless -> when -> if
(eval ; if
(eval (eval)) ; (not (plusp x))
(eval)))) ; (progn ...) か NIL
;; eval するコード
(my-unless (plusp x)
(my-unless (minusp x)
(print :zero)))
;; 展開してできるコード
(IF (NOT (PLUSP X))
(PROGN
(IF (NOT (MINUSP X))
(PROGN (PRINT :ZERO))
NIL))
NIL)
;; コールスタック
(eval ; my-unless
(macroexpand) ; my-unless -> when -> if
(eval ; if
(eval ; (not (plusp))
(eval ; my-unless
(macroexpand) ; my-unless -> when -> if
(eval ...))))) ; (not (minusp))
;; iterate の簡易バージョン
;; iter の中にいるばあい、
;; while は 引数が true の時だけ実行を次の節に続けます。
;; collect はその引数を内部的な変数に集め、iterの返り値にします。
;; iter の外で使われた時は、for と collect はエラーを投げます。
(let ((i 0))
(iter (while (< i 5))
(incf i)
(collect (+ 3 (* 4 i)))))
(define-condition compile-error (simple-error)
((message :initarg :message :accessor message))
(:report (lambda (c s)
(princ (message c) s))))
(defmacro collect (x)
(declare (ignore x))
(error 'compile-error
:message "`collect' must be used under `iter'"))
(defmacro while (x)
(declare (ignore x))
(error 'compile-error
:message "`while' must be used under `iter'"))
(require :alexandria)
(use-package :alexandria)
(defmacro iter (&body body)
(with-gensyms (iter-block accumulate start)
`(block ,iter-block
(let ((,accumulate nil))
(macrolet ((while (condition)
`(unless ,condition
(return-from ,',iter-block
(nreverse ,',accumulate))))
(collect (thing)
`(push ,thing ,',accumulate)))
(tagbody
,start
,@body
(go ,start)))))))
(let ((i 0))
(iter (while (< i 5))
(incf i)
(collect (+ 3 (* 4 i)))))
;; --> (7 11 15 19 23)
(let ((i 0))
(iter (while (< i 5))
(incf i)
(print acc) ; <--
(collect (+ 3 (* 4 i)) :into acc))) ; <--
(collect (thing &key into)
(if into
(error 'accumulate-target :name into) ; コンパイルやり直し!
`(push ,thing ,',accumulate)))
(defmacro iter (&body body &continuation cont)
(with-gensyms (iter-block accumulate start)
(handler-case
(funcall #'cont
`(block ,iter-block
(let ((,accumulate nil))
(macrolet ((while (condition)
`(unless ,condition
(return-from ,',iter-block
(nreverse ,',accumulate))))
(collect (thing &key into)
(if into
(signal 'accumulate-target :name into)
`(push ,thing ,',accumulate))))
(tagbody
,start
,@body
(go ,start))))))
;; handler
(accumulate-target (c)
(setf accumulate (name c))
(funcall #'cont
`(block ,iter-block
(let ((,accumulate nil))
(macrolet ((while (condition)
`(unless ,condition
(return-from ,',iter-block
(nreverse ,',accumulate))))
(collect (thing &key into)
`(push ,thing ,',accumulate)))
(tagbody
,start
,@body
(go ,start))))))))))
;; 一階層隠蔽する
(defmacro my-collect (&rest args)
`(collect ,@args))
(let ((i 0))
(iter (while (< i 5))
(incf i)
(print acc)
(my-collect (+ 3 (* 4 i)) into acc)))
(defmacro my-unless (condition &body body)
`(when (not ,condition)
,@body))
(defparameter *form*
`(let ((x 5))
(my-unless (plusp x)
(print :minus))))
(macroexpand-1 *form*)
; -->
; (LET ((X 5))
; (MY-UNLESS (PLUSP X)
; (PRINT :MINUS)))
; NIL
(require :iterate)
(in-package :iterate)
(let ((i 0))
(iter (while (< i 5))
(incf i)
(print acc)
(macrolet ((my-collect (&rest args)
`(collect ,@args)))
(my-collect (+ 3 (* 4 i)) into acc))))
; in: LET ((I 0)) ...
;
; caught WARNING:
; Iterate:
; Iterate does not know how to handle
; the special form (MACROLET ...)
; It will not be walked, which means that Iterate clauses
; inside it will not be seen.
;
; compilation unit finished
; Undefined function:
; COLLECT
; Undefined variables:
; ACC INTO
(defmacro iter (... &environment env)
...
(walk-code ... env))
(defun walk-code (form env)
...
(when (eq 'macrolet (car form))
(destructuring-bind (car macros . body) form
(declare (ignore car))
(walk-code
`(progn ,@body)
(augment-environment
env
:macro (make-macro-functions macros))))))
(eval ; let
(eval ; iter
(macroexpand) ; iterを展開 (中でwalk-codeを呼ぶ)
(eval))) ; 展開したコードをまた読み込み直す -- マクロのチェックもする
(let ((x 0))
(let ((y x))
(declare (fixnum y))
...))
macroexpand -> eval -...-> eval -> macroexpand -...->
(defparameter *form2*
`(my-unless (plusp x)
(print :minus)))
(macroexpand-1 *form2*)
;; -->
;; (WHEN (NOT (PLUSP X)) (PRINT :MINUS))
;; T
(macroexpand-1 (macroexpand-1 *form2*))
;; ->
;; (IF (NOT (PLUSP X))
;; (PROGN (PRINT :MINUS))
;; NIL)
;; T
(macroexpand-1 (macroexpand-1 (macroexpand-1 *form2*)))
;; ->
;; (IF (NOT (PLUSP X)) ; 変化なし
;; (PROGN (PRINT :MINUS))
;; NIL)
;; NIL
(macroexpand *form2*)
;; -->
;; (IF (NOT (PLUSP X))
;; (PROGN (PRINT :MINUS))
;; NIL)
;; T
(macroexpand *form*)
;; -->
;; (LET ((X 5))
;; (MY-UNLESS (PLUSP X)
;; (PRINT :MINUS)))
;; NIL
CL-USER> (setf *evaluator-mode* :interpret)
; --> :INTERPRET
(defun eval (original-exp)
(eval-in-lexenv original-exp (make-null-lexenv)))
;; ↓
(defun eval-in-lexenv (exp lexenv)
(if (eq *evaluator-mode* :compile)
...
(sb!eval:eval-in-native-environment exp lexenv)))
;; ↓
(defun eval-in-native-environment (form lexenv)
(handler-bind
((sb!impl::eval-error ...))
...
(%eval form env)))
;; ↓
(defun %eval (exp env)
...
(%%eval exp env))
;; ↓
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment