;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
;;;; *************************************************************************
;;;; FILE IDENTIFICATION
;;;;
;;;; Name: lml.cl
;;;; Purpose: Lisp Markup Language functions
;;;; Programmer: Kevin M. Rosenberg
;;;; Date Started: Aug 2002
;;;;
;;;; $Id: base.lisp,v 1.3 2003/01/04 20:42:02 kevin Exp $
;;;;
;;;; This file, part of LML, is Copyright (c) 2002 by Kevin M. Rosenberg
;;;;
;;;; LML users are granted the rights to distribute and use this software
;;;; as governed by the terms of the GNU General Public License v2
;;;; (http://www.gnu.org/licenses/gpl.html)
;;;; *************************************************************************
(declaim (optimize (debug 3) (speed 3) (safety 3) (compilation-speed 0)))
(in-package :lml)
(defun html4-prologue-string ()
"")
(defun xml-prologue-string ()
"")
(defun xhtml-prologue-string ()
"")
(defvar *print-spaces* nil)
(defvar *indent* 0)
(defun reset-indent ()
(setq *indent* 0))
(defun lml-print (str &rest args)
(when (streamp *html-output*)
(when *print-spaces* (indent-spaces *indent* *html-output*))
(if args
(apply #'format *html-output* str args)
(princ str *html-output*))
(when *print-spaces* (format *html-output* "~%"))
(values)))
(defmacro lml-line (str &rest args)
`(lml-print ,str ,@args))
(defun lml-print-date (date)
(lml-print (date-string date)))
(defmacro lml-exec-body (&body forms)
`(progn
,@(mapcar
#'(lambda (form)
(etypecase form
(string
`(lml-print ,form))
(number
`(lml-print "~D" ,form))
(symbol
(when form
`(lml-print ,form)))
(cons
form)))
forms)))
(defmacro with-attr-string (tag attr-string &body body)
(let ((attr (gensym)))
`(let ((,attr ,attr-string))
(lml-print "<~(~A~)~A>" ',tag
(if (and (stringp ,attr) (plusp (length ,attr)))
(format nil "~A" ,attr)
""))
(incf *indent*)
(lml-exec-body ,@body)
(decf *indent*)
(lml-print "~(~A~)>" ',tag))))
(defun one-keyarg-string (key value)
"Return attribute string for keys"
(format nil "~(~A~)=\"~A\"" key
(typecase value
(symbol
(string-downcase (symbol-name value)))
(string
value)
(t
(eval value)))))
(defmacro with-keyargs (tag keyargs &body body)
(let ((attr (gensym))
(kv (gensym)))
`(progn
(let ((,attr '()))
(dolist (,kv ,keyargs)
(awhen (cdr ,kv)
(push (one-keyarg-string (car ,kv) it) ,attr)))
(with-attr-string ,tag (list-to-spaced-string (nreverse ,attr)) ,@body)))))
(defmacro bind-one-keyarg (keyarg)
`(list ,(car keyarg) ,(cdr keyarg)))
(defmacro bind-all-keyargs (keyargs)
"Convert a list of keyarg pairs and convert eval/bind arguments"
(let* ((npairs (length keyargs))
(syms (make-array npairs))
(ipair 0)
(ipair2 0))
(declare (dynamic-extent syms))
(dotimes (i npairs)
(setf (aref syms i) (gensym)))
`(let ,(mapcar #'(lambda (ka)
(prog1
(list (aref syms ipair) (cdr ka))
(incf ipair)))
keyargs)
(list ,@(mapcar #'(lambda (ka)
(prog1
`(cons ,(car ka) ,(aref syms ipair2))
(incf ipair2)))
keyargs)))))
(defmacro with (tag &rest args)
"Return a list of keyargs and also the body of LML form"
(let ((body '())
(keyargs '())
(bound-keyargs (gensym)))
(do* ((n (length args))
(i 0 (+ 2 i))
(arg (nth i args) (nth i args))
(value (when (< (1+ i) n)
(nth (1+ i) args))
(when (< (1+ i) n)
(nth (1+ i) args))))
((or (not (keyword-symbol? arg))
(>= i n))
(dotimes (j (- n i))
(push (nth (+ i j) args) body)))
(push (cons arg value) keyargs))
(setq keyargs (nreverse keyargs))
(setq body (nreverse body))
`(let ((,bound-keyargs ,(macroexpand `(bind-all-keyargs ,keyargs))))
,(macroexpand `(with-keyargs ,tag ,bound-keyargs ,@body)))))
(defmacro keyargs-string (&rest args)
"Returns a string of attributes and values. Result contains a leading space."
(let ((keyarg-list '()))
(loop for ( name val ) on args by #'cddr
do
(when val
(push (one-keyarg-string name val) keyarg-list)))
(list-to-spaced-string (nreverse keyarg-list))))
(defmacro xhtml-prologue ()
`(progn
(lml-print "~A~%" (xml-prologue-string))
(lml-print "~A~%" (xhtml-prologue-string))))
(defmacro link (dest &body body)
`(with a :href ,dest ,@body))
(defmacro link-c (class dest &body body)
`(with a :href ,dest :class (quote ,class) ,@body))
(defmacro img (dest &key class id alt style width height align)
(let ((attr
(eval `(keyargs-string :class ,class :id ,id :alt ,alt :style ,style
:width ,width :height ,height :align ,align))))
`(lml-print ,(format nil "" dest attr))))
(defmacro input (&key name class id type style size maxlength value checked)
(let ((attr
(eval `(keyargs-string :name ,name :class ,class :id ,id :style ,style
:size ,size :maxlength ,maxlength :value ,value
:type ,type :checked ,checked))))
`(lml-print ,(format nil "" attr))))
(defmacro meta (name content)
`(with meta :name ,name :content ,content))
(defmacro meta-key (&key name content http-equiv)
`(with meta :name ,name :content ,content :http-equiv ,http-equiv))
(defmacro br ()
`(lml-print "
"))
(defmacro hr ()
`(lml-print "