PXSL ("pixel") is a convenient shorthand for writing markup-heavy XML
documents. The following document explains why PXSL is needed and
shows you how to use it. For additional information, such as the FAQ
list, visit the community site:
You'll get more out of this document if you read it from start to
finish, but you can stop anywhere after the "Gentle Introduction to
PXSL" and be able to take advantage of PXSL in your documents. The
later sections explain PXSL's advanced features. If you're willing to
invest some time in learning them, you will have at your disposal new
and powerful ways to create and refactor XML documents. The advanced
features are more complicated to master, but they can greatly reduce
the complexity of your documents.
Table of Contents
* Getting PXSL * Getting help * License * Getting or building the PXSL tools * Gentle Introduction to PXSL - Why PXSL ? - A closer look at PXSL - Using PXSL documents * Advanced topics - Element defaults provide convenient, customizable shortcuts Using element defaults to create attribute shortcuts Using element defaults to create virtual elements Making and using your own element defaults Built-in element defaults for XSLT stylesheets - Advanced quoting with << >> and <{ }> - Macro facility - Advanced macros and passing parameters with the <( )> delimiters - More advanced macros and functional programming - Automatic PXSL-to-XML conversion via Make * Reference: pxlscc * Reference: PXSL syntax * Authors
Getting PXSL
The most-recent official version of PXSL can always be found here:
The PXSL toolkit is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
The text of the GNU GPL may be found in the LICENSE file,
included with this software.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Except as provided for under the terms of the GNU GPL, all rights
are reserved worldwide.
Getting or building the PXSL tools
If you don't want to build the PXSL tools from source code, you can
download one of the pre-built binary packages on the PXSL web site.
If a binary package isn't available for your computing platform of
choice, you can use the following procedure to build the PXSL tools
for your platform.
In order to build the tools you will need the following:
Just uncompress the tarball and build the project using the following
commands. If you want to install a personal copy of pxslcc instead of
the doing the default, system-wide installation, uncomment the extra
command-line flags on the third command.
PXSL ("pixel") is a convenient shorthand for writing markup-heavy XML
documents. This introduction assumes that you are familiar with XML.
If you want a refresher, see the introductions on XML available here:
XML is a descendant of the markup language SGML and inherits its
ancestor's historical bias toward marking up textual documents.
However, XML is becoming an increasingly popular medium for the
representation of non-textual information such as metadata (RSS, XSD,
RELAX-NG), remote procedure calls (SOAP), and even information
that looks much like programming languages (XSLT, SVG, MathML).
For these uses, XML's text-centric syntax gets in the way.
Consider, for example, this snippet of MathML:
MathML in XML
<declare type="fn"> <ci> f </ci> <lambda> <bvar><ci> x </ci></bvar> <apply> <plus/> <apply> <power/> <ci> x </ci> <cn> 2 </cn> </apply> <ci> x </ci> <cn> 3 </cn> </apply> </lambda> </declare>
Notice something about MathML's structure: There is more markup than
text. In fact, the only text in the snippet is "f x x2 x3"; the rest
is markup. As you can see above, XML's document-centric style of
markup, in which the markup is delimited from the flow of surrounding
text, becomes a hindrance when markup is in the majority.
PXSL, in contrast, was designed specifically to handle this case well.
It makes dense markup easy because it assumes that everything is
markup to begin with. You need only delimit the few portions of text
that are mixed into the flow of surrounding markup.
In other words, PXSL is what you get when you turn XML inside out:
XML PXSL
<markup>text</markup> markup <<text>>
Let's see how this inside-out transformation simplifies our MathML
example from above:
MathML in XML MathML in PXSL
<declare type="fn"> declare -type=fn <ci> f </ci> ci << f >> <lambda> lambda <bvar><ci> x </ci></bvar> bvar <apply> ci << x >> <plus/> apply <apply> plus <power/> apply <ci> x </ci> power <cn> 2 </cn> ci << x >> </apply> cn << 2 >> <ci> x </ci> ci << x >> <cn> 3 </cn> cn << 3 >> </apply> </lambda> </declare>
There are two things to notice about the PXSL version in comparison
to the XML version. First, the PXSL version is shorter. Second, and
most important, PXSL is comparatively free of noisy characters like <>
/ and ". In PXSL, noise is the exception rather than the rule.
A closer look at PXSL
Writing PXSL is simple. If you know how to write XML, you can write
PXSL. In fact, PXSL is XML, just written in a different, inside-out
syntax. Let's see how it works by way of comparison.
First, every XML document and hence every PXSL document has a root
element. Here is a tiny document that has a root element and nothing
else:
XML PXSL
<doc/> doc
If the document contains other elements, they are simply placed
underneath the root element, but indented to indicate that the root
element contains them. In XML this indenting is optional, but most
people do it anyway because it is an established practice that makes
documents easier to understand. In PXSL however, the indenting is
mandatory because indentation determines which elements contain
others. (This requirement is what enables PXSL to do away with the
closing tags that XML uses to determine containment.)
<doc> doc <title/> title <body/> body </doc>
If an element has attributes, they are written in the form of
-name=value in PXSL.
<doc> doc <title/> title <body id="db13"/> body -id=db13 </doc>
If an attribute value contains whitespace, it must be quoted within
the literal delimiters << and >>.
<doc keywords="x y z"> doc -keywords=<<x y z>> <title/> title <body id="db13"/> body -id=db13 </doc>
Now let's consider text. If an element contains text, the text is
quoted in << and >> delimiters and indented underneath the element
that owns the text.
<doc keywords="x y z"> doc -keywords=<<x y z>> <title/> title <body id="db13"> body -id=db13 This is text. <<This is text.>> </body> </doc>
The << and >> delimiters are powerful. The text within them,
including all whitespace, is quoted verbatim. The text can span
multiple lines and even stray outside of the outline-like indentation
hierarchy. If you place sections of quoted text next to one another
<<like>><<so>> they effectively become one section <<likeso>>.
<doc keywords="x y z"> doc -keywords=<<x y z>> <title> title My title <<My title>> </title> body -id=db13 <body id="db13"> <<This is multi- This is multi- line text.>> line text. </body> </doc>
If you want to add an XML comment, introduce it with the -- delimiter.
The comment extends to the end of the line.
<!-- my document --> -- my document <doc keywords="x y z"> doc -keywords=<<x y z>> <title> title My title <<My title>> </title> body -id=db13 <body id="db13"> <<This is multi- This is multi- line text.>> line text. </body> </doc>
You can also use the # delimiter, which creates a PXSL comment that is
invisible in XML:
<!-- my document --> -- my document <doc keywords="x y z"> doc -keywords=<<x y z>> <title> title My title <<My title>> </title> body -id=db13 <body id="db13"> <<This is multi- This is multi- line text.>> line text. </body> # hidden comment, for </doc> # PXSL readers only
That's it. You now know everything necessary to create PXSL
documents.
PXSL lets you do more, however, and if you want to take full advantage
of it, read the Advanced Topics section. For now, though, let's
consider how to use PXSL documents with your existing XML-based
software.
Using PXSL documents
Using PXSL documents is easy because they are really XML documents in
disguise. (In fact, you may wish to consider PXSL as a convenient
shorthand for writing XML.) Any program that can read XML can handle
PXSL. All you need to do is remove the disguise first so that the
programs will recognize your documents for what they are.
The included tool pxlscc (short for PXSL conversion compiler) performs
this task. Just feed it a PXSL document, and it returns the
equivalent plain-old XML document:
$ pxlscc document.pxsl > document.xml
You can then use the returned document in your XML-aware programs.
If you know how to use Make or Ant or similar tools, you can easily
automate this process so that your PXSL files are automagically
converted into XML when needed.
NOTE: The pxslcc program expects UTF-8 encoded input and emits UTF-8
encoded output.
Advanced topics
The following sections describe the more advanced capabilities of PXSL
that can make your life easier. The element defaults, in particular,
can significantly reduce markup burdens.
Element defaults provide convenient, customizable shortcuts
Most XML documents conform to established vocabularies. Once you
become familiar with your documents' vocabularies, you'll probably
find that certain elements and attributes always or often occur
together -- to the point where typing them becomes repetitive. For
example, in XHTML, almost all img elements take the following form:
<img src="..." alt="..." [ additional attributes here ] />
Or, in PXSL:
img -src=... -alt=... [ additional attributes here ]
So, why should you have to type in the repetitive src="" and alt=""
every time you use an img element? With PXSL's element defaults,
you don't need to.
Using element defaults to create attribute shortcuts
Element defaults are shortcuts that are defined in a separate file
using a simple syntax. (For the specifics of creating and loading
these files, see the Reference section on pxslcc.) For example:
img = img src alt
This shortcut allows you optionally to leave off the -src= and -alt=
part whenever you write the PXSL markup for an img element. For
example, with this definition in place, all three of these PXSL
statements mean the exact same thing:
img -src=/images/logo.png -alt=Logo img /images/logo.png -alt=Logo img /images/logo.png Logo
In other words, shortcuts let you pass attribute values by position
instead of by the -name=value syntax. You provide only the values,
and the shortcut provides the corresponding -name= parts behind the
scenes.
But there are a couple of restrictions to keep in mind. First,
attribute values passed by position must come first, before any values
passed using the -name=value syntax, and they must occur in the same
order as declared in the shortcut definition.
Second, you can only pass values this way if they do not contain
whitespace. If a value contains whitespace, you must use the
-name=value syntax and quote the value: -name=<<myvalue>> (There is
an advanced feature, the <()> delimiters, that overcome this
restriction. They are described in the section on advanced macros,
later in this document.)
Using element defaults to create virtual elements
You can also use the element defaults to create your own virtual
elements. If you work in XHTML, you have probably noticed that the
<a> element is used to create both hypertext links and anchors. For
example:
<a name="anchor-name">Anchored text</a> <a href="#anchor-name">Link to anchored text</a>
Why not make these two uses more obviously distinct while cutting down
on markup at the same time? Let's create virtual "anchor" and "hlink"
elements that do just that:
anchor = a name hlink = a href
Now we can use these elements in PXSL to express the above XHTML more
clearly:
anchor anchor-name <<Anchored text>> hlink #anchor-name <<Link to anchored text>>
(Notice that we used << and >> in an advanced way that lets us put
quoted text on the same line as the element that contains it. This is
discussed further in the "Advanced quoting" section.)
When we convert the above PXSL into XML, it results in exactly the
same XHTML that we discussed earlier:
<a name="anchor-name">Anchored text</a> <a href="#anchor-name">Link to anchored text</a>
Making and using your own element defaults
Making your own shortcuts is easy. Just create a file that contains
lines of this form:
You can --add more than one set of defaults, and pxslcc will use them
all.
Built-in element defaults for XSLT stylesheets
PXSL was originally created to reduce the verbosity of XSLT
stylesheets. As a result, pxslcc has a built-in set of element
defaults for XSLT that you can enable by passing the --xslt
flag:
$ pxslcc --xslt stylesheet.pxsl > stylesheet.xsl
The built-in defaults provide two benefits: First, you can use element
names from within the XSLT namespace without having to use the xsl:
prefix. Second, you can pass common required attributes like "select"
and "match" by position.
Together, these benefits result in massive markup reductions, making
your life as an XSLT author much easier. Compare the following
snippet of XSLT in XML
with the same snippet rewritten in PXSL (using --xslt defaults):
template / for-each //*/@src|//*/@href value-of . text << >>
Among the many XSLT shortcuts enabled by the --xslt flag, the above
PXSL snippet uses the following:
template = xsl:template match name for-each = xsl:for-each select xml:space value-of = xsl:value-of select disable-output-escaping text = xsl:text disable-output-escaping
To see the complete list of XSLT shortcuts, --export them:
$ pxslcc --xslt --export
Advanced quoting with the <<>> and <{}> delimiters
PXSL has two kinds of quoting delimiters that can be used to quote
mixed and text-only content. Both are described in this section.
XML quoting <<>> delimiters
The << and >> delimiters not only let you insert text into your PXSL
documents, but also let you insert raw, full-featured XML. This works
great for those times when it's just easier to write a bit of XML than
its PXSL equivalent. For example, if you're writing an XSLT
stylesheet that generates XHTML output, you'll certainly want to use
PXSL to express the markup-dense xsl:stylesheet directives. But, if
you need to drop in some XHTML boilerplate that a designer gave you to
use in the page footer, just copy-and-paste it using << and >>:
Another great use for the <<>> delimiters is to drop XML specials
like processing instructions into your code:
<<<?xml version="1.0" encoding="ISO-8859-1"?>>>
The above PXSL is equivalent to the following XML:
<?xml version="1.0" encoding="ISO-8859-1"?>
Because the <<>> delimiters quote XML, you must follow XML's
syntactical rules when using them. That means that if you
want to include literal less-than "<" and ampersand "&"
characters, you must use character entity references:
<< less-than: < >> << ampersand: & >>
Verbatim text <{}> delimiters (CDATA)
When copy-and-pasting blocks of text from outside sources, you must be
careful to "escape" any literal "<" and "&" characters that may be
within. This can be annoying, especially for large blocks of text.
Another place where this requirement is burdensome is in mathematical
expressions that sometimes occur in XSLT markup:
xsl:test -when=<< $i < 5 >>
For this reason, PXSL provides the verbatim-text delimiters <{ and }>
that perform the same function as XML's more verbose CDATA delimiters:
XML PXSL
<![CDATA[ toast & jelly ]]> <{ toast & jelly }>
Any characters that you place inside of <{}> will come out as a
character literals. PXSL will take care of any escaping that is
necessary to prevent XML from misinterpreting your text as markup.
For example, we can rewrite the above XSLT snippet more clearly
using the verbatim-text delimiters:
xsl:test -when=<{ $i < 5 }>
These delimiters are especially handy for including examples of XML
markup in your documents. Like <<>>,<{}> can handle large blocks
of multi-line text and preserves whitespace and indentation.
Text-content shortcut
As you may have noticed from the MathML example at the beginning of
this document, if an element contains text, you can declare the
text on the same line as the element. This saves space and often
reads more easily:
PXSL has a simple macro facility that you can use to reorganize your
markup and "factor out" boilerplate. A macro is defined with a
leading comma and a trailing equals sign, like so:
,name = body-of-the-macro
where "name" is the name of the macro and "body-of-the-macro" can be
any series of elements and text. Macros can be defined at any level
of nesting within a PXSL document, but they are only visible (i.e.,
available for use) at the level where they were defined and at levels
nested underneath. (If two macros with the same name are visible at
the same time, the deepest one will hide the other, or if both are on
the same level, the one defined latest in the document will hide the
earlier. In other words, the policy is "the last, deepest one wins.")
Using macros (i.e., macro expansion)
To use a macro, simply refer to it by name anywhere that an element
can be used:
,hello = <<Hello!>>
html head title ,hello body <<Hello! Again!>>
When processed with pxslcc (using the --indent flag), this is the
result:
Note that the macro definition has been removed and that the reference
to the macro inside of the "title" element has been replaced by the
macro's body. This is called macro expansion.
Macros don't need to be defined before they are expanded, as long as
they are visible from the sites (locations) where they are expanded.
Also, macros can call other macros:
,hello = <<Hello!>>
html ,head ,body
,head = head title ,hello
,body = body <<Hello! Again!>>
This snippet results in exactly the same XML as the one above.
Nevertheless, we have made a number of organizational changes.
The "head" and "body" within the "html" element have been
factored out into the macros ,head and ,body and relocated within
the document. These macros are defined within the "html"
element, after the sites where they are expanded. Note that the
,head macro calls upon the ,hello macro that we defined earlier.
Although contrived in this small example, factoring out blocks of
markup makes the structure of large documents easier to understand and
manage because you are free to move them around, subdivide them
further, and reuse them in many locations.
Parameterized macros
Macros can take any number of parameters, which allows you to customize
their definitions.
Using named parameters
For example, we could customize the definition of the ,head macro that
we used above to accept the title as a parameter:
,make-head my-title = head title ,my-title
Now we can use it to create a head element that contains any title
that we want:
,make-head -my-title=<<This is my title.>>
Note that we pass parameters to a macro just like we pass attributes
to an element definition.
Using the magic, implicit BODY parameter
But what if we want to pass more than strings? What if we want to
pass large sections of documents as parameters? We can do this using
the special BODY parameter that all macros have implicitly:
,make-section title = section -- start of section title ,title ,BODY -- end of section
(Note that the BODY parameter must be spelled exactly "BODY" and in
all caps.) The BODY parameter accepts any content defined underneath
the macro-expansion site (i.e., the body of the macro-expansion
invocation):
,make-section -title=<<This is my title>> p <<This is a paragraph.>> p <<And another.>> p <<And so on.>>
The result of calling this macro is the following XML:
<section> <!-- start of section --> <title>This is my title</title> <p>This is a paragraph.</p> <p>And another.</p> <p>And so on.</p> <!-- end of section --> </section>
Advanced macros and passing parameters with the <()> delimiters
As we showed earlier, one way of passing document fragments to macros
is via the implicit BODY parameter that all macros have. Another is
to pass them as normal arguments using the <()> delimiters, which let
you group PXSL document fragments into chunks that you can pass as
arguments.
For example, let's redefine the make-section macro we defined above to
accept the body of the section as a normal parameter:
,make-section title body = section -- start of section title ,title body ,body -- end of section
Now we can call it like so:
,make-section -title=<<This is my title>> \ -body=<( p <<This is a paragraph.>> p <<And another.>> p <<And so on.>> )>
(Note the use of the backslash in between parameters to continue the
parameter list to the next line. This useful trick also works to
continue attribute lists when you are creating elements.)
Because the <()> delimiters can be used only to pass arguments, you
can use them to "quote" arguments that otherwise could not be passed
via position, e.g., a fragment of text that contains whitespace:
,make-section <( <<This is my title>> )> \ <( p <<This is a paragraph.>> p <<And another.>> p <<And so on.>> )>
You can even use the <()> delimiters to pass the results of macro
calls to elements and other macros:
,h1 x = -- level one heading h1 ,x
,bang x = ,x <<!>>
,h1 <( <<Hello, >> ,bang World )>
The above produces the following XML:
<!-- level one heading --> <h1>Hello, World!</h1>
More advanced macros and functional programming
Like functions in functional programming languages, macros in PXSL are
first-class values that can be created, bound to parameters, and
passed to other macros. While this might initially seem like a
programming-language curiosity, it is actually a simple yet immensely
powerful tool that you can use to reduce the size and complexity of
your XML documents. In particular, this tool lets you "factor out"
and reuse common, boilerplate portions of your documents.
To see how this works, consider the following XML document that
represents an address book:
The address book contains a long list of persons, each of which has a
first and last name and a "preferred name" that is usually the first
and last named joined together (but might be something else).
We might write the address book in PXSL like this:
address-book person first <<Joe>> last <<Smith>> preferred <<Joe Smith>> person first <<John> last <<Doe>> preferred <<John Doe>> -- ... more persons ...
But, seeing how repetitive that is, we might create a ,person macro
to make our lives easier:
,person first last = person first ,first last ,last preferred ,first << >> ,last
Now, with our new macro, we can simply write
address-book ,person Joe Smith ,person John Doe -- ... more persons ...
And, indeed, running the above PXSL code through pxslcc, yields the
identical XML:
Already, we have saved a great deal of work, but let's say that the
situation is a little more complicated. Let's say that in addition
to the address-book, we also need to make a roster of persons:
and, most important, we need to keep the address-book and roster
synchronized. In other words, we have one list of names and we
must use it in two places.
At this point, we might be tempted to put the list of names in a
separate XML document and write a small external program or a couple
of XSLT stylesheets to transform the document into the address-book
and roster. After all, we don't want to have to keep the address-book
and roster synchronized by hand.
But we can do this without leaving PXSL. All we have to do is create
a macro that builds things out of our list of people:
,build-from-people builder-macro = ,builder-macro Joe Smith ,builder-macro John Doe -- ... more persons ...
The interesting thing is that our ,build-from-people macro takes
another macro as a parameter and binds it to the name "builder-macro",
just like it would any other kind of parameter. It uses this macro to
transform a first and last name into something else. What that
something else is, is up to us: We simply tailor the ,builder-macro to
suit our purpose.
For example, to build an address book:
address-book ,build-from-people <( , first last = ,person <(,first)> <(,last)> )>
or, to build a roster:
roster ,build-from-people <( , first last = formal ,last <<, >> ,first )>
That's it. We have just built an address book and a roster from our
list of people.
Now, you may have noticed something new in the above two snippets of
PXSL. In each snippet, inside of the outer-most <()> delimiters, we
created a macro on the fly -- an anonymous macro, so called because we
didn't give it a name. (It doesn't need a name because we're using it
just this one time; nobody else will ever call it.) We simply created
it right when we needed it and passed it to the ,build-from-people
macro, where it was bound to the name "builder-macro." Then
,build-from-people used it to construct "person" or "formal" elements
(depending on how we defined the anonymous macro). It's a pretty neat
trick.
You can create anonymous macros using the familiar comma syntax --
just don't provide a name. Note the space between the comma and the
start of the argument list:
, arg1 arg2... = body
To call an anonymous macro, of course, you'll first have to bind it to
a name. The way you do this is to pass the anonymous macro to another
macro, just like we did earlier, causing the anonymous macro to be
bound to one of the other macro's parameters:
,some-other-macro <( , arg1 arg2... = body )>
Then that other macro can call it via the parameter's name:
Pxslcc compiles the above into the following output:
<!-- let's quote a couple of strings -->
"Quote Me" 'Please!'
In this example, the two calls to the ,add-quotes macro each pass in
an anonymous macro that performs the desired quoting operation. The
anonymous macro is bound to "quote-fn" when the ,add-quotes macro is
called and expanded. Thus, when ,add-quotes calls ,quote-fn, it is
really calling the anonymous macro that we passed to it. This lets us
customize the behavior of ,add-quotes without having to rewrite it.
Real-world example
The examples above are contrived and don't do justice to the
usefulness of this tool. This type of refactoring shines when dealing
with large, complicated, and repetitive data structures, but such
examples are too unwieldy to include in an introduction like this.
For this reason, I urge you to take a look at the
"xsl-menus-w-macros.pxsl" example, in examples directory. It shows
one way that you can use anonymous macros to factor out common code in
production XSLT stylesheets.
Most Make utilities allow you to define pattern rules that are then
used automatically to convert one class of documents into another.
Pattern rules can be used to automate the conversion of PXSL documents
into their XML counterparts. For example, if you place the following
rule into a makefile (this is for GNU make),
%.xml: %.pxsl pxlscc --indent=2 --header $< > $@
Make will automatically generate .xml documents from the
corresponding .pxsl documents whenever they are needed. This
frees you to substitute .pxsl documents anywhere that your
project calls for .xml documents, knowing that make will keep all
of the .xml documents up to date, regenerating them as needed
when you update your .pxsl documents.
Reference: pxlscc
Usage: pxslcc [OPTION...] [file] -i[NUM] --indent[=NUM] Re-indent XML using NUM spaces per nesting level -h --header Insert edit-the-PXSL-instead header into output XML -x --xslt Add XSLT defaults -a FILE --add=FILE Add the given defaults file --export Export (print) all of the active defaults --dump Dump internal parse format (for debugging)
The --header option requires some explanation. It inserts the following
header comment into the output XML:
<!--
NOTICE: This XML document was generated from PXSL source. If you want to edit this file, you should probably edit the original PXSL source file instead.
-->
It's a good idea to use the --header option all of the time. This
prevents you (or somebody else) from accidentally editing an XML file
when you really ought to be editing the PXSL file from which the
XML file is generated.
Local Variables: mode:outline End:
pxsl-tools-1.0/src/ 0000775 0000764 0000764 00000000000 10731010155 013002 5 ustar thor thor pxsl-tools-1.0/src/PxsltFormat.hs 0000664 0000764 0000764 00000010430 10731010155 015617 0 ustar thor thor -- | PxsltFormat -- Format PXSLT statements for output.
--
-- CVS: $Id: PxsltFormat.hs,v 1.8 2003/06/05 19:37:02 thor Exp $
--
-- Copyright (C) 2003 Tom Moertel
-- Licensed under the terms of the GNU General Public License.
-- See the LICENSE file in the project distribution for details.
module PxsltFormat
( format
, formatWithIndenter
, sourceIndenter
, nestingIndenter
, Indenter
)
where
import Data.List (intersperse)
import Control.Monad.State
import PxsltParserTerms
import PxsltParserTreeTransforms
import XmlString
-- | The following data types
data FormatContext = FC { shouldIndentQ :: Bool, nesting :: Int }
type Indenter = Int -> Int -> Int
type FormatterState = State (Indenter, FormatContext)
type FormatterStringS = FormatterState (String -> String)
sourceIndenter :: Indenter
sourceIndenter sourceColumn _ = sourceColumn -- use indent from source
nestingIndenter :: Int -> Indenter
nestingIndenter n _ nestingDepth = n * nestingDepth -- use nesting for indent
getIndent :: Int -> FormatterStringS
getIndent sourceColumn = do
(indenter, ctx) <- get
return $ if shouldIndentQ ctx
then spacer (indenter sourceColumn (nesting ctx))
else id
allowIndenting :: Bool -> FormatterState ()
allowIndenting allowQ =
modify (\ (idr, ctx) -> (idr, ctx { shouldIndentQ = allowQ }))
modifyNesting :: (Int -> Int) -> FormatterState ()
modifyNesting f =
modify (\ (idr, ctx) -> (idr, ctx { nesting = f (nesting ctx) }))
spacer :: Int -> (String -> String)
spacer n = (replicate n ' ' ++)
-- | Formats a PXSL statement as XML using the PXSL source's indenting.
format :: Statement -> String
format = formatWithIndenter const
-- | Formats a PXSL statement as XML.
formatWithIndenter :: Indenter -> Statement -> String
formatWithIndenter indenter stmt =
evalState (formatter stmt) state0 $ ""
where
state0 = (indenter, FC { shouldIndentQ = True, nesting = 0 })
formatter :: Statement -> FormatterStringS
formatter stmt = case stmt of
Empty -> allowIndenting True >> return ('\n':)
Literal _ xs -> allowIndenting False >> return (xsToXmlS xs)
Comment (_, scol+1) txt -> do
indent <- getIndent scol
allowIndenting True
return (indent . ("\n"++))
Element (_, scol+1) name pAttrs nAttrs children -> do
startIndent <- getIndent scol
allowIndenting True
modifyNesting (+1)
contentFmts <- mapM formatter (dropEmpty `fromBack` children)
trailerFmts <- mapM formatter (takeWhile (==Empty) (reverse children))
modifyNesting (flip (-) 1)
(startTagClose, endTag) <- getIndent scol >>= return . getTags
allowIndenting True
let startTag = ('<':) . (name++) . pAttrRep . nAttrRep . startTagClose
return (startIndent . startTag . -- opening tag
foldr (.) id contentFmts . -- element contents
endTag . -- closing tag
foldr (.) id trailerFmts) -- trailing blank lines
where
getTags indentFn =
if all (==Empty) children
then (("/>" ++), id)
else ((">"++) , indentFn . ("" ++) . (name ++) . (">\n" ++))
pAttrRep = case pAttrs of
[] -> id
ps -> (' ':) .
foldr (.) id (intersperse (' ':)
(map (curry showAttr "DEFAULT") ps))
nAttrRep = case nAttrs of
[] -> id
ns -> (' ':) .
foldr (.) id (intersperse (' ':) (map showAttr ns))
showAttr (name, val) = (name++) . ('=':) . quote (concatMap fmtA val)
fmtA = formatWithIndenter (nestingIndenter 2)
quote s sn = '\"' : concatMap q s ++ "\"" ++ sn
q '\"' = """
q c = [c]
Error (line, col) msg -> return $
("(ERROR: (line " ++) . (shows line) . (", col "++) . (shows col)
. (") "++) . (msg++) . (")\n"++)
x ->
return $ ("(UNKNOWN: " ++) . shows x . (')':)
escComment :: String -> String
escComment "" = ""
escComment ('-':'-':rest) = "-=" ++ escComment rest
escComment (x:rest) = x : escComment rest
pxsl-tools-1.0/src/UTF8.hs 0000664 0000764 0000764 00000003041 10731010155 014062 0 ustar thor thor -- $Id: UTF8.hs,v 1.4 2004/02/28 04:20:46 john Exp $
-- arch-tag: 596040c5-d420-4cc6-add6-c4612cfe2d27
{-
This file is borrowed from John Meacham's JHC project, which he
has licensed under the GPL.
http://repetae.net/john/computer/jhc/
-}
module UTF8(toUTF, fromUTF) where
import Data.Bits
import Data.Char
import Data.Word(Word8)
-- | Convert Unicode characters to UTF-8.
toUTF :: String -> [Word8]
toUTF [] = []
toUTF (x:xs) | ord x<=0x007F = (fromIntegral $ ord x):toUTF xs
| ord x<=0x07FF = fromIntegral (0xC0 .|. ((ord x `shift` (-6)) .&. 0x1F)):
fromIntegral (0x80 .|. (ord x .&. 0x3F)):
toUTF xs
| otherwise = fromIntegral (0xE0 .|. ((ord x `shift` (-12)) .&. 0x0F)):
fromIntegral (0x80 .|. ((ord x `shift` (-6)) .&. 0x3F)):
fromIntegral (0x80 .|. (ord x .&. 0x3F)):
toUTF xs
-- | Convert UTF-8 to Unicode.
fromUTF :: [Word8] -> String
fromUTF xs = fromUTF' (map fromIntegral xs) where
fromUTF' [] = []
fromUTF' (all@(x:xs))
| x<=0x7F = (chr (x)):fromUTF' xs
| x<=0xBF = err
| x<=0xDF = twoBytes all
| x<=0xEF = threeBytes all
| otherwise = err
twoBytes (x1:x2:xs) = chr ((((x1 .&. 0x1F) `shift` 6) .|.
(x2 .&. 0x3F))):fromUTF' xs
twoBytes _ = error "fromUTF: illegal two byte sequence"
threeBytes (x1:x2:x3:xs) = chr ((((x1 .&. 0x0F) `shift` 12) .|.
((x2 .&. 0x3F) `shift` 6) .|.
(x3 .&. 0x3F))):fromUTF' xs
threeBytes _ = error "fromUTF: illegal three byte sequence"
err = error "fromUTF: illegal UTF-8 character"
pxsl-tools-1.0/src/GetInput.hs 0000664 0000764 0000764 00000005363 10731010155 015104 0 ustar thor thor -- GetInput.hs
-- Tom Moertel
-- CVS $Id: GetInput.hs,v 1.1 2003/05/21 18:50:21 thor Exp $
-- | This module provides a method of getting input from files named
-- on the command line or, if no files are provided, from standard
-- input. This mimics Perl's default input handling, which is
-- convenient. Also, the module provides versions of the standard
-- 'interact' function that use these input-getting behaviors.
module GetInput ( getInputDefault, getInputFromArgs
, interactDefault, interactFromArgs) where
import Control.Monad (liftM)
import System.Environment (getArgs)
-- | Reads the arguments passed on the command line and then passes
-- them to 'getInputFromArgs' for handling.
getInputDefault :: IO String
getInputDefault = getArgs >>= getInputFromArgs
-- | Treats the input list as a list of files from which to read
-- input sequentially. If the list is empty, input is read from
-- standard input. If "-" is passed as a file, it is taken to
-- mean standard input.
getInputFromArgs :: [String] -> IO String
getInputFromArgs [] = getContents
getInputFromArgs xs = liftM concat (mapM readFromFile xs)
where
readFromFile "-" = getContents
readFromFile file = readFile file
-- | Gets input via 'getInputDefault', processes it with the
-- function argument @f@, and then prints the @String@ that
-- @f@ returns.
interactDefault :: (String -> String) -> IO ()
interactDefault f = getInputDefault >>= putStrLn . f
-- | Gets input via 'getInputFromArgs', processes it with the
-- function argument @f@, and then prints the @String@ that
-- @f@ returns.
interactFromArgs :: [String] -> (String -> String) -> IO ()
interactFromArgs args f
= getInputFromArgs args >>= putStrLn . f
-- =================================================================
--
-- Copyright (C) 2002 Thomas Moertel.
--
-- This program is free software; you can redistribute it and/or
-- modify it under the terms of the GNU General Public License
-- as published by the Free Software Foundation; either version 2
-- of the License, or (at your option) any later version.
--
-- The text of the GNU GPL may be found in the LICENSE file,
-- included with this software, or online at the following URL:
--
-- http://www.gnu.org/copyleft/gpl.html
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- Except as provided for under the terms of the GNU GPL, all rights
-- are reserved worldwide.
--
-- =================================================================
pxsl-tools-1.0/src/PxsltElementDefaults.hs 0000664 0000764 0000764 00000011671 10731010155 017460 0 ustar thor thor -- | PxsltElementDefaults contains the code to read element-defaults
-- databases and apply them to PXSL statements.
--
-- CVS: $Id: PxsltElementDefaults.hs,v 1.6 2003/05/29 03:43:58 thor Exp $
--
-- Copyright (C) 2003 Tom Moertel
-- Licensed under the terms of the GNU General Public License.
-- See the LICENSE file in the project distribution for details.
module PxsltElementDefaults
( applyDefaults
, readDefaults
, showDefaults
, mergeDefaults
, xsltDefaults
, emptyDefaults
)
where
import PxsltParserTerms
import qualified Data.Map as Map
import Data.List (isPrefixOf, intersperse)
import Data.Char (isSpace)
type ElementDefaultDatabase = Map.Map String (String, [String])
applyDefaults :: ElementDefaultDatabase -> Statement -> Statement
applyDefaults database e@(Element _ nm pattrs nvattrs chldn) =
case Map.lookup nm database of
Nothing -> e { children = chldn' }
Just (name', pnames) ->
e { name = name'
, posnAttrs = []
, namedAttrs = zip pnames pattrs ++ nvattrs
, children = chldn'
}
where chldn' = map (applyDefaults database) chldn
applyDefaults _ e = e
readDefaults :: String -> ElementDefaultDatabase
readDefaults = Map.fromList . concatMap parseLine . stripComments . lines
where
stripComments = filter (any (not.isSpace)) . filter (not . isPrefixOf "#")
parseLine l = map (\nm -> (nm, (canonName, pAttrs))) (words shortNames)
where
(shortNames, defnStr) = break (=='=') l
(canonName:pAttrs) = words (tail defnStr)
showDefaults :: ElementDefaultDatabase -> String
showDefaults = unlines . map showEntry . Map.toList
where
showEntry (shortName, (canonName, pAttrs)) =
pad 20 shortName ++ " = " ++ canonName ++ " " ++ join pAttrs
pad n s = s ++ take (n - length s) (repeat ' ')
join = concat . intersperse " "
mergeDefaults :: ElementDefaultDatabase -> ElementDefaultDatabase
-> ElementDefaultDatabase
mergeDefaults = Map.union
emptyDefaults :: ElementDefaultDatabase
emptyDefaults = Map.empty
xsltDefaults :: ElementDefaultDatabase
xsltDefaults = Map.fromList . concatMap copyForNS $
[ ("stylesheet" , ("xsl:stylesheet" , []))
, ("transform" , ("xsl:transform" , []))
, ("import" , ("xsl:import" , nHref))
, ("include" , ("xsl:include" , nHref))
, ("strip-space" , ("xsl:strip-space" , nElements))
, ("preserve-space" , ("xsl:preserve-space" , nElements))
, ("output" , ("xsl:output" , []))
, ("key" , ("xsl:key" , ["name","match","use"]))
, ("decimal-format" , ("xsl:decimal-format" , []))
, ("namespace-alias" , ("xsl:namespace-alias" , nNamespaceMap))
, ("template" , ("xsl:template" , ["match", "name"]))
, ("value-of" , ("xsl:value-of" , nSelect ++ nDisableEscing))
, ("copy-of" , ("xsl:copy-of" , nSelect))
, ("number" , ("xsl:number" , []))
, ("apply-templates" , ("xsl:apply-templates" , nSelect ++ nMode))
, ("apply-imports" , ("xsl:apply-imports" , []))
, ("for-each" , ("xsl:for-each" , nSelect ++ nSpace))
, ("sort" , ("xsl:sort" , nSelect))
, ("if" , ("xsl:if" , nTest ++ nSpace))
, ("choose" , ("xsl:choose" , nSpace))
, ("when" , ("xsl:when" , nTest ++ nSpace))
, ("otherwise" , ("xsl:otherwise" , nSpace))
, ("attribute-set" , ("xsl:attribute-set" , nName ++ ["use-attribute-sets"]))
, ("call-template" , ("xsl:call-template" , nName))
, ("with-param" , ("xsl:with-param" , nName ++ nSelect))
, ("variable" , ("xsl:variable" , nName ++ nSelect))
, ("param" , ("xsl:param" , nName ++ nSelect))
, ("text" , ("xsl:text" , nDisableEscing))
, ("element" , ("xsl:element" , nName))
, ("attribute" , ("xsl:attribute" , nName ++ nNamespace))
, ("comment" , ("xsl:comment" , nSpace))
, ("copy" , ("xsl:copy" , nSpace))
, ("message" , ("xsl:message" , []))
, ("fallback" , ("xsl:fallback" , nSpace))
, ("processing-instruction"
, ("xsl:processing-instruction", nName ++ nSpace))
]
where
copyForNS e@(_, (nsName, pAttrs)) = [e, (nsName, (nsName, pAttrs))]
nDisableEscing = ["disable-output-escaping"]
nElements = ["elements"]
nHref = ["href"]
nMode = ["mode"]
nName = ["name"]
nNamespace = ["namespace"]
nNamespaceMap = ["stylesheet-prefix", "result-prefix"]
nSelect = ["select"]
nSpace = ["xml:space"]
nTest = ["test"]
pxsl-tools-1.0/src/PxsltParser.hs 0000664 0000764 0000764 00000017116 10731010155 015633 0 ustar thor thor -- PxsltParser
--
-- CVS: $Id: PxsltParser.hs,v 1.11 2004/02/23 08:19:27 thor Exp $
--
-- Copyright (C) 2003 Tom Moertel
-- Licensed under the terms of the GNU General Public License.
-- See the LICENSE file in the project distribution for details.
module PxsltParser (pxslParser) where
import Text.ParserCombinators.Parsec
import PxsltParserTerms
import XmlString
pxslParser :: Parser [Statement]
pxslParser = do
sts <- many (statement (-1) > "top-level statement")
eof
return sts
statement :: Int -> Parser Statement
statement col = (try empty <|> nonEmpty) > "statement"
where
nonEmpty = do
hspaces
indent <- getSourceColumn
if indent <= col
then pzero
else ((pxslComment >> statement col)
<|> xmlComment <|> literal <|> element
<|> try macroRef <|> try macroDef <|> macroApp)
empty :: Parser Statement
empty = blankLine >> return Empty
pxslComment :: Parser ()
pxslComment = do
char '#'
manyTill anyChar (skip newline <|> eof)
return ()
xmlComment :: Parser Statement
xmlComment = do
ctx <- getSourceContext
string "--" > "comment delimiter"
text <- manyTill anyChar (skip newline <|> eof)
return (Comment ctx text)
literal :: Parser Statement
literal = do
ctx <- getSourceContext
xstr <- quotedXmlString
hspaces
optional newline
return (Literal ctx xstr)
element :: Parser Statement
element = do
ctx@(_, col) <- getSourceContext
name <- xmlName > "element name"
hspaces
posnArgs <- exprList > "positional arguments"
nvpArgs <- nameValuePairList > "named arguments"
children <- subStatements col
hspaces
return (Element ctx name posnArgs nvpArgs children)
where
exprList = many $ try (do optional lineContinuation
e <- argExpr; hspaces; return e)
subStatements col = many (try (statement col > "children statements"))
macroRef :: Parser Statement
macroRef = do
ctx@(_, col) <- getSourceContext
string ",," > ",, introducing macro reference"
name <- xmlName > "macro name"
hspaces
posnArgs <- exprList > "positional arguments"
nvpArgs <- nameValuePairList > "named arguments"
children <- subStatements col > "BODY argument"
hspaces
return (MacroRef ctx name posnArgs nvpArgs children)
where
exprList = many $ try (do optional lineContinuation
e <- argExpr; hspaces; return e)
subStatements col = many (try (statement col > "children statements"))
macroDef :: Parser Statement
macroDef = do
ctx@(_, col) <- getSourceContext
char ',' > ", introducing macro defn"
name <- (hspace >> return "") <|> xmlName > "macro name"
parmNames <- xmlNameList > "parameter list"
char '=' > "= [macro defn]"
many1 space -- we require at least one space to disambiguate
-- from macro application ",test var=val"
bodyStatements <- subStatements col > "macro defn body"
hspaces
return (MacroDef ctx name parmNames bodyStatements)
where
xmlNameList = many $ try (optional lineContinuation >> xmlName)
subStatements col = many (try (statement col > "macro body statement"))
macroApp :: Parser Statement
macroApp = do
ctx@(_, col) <- getSourceContext
char ',' > ", introducing macro application"
name <- xmlName > "macro name"
hspaces
posnArgs <- exprList > "positional arguments"
nvpArgs <- nameValuePairList > "named arguments"
children <- subStatements col > "BODY argument"
hspaces
return (MacroApp ctx name posnArgs nvpArgs children)
where
exprList = many $ try (do optional lineContinuation
e <- argExpr; hspaces; return e)
subStatements col = many (try (statement col > "children statements"))
nameValuePairList :: Parser [(String, [Statement])]
nameValuePairList = many (do optional lineContinuation
nvp <- nameValuePair
optional lineContinuation <|> hspaces1
return nvp)
where
nameValuePair = do
try (char '-' >> notFollowedBy (char '-')) > "-name=value pair"
optName <- xmlName > "name for -name=value pair"
spacesDelimited (char '=') > "equals sign for -name=value pair"
optValue <- expr > "value for -name=value pair"
hspaces
return (optName, optValue)
expr :: Parser [Statement]
expr = (exprList <|> exprSingle) > "expression"
where
exprSingle = do
ctx <- getSourceContext
lit <- quotedXmlString <|> rawString
return [Literal ctx lit]
exprList = do
pxp <- parenExpr <|> evalExpr
pxps <- option [] expr
return (pxp ++ pxps)
rawString = do
str <- many1 rawChar
return (XSCdata str)
rawChar :: Parser Char
rawChar = do
notFollowedBy space
try ((try (string ")>") >> unexpected ")>")
<|> return ())
anyChar
evalExpr :: Parser [Statement]
evalExpr = do
ctx <- getSourceContext
try (string "<(") > "opening <( for eval exprssion"
spaces -- layout starts at next non-whitespace char
sts <- many (statement (-1)) > "eval-expression statements"
spaces
string ")>" > "closing )> for eval expression"
return [Eval ctx sts]
parenExpr :: Parser [Statement]
parenExpr = pexp > "parenthesized expression"
where
pexp = do
ctx <- getSourceContext
middle <- inBetweenParens
return [Literal ctx . XSMixed $ middle]
inBetweenParens = do
gexp <- between (char '(') (char ')') generalExpr
return $ "(" ++ gexp ++ ")"
generalExpr = do
pieces <- many (inBetweenParens <|> many1 (noneOf "()"))
return (concat pieces)
argExpr :: Parser [Statement]
argExpr = do
ctx <- getSourceContext
e <- ((notFollowedBy (oneOf "-<)" <|> newline) >> expr) <|> evalExpr)
> "argument expression"
return e
xmlName :: Parser String
xmlName = do
firstChar <- letter <|> oneOf "_:"
rest <- many (letter <|> digit <|> oneOf ".-_:")
hspaces
return (firstChar : rest)
quotedXmlString :: Parser XmlString
quotedXmlString = do
char '<'
cdataLiteral <|> mixedLiteral
where
cdataLiteral = do
char '{'
txt <- manyTill anyChar (string "}>")
return (XSCdata txt)
mixedLiteral = do
char '<'
txt <- manyTill anyChar (try (string ">>" >> notFollowedBy (char '>')))
return (XSMixed txt)
hspaces :: Parser ()
hspaces = skipMany hspace
hspaces1 :: Parser ()
hspaces1 = skipMany1 hspace
hspace :: Parser Char
hspace = char ' '
blankLine :: Parser ()
blankLine = skipMany hspace >> newline >> return ()
lineContinuation :: Parser ()
lineContinuation = (char '\\' >> newline >> hspaces) > "line continuation"
getSourceColumn :: Parser Int
getSourceColumn = getPosition >>= return . sourceColumn
getSourceLine :: Parser Int
getSourceLine = getPosition >>= return . sourceLine
getSourceContext :: Parser SourceContext
getSourceContext = do
line <- getSourceLine
col <- getSourceColumn
return (line, col)
skip :: Parser a -> Parser ()
skip a = a >> return ()
-- | Allows any amount of whitespace (including none) on either side
-- of parser 'p'.
spacesDelimited :: Parser a -> Parser a
spacesDelimited p = do spaces; val <- p; spaces; return val
pxsl-tools-1.0/src/XmlString.hs 0000664 0000764 0000764 00000002604 10731010155 015267 0 ustar thor thor -- | Strings representing XML content.
--
-- CVS: $Id: XmlString.hs,v 1.4 2005/01/27 14:43:52 thor Exp $
--
-- Copyright (C) 2003 Tom Moertel
-- Licensed under the terms of the GNU General Public License.
-- See the LICENSE file in the project distribution for details.
module XmlString where
data XmlString = XSMixed String
| XSCdata String
| XSRun [XmlString]
deriving (Eq, Read, Show)
xsToXml :: XmlString -> String
xsToXml = flip xsToXmlS ""
xsToXmlS :: XmlString -> String -> String
xsToXmlS (XSCdata s) = (escapeEntities s ++)
xsToXmlS (XSMixed s) = (s ++)
xsToXmlS (XSRun ss) = foldr (.) id (map xsToXmlS ss)
xsToString :: XmlString -> String
xsToString = flip xsToStringS ""
xsToStringS :: XmlString -> String -> String
xsToStringS (XSMixed s) = (s ++)
xsToStringS (XSCdata s) = (s ++)
xsToStringS (XSRun ss) = foldr (.) id (map xsToStringS ss)
xsToAttrVal :: XmlString -> String
xsToAttrVal = xsToXml
{- no such thing as mixed attr values so we treat all as cdata
xsToAttrValS :: XmlString -> String -> String
xsToAttrValS (XSCdata s) = (escapeEntities s ++)
xsToAttrValS (XSMixed s) = (escapeEntities s ++)
xsToAttrValS (XSRun ss) = foldr (.) id (map xsToAttrValS ss)
-}
escapeEntities :: String -> String
escapeEntities = concatMap quote
where
quote '&' = "&"
quote '<' = "<"
quote x = [x]
pxsl-tools-1.0/src/PxsltMacros.hs 0000664 0000764 0000764 00000007567 10731010155 015634 0 ustar thor thor -- PxsltMacros
--
-- CVS: $Id: PxsltMacros.hs,v 1.9 2004/02/23 08:19:27 thor Exp $
--
-- Copyright (C) 2003 Tom Moertel
-- Licensed under the terms of the GNU General Public License.
-- See the LICENSE file in the project distribution for details.
module PxsltMacros
( applyMacros
, emptyEnv
)
where
import Data.List
import Control.Monad.Reader
import Data.Maybe (mapMaybe)
import PxsltParserTerms
import XmlString
newtype MacroEnv = MacroEnv [(String, Either (MacroEnv,Statement) [Statement])]
type MacroReader = Reader MacroEnv
applyMacros :: MacroEnv -> [Statement] -> [Statement]
applyMacros env sts = runReader (app sts) env
-- | We want to remove all MacroDefs and MacroApps from the parse
-- tree and replace the MacroApps with their results, which are
-- either Errors or expansions ([Statement]).
app :: [Statement] -> MacroReader [Statement]
app sts = do
env <- ask
let (envAdditions, sts') = extractMacroDefs env sts
expansions <- local (envAdditions `joinEnv`) (mapM expandMacros sts')
return (concat expansions)
-- | W.r.t. the given environment, extract the macro definitions in
-- the input list and return an environment that contains them (and
-- the list less the macro definitions).
extractMacroDefs :: MacroEnv -> [Statement] -> (MacroEnv, [Statement])
extractMacroDefs env sts = (mdefs, sts')
where
mdefs = MacroEnv . reverse $ mapMaybe mkEnvEntry sts
env' = mdefs `joinEnv` env -- closure for the new macros
sts' = filter (not . isMacroDefQ) sts
mkEnvEntry mdef@(MacroDef _ nm _ _) = Just (nm, Left (env', mdef))
mkEnvEntry _ = Nothing
isMacroDefQ MacroDef{} = True
isMacroDefQ _ = False
-- | If the input Statement is a macro application, evaluate it in the
-- context of the active environment and return the resulting
-- statement list after the applications have been replaced with their
-- results, which can be either an Error or an expansion [Statement].
-- Then recurse through the result via app, expanding any macro
-- applications within.
expandMacros :: Statement -> MacroReader [Statement]
expandMacros (MacroApp ctx nm pArgs nArgs body) = do
menv@(MacroEnv env) <- ask
case lookup nm env of
-- name doesn't exist within this evaluation environment ?
Nothing -> return (errNotDefined nm)
-- name exists and maps to a macro definition ?
Just (Left (macroEnv, MacroDef mctx _ mparms mexpansion)) ->
return $ applyMacros (enterMacro macroEnv) mexpansion
where
enterMacro = joinEnv . MacroEnv . map (prepareArgs menv) $
nArgs ++ zip mparms pArgs' ++ [("BODY",body)]
pArgs' = pArgs ++ repeat [Literal ctx (XSCdata "(UNDEFINED)")]
{- MacroRef support isn't official
Just (Right (MacroRef rctx rnm rpArgs rnArgs rbody : _)) ->
expandMacros (MacroApp OOOOOOOOOO MaroRef work in progress OOOOO
-}
-- name exists and maps to a document fragment ?
Just (Right x) -> app x
where
prepareArgs menv (n, [Eval _ (mdef@MacroDef{name=""}:_)]) =
(n, Left (menv,mdef)) -- anonymous macro
prepareArgs menv (n, x) =
(n, Right (applyMacros menv x))
errNotDefined nm = [Error ctx ("Could not find a value for \","
++nm++ "\".")]
expandMacros (Element ctx nm pa na chldn) = do
pa' <- mapM app pa
na' <- mapM (\ (n,v) -> do v' <- app v; return (n,v') ) na
chldn' <- app chldn
return [Element ctx nm pa' na' chldn']
expandMacros (Eval ctx sts) = do
sts' <- app sts
return sts'
expandMacros x = return [x]
-- | An empty environment.
emptyEnv :: MacroEnv
emptyEnv = MacroEnv []
-- | Join two environments. The first shadows the second if
-- their are overlaps.
joinEnv :: MacroEnv -> MacroEnv -> MacroEnv
joinEnv (MacroEnv a) (MacroEnv b) = MacroEnv (a ++ b)
pxsl-tools-1.0/src/PxsltParserTreeTransforms.hs 0000664 0000764 0000764 00000005276 10731010155 020536 0 ustar thor thor -- | PxsltParserTreeTransforms -- Transform parse trees
--
-- CVS: $Id: PxsltParserTreeTransforms.hs,v 1.2 2003/05/29 03:47:07 thor Exp $
--
-- Copyright (C) 2003 Tom Moertel
-- Licensed under the terms of the GNU General Public License.
-- See the LICENSE file in the project distribution for details.
module PxsltParserTreeTransforms
( cullLiteralDominatedEmpties
, liftTrailingEmpties
, trimEmpties
, dropEmpty
, fromBack
, bidir
)
where
import PxsltParserTerms
-- | Trim away Empties surrounding Literals because the literals gobble
-- surrounding whitespace.
cullLiteralDominatedEmpties :: Statement -> Statement
cullLiteralDominatedEmpties = cull
where
cull e@(Element _ _ _ _ children) =
e { children = map cull (cull' children) }
cull md@(MacroDef _ _ _ body) =
md { body = map cull (cull' body) }
cull nonElement = nonElement
cull' children = children'
where
children' = case (dropEmpty children, dropEmpty (reverse children)) of
(x@(Literal{}:_), (Literal{}:_)) -> bidir dropEmpty x
(x@(Literal{}:_), _ ) -> x
( _ , y@(Literal{}:_)) -> reverse y
( _ , _ ) -> children
-- | Reparent a statement's excess trailing Empty elements so that
-- they are as high as possible in the tree. That has the effect of
-- allowing parents to be closed early instead of late:
--
--
-- ===>
--
--
liftTrailingEmpties :: Statement -> Statement
liftTrailingEmpties e@(Element _ _ _ _ cs@(_:_)) =
e { children = concatMap (trimEmpties . liftTrailingEmpties) cs }
liftTrailingEmpties md@(MacroDef _ _ _ cs@(_:_)) =
md { body = concatMap (trimEmpties . liftTrailingEmpties) cs }
liftTrailingEmpties x = x
trimEmpties :: Statement -> [Statement]
trimEmpties e@(Element _ _ _ _ (ch:ct)) = e { children = cs' } : trimmedEs
where
cs' = ch : reverse rct'
(trimmedEs, rct') = span (==Empty) (reverse ct)
trimEmpties e@(MacroDef _ _ _ (ch:ct)) = e { children = cs' } : trimmedEs
where
cs' = ch : reverse rct'
(trimmedEs, rct') = span (==Empty) (reverse ct)
trimEmpties x = [x]
-- | Drop Empty statements at the beginning of a list.
dropEmpty :: [Statement] -> [Statement]
dropEmpty = dropWhile (==Empty)
-- | Apply a list function to a list from the back.
fromBack :: ([a] -> [a]) -> [a] -> [a]
fromBack f = reverse . f . reverse
-- | Apply a list function to a list from the front and back, i.e.,
-- bidirectionally.
bidir :: ([a] -> [a]) -> [a] -> [a]
bidir f = reverse . f . reverse . f
pxsl-tools-1.0/src/pxslcc.hs 0000664 0000764 0000764 00000010313 10731010155 014630 0 ustar thor thor -- | pxslcc - compiles PXSL into XML
--
-- CVS: $Id: pxslcc.hs,v 1.11 2005/01/27 14:43:52 thor Exp $
--
-- Copyright (C) 2003-05 Tom Moertel
-- Licensed under the terms of the GNU General Public License.
-- See the LICENSE file in the project distribution for details.
module Main (main) where
import Control.Monad (liftM)
import Data.Char (ord, chr)
import Data.Maybe (maybe)
import System.IO
import System.Environment (getArgs)
import System.Console.GetOpt
import Text.ParserCombinators.Parsec
import GetInput
import PxsltElementDefaults
import PxsltFormat
import PxsltMacros
import PxsltParser
import PxsltParserTerms
import PxsltParserTreeTransforms
import UTF8 (fromUTF, toUTF)
printVersion :: IO ()
printVersion =
putStrLn "pxslcc 1.0 Copyright 2003-07 Thomas Moertel"
main :: IO ()
main = do
rawArgs <- getArgs
case getOpt Permute opts rawArgs of
(flags, args, []) -> run args flags
(_, _, errs) -> handleArgErrors errs
data Flag = Dump | UseDB String | UseXsltDB | ExportDB | Indent Indenter
| AddHeader | Version
opts :: [OptDescr Flag]
opts =
[ o "i" ["indent"] (OptArg mkIndent "NUM")
"Re-indent XML using NUM spaces per nesting level"
, o "h" ["header"] (NoArg AddHeader)
"Insert edit-the-PXSL-instead header into output XML"
, o "x" ["xslt"] (NoArg UseXsltDB) "Add XSLT defaults"
, o "a" ["add"] (ReqArg UseDB "FILE") "Add the given defaults file"
, o "" ["export"] (NoArg ExportDB)
"Export (print) all of the active defaults"
, o "" ["dump"] (NoArg Dump) "Dump internal parse format (for debugging)"
, o "v" ["version"] (NoArg Version) "Print version number and exit"
]
where
o = Option
mkIndent = Indent . nestingIndenter . maybe 2 read
run :: [String] -> [Flag] -> IO ()
run args flags = do
case [() | Version <- flags] of
_:_ -> printVersion
_ -> run2
where
run2 = do
loadedDBs <- mapM loadDB [fileName | UseDB fileName <- flags]
let indenter = head [i | Indent i <- flags++[Indent sourceIndenter]]
let formatter = case [() | Dump <- flags] of
[] -> formatWithIndenter indenter . tightenTree
_ -> (++"\n").show
let combinedDB = mkCombinedDB loadedDBs
let addHead = case [() | AddHeader <- flags] of
[] -> id
_ -> addHeader
case [() | ExportDB <- flags] of
[] -> parseAndPrint addHead (formatter . applyDefaults combinedDB) args
_ -> putStr ( "# BEGIN PXSL ELEMENT DEFAULTS DATABASE\n"
++ showDefaults combinedDB
++ "# END PXSL ELEMENT DEFAULTS DATABASE\n")
where
loadDB fn = readFile fn >>= return . readDefaults
builtinDBs = [xsltDefaults | UseXsltDB <- flags]
mkCombinedDB dbs = foldl mergeDefaults emptyDefaults (builtinDBs ++ dbs)
tightenTree = liftTrailingEmpties . cullLiteralDominatedEmpties
handleArgErrors :: [String] -> IO ()
handleArgErrors errs =
ioError (userError (concat errs ++ usageInfo header opts))
where
header = "Usage: pxslcc [OPTION...] [file]"
parseAndPrint :: (String->String) -> (Statement -> String) -> [String] -> IO ()
parseAndPrint headerFn formatFn args = do
input <- liftM fromUTF8 (getInputFromArgs args)
case parse pxslParser (fileName args) input of
Left err -> do hPutStrLn stderr (show err); ioError (userError "Parse error; aborted.")
Right x -> putStr . toUTF8 . headerFn $
concatMap formatFn (applyMacros emptyEnv x)
where
fileName args = case args of f:_ -> f; _ -> ""
toUTF8 s = (map (chr. fromIntegral) $ toUTF s)
fromUTF8 s = fromUTF (map (fromIntegral . ord) s)
addHeader :: String -> String
addHeader = unlines . addHeader' . lines
where
addHeader' (pi@('<':'?':_):rest) = pi : header : rest
addHeader' doc = header : doc
header = "\n"
pxsl-tools-1.0/src/PxsltParserTerms.hs 0000664 0000764 0000764 00000007233 10731010155 016645 0 ustar thor thor -- | Base PXSL data types.
--
-- CVS: $Id: PxsltParserTerms.hs,v 1.1 2005/01/27 14:43:52 thor Exp $
--
-- Copyright (C) 2003 Tom Moertel
-- Licensed under the terms of the GNU General Public License.
-- See the LICENSE file in the project distribution for details.
module PxsltParserTerms where
import XmlString
type Line = Int
type Column = Int
type SourceContext = (Line, Column)
type Attribute = ( String, [Statement] )
data Statement = Element { context :: SourceContext
, name :: String
, posnAttrs :: [[Statement]]
, namedAttrs :: [Attribute]
, children :: [Statement] }
| Literal { context :: SourceContext
, xstr :: XmlString }
| Comment { context :: SourceContext
, text :: String }
| MacroDef{ context :: SourceContext
, name :: String
, parms :: [String]
, body :: [Statement] }
| MacroApp{ context :: SourceContext
, name :: String
, posnArgs :: [[Statement]]
, namedArgs :: [(String, [Statement])]
, children :: [Statement] }
| MacroRef{ context :: SourceContext
, name :: String
, posnArgs :: [[Statement]]
, namedArgs :: [(String, [Statement])]
, children :: [Statement] }
| Eval { context :: SourceContext
, children :: [Statement] }
| Error { context :: SourceContext
, text :: String }
| Empty -- ^ blank line
deriving (Eq, Read)
instance Show Statement where
showsPrec _ l@Literal{} = ("XML: " ++) . xsToXmlS (xstr l)
showsPrec _ c@Comment{} = ("Comment: " ++) . (text c ++)
showsPrec _ Empty = ("Empty" ++)
showsPrec p e@Element{} = ("Element: " ++) . (name e ++) . (' ':) . attrs
. foldr (.) id (map show1 (children e))
where
attrs = showsPrec p (posnAttrs e) . (' ':) . showsPrec p (namedAttrs e)
show1 child = (concatMap ("\n "++) (lines (show child)) ++)
showsPrec p md@MacroDef{} = ("MacroDef: " ++) . (name md ++) . (' ':)
. showsPrec p (parms md) . (" =" ++)
. foldr (.) id (map show1 (body md))
where
show1 child = (concatMap ("\n "++) (lines (show child)) ++)
showsPrec p e@MacroApp{} = ("MacroApp: " ++) . (name e ++) . (' ':) . attrs
. foldr (.) id (map show1 (children e))
where
attrs = showsPrec p (posnArgs e) . (' ':) . showsPrec p (namedArgs e)
show1 child = (concatMap ("\n "++) (lines (show child)) ++)
showsPrec p e@MacroRef{} = ("MacroRef: " ++) . (name e ++) . (' ':) . attrs
. foldr (.) id (map show1 (children e))
where
attrs = showsPrec p (posnArgs e) . (' ':) . showsPrec p (namedArgs e)
show1 child = (concatMap ("\n "++) (lines (show child)) ++)
showsPrec p e@Eval{} = ("Eval: " ++)
. foldr (.) id (map show1 (children e))
where
show1 child = (concatMap ("\n "++) (lines (show child)) ++)
showsPrec p (Error ctx text) = ("Error: "++) . showsPrec p ctx
. (": "++) . (text++)
pxsl-tools-1.0/Makefile 0000664 0000764 0000764 00000007740 10731010155 013663 0 ustar thor thor #-----------------------------------------------------------------------------
# PXSL Makefile
#
# CVS: $Id: Makefile,v 1.11 2005/01/27 14:43:52 thor Exp $
#
# Copyright (C) 2003-07 Tom Moertel
# Licensed under the terms of the GNU General Public License.
# See the LICENSE file in the project distribution for details.
#-----------------------------------------------------------------------------
PROJECT = pxsl
VERSION = 1.0
TARGETS = dist/build/pxslcc/pxslcc
DOCDIR = docs
TESTDIR = tests
MIRRORHOST =
SPECFILE = pxsl-tools.spec
DISTDIR = sources/$(PROJECT)-$(VERSION)
TARBALL = $(DISTDIR).tar.gz
BINDISTDIR = Linux-binaries/$(PROJECT)-$(VERSION)-$(shell uname -ms | tr 'A-Z ' 'a-z-')
BINTARGETS := README $(TARGETS) examples
HASKELL := $(wildcard src/*.hs src/*.hs.in)
TESTS_IN := $(wildcard $(TESTDIR)/*.xsl)
TESTS_OUT_PXSL := $(TESTS_IN:.xsl=.pxsl)
TESTS_OUT_XSL2 := $(TESTS_IN:.xsl=.xsl2)
TESTS_OUT_DIFF := $(TESTS_IN:.xsl=.diff)
TESTS_OUT_PXSL2 := $(TESTS_IN:.xsl=.pxsl2)
DIST_MANIFEST := README README.html LICENSE Makefile xsl2pxsl.xsl \
text-to-html.pl $(HASKELL) examples TODO \
$(SPECFILE)
GENERATED := $(sort $(TARGETS) $(TESTS_OUT_PXSL) $(TESTS_OUT_XSL2) \
$(TESTS_OUT_PXSL2) $(TESTS_OUT_DIFF))
YEAR2 := $(shell date +%y)
#-----------------------------------------------------------------------------
# top level targets: all docs dist
#-----------------------------------------------------------------------------
.PHONY : all allfast allopt allstatic dist bindist
all : $(TARGETS) README.html
allfast : ; make GHC_OPT='' all
allopt : ; make GHC_OPT='-O2' all
allstatic : ; make GHC_OPT='-O2 -optl-static' all
dist : $(DISTDIR)
bindist : $(BINDISTDIR)
$(DISTDIR) : $(DIST_MANIFEST)
mkdir -p $@
rm -rf $@
rsync -avC --delete-excluded $(DIST_MANIFEST) $@/
echo $(DIST_MANIFEST) | tr ' ' \\012 > $@/MANIFEST
touch $@
cd $(dir $@) && \
rm -f $(notdir $@.tar.gz) && \
tar zcf $(notdir $@.tar.gz) $(notdir $@)
$(BINDISTDIR) : $(BINTARGETS)
@[ -d $@ ] || mkdir -p $@
rsync -avC $(BINTARGETS) $@/
touch $@
rm -f $@.tar.gz && tar zcf $@.tar.gz $@
$(DOCDIR) : $(HASKELL)
@[ -d $@ ] || mkdir -p $@
haddock --html -o $(DOCDIR) $(HASKELL) && touch $@
.PHONY : test
test : $(TESTS_OUT_PXSL2) $(TESTS_OUT_DIFF)
.PHONY : rpms
rpms : dist
rpmbuild -ta $(TARBALL)
.PHONY : mirror
mirror : README-online.html
rsync -e ssh -avC README-online.html $(MIRRORHOST)/pxsl/README.html
rsync -e ssh -avRC examples sources/*.gz Linux-binaries/*.gz \
$(MIRRORHOST)/pxsl/
rsync -e ssh -avC $(HOME)/rpm/RPMS/*/pxsl*rpm $(HOME)/rpm/SRPMS/pxsl*rpm \
$(MIRRORHOST)/pxsl/RPMS
README-online.html : README.html
perl -lpe'BEGIN{print "}i' $< > $@
#-----------------------------------------------------------------------------
# helpers
#-----------------------------------------------------------------------------
# run tests
.SECONDARY : $(TESTS_OUT_PXSL) $(TESTS_OUT_XSL2)
%.pxsl : %.xsl xsl2pxsl.xsl ; xsltproc xsl2pxsl.xsl $< > $@
%.xsl2 : %.pxsl $(TARGETS) ; ./pxslcc -i --xslt $< > $@
%.pxsl2 : %.xsl2 xsl2pxsl.xsl ; xsltproc xsl2pxsl.xsl $< > $@
%.diff : %.xsl %.xsl2 ; diff -uw $^ > $@ || :
# build rules for Haskell
dist/build/pxslcc/pxslcc : $(HASKELL) Makefile
sed -e 's/@VERSION@/$(VERSION)/' -e 's/@YEAR@/$(YEAR2)/' src/pxslcc.hs.in > src/pxslcc.hs
runhaskell Setup.lhs configure
runhaskell Setup.lhs build
# make README.html from README
README.html : README text-to-html.pl
perl text-to-html.pl $< > $@
# stamp RPM spec files
%.spec : %.spec.in Makefile
perl -pe's/\@\@VERSION\@\@/$(VERSION)/g;' $< > $@
# maintenance rules
.PHONY : clean
clean :
rm -f *.o *.hi src/*.o src/*.hi src/*.bak $(GENERATED)
rm -rf $(DOCDIR)
.PHONY : cleandeps
cleandeps :
rm -f *.d
.PHONY : squeakyclean
squeakyclean : clean cleandeps
TAGS : $(HASKELL)
hasktags $(HASKELL)
pxsl-tools-1.0/Setup.lhs 0000775 0000764 0000764 00000000114 10731010155 014022 0 ustar thor thor #!/usr/bin/env runhaskell
> import Distribution.Simple
> main = defaultMain
pxsl-tools-1.0/LICENSE 0000664 0000764 0000764 00000035366 10731010155 013235 0 ustar thor thor GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
pxsl-tools-1.0/README 0000664 0000764 0000764 00000112256 10731010155 013102 0 ustar thor thor
____ _ _ ___ __
( _ \( \/ )/ __)( )
)___/ ) ( \__ \ )(__
(__) (_/\_)(___/(____)
PARSIMONIOUS XML SHORTHAND LANGUAGE
Updated 2007-12-15
PXSL ("pixel") is a convenient shorthand for writing markup-heavy XML
documents. The following document explains why PXSL is needed and
shows you how to use it. For additional information, such as the FAQ
list, visit the community site:
http://community.moertel.com/ss/space/pxsl
You'll get more out of this document if you read it from start to
finish, but you can stop anywhere after the "Gentle Introduction to
PXSL" and be able to take advantage of PXSL in your documents. The
later sections explain PXSL's advanced features. If you're willing to
invest some time in learning them, you will have at your disposal new
and powerful ways to create and refactor XML documents. The advanced
features are more complicated to master, but they can greatly reduce
the complexity of your documents.
* Table of Contents
* Getting PXSL
* Getting help
* License
* Getting or building the PXSL tools
* Gentle Introduction to PXSL
- Why PXSL ?
- A closer look at PXSL
- Using PXSL documents
* Advanced topics
- Element defaults provide convenient, customizable shortcuts
Using element defaults to create attribute shortcuts
Using element defaults to create virtual elements
Making and using your own element defaults
Built-in element defaults for XSLT stylesheets
- Advanced quoting with << >> and <{ }>
- Macro facility
- Advanced macros and passing parameters with the <( )> delimiters
- More advanced macros and functional programming
- Automatic PXSL-to-XML conversion via Make
* Reference: pxlscc
* Reference: PXSL syntax
* Authors
* Getting PXSL
The most-recent official version of PXSL can always be found here:
http://community.moertel.com/pxsl/
By the way, you pronounce PXSL like "pixel".
* Getting help
If you need help with PXSL, there is a discussion site for PXSL
users and developers. Feel free to ask questions and leave your
comments:
PXSL Community Forum
http://community.moertel.com/ss/space/pxsl
PXSL FAQs
http://community.moertel.com/ss/space/PXSL+FAQs
* LICENSE
Copyright (C) 2003--2005 Thomas Moertel & Bill Hubauer.
The PXSL toolkit is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
The text of the GNU GPL may be found in the LICENSE file,
included with this software.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Except as provided for under the terms of the GNU GPL, all rights
are reserved worldwide.
* Getting or building the PXSL tools
If you don't want to build the PXSL tools from source code, you can
download one of the pre-built binary packages on the PXSL web site.
If a binary package isn't available for your computing platform of
choice, you can use the following procedure to build the PXSL tools
for your platform.
In order to build the tools you will need the following:
- A PXSL source tarball (typically named "pxsl-{version}.tar.gz")
http://community.moertel.com/pxsl/
- A Haskell compiler supporting the Cabal build system.
I use GHC: http://www.haskell.org/ghc/
Just uncompress the tarball and build the project using the following
commands. If you want to install a personal copy of pxslcc instead of
the doing the default, system-wide installation, uncomment the extra
command-line flags on the third command.
$ tar zxvf pxsl-{version}.tar.gz
$ cd pxsl-{version}
$ runhaskell Setup.lhs configure # --user --prefix=$HOME
$ runhaskell Setup.lhs build
$ runhaskell Setup.lhs install
(Replace {version} with the version of PXSL that you downloaded.)
That's it. You should now have a fully functional version of pxslcc.
** RPMs
If you are on a Red Hat or Fedora Core Linux system (or a similar
RPM-based distribution), you can build RPMs using the cabal-rpm
tool:
http://hackage.haskell.org/cgi-bin/hackage-scripts/package/cabal-rpm-0.3.2
* Gentle Introduction to PXSL
PXSL ("pixel") is a convenient shorthand for writing markup-heavy XML
documents. This introduction assumes that you are familiar with XML.
If you want a refresher, see the introductions on XML available here:
http://xml.coverpages.org/xmlIntro.html
** Why PXSL ?
XML is a descendant of the markup language SGML and inherits its
ancestor's historical bias toward marking up textual documents.
However, XML is becoming an increasingly popular medium for the
representation of non-textual information such as metadata (RSS, XSD,
RELAX-NG), remote procedure calls (SOAP), and even information
that looks much like programming languages (XSLT, SVG, MathML).
For these uses, XML's text-centric syntax gets in the way.
Consider, for example, this snippet of MathML:
MathML in XML
f x x 2 x 3
Notice something about MathML's structure: There is more markup than
text. In fact, the only text in the snippet is "f x x2 x3"; the rest
is markup. As you can see above, XML's document-centric style of
markup, in which the markup is delimited from the flow of surrounding
text, becomes a hindrance when markup is in the majority.
PXSL, in contrast, was designed specifically to handle this case well.
It makes dense markup easy because it assumes that everything is
markup to begin with. You need only delimit the few portions of text
that are mixed into the flow of surrounding markup.
In other words, PXSL is what you get when you turn XML inside out:
XML PXSL
text markup <>
Let's see how this inside-out transformation simplifies our MathML
example from above:
MathML in XML MathML in PXSL
declare -type=fn
f ci << f >>
lambda
x bvar
ci << x >>
apply
plus
apply
x power
2 ci << x >>
cn << 2 >>
x ci << x >>
3 cn << 3 >>
There are two things to notice about the PXSL version in comparison
to the XML version. First, the PXSL version is shorter. Second, and
most important, PXSL is comparatively free of noisy characters like < >
/ and ". In PXSL, noise is the exception rather than the rule.
** A closer look at PXSL
Writing PXSL is simple. If you know how to write XML, you can write
PXSL. In fact, PXSL is XML, just written in a different, inside-out
syntax. Let's see how it works by way of comparison.
First, every XML document and hence every PXSL document has a root
element. Here is a tiny document that has a root element and nothing
else:
XML PXSL
doc
If the document contains other elements, they are simply placed
underneath the root element, but indented to indicate that the root
element contains them. In XML this indenting is optional, but most
people do it anyway because it is an established practice that makes
documents easier to understand. In PXSL however, the indenting is
mandatory because indentation determines which elements contain
others. (This requirement is what enables PXSL to do away with the
closing tags that XML uses to determine containment.)
doc
title
body
If an element has attributes, they are written in the form of
-name=value in PXSL.
doc
title
body -id=db13
If an attribute value contains whitespace, it must be quoted within
the literal delimiters << and >>.
doc -keywords=<>
title
body -id=db13
Now let's consider text. If an element contains text, the text is
quoted in << and >> delimiters and indented underneath the element
that owns the text.
doc -keywords=<>
title
body -id=db13
This is text. <>
The << and >> delimiters are powerful. The text within them,
including all whitespace, is quoted verbatim. The text can span
multiple lines and even stray outside of the outline-like indentation
hierarchy. If you place sections of quoted text next to one another
<> <> they effectively become one section <>.
doc -keywords=<>
title
My title <>
body -id=db13
<>
line text.
If you want to add an XML comment, introduce it with the -- delimiter.
The comment extends to the end of the line.
-- my document
doc -keywords=<>
title
My title <>
body -id=db13
<>
line text.
You can also use the # delimiter, which creates a PXSL comment that is
invisible in XML:
-- my document
doc -keywords=<>
title
My title <>
body -id=db13
<>
line text.
# hidden comment, for
# PXSL readers only
That's it. You now know everything necessary to create PXSL
documents.
PXSL lets you do more, however, and if you want to take full advantage
of it, read the Advanced Topics section. For now, though, let's
consider how to use PXSL documents with your existing XML-based
software.
** Using PXSL documents
Using PXSL documents is easy because they are really XML documents in
disguise. (In fact, you may wish to consider PXSL as a convenient
shorthand for writing XML.) Any program that can read XML can handle
PXSL. All you need to do is remove the disguise first so that the
programs will recognize your documents for what they are.
The included tool pxlscc (short for PXSL conversion compiler) performs
this task. Just feed it a PXSL document, and it returns the
equivalent plain-old XML document:
$ pxlscc document.pxsl > document.xml
You can then use the returned document in your XML-aware programs.
If you know how to use Make or Ant or similar tools, you can easily
automate this process so that your PXSL files are automagically
converted into XML when needed.
NOTE: The pxslcc program expects UTF-8 encoded input and emits UTF-8
encoded output.
* Advanced topics
The following sections describe the more advanced capabilities of PXSL
that can make your life easier. The element defaults, in particular,
can significantly reduce markup burdens.
** Element defaults provide convenient, customizable shortcuts
Most XML documents conform to established vocabularies. Once you
become familiar with your documents' vocabularies, you'll probably
find that certain elements and attributes always or often occur
together -- to the point where typing them becomes repetitive. For
example, in XHTML, almost all img elements take the following form:
Or, in PXSL:
img -src=... -alt=... [ additional attributes here ]
So, why should you have to type in the repetitive src="" and alt=""
every time you use an img element? With PXSL's element defaults,
you don't need to.
*** Using element defaults to create attribute shortcuts
Element defaults are shortcuts that are defined in a separate file
using a simple syntax. (For the specifics of creating and loading
these files, see the Reference section on pxslcc.) For example:
img = img src alt
This shortcut allows you optionally to leave off the -src= and -alt=
part whenever you write the PXSL markup for an img element. For
example, with this definition in place, all three of these PXSL
statements mean the exact same thing:
img -src=/images/logo.png -alt=Logo
img /images/logo.png -alt=Logo
img /images/logo.png Logo
All of them convert into the same XHTML:
In other words, shortcuts let you pass attribute values by position
instead of by the -name=value syntax. You provide only the values,
and the shortcut provides the corresponding -name= parts behind the
scenes.
But there are a couple of restrictions to keep in mind. First,
attribute values passed by position must come first, before any values
passed using the -name=value syntax, and they must occur in the same
order as declared in the shortcut definition.
Second, you can only pass values this way if they do not contain
whitespace. If a value contains whitespace, you must use the
-name=value syntax and quote the value: -name=<> (There is
an advanced feature, the <( )> delimiters, that overcome this
restriction. They are described in the section on advanced macros,
later in this document.)
*** Using element defaults to create virtual elements
You can also use the element defaults to create your own virtual
elements. If you work in XHTML, you have probably noticed that the
element is used to create both hypertext links and anchors. For
example:
Anchored textLink to anchored text
Why not make these two uses more obviously distinct while cutting down
on markup at the same time? Let's create virtual "anchor" and "hlink"
elements that do just that:
anchor = a name
hlink = a href
Now we can use these elements in PXSL to express the above XHTML more
clearly:
anchor anchor-name <>
hlink #anchor-name <>
(Notice that we used << and >> in an advanced way that lets us put
quoted text on the same line as the element that contains it. This is
discussed further in the "Advanced quoting" section.)
When we convert the above PXSL into XML, it results in exactly the
same XHTML that we discussed earlier:
Anchored textLink to anchored text
*** Making and using your own element defaults
Making your own shortcuts is easy. Just create a file that contains
lines of this form:
element-name = preferred-element-name opt-attr-1 opt-attr-2 ...
It's a good idea to extend the file's name with a suffix of ".edf",
which is short for "element defaults," but feel free to ignore this
convention.
For example, we might create a "xhtml-shortcuts.edf" file to capture
our shortcuts from above:
# File: xhtml-shortcuts.edf
anchor = a name
hlink = a href
(Notice that you can place comment lines in your .edf files by
starting them with a "#" character.)
To use the shortcuts, tell pxslcc to --add them to the set of active
element defaults that are used when processing your PXSL files:
$ pxslcc --add=xhtml-shortcuts.edf my-doc.pxsl > my-doc.xhtml
You can --add more than one set of defaults, and pxslcc will use them
all.
*** Built-in element defaults for XSLT stylesheets
PXSL was originally created to reduce the verbosity of XSLT
stylesheets. As a result, pxslcc has a built-in set of element
defaults for XSLT that you can enable by passing the --xslt
flag:
$ pxslcc --xslt stylesheet.pxsl > stylesheet.xsl
The built-in defaults provide two benefits: First, you can use element
names from within the XSLT namespace without having to use the xsl:
prefix. Second, you can pass common required attributes like "select"
and "match" by position.
Together, these benefits result in massive markup reductions, making
your life as an XSLT author much easier. Compare the following
snippet of XSLT in XML
with the same snippet rewritten in PXSL (using --xslt defaults):
template /
for-each //*/@src|//*/@href
value-of .
text <<
>>
Among the many XSLT shortcuts enabled by the --xslt flag, the above
PXSL snippet uses the following:
template = xsl:template match name
for-each = xsl:for-each select xml:space
value-of = xsl:value-of select disable-output-escaping
text = xsl:text disable-output-escaping
To see the complete list of XSLT shortcuts, --export them:
$ pxslcc --xslt --export
** Advanced quoting with the << >> and <{ }> delimiters
PXSL has two kinds of quoting delimiters that can be used to quote
mixed and text-only content. Both are described in this section.
*** XML quoting << >> delimiters
The << and >> delimiters not only let you insert text into your PXSL
documents, but also let you insert raw, full-featured XML. This works
great for those times when it's just easier to write a bit of XML than
its PXSL equivalent. For example, if you're writing an XSLT
stylesheet that generates XHTML output, you'll certainly want to use
PXSL to express the markup-dense xsl:stylesheet directives. But, if
you need to drop in some XHTML boilerplate that a designer gave you to
use in the page footer, just copy-and-paste it using << and >>:
<<
>>
Another great use for the << >> delimiters is to drop XML specials
like processing instructions into your code:
<<>>
The above PXSL is equivalent to the following XML:
Because the << >> delimiters quote XML, you must follow XML's
syntactical rules when using them. That means that if you
want to include literal less-than "<" and ampersand "&"
characters, you must use character entity references:
<< less-than: < >>
<< ampersand: & >>
*** Verbatim text <{ }> delimiters (CDATA)
When copy-and-pasting blocks of text from outside sources, you must be
careful to "escape" any literal "<" and "&" characters that may be
within. This can be annoying, especially for large blocks of text.
Another place where this requirement is burdensome is in mathematical
expressions that sometimes occur in XSLT markup:
xsl:test -when=<< $i < 5 >>
For this reason, PXSL provides the verbatim-text delimiters <{ and }>
that perform the same function as XML's more verbose CDATA delimiters:
XML PXSL
<{ toast & jelly }>
Any characters that you place inside of <{ }> will come out as a
character literals. PXSL will take care of any escaping that is
necessary to prevent XML from misinterpreting your text as markup.
For example, we can rewrite the above XSLT snippet more clearly
using the verbatim-text delimiters:
xsl:test -when=<{ $i < 5 }>
These delimiters are especially handy for including examples of XML
markup in your documents. Like << >>, <{ }> can handle large blocks
of multi-line text and preserves whitespace and indentation.
*** Text-content shortcut
As you may have noticed from the MathML example at the beginning of
this document, if an element contains text, you can declare the
text on the same line as the element. This saves space and often
reads more easily:
NORMAL SHORTCUT
h1 h1 <>
<> h2 <{Sections 1 & 2}>
h2
<{Sections 1 & 2}>
** Macro facility
PXSL has a simple macro facility that you can use to reorganize your
markup and "factor out" boilerplate. A macro is defined with a
leading comma and a trailing equals sign, like so:
,name =
body-of-the-macro
where "name" is the name of the macro and "body-of-the-macro" can be
any series of elements and text. Macros can be defined at any level
of nesting within a PXSL document, but they are only visible (i.e.,
available for use) at the level where they were defined and at levels
nested underneath. (If two macros with the same name are visible at
the same time, the deepest one will hide the other, or if both are on
the same level, the one defined latest in the document will hide the
earlier. In other words, the policy is "the last, deepest one wins.")
*** Using macros (i.e., macro expansion)
To use a macro, simply refer to it by name anywhere that an element
can be used:
,hello =
<>
html
head
title
,hello
body
<>
When processed with pxslcc (using the --indent flag), this is the
result:
Hello!
Hello! Again!
Note that the macro definition has been removed and that the reference
to the macro inside of the "title" element has been replaced by the
macro's body. This is called macro expansion.
Macros don't need to be defined before they are expanded, as long as
they are visible from the sites (locations) where they are expanded.
Also, macros can call other macros:
,hello =
<>
html
,head
,body
,head =
head
title
,hello
,body =
body <>
This snippet results in exactly the same XML as the one above.
Nevertheless, we have made a number of organizational changes.
The "head" and "body" within the "html" element have been
factored out into the macros ,head and ,body and relocated within
the document. These macros are defined within the "html"
element, after the sites where they are expanded. Note that the
,head macro calls upon the ,hello macro that we defined earlier.
Although contrived in this small example, factoring out blocks of
markup makes the structure of large documents easier to understand and
manage because you are free to move them around, subdivide them
further, and reuse them in many locations.
*** Parameterized macros
Macros can take any number of parameters, which allows you to customize
their definitions.
**** Using named parameters
For example, we could customize the definition of the ,head macro that
we used above to accept the title as a parameter:
,make-head my-title =
head
title
,my-title
Now we can use it to create a head element that contains any title
that we want:
,make-head -my-title=<>
Note that we pass parameters to a macro just like we pass attributes
to an element definition.
**** Using the magic, implicit BODY parameter
But what if we want to pass more than strings? What if we want to
pass large sections of documents as parameters? We can do this using
the special BODY parameter that all macros have implicitly:
,make-section title =
section
-- start of section
title
,title
,BODY
-- end of section
(Note that the BODY parameter must be spelled exactly "BODY" and in
all caps.) The BODY parameter accepts any content defined underneath
the macro-expansion site (i.e., the body of the macro-expansion
invocation):
,make-section -title=<>
p <>
p <>
p <>
The result of calling this macro is the following XML:
This is my title
This is a paragraph.
And another.
And so on.
*** Advanced macros and passing parameters with the <( )> delimiters
As we showed earlier, one way of passing document fragments to macros
is via the implicit BODY parameter that all macros have. Another is
to pass them as normal arguments using the <( )> delimiters, which let
you group PXSL document fragments into chunks that you can pass as
arguments.
For example, let's redefine the make-section macro we defined above to
accept the body of the section as a normal parameter:
,make-section title body =
section
-- start of section
title
,title
body
,body
-- end of section
Now we can call it like so:
,make-section -title=<> \
-body=<(
p <>
p <>
p <>
)>
(Note the use of the backslash in between parameters to continue the
parameter list to the next line. This useful trick also works to
continue attribute lists when you are creating elements.)
Because the <( )> delimiters can be used only to pass arguments, you
can use them to "quote" arguments that otherwise could not be passed
via position, e.g., a fragment of text that contains whitespace:
,make-section <( <> )> \
<(
p <>
p <>
p <>
)>
You can even use the <( )> delimiters to pass the results of macro
calls to elements and other macros:
,h1 x =
-- level one heading
h1
,x
,bang x =
,x
<>
,h1 <( <>
,bang World )>
The above produces the following XML:
Hello, World!
*** More advanced macros and functional programming
Like functions in functional programming languages, macros in PXSL are
first-class values that can be created, bound to parameters, and
passed to other macros. While this might initially seem like a
programming-language curiosity, it is actually a simple yet immensely
powerful tool that you can use to reduce the size and complexity of
your XML documents. In particular, this tool lets you "factor out"
and reuse common, boilerplate portions of your documents.
To see how this works, consider the following XML document that
represents an address book:
JoeSmithJoe SmithJohnDoeJohn Doe
The address book contains a long list of persons, each of which has a
first and last name and a "preferred name" that is usually the first
and last named joined together (but might be something else).
We might write the address book in PXSL like this:
address-book
person
first <>
last <>
preferred <>
person
first <
last <>
preferred <>
-- ... more persons ...
But, seeing how repetitive that is, we might create a ,person macro
to make our lives easier:
,person first last =
person
first
,first
last
,last
preferred
,first
<< >>
,last
Now, with our new macro, we can simply write
address-book
,person Joe Smith
,person John Doe
-- ... more persons ...
And, indeed, running the above PXSL code through pxslcc, yields the
identical XML:
JoeSmithJoe SmithJohnDoeJohn Doe
Already, we have saved a great deal of work, but let's say that the
situation is a little more complicated. Let's say that in addition
to the address-book, we also need to make a roster of persons:
Smith, JoeDoe, John
and, most important, we need to keep the address-book and roster
synchronized. In other words, we have one list of names and we
must use it in two places.
At this point, we might be tempted to put the list of names in a
separate XML document and write a small external program or a couple
of XSLT stylesheets to transform the document into the address-book
and roster. After all, we don't want to have to keep the address-book
and roster synchronized by hand.
But we can do this without leaving PXSL. All we have to do is create
a macro that builds things out of our list of people:
,build-from-people builder-macro =
,builder-macro Joe Smith
,builder-macro John Doe
-- ... more persons ...
The interesting thing is that our ,build-from-people macro takes
another macro as a parameter and binds it to the name "builder-macro",
just like it would any other kind of parameter. It uses this macro to
transform a first and last name into something else. What that
something else is, is up to us: We simply tailor the ,builder-macro to
suit our purpose.
For example, to build an address book:
address-book
,build-from-people <( , first last =
,person <(,first)> <(,last)> )>
or, to build a roster:
roster
,build-from-people <( , first last =
formal
,last
<<, >>
,first )>
That's it. We have just built an address book and a roster from our
list of people.
Now, you may have noticed something new in the above two snippets of
PXSL. In each snippet, inside of the outer-most <( )> delimiters, we
created a macro on the fly -- an anonymous macro, so called because we
didn't give it a name. (It doesn't need a name because we're using it
just this one time; nobody else will ever call it.) We simply created
it right when we needed it and passed it to the ,build-from-people
macro, where it was bound to the name "builder-macro." Then
,build-from-people used it to construct "person" or "formal" elements
(depending on how we defined the anonymous macro). It's a pretty neat
trick.
You can create anonymous macros using the familiar comma syntax --
just don't provide a name. Note the space between the comma and the
start of the argument list:
, arg1 arg2... =
body
To call an anonymous macro, of course, you'll first have to bind it to
a name. The way you do this is to pass the anonymous macro to another
macro, just like we did earlier, causing the anonymous macro to be
bound to one of the other macro's parameters:
,some-other-macro <( , arg1 arg2... =
body )>
Then that other macro can call it via the parameter's name:
,some-other-macro marco-arg =
,macro-arg -arg1=... -arg2=...
Here's another example, less practical but illustrative. See if you
can figure out how the code works before reading the explanation
that follows.
,double str =
<{"}>
,str
<{"}>
,single str =
<{'}>
,str
<{'}>
,add-quotes quote-fn str =
,quote-fn <( ,str )>
-- let's quote a couple of strings
,add-quotes <( , x = ,double <(,x)> )> -str=<>
<< >>
,add-quotes <( , x = ,single <(,x)> )> Please!
Pxslcc compiles the above into the following output:
"Quote Me" 'Please!'
In this example, the two calls to the ,add-quotes macro each pass in
an anonymous macro that performs the desired quoting operation. The
anonymous macro is bound to "quote-fn" when the ,add-quotes macro is
called and expanded. Thus, when ,add-quotes calls ,quote-fn, it is
really calling the anonymous macro that we passed to it. This lets us
customize the behavior of ,add-quotes without having to rewrite it.
*** Real-world example
The examples above are contrived and don't do justice to the
usefulness of this tool. This type of refactoring shines when dealing
with large, complicated, and repetitive data structures, but such
examples are too unwieldy to include in an introduction like this.
For this reason, I urge you to take a look at the
"xsl-menus-w-macros.pxsl" example, in examples directory. It shows
one way that you can use anonymous macros to factor out common code in
production XSLT stylesheets.
http://community.moertel.com/pxsl/examples/xsl-menus-w-macros.pxsl
** Automatic PXSL-to-XML conversion via Make
Most Make utilities allow you to define pattern rules that are then
used automatically to convert one class of documents into another.
Pattern rules can be used to automate the conversion of PXSL documents
into their XML counterparts. For example, if you place the following
rule into a makefile (this is for GNU make),
%.xml: %.pxsl
pxlscc --indent=2 --header $< > $@
Make will automatically generate .xml documents from the
corresponding .pxsl documents whenever they are needed. This
frees you to substitute .pxsl documents anywhere that your
project calls for .xml documents, knowing that make will keep all
of the .xml documents up to date, regenerating them as needed
when you update your .pxsl documents.
* Reference: pxlscc
Usage: pxslcc [OPTION...] [file]
-i[NUM] --indent[=NUM] Re-indent XML using NUM spaces per nesting level
-h --header Insert edit-the-PXSL-instead header into output XML
-x --xslt Add XSLT defaults
-a FILE --add=FILE Add the given defaults file
--export Export (print) all of the active defaults
--dump Dump internal parse format (for debugging)
The --header option requires some explanation. It inserts the following
header comment into the output XML:
It's a good idea to use the --header option all of the time. This
prevents you (or somebody else) from accidentally editing an XML file
when you really ought to be editing the PXSL file from which the
XML file is generated.
[TODO: Expand this section]
* Reference: PXSL syntax
The PXSL grammar, in EBNF-like notation:
pxsl-document ::= statement*, EOF
statement ::= pxsl-comment
| xml-comment
| literal-constructor
| element-constructor
| macro-def
| macro-app
pxsl-comment ::= '#', all-text-until-newline, NEWLINE
xml-comment ::= "--", all-text-until-newline, NEWLINE
literal-constructor ::= mixed-literal | cdata-literal
element-constructor ::= xml-name, posn-args, nv-args, children
macro-def ::= ',', xml-name?, param-names, '=', macro-body
macro-app ::= ',', xml-name, posn-args, nv-args, children
xml-name ::= ( LETTER | '_' | ':' ),
( LETTER | DIGIT | '_' | ':' | '.' | '-' )*
posn-args ::= expr-list
nv-args ::= ( line-continuation?, name-value-pair )*
name-value-pair ::= '-', xml-name, '=', expr
children ::= statement* /* must be indented */
macro-body ::= children
param-names ::= ( line-continuation?, xml-name )*
line-continuation ::= '\', newline
expr-list ::= ( line-continuation?, arg-expr )*
arg-expr ::= expr /* cannot start with '-' */
expr ::= expr-single | NON-WHITESPACE+
expr-single ::= mixed-literal | cdata-literal | pxsl-fragment
mixed-literal ::= "<<", all-text-until->>-delimiter, ">>"
cdata-literal ::= "<{", all-text-until-}>-delimiter, "}>"
pxsl-fragment ::= "<(", statement*, ")>"
* Authors
Tom Moertel http://www.moertel.com/
Bill Hubauer
* (For Emacs)
Local Variables:
mode:outline
End:
pxsl-tools-1.0/pxslcc.cabal 0000664 0000764 0000764 00000002733 10731010155 014500 0 ustar thor thor Name: pxsl-tools
Homepage: http://community.moertel.com/ss/space/PXSL
Version: 1.0
Stability: stable
License: GPL
License-file: LICENSE
Author: Tom Moertel
Maintainer: Tom Moertel
Copyright: 2003-2007 Thomas G. Moertel
Category: XML
Build-type: Simple
Build-Depends: base, mtl >= 1.0, parsec >= 2.0, containers
Synopsis: Parsimonious XML Shorthand Language--to-XML compiler
Description:
The package contains a compiler that converts Parsimonious XML
Shorthand Language (PXSL) into XML. PXSL ("pixel") is a convenient
shorthand for writing markup-heavy XML documents. It provides XML
authors and programmers with a simple, concise syntax that they can
use to create XML documents. For more advanced users, PXSL offers
customizable shortcuts and sophisticated refactoring tools like
functional macros that can markedly reduce the size and complexity
of markup-dense XML documents.
Cabal-version: >= 1.2
extra-source-files: Makefile
README
README.html
src/GetInput.hs
src/PxsltElementDefaults.hs
src/PxsltFormat.hs
src/PxsltMacros.hs
src/PxsltParser.hs
src/PxsltParserTerms.hs
src/PxsltParserTreeTransforms.hs
src/UTF8.hs
src/XmlString.hs
Executable: pxslcc
Main-is: pxslcc.hs
ghc-options: -O2
hs-source-dirs: src