DEM - A simple Demeter system
The paper,
"Chris Houser, Manual and compiler for the terse and modular lagnuage DEM,
ACM SIGPLAN Notices, V.31(12), Dec 96, 41-51",
describes DEM a Demeter subset with a 100 line Perl program that compiles
it into C++. It operates strictly in batch mode.
Here is a less than 200 line Common Lisp version that can be used for incremental development as well a batch compiler. An earlier
version with the capabilites of the Perl version is 125 lines.
Rather than parsing the DEM syntax, it uses two terse macros, define-class and define-event.
Each generate the appropriate Lisp class or methods, respectively, based
on the Lisp compiler's current knowledge of the class definitions it has seen.
When a class is redefined the appropriate methods are also redefined.
(define-class name supers slots)
- Define a class.
-
name - Name of the class.
-
supers - Names of superclasses of the class.
slots.- List of slots.
Each slot has the form (name type)
where name is a symbol naming the slot, and type
is either a symbol naming a Common Lisp type or a list of the form
`(list ,type)
representing a list of elements of that type. You only get
lists, sorry .
(define-event name args flowspecs fragments)
- Defines an event. This generates a group of methods for a generic function.
name - Name of the generic function.
args- Additional arguments to the generic function.
There is an implied first argument of
self.
flowspecs- A list of flow specifications.
fragments- A list of method fragments.
Aflowspec is a list of the form (from to ins outs).
See the Dem paper. From and to are class names.
Ins and outs are lists of filters. A filter
is a list of the form (source label target).
A fragment defines a method for a particular class.
It is a list of the form
(name qualifier body) where name is the name
of a class. The qualifier can be either
= primary method
< before method
> after method.
And body is the body of the method. It can refer to slots as
lexical variables.
Class and event information is kept on simple global data structures
that are independent of CLOS. This makes it easy to port to other
lisp or object dialects.
Original DEM code
Sketch App shapes mode
Shape x# y# X# Y#
Box Shape
Line Shape a# b#
Text Shape string
Mode sketch next:Mode
Gesture Mode shape origin:point
Drag Mode shapes knob# last:point
Gather Mode points xx# yy# XX# YY#
draw()
Sketch -> Shape - :mode:
Line = move(x,y); line(X,Y); arrow(a, 0); arrow(b,1);
Box = move(x,y); line(x,Y): line(X, Y); line(X, y); line(x,y);
Text = move(x,y); text(string);
|
Lisp version
(define-class App () ())
(define-class Sketch (App)
((shapes (list Shape)) (mode Mode)))
(define-class Shape ()
((x1 fixnum) (y1 fixnum) (x2 fixnum) (y2 fixnum)))
(define-class Box (Shape) ())
(define-class Line (Shape)
((a fixnum) (b fixnum)))
(define-class Text (Shape)
((string string)))
(define-class Mode () ((sketch Sketch) (next Mode)))
(define-class Gesture (Mode) ((shape Shape) (origin Point)))
(define-class Drag (Mode) ((shapes (list Shape)) (knob fixnum)))
(define-class Gather (Mode)
((points (list point))
(x1 fixnum)
(y1 fixnum)
(x2 fixnum)
(y2 (fixum))))
(define-event draw ()
(((Sketch) (Shape) () ((* mode *))))
(
(Line = (progn (move x1 y1) (line x2 y2)
(arrow a 0) (arrow b 1)))
(Box = (progn (move x1 y1) (line x1 y2)
(line x2 y2) (line x2 y1)
(line x1 y1)))
(Text = (progn (move x1 y1) (text string)))
))
|
Generated software
(PROGN
(DEFCLASS GATHER (MODE)
((POINTS :TYPE LIST :INITARG POINTS)
(X1 :TYPE FIXNUM :INITARG X1)
(Y1 :TYPE FIXNUM :INITARG Y1)
(X2 :TYPE FIXNUM :INITARG X2)
(Y2 :TYPE FIXUM :INITARG Y2)))
(DEFCLASS DRAG (MODE)
((SHAPES :TYPE LIST :INITARG SHAPES)
(KNOB :TYPE FIXNUM :INITARG KNOB)))
(DEFCLASS GESTURE (MODE)
((SHAPE :TYPE SHAPE :INITARG SHAPE)
(ORIGIN :TYPE POINT :INITARG ORIGIN)))
(DEFCLASS MODE ()
((SKETCH :TYPE SKETCH :INITARG SKETCH)
(NEXT :TYPE MODE :INITARG NEXT)))
(DEFCLASS TEXT (SHAPE)
((STRING :TYPE STRING :INITARG STRING)))
(DEFCLASS LINE (SHAPE)
((A :TYPE FIXNUM :INITARG A)
(B :TYPE FIXNUM :INITARG B)))
(DEFCLASS BOX (SHAPE) NIL)
(DEFCLASS SHAPE ()
((X1 :TYPE FIXNUM :INITARG X1)
(Y1 :TYPE FIXNUM :INITARG Y1)
(X2 :TYPE FIXNUM :INITARG X2)
(Y2 :TYPE FIXNUM :INITARG Y2)))
(DEFCLASS SKETCH (APP)
((SHAPES :TYPE LIST :INITARG SHAPES)
(MODE :TYPE MODE :INITARG MODE)))
(DEFCLASS APP () NIL)
(FMAKUNBOUND 'DRAW)
(DEFMETHOD DRAW ((SELF TEXT))
(WITH-SLOTS (X1 Y1 X2 Y2 STRING)
SELF
(PROGN (MOVE X1 Y1) (TEXT STRING))))
(DEFMETHOD DRAW ((SELF BOX))
(WITH-SLOTS (X1 Y1 X2 Y2)
SELF
(PROGN (MOVE X1 Y1)
(LINE X1 Y2)
(LINE X2 Y2)
(LINE X2 Y1)
(LINE X1 Y1))))
(DEFMETHOD DRAW ((SELF LINE))
(WITH-SLOTS (X1 Y1 X2 Y2 A B)
SELF
(PROGN (MOVE X1 Y1)
(LINE X2 Y2)
(ARROW A 0)
(ARROW B 1))))
(DEFMETHOD DRAW ((SELF SKETCH))
(WITH-SLOTS (SHAPES MODE) SELF (MAPC #'DRAW SHAPES))))
|
Ken Anderson