Skip to content

Commit

Permalink
Finished Chapter 17: Domain Specific Languages
Browse files Browse the repository at this point in the history
On this chapter we explore the utilities of using macros to defined
sub-languages in Lisp to solve a subset of problems in a desired domain.

The chosen areas for this example was a macro-system of tags for XML,
HTML and SVG. Later was defined a game-action macro to create dynamically
functions command for the TEXT ADVENTURE GAME written in the Chapter 5~6.

Pretty nice.

What I've learned:

* When you need to do some weird programming for a very specific domain,
  Macros are a great solution. With them, you can create your own DSL.
* Often, it makes sense to first write a helper function for a macro
  (like print-tag), and then write a macro (like tag) to add improvements
  that only a macro can provide. These improvements usually involve being
  able to access the code with a clearer, and often safer, syntax.
* You can mix DSLs with regular Lisp code, which gives you a lot of power
* DSLS are useful when you need to write very specific code — whether it's
  code for a web page, code that draws a picture, or code that builds
  special game commands.
  • Loading branch information
ryukinix committed Mar 15, 2017
1 parent 7bea01c commit ad049b4
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 3 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ A personal repository for annotation about learning lisp patterns.
The current content are answers plus code covering of the book [Land of Lisp](http://www.landoflisp.com) and the insights at the MIT 6.001 Course: Structures and Interpretations of Computer Programs.


# Land of Lisp book (reading) [364/482]
![progress](http://progressed.io/bar/75)
# Land of Lisp book (reading) [374/482]
![progress](http://progressed.io/bar/77)


- [x] Section I: Lisp is Power
Expand All @@ -28,7 +28,7 @@ The current content are answers plus code covering of the book [Land of Lisp](ht
- [x] Chapter 14 (Ramping lisp up a Notch with Functional Programming)
- [x] Chapter 15 (Dice of Doom, a Game Written in the Functional Style)
- [x] Chapter 16 (The Magic of Lisp Macros)
- [ ] Chapter 17 (Domain-Specific Languages)
- [x] Chapter 17 (Domain-Specific Languages)
- [ ] Chapter 18 (Lazy Programming)
- [ ] Chapter 19 (Creating a Graphical, Web-Based Version of Dice of Doom)
- [ ] Chapter 20 (Making Dice of Doom More Fun)
Expand Down
117 changes: 117 additions & 0 deletions land-of-lisp/cap17-domain-specific-languages.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
;; Remember SVG (Scalable Vector Graphics) are XML-like,


(load "cap5-building-a-text-game-engine")

;; Would be better avoid copy-paste functions,
;; but the chapter 16 has several re-definitions
;; and top-level execution of demonstrations
;; So i just adapted the +SPLIT and +PAIRS functions

(defmacro split (val yes no)
"Exported from the chapter 16"
(let ((g (gensym)))
Expand Down Expand Up @@ -61,3 +68,113 @@
(defmacro body (&body body)
`(tag body ()
,@body))

(defmacro svg (&body body)
`(tag svg (xmlns "http://www.w3.org/2000/svg"
"xmlns:xlink" "http://www.w3.org/1999/xlink")
,@body))

(defun brightness (col amt)
(mapcar (lambda (x)
(min 255 (max 0 (+ x amt))))
col))

(defun svg-style (color)
(format nil
"~{fill:rgb(~a,~a,~a);stroke:rgb(~a,~a,~a)~}"
(append color
(brightness color -100))))

(defun circle (center radius color)
(tag circle (cx (car center)
cy (cdr center)
r radius
style (svg-style color))))

(defun polygon (points color)
(tag polygon (points (format nil
"~{~a, ~a ~}"
(mapcan (lambda (tp)
(list (car tp) (cdr tp)))
points))
style (svg-style color))))


(defun random-walk (value length)
(unless (zerop length)
(cons value
(random-walk (if (zerop (random 2))
(1- value)
(1+ value))
(1- length)))))


;; create random polygons at random_walk.svg

(defun random-walk-svg ()
(with-open-file (*standard-output* "random_walk.svg"
:direction :output
:if-exists :supersede)
(svg (loop repeat 10
do (polygon (append '((0 . 200))
(loop for x from 0
for y in (random-walk 100 400)
collect (cons x y))
'((400 . 200)))
(loop repeat 3
collect (random 256)))))))


;; svg finished

;; starts extension of the wizard_game from chapter 5-6

(defun have (object)
(member object (inventory)))

(defparameter *chain-welded* nil)

(defun weld (subject object)
(if (and (eq *location* 'attic)
(eq subject 'chain)
(eq object 'bucket)
(have 'chain)
(have 'bucket)
(not *chain-welded*))
(progn (setf *chain-welded* t)
'(the chain is now securely welded to the bucket.))
'(you cannot weld like that.)))

(pushnew 'weld *allowed-commands*)
(defparameter *bucket-filled* nil)

(defun dunk (subject object)
(if (and (eq *location* 'garden)
(eq subject 'bucket)
(eq object 'well)
(have 'bucket)
*chain-welded*)
(progn (setf *bucket-filled* t)
'(the bucket is now full of water))
'(you cannot dunk like that)))
(pushnew 'dunk *allowed-commands*)


;; super cool macro to avoid replication like the commands above
(defmacro game-action (command subj obj place &body body)
`(progn (defun ,command (subject object)
(if (and (eq *location* ',place)
(eq subject ',subj)
(eq object ',obj)
(have ',subj))
,@body
'(i cant ,command like that.)))
(pushnew ',command *allowed-commands*)))

(game-action splash bucket wizard living-room
(cond ((not *bucket-filled*) '(the bucket has nothing in it.))
((have 'frog) '(the wizard awakens and sees that you stole his frog.
he is so upset he banishes you to the netherworlds-
you lose! the end.))
(t '(the wizard awakens from his slumber and greets your warmly.
he hands you the magic low-carb donut- you win! the end.))))
Loading

0 comments on commit ad049b4

Please sign in to comment.