From: Max Ischenko
Subject: Metaprogramming samples wanted
Date: 
Message-ID: <2640n9.mtb.ln@barloo.malva.com.ua>
Hi.


I've read that Lisp can be effectivly used to implement domain-specific
language to solve a particular problem.

Can anyone give me a pointer with samples of such usage?


-- 
Nothing is easier than to denounce the evildoer; nothing is more difficult
than to understand him.
- Fyodor Dostoevski

From: Jochen Schmidt
Subject: Re: Metaprogramming samples wanted
Date: 
Message-ID: <9n06p2$30k$1@rznews2.rrze.uni-erlangen.de>
Max Ischenko wrote:

> 
> 
> Hi.
> 
> 
> I've read that Lisp can be effectivly used to implement domain-specific
> language to solve a particular problem.
> 
> Can anyone give me a pointer with samples of such usage?

In ANSI Common Lisp itself e.g.

- The Extended Loop Macro 
 (advanced iteration facility using a Algol'ish syntax)

- FORMAT (Formatted I/O - something similar to printf() in C)

- CLOS an advanced objekt-oriented System

In (somewhat) external modules

- The "Meta Object Protocol" 

- Xanalys CommonSQL or Onshore's UncommonSQL
  creates database oriented domain-specific languages

- The META parsing technique (Henry Baker)
  A parser generating language
  http://linux.rice.edu/~rahul/hbaker/Prag-Parse.html

- The "Series" facility (Richard C. Waters)
  "Series are to loops as control structures are to GOTOs"

- Several Html-generation languages like HTMLGEN from 
  http://opensource.franz.com.

There are _many_ other examples left...

ciao,
Jochen

--
http://www.dataheaven.de
From: Pierre R. Mai
Subject: Re: Metaprogramming samples wanted
Date: 
Message-ID: <878zfvx4m1.fsf@orion.bln.pmsf.de>
Max Ischenko <···@malva.com.ua> writes:

> I've read that Lisp can be effectivly used to implement domain-specific
> language to solve a particular problem.
> 
> Can anyone give me a pointer with samples of such usage?

As a more elaborate example, some time in the not too distant past, we
implemented a specialized predicate-dispatching framework for CL, with
special pattern-matching predicates for XML (or other tree)
transformations.  This leveraged off of ANSI CL and the MOP, and
allowed one to write code like the stuff included below.  The dramatis
personae involved are:

DEFINE-TRANSFORMATION defines a TRANSFORMATION, which is the
  predicate-dispatching equivalent of a generic function.  It is
  implemented as a new sub-class of FUNCALLABLE-STANDARD-OBJECT, with
  a metaclass of FUNCALLABLE-STANDARD-CLASS, thereby ensuring that
  TRANSFORMATIONS are directly funcallable.

  Transformations have one implicit anonymous argument, the top-node
  of the (sub-)tree currently being transformed.

DEFINE-RULE defines a method on a transformation, with a
  pattern-matching based dispatch-predicate.  The pattern language
  implemented provides numerous general and XML-specific patterns,
  with variable-binding patterns, element-selection patterns, element
  PCDATA extraction, logical connectives, function calling,
  pattern-macros, etc.  The pattern is matched against the implict
  argument of the transformation.

Some of the available patterns that occur in the example (there are
lots of others, and some of those used might not be the best of ideas
in terms of readability, but this system only reached the prototyping
stage before it became clear that it was not going to be used for
unrelated reasons):

(BIND <VAR> <PAT>) -- Bind <VAR> to the value of the pattern <PAT> in
                      the body of the rule.
(PATH <SPEC> ...)  -- Matches a sub-element if there exists a path
                      from the current tree to the sub-element which
                      matches with SPEC at each level.  A SPEC is
                      either a (mapped to CL) GI (element-name for
                      non-SGML experts), or a list giving both a
                      variable name and a GI, which will also cause
                      variable to be bound to the element matching the
                      spec.  The value of the whole pattern is the
                      last sub-element matched.  This is an example of
                      a pattern-macro, since PATH expands into more
                      low-level patterns like BIND, ELEMENT, etc.
(TEXT <PAT>)       -- Matches if <PAT> matches an element.  The
                      pattern value is the PCDATA content of the
                      element (there is lots of SGML-specific problems
                      with this, not of concern here).

The framework has provisions for patterns (which are internally
represented by pattern objects) to be pre-compiled at compile-time, or
interpreted at run-time, although most of the pattern-compilers were
not implemented because speed was not an issue even for the
interpreted version (quite unlike the Java version, see below).

While the whole approach was later dismissed by us as being the wrong
approach to XML handling, it highlighted the ability of CL to
embed domain-specific extensions/languages quite well, IMHO.  The
whole code needed to implement the transformation extension ran to
less than 1000 LoC (this excludes the ~500 LoC of the XML handling
framework itself, which provides for parsing (via expat), rendering
and internal representation of XML), and was implemented by two
developers in a short amount of part-time work.  Another group working
on a similar (though slightly less general) extension for Java had to
write a complete pre-Compiler for their extended Java, the complete
code running into 10s of KLoCs in the end, and performance on stock
JVMs not being competitive with the CL prototype by a wide margin.

Regs, Pierre.

;;;; CLiX --- Common Lisp (improves|incorporates|infiltrates|is) XML
;;;; This is copyrighted software.  See documentation for terms.
;;;; 
;;;; teitotex.cl --- Further Demonstration of Transformations on TEI XML
;;;; 
;;;; Checkout Tag: $Name:  $
;;;; $Id: teitotex.cl,v 1.1 2000/11/09 02:13:59 dent Exp $

(in-package :CL)

;;;; %File Description:
;;;; 
;;;; This file demonstrates how one might transform XML (TEI Play)
;;;; into LaTeX.
;;;; 

(eval-when (:compile-toplevel :load-toplevel :execute)
  (require "CLiX-Transform"))

;;; Set up specialized representations for the TEI Play subset we use.

(defpackage :tei (:use :CL :CLiX)
  (:export #:tei #:play #:title #:personae #:persona #:pgroup #:grpdescr
	   #:fm #:p #:scndescr #:playsubt
	   #:act #:scene #:stagedir #:speech #:speaker #:line))

(in-package :tei)

(defmacro define-simple-xml-elements (namespace supers &rest names)
  `(progn
     ,@(loop for name in names
	     collect
	     `(define-xml-element (,name ,(symbol-name name) ,namespace)
	          ,supers
	        ()))))

(define-simple-xml-elements tei ()
  play title personae persona pgroup grpdescr
  fm p
  scndescr playsubt
  act scene stagedir speech speaker line)

;;; The transformation code itself

(defpackage :CLiX-Demo (:use :CL :CLiX :CLiX-Transform)
  (:export #:transform-file))

(in-package :CLiX-Demo)

;;; Support utility functions

(defun map-children (fun node)
  (mapc fun (node-children node)))

(defun write-tex-escaped-string (string &optional (stream *standard-output*))
  "This behaves like `write-string', but escapes all TeX relevant characters."
  (declare (type string string) (type stream stream))
  (loop for char of-type character across string
	do
	(case char
	  ((#\$ #\&) (write-char #\\ stream) (write-char char stream))
	  (t
	   (write-char char stream)))))

(defun write-tex-escaped-line (string &optional (stream *standard-output*))
  "This behaves like `write-line', but escapes all TeX relevant characters."
  (write-tex-escaped-string string stream)
  (terpri stream))

;;; The transformation proper

;;; During the transformation, *standard-output* will be bound to the
;;; LaTeX output stream.

(define-transformation transform-play)

;;; Top-level transformation

(define-rule transform-play (path (node tei:play))
  (write-line "% LaTeX generated from XML TEI PLAY DTD by CLiX-Transform")
  (write-line "\\documentclass{article}")
  (write-line "\\begin{document}")
  (write-line "\\parindent 0pt")
  (map-children #'transform-play node)
  (write-line "\\end{document}"))

;;; Frontmatter stuff

(define-rule transform-play (bind title (text (path tei:play tei:title)))
  (write-string "\\title{")
  (write-tex-escaped-string title)
  (write-line "}"))

(define-rule transform-play (path (node tei:fm))
  (write-line "\\author{")
  (map-children #'transform-play node)
  (write-line "}")
  (write-line "\\maketitle{}")
  (write-line "\\newpage"))

(define-rule transform-play (bind text (text (path tei:fm tei:p)))
  (write-tex-escaped-string text)
  (write-line "\\\\"))

(define-rule transform-play (path (node tei:personae))
  (map-children #'transform-play node)
  (write-line "\\newpage"))

(define-rule transform-play (bind title (text (path tei:personae tei:title)))
  (write-string "\\section*{")
  (write-tex-escaped-string title)
  (write-line "}"))

(define-rule transform-play (bind name (text (path tei:personae tei:persona)))
  (write-string "{\\tt ")
  (write-tex-escaped-string name)
  (write-line "}\\\\")
  (terpri))

(define-rule transform-play (path (node tei:pgroup))
  (map-children #'transform-play node)
  (terpri))

(define-rule transform-play (bind name (text (path tei:pgroup tei:persona)))
  (write-string "{\\tt ")
  (write-tex-escaped-string name)
  (write-line "}\\\\"))

(define-rule transform-play (bind description (text (path tei:grpdescr)))
  (write-string "{\\it (")
  (write-tex-escaped-string description)
  (write-line ")}\\\\"))

;;; We get to the meat of the play.

(define-rule transform-play (path (node tei:act))
  (map-children #'transform-play node))

(define-rule transform-play (bind title (text (path tei:act tei:title)))
  (write-string "\\section*{")
  (write-tex-escaped-string title)
  (write-line "}"))

(define-rule transform-play (path (node tei:scene))
  (map-children #'transform-play node))

(define-rule transform-play (bind title (text (path tei:scene tei:title)))
  (write-string "\\subsection*{")
  (write-tex-escaped-string title)
  (write-line "}"))

(define-rule transform-play (bind direction (text (path tei:stagedir)))
  (write-string "\\centering{\\it [")
  (write-tex-escaped-string direction)
  (write-line "]}\\\\"))

(define-rule transform-play (path (node tei:speech))
  (write-line "\\begin{description}")
  (map-children #'transform-play node)
  (write-line "\\end{description}"))

(define-rule transform-play (bind speaker (text (path tei:speaker)))
  (write-string "\\item[")
  (write-tex-escaped-string speaker)
  (write-line ":] "))

(define-rule transform-play (bind line (text (path tei:line)))
  (write-string "\\hspace{1pt}")
  (write-tex-escaped-string line)
  (write-line "\\\\"))

(define-rule transform-play (bind dir (text (path tei:speech tei:stagedir)))
  (write-string "{\\it [")
  (write-tex-escaped-string dir)
  (write-line "]}\\\\"))

;;; Default rule for all other stuff (like e.g. PCDATA nodes, PI nodes, etc.)

(define-rule transform-play t
  nil)

;;; Driver code

(defun transform-file (from to)
  (let ((play-xml (parse-xml-from-file from 'tei:tei)))
    (with-open-file (*standard-output* to :direction :output)
      (transform-play play-xml))))

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein