[pro] Fwd: Re: Lisp and DSLs
raito at raito.com
raito at raito.com
Wed Jul 20 16:20:06 UTC 2011
This may not be much of an example, but it's not only what I have, it's
what got me hooked on Lisp.
When I was a young lad, and because I lived not too far from Lake
Geneva, I played this newfangled thing called D&D (back when it first
came out. Get off my lawn). And one of the things that intrigued me was
the tables (the kind where you rolled dice and looked up the results).
And also being as I was learning programming (on a mainframe. There
were no microcompputers. See my previous comment about lawns.) I
thought about writing a table language. One where I could specify not
only how many chances a particular result had (because I'd already
figured out that that was better than using the dice themselves for a
bell curve), but also being able to modify the tables themselves at
run-time by feeding them arguments, being able to have sub-tables,
detailed results, etc. But at the time all we had was Honeywell
mainframe BASIC (supposedly FORTRAN was available, but we couldn't get
a manual), and even the instructor didn't know anything about writing
parsers, let alone languages.
Fast forward, and the idea stuck with me. I mean, I'd gone to school
and doubles in ECE and Comp. Sci., I should be able to do this. But I
never did, because it's a very big hassle to write your own language.
Especially in languages like C.
So when I started using Lisp, I read up all this stuff about DSLs. Hm,
I wonder if I could use Lisp to do my language. Well, half an hour and
15 lines of code later, I not only had my language, but more than I
wanted. And here it is (ta da):
(defmacro table (name args &rest rest)
`(defun ,name (, at args)
(declare (special , at args))
(let* ((base ',(loop for x in rest collect (car x)))
(results ',(loop for x in rest collect (cadr x)))
(chances (loop for x in base collect (eval x)))
(total (loop for x in chances sum x))
(roll (+ 1 (random total))))
;(format t "Base:~{~a~^, ~}~%Chances:~{~a~^, ~}~%Results:~{~a~^,
~}~%total:~A roll:~A~%"
; base chances results total roll)
(do ((n chances (cdr n))
(cnt 0)
(item 0 (+ item 1)))
((eq n nil) nil)
(setf cnt (+ cnt (car n)))
(when (<= roll cnt)
(return (eval (nth item results))))))))
and it's used like this (a simple example):
(table my-table (added-chances)
(1 "one")
(1 "two")
((+ 1 added-chances) "three")
)
I've done some stuff with it that's been quite a bit of fun. Like
another layer of macro on top for those who think in terms of dice.
The upshot is that it's a project I didn't do for 25 years because it
was too big a pain. And I did it in Lisp in half an hour, including
figuring out that I had to use dynamic scoping for args. I have to use
dynamic scoping because the forms being eval'ed need to be able to
access args, but args is not in their lexical scope (though it looks as
if they are in the macro invocation -- which is the point).
Neil Gilmore
raito at raito.com
More information about the pro
mailing list