LOLのdefmacro!gをGaucheで

30分プログラム、その607。id:athosに影響されて、Let Over LambdaのマクロをGaucheで書いてみる。

今回書くのはdefmacro!gというマクロ。これは、g!で始まるシンボルを自動でgensymにしてくれる。使い方は下に書いてあるのを見てください。
で、このdefmacro!gはコード全体を走査して、g!で始まるシンボルを取り出してくる。これが、flattenでリストを潰してfilterするだけで簡単に書けちゃう。こういうのが、全部の構文要素がリストでできてるLispの強みだと思う。

使い方

;; nifは第1引数が正、ゼロ、負で場合分けするマクロ
;;    (nif 42 'plus 'zero 'minus) => 'plus
;;
;; g!resultが自動でgensymになる
(define-macro!g (nif a plus zero neg)
  `(let1 ,g!result ,a
	 (cond
	  ((< 0 ,g!result) ,plus)
	  ((= 0 ,g!result) ,zero)
	  ((> 0 ,g!result) ,neg))))

ソースコード

#! /opt/local/bin/gosh
;; -*- mode:scheme; coding:utf-8 -*-
;;
;; defmacro.scm -
;;
;; Copyright(C) 2009 by mzp
;; Author: MIZUNO Hiroki / mzpppp at gmail dot com
;; http://howdyworld.org
;;
;; Timestamp: 2009/06/21 21:37:24
;;
;; This program is free software; you can redistribute it and/or
;; modify it under MIT Lincence.
;;

(use srfi-1)

(define (flatten xs)
  (if (pair? xs)
      (append-map flatten xs)
      (list xs)))

(define (g!symbol? name)
  (and (symbol? name)
       (> (string-length (symbol->string name)) 2)
       (string=? "g!" (substring (symbol->string name) 0 2))))

(define-macro (define-macro!g name . body)
  (let1 gensyms
	(delete-duplicates (filter g!symbol? (flatten body)))
	`(define-macro ,name
	   (let ,(map (lambda (sym) `(,sym (gensym))) gensyms)
	     ,@body))))

;; usage:
(define-macro!g (nif a plus zero neg)
  `(let1 ,g!result ,a
	 (cond
	  ((< 0 ,g!result) ,plus)
	  ((= 0 ,g!result) ,zero)
	  ((> 0 ,g!result) ,neg))))

(nif -11 'plus 'zero 'minus)