elispでちょこっとpythonを生成する

動的型付け言語だと,関数の型チェックを事前に行いたくなったりする.

なわけで,emacsで関数の型をチェックさせる部分を自動で吐かせるスクリプトを書いてみた.

役立つかわからないけど.

(defun filtercar (func lst)
  (let ((result '())
	(rest lst))
    (while rest
      (let ((func-result (funcall func (car rest))))
	(if func-result
	    (progn
	      (push (car rest) result))))
      (setq rest(cdr rest)))
    result))

(defun alistp (lst)
  (catch 'break
    (while lst
      (if (consp (car lst))
	  (setq lst (cdr lst))
	(throw 'break nil)))
    t))

(defun string-consp (cns)
  (if (and (stringp (car cns)) (stringp (cdr cns)))
      t
    nil))

(defun assoc-keys (alist)
  (mapcar (lambda (a) (car a)) alist))

(defun eval-minibuffer-with-checker (checker default-prompt default-init 
					     error-prompt error-init)
  (let ((result (condition-case nil
		    (eval-minibuffer default-prompt default-init)
		  (error nil))))
    (while (or (not result) (not (reduce (lambda (a b) (and a b))
					 (mapcar (lambda (cns) (string-consp cns)) result))))
      (setq result (condition-case nil
		       (eval-minibuffer error-prompt error-init)
		     (error nil))))
    result))

(defun reduce (func lst)
  (if (= (length lst) 1)
      (car lst)
    (let ((prec-result (funcall func (car lst) (cadr lst)))
	  (rest (cddr lst)))
      (while rest
	(setq prec-result (funcall func prec-result (car rest)))
	(setq rest (cdr rest)))
      prec-result)))

(defun py-function-with-type ()
  (interactive)
  (let ((func-name (read-from-minibuffer "function name : "))
	(arg-lst (eval-minibuffer-with-checker #'alistp
					       "write arguments alist ((val type) ...) : " "'"
					       "something wrong, rewrite arguments alist ((val type) ...) : " "'")))
    (insert (concat "def " func-name " ("
		    (reduce (lambda (a b) (concat a ", " b))
			    (assoc-keys arg-lst))
		    "):\n"))
    (while arg-lst
      (insert (concat "if type (" (caar arg-lst) ") != " (cdar arg-lst) ":\n"))
      (insert (concat "raise TypeError ('argument " (caar arg-lst) " is type mismatch. it must be type " (cdar arg-lst) "')\n"))
      (setq arg-lst (cdr arg-lst)))))

使うときは,pythonのバッファで"M-x py-function-with-type"と打つ.

すると,ミニバッファが立ち上がって,関数名と引数と型のリストを入力するよう促される.

引数と型のリストはこういう感じ.

'(("val1" . "int") ("val2" . "int") ("val3" . "str"))

でこんな感じで生成される.

def func-name (val1, val2, val3):
    if type (val1) != int:
        raise TypeError ('argument val1 is type mismatch. it must be type int')
    if type (val2) != int:
        raise TypeError ('argument val2 is type mismatch. it must be type int')
    if type (val3) != str:
        raise TypeError ('argument val3 is type mismatch. it must be type str')

ちなみに自動でインデントされません!まだまだ改善が必要か.