1 XML
The XML language in mirrors allows the programatic assembly of syntactically valid XML and XHTML, without the problems of traditional list-representations:
the data type of an element is distinct from the data type of a block of nodes;
blocks can be arbitrarily nested without using forms such as append, quasiquote and unquote-splicing;
raw blocks of unprocessed text can be inserted into an otherwise well-formed XML document;
many XHTML browser bugs are automatically worked around;
blocks of Javascript are automatically rendered as appropriately quoted attribute values and text;
static blocks of output are pre-rendered to strings at macro expansion time.
Constructing XML fragments describes the syntax for creating blocks of XML, Rendering XML and sending XML responses describes how to send XML responses in the PLT web server, XML utilities describes some useful blocks of XML such as a DOCTYPES, and XML abstract syntax tree describes the underlying AST representation.
1.1 Constructing XML fragments
(require (planet untyped/mirrors/xml/syntax)) |
Mirrors provides the following macros for creating blocks of XML:
Example: | |||||
| |||||
|
To prevent double-quoting errors, it is a syntax error to use the following Mirrors quote forms within an xml block: xml, xml*, xml-attrs, xml-attrs*, opt-xml, opt-xml-attr, js, opt-js:
> (xml (xml)) |
eval:3:0: mirrors/xml: cannot use this identifier here |
(possible double quoting error): switch the surrounding |
quote macro to "XML" or another uppercase equivalent at: |
xml in: (xml) |
You can get around this restriction using the uppercase form, XML, described below.
> (XML (xml)) ; '<xml/>' tag |
#s((raw xml 0) ("<xml />")) |
It is a syntax error to use the following identifiers in expression position within an XML block: XML, XML*, XML-ATTRS, XML-ATTRS*, OPT-XML, OPT-XML-ATTR, JS, OPT-JS:
> (XML (XML)) |
eval:5:0: mirrors/xml: cannot use this identifier here |
(possible double quoting error): switch the surrounding |
quote macro to "xml" or another lowercase equivalent at: |
XML in: (XML) |
Example: | ||||||
| ||||||
|
| ||
"<a title=\"title\" href=\"href\">text</a>" |
(if boolean-expr |
(xml xml-expr ...) |
(xml)) |
Examples: |
> (xml->string (opt-xml #t (span "This span will be printed...."))) |
"<span>This span will be printed....</span>" |
> (xml->string (opt-xml #f (span "...but this span won't."))) |
"" |
(if boolean-expr |
(xml-attrs [id ,expr]) |
(xml-attrs)) |
The two- and one-argument forms are specialisations of the above. The two-argument form expands to:
(if boolean-expr |
(xml-attrs [id ,id]) |
(xml-attrs)) |
and the one-argument form expands to:
(if id |
(xml-attrs [id ,id]) |
(xml-attrs)) |
Example: | ||||
| ||||
"<li>Item 1</li><li class=\"even\">Item 2</li><li>Item 3</li>" |
1.1.1 XML expression syntax
The forms above use the same XML syntax, inspired by the syntax of Neil van Dyke’s HtmlPrag package:
xml-expr | = | quotable-value | ||
| | (custom-syntax-id xml-expr ...) | |||
| | (tag xml-expr ...) | |||
| | (tag (@ attr-expr ...) xml-expr ...) | |||
| | (& code-expr) | |||
| | (!comment value-expr) | |||
| | (!cdata value-expr) | |||
| | (!pi value-expr) | |||
| | (!raw value-expr) | |||
| | (unquote xml) | |||
| | (unquote-splicing (listof xml)) | |||
custom-syntax-id | = | id ; bound with define-xml-syntax | ||
tag | = | id | ||
code-expr | = | id | ||
| | integer | |||
| | (unquote (U symbol integer)) | |||
value-expr | = | quotable-value | ||
| | (unquote quotable-value) | |||
attr-expr | = | (id quotable-value) | ||
| | (id (unquote quotable-value)) | |||
| | (unquote attribute) | |||
| | (unquote-splicing (listof attribute)) |
1.1.2 Custom XML syntax
You can extend the XML language by introducing new syntax forms:
(define-xml-syntax (id arg ...) xml-expr) |
(define-xml-syntax id xml-transformer) |
(define-xml-syntax id xml-transformer expr-transformer) |
The first form above behaves like define-syntax-rule. xml-expr should be a regular xml block.
Examples: | ||
| ||
> (xml->string (xml (!linkto "http://www.plt-scheme.org"))) | ||
"<a href=\"http://www.plt-scheme.org\">http://www.plt-scheme.org</a>" | ||
> (!linkto "http://www.plt-scheme.org") | ||
eval:13:0: !linkto: must be used as an xml expression in: | ||
(!linkto "http://www.plt-scheme.org") |
The second and third forms behave like define-match-expander. xml-transformer is a syntax transformer procedure used during XML expansion, which accepts an XML form as an input and returns a complete xml or xml* block representing the expansion. expr-transformer is a transformer procedure that is used in regular Scheme expansion. When xml-transformer is omitted, use of the syntax form outside of a XML block results in a syntax error.
Examples: | |||||||
| |||||||
> (xml->string (xml (!linkto "http://www.plt-scheme.org"))) | |||||||
"<a href=\"http://www.plt-scheme.org\">http://www.plt-scheme.org</a>" | |||||||
> (!linkto "http://www.plt-scheme.org") | |||||||
"http://www.plt-scheme.org" |
1.2 Rendering XML and sending XML responses
(require (planet untyped/mirrors/xml/xml)) |
1.2.1 Rendering XML in string form
Mirrors does not contain a procedure for rendering XML as a multi-line string. This is largely because such a facility would be at odds with the pre-rendering behaviour of the xml macro. If you want to debug the XML or XHTML output of our web application, you may want to try one of the variety of HTML and XML prettification add-ons that are available for Firefox.
1.2.2 Sending HTTP responses with XML and XHTML content
The PLT web server has built-in support for the “xexpr” representation of the PLT xml package. This lets you write request handling procedures in short-hand:
#lang scheme/base |
(require web-server/servlet |
xml/xml) |
; request -> xexpr |
(define (start initial-request) |
'(head (body "Hello world."))) |
instead of manually creating an HTTP response using make-respone/full:
#lang scheme/base |
(require web-server/servlet |
xml/xml) |
; request -> response |
(define (start initial-request) |
(make-response/full |
200 |
"Okay" |
(current-seconds) |
#"text/html; charset=utf-8" |
null |
(list (xexpr->string '(head (body "Hello world.")))))) |
Naturally, the PLT web server does not have built-in support for Mirrors XML expressions. Mirrors provides a couple of useful procedures to help you send responses:
| ||||||||||||||||||||||||||||||||||||||||||
code : integer = 200 | ||||||||||||||||||||||||||||||||||||||||||
message : string = "OK" | ||||||||||||||||||||||||||||||||||||||||||
seconds : integer = (current-seconds) | ||||||||||||||||||||||||||||||||||||||||||
mime-type : (U string bytes) = #"text/html; charset=utf-8" | ||||||||||||||||||||||||||||||||||||||||||
headers : (alistof symbol string) = no-cache-http-headers | ||||||||||||||||||||||||||||||||||||||||||
content : xml |
1.3 XML utilities
Examples: |
> (xml-quotable? 123) |
#t |
> (xml-quotable? (xml 123)) |
#f |
> (xml-quotable? (box 123)) |
#f |
Examples: |
> (xml+quotable? 123) |
#t |
> (xml+quotable? (xml 123)) |
#t |
> (xml+quotable? (box 123)) |
#f |
Examples: |
> (equal? (xml-quote 123) (xml ,123)) |
#t |
> (equal? (xml-quote (xml 123)) (xml 123)) |
#t |
Examples: |
> (xml-empty? (xml)) |
#t |
> (xml-empty? (xml "Hi")) |
#f |
> (xml-empty? (xml ,(xml))) |
#t |
; Odd case: #f renders as an empty string: |
> (xml-empty? (xml #f)) |
#t |
(require (planet untyped/mirrors/xml/util)) |
The following XML expressions are defined for convenience:
> (display (xml->string xml-1.0-header)) |
<?xml version="1.0" encoding="utf-8"?> |
> (display (xml->string xhtml-1.0-strict-doctype)) |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
> (display (xml->string xhtml-1.0-transitional-doctype)) |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
> (display (xml->string xhtml-1.0-frameset-doctype)) |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> |
1.4 XML abstract syntax tree
(require (planet untyped/mirrors/xml/struct)) |
The xml and xml* macros expand into trees of structures of the following types. You shouldn’t normally have to manipulate these types explicitly unless you want to build or manipulate XML without the syntax layer:
> (xml->string (make-atom 12345)) |
"12345" |
> (xml->string (make-atom #t)) |
"yes" |
> (xml->string (make-atom "Apples & pears.")) |
"Apples & pears." |
quotable-values include booleans, numbers, strings, symbols, byte strings, SRFI 19 times (UTC and TAI types only) and URLs from the net/url library.
| ||||
"Apples & pears." |
> (xml->string (make-entity 12345)) |
"〹" |
> (xml->string (make-entity 'nbsp)) |
" " |
> (xml->string (make-comment "Apples & pears")) |
"<!--Apples & pears-->" |
> (xml->string (make-cdata "Apples & pears")) |
"<![CDATA[Apples & pears]]>" |
> (xml->string (make-pi "Apples & pears")) |
"<?Apples & pears?>" |
> (xml->string (make-raw "<ul><li>Apples</li><li>Pears</li></ul>")) |
"<ul><li>Apples</li><li>Pears</li></ul>" |
(struct (element xml) (tag attributes child)) |
tag : symbol |
attributes : (listof attribute) |
child : xml |
| ||||
"<span title=\"attribute\">child element</span>" |