Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial checkin to darcs

darcs-hash:20060323004548-8fbd9-21f2f840b98acc0b0ac32e9a13bd25b41b41a525.gz
  • Loading branch information...
commit b259811809c938c6a9a401db15328a1b6e46b5d7 0 parents
@tmoertel authored
73 GetInput.hs
@@ -0,0 +1,73 @@
+-- GetInput.hs
+-- Tom Moertel <tom@moertel.com>
+-- 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.
+--
+-- =================================================================
280 LICENSE
@@ -0,0 +1,280 @@
+ 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
156 Makefile
@@ -0,0 +1,156 @@
+#-----------------------------------------------------------------------------
+# PXSL Makefile
+#
+# CVS: $Id: Makefile,v 1.11 2005/01/27 14:43:52 thor Exp $
+#
+# Copyright (C) 2003-5 Tom Moertel <tom@moertel.com>
+# Licensed under the terms of the GNU General Public License.
+# See the LICENSE file in the project distribution for details.
+#-----------------------------------------------------------------------------
+
+PROJECT = pxsl
+VERSION = 0.9.3
+
+TARGETS = 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
+
+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)
+
+HASKELL := $(wildcard *.hs)
+LIB_HASKELL :=
+ALL_HASKELL := $(sort $(HASKELL) $(LIB_HASKELL))
+
+GHC_PACKAGE = -package lang
+GHC_OPT =
+# GHC_WARN = -Wall -fno-warn-name-shadowing
+GHC_OPTS =
+
+GHC := $(strip ghc $(GHC_WARN) $(GHC_OPT) $(GHC_OPTS) \
+ $(GHC_PACKAGE))
+
+STRIP = strip
+
+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 "<!--"} s{(<body>)}{$$1 -->}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
+
+% : %.hs $(ALL_HASKELL) Makefile
+ sed -e 's/@VERSION@/$(VERSION)/' -e 's/@YEAR@/$(YEAR2)/' $< > $@.x.hs
+ $(GHC) -o $@ --make $@.x.hs
+ rm -f $@.x.hs
+ $(STRIP) $@ || echo "(Binary not stripped. No big deal.)"
+
+# 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 *.bak $(GENERATED)
+ rm -rf $(DOCDIR)
+
+.PHONY : cleandeps
+cleandeps :
+ rm -f *.d
+
+.PHONY : squeakyclean
+squeakyclean : clean cleandeps
+
+TAGS : $(HASKELL)
+ hasktags $(HASKELL)
114 PxsltElementDefaults.hs
@@ -0,0 +1,114 @@
+-- | 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 <tom@moertel.com>
+-- 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 Data.FiniteMap
+import Data.List (isPrefixOf, intersperse)
+import Data.Char (isSpace)
+
+type ElementDefaultDatabase = FiniteMap String (String, [String])
+
+applyDefaults :: ElementDefaultDatabase -> Statement -> Statement
+applyDefaults database e@(Element _ nm pattrs nvattrs chldn) =
+ case lookupFM database nm 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 = listToFM . 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 . fmToList
+ 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 = plusFM
+
+emptyDefaults :: ElementDefaultDatabase
+emptyDefaults = emptyFM
+
+xsltDefaults :: ElementDefaultDatabase
+xsltDefaults = listToFM . 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"]
131 PxsltFormat.hs
@@ -0,0 +1,131 @@
+-- | 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 <tom@moertel.com>
+-- 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 . ("<!--"++) . (escComment txt ++) . (" -->\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 '\"' = "&#34;"
+ 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
118 PxsltMacros.hs
@@ -0,0 +1,118 @@
+-- PxsltMacros
+--
+-- CVS: $Id: PxsltMacros.hs,v 1.9 2004/02/23 08:19:27 thor Exp $
+--
+-- Copyright (C) 2003 Tom Moertel <tom@moertel.com>
+-- 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 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)
243 PxsltParser.hs
@@ -0,0 +1,243 @@
+-- PxsltParser
+--
+-- CVS: $Id: PxsltParser.hs,v 1.11 2004/02/23 08:19:27 thor Exp $
+--
+-- Copyright (C) 2003 Tom Moertel <tom@moertel.com>
+-- 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
+
79 PxsltParserTerms.hs
@@ -0,0 +1,79 @@
+-- | Base PXSL data types.
+--
+-- CVS: $Id: PxsltParserTerms.hs,v 1.1 2005/01/27 14:43:52 thor Exp $
+--
+-- Copyright (C) 2003 Tom Moertel <tom@moertel.com>
+-- 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++)
85 PxsltParserTreeTransforms.hs
@@ -0,0 +1,85 @@
+-- | PxsltParserTreeTransforms -- Transform parse trees
+--
+-- CVS: $Id: PxsltParserTreeTransforms.hs,v 1.2 2003/05/29 03:47:07 thor Exp $
+--
+-- Copyright (C) 2003 Tom Moertel <tom@moertel.com>
+-- 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:
+
+-- <a> <a>
+-- <b/> <b/>
+-- ===> </a>
+-- </a>
+-- <c/> <c/>
+
+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
1,119 README
@@ -0,0 +1,1119 @@
+
+ ____ _ _ ___ __
+ ( _ \( \/ )/ __)( )
+ )___/ ) ( \__ \ )(__
+ (__) (_/\_)(___/(____)
+
+ PARSIMONIOUS XML SHORTHAND LANGUAGE
+
+ $Id: README,v 1.17 2005/02/10 21:37:50 thor Exp $
+
+
+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/
+
+ - GHC Haskell compiler, version 5.04.3 or greater
+ http://www.haskell.org/ghc/
+
+ - Make (GNU's flavor preferred)
+ Windows users: install Cygwin, which includes GNU make:
+ http://www.cygwin.com/
+
+Once GHC and Make are installed on your computer, uncompress the
+tarball and build the project via the "make" command:
+
+ $ tar zxvf pxsl-{version}.tar.gz
+ $ cd pxsl-{version}
+ $ make
+
+(Replace {version} with the version of PXSL that you downloaded.)
+
+That's it. You should now have a fully functional version of pxslcc.
+
+
+* 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
+
+ <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, by 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.
+
+
+* 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
+
+All of them convert into the same XHTML:
+
+ <img src="/images/logo.png" alt="Logo"/>
+ <img src="/images/logo.png" alt="Logo"/>
+ <img src="/images/logo.png" alt="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=<<my value>> (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:
+
+ 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
+
+ <xsl:template match="/">
+ <xsl:for-each select="//*/@src|//*/@href">
+ <xsl:value-of select="."/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:for-each>
+ </xsl:template>
+
+with the same snippet rewritten in PXSL (using --xslt defaults):
+
+ template /
+ for-each //*/@src|//*/@href
+ value-of .
+ text <<&#10;>>
+
+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 >>:
+
+ <<
+ <div class="footer">
+ Copyright (C) 2003 Blah, Blah, Blah, Inc.
+ <!-- lots more boilerplate ... -->
+ </div>
+ >>
+
+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: &lt; >>
+ << ampersand: &amp; >>
+
+*** 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 &lt; 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:
+
+ NORMAL SHORTCUT
+
+ h1 h1 <<Chapter 1>>
+ <<Chapter 1>> 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 =
+ <<Hello!>>
+
+ html
+ head
+ title
+ ,hello
+ body
+ <<Hello! Again!>>
+
+When processed with pxslcc (using the --indent flag), this is the
+result:
+
+ <html>
+ <head>
+ <title>Hello!</title>
+ </head>
+ <body>Hello! Again!</body>
+ </html>
+
+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:
+
+ <address-book>
+ <person>
+ <first>Joe</first>
+ <last>Smith</last>
+ <preferred>Joe Smith</preferred>
+ </person>
+ <person>
+ <first>John</first>
+ <last>Doe</last>
+ <preferred>John Doe</preferred>
+ </person>
+ <!-- ... more persons ... -->
+ </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:
+
+ <address-book>
+ <person>
+ <first>Joe</first>
+ <last>Smith</last>
+ <preferred>Joe Smith</preferred>
+ </person>
+ <person>
+ <first>John</first>
+ <last>Doe</last>
+ <preferred>John Doe</preferred>
+ </person>
+ <!-- ... more persons ... -->
+ </address-book>
+
+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:
+
+ <roster>
+ <formal>Smith, Joe</formal>
+ <formal>Doe, John</formal>
+ <!-- ... more persons ... -->
+ </roster>
+
+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=<<Quote Me>>
+ << >>
+ ,add-quotes <( , x = ,single <(,x)> )> Please!
+
+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.
+
+ 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:
+
+ <!--
+
+ 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.
+
+
+[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 <tom@moertel.com> http://www.moertel.com/
+
+Bill Hubauer <bill@hubauer.com>
+
+* (For Emacs)
+
+ Local Variables:
+ mode:outline
+ End:
+
89 TODO
@@ -0,0 +1,89 @@
+$Id: TODO,v 1.5 2004/02/23 08:19:27 thor Exp $
+
+ERRORS TO FIX
+
+* If --header flag is active and the output XML contains a multi-line
+ introductory <?xml?> processing instruction, the header is inserted
+ in the middle. Oops.
+
+* Vertical whitespace rules could be clearer (see Rewrite output stage todo)
+
+TODO
+
+* Rewrite output stage from
+ Parse Tree -> String
+ to
+ Parse Tree -> Output stream -> String
+ in order to make white space optimization cleaner and better.
+
+ Rules:
+
+ All blank lines after the last non-blank line of a macro
+ definition are ignored.
+
+ Otherwise, blank lines in content are preserved, as is.
+
+ What about initial blank lines inside of a macro-definition body?
+
+
+
+
+WISH LIST
+
+
+
+
+----------------------------------------------------------------------------
+COMPLETED ITEMS
+----------------------------------------------------------------------------
+
+X Create RPM spec file for pxsl-tools.
+
+X Add # as a PXSL comment that is stripped out upon processing
+
+X Add switch that inserts a "translated-from-PXSL comment" to output XML
+
+X Add new bracketing delimiters:
+ X <( )> -- contents are evaluated and inserted into the parse tree
+ mainly for use in macro expansion in attribute values
+ X <{ }> -- exactly like << >> except that contents are treated as
+ CDATA (i.e., < becomes &lt; in emitted XML)
+
+X If (ddd)x is used a positional parameter, only x shows in value.
+
+X If you leave off the -name=value (name=value), name=value gets
+ parsed as an element.
+
+X What if a line is continued between positional and named args?
+
+X When <<text>> is expanded as an attribute value, < and & should be
+ escaped. Rationale: There is no such thing as mixed-mode content in
+ attribute values -- it's CDATA w/ char-entity refs. So escaping
+ is the right thing to do.
+
+ Resolution: No it isn't. If somebody used to programming in XML
+ writes "xsl:test -when=$i&lt;5", he expects that the & won't be
+ escaped. So we need different behavior between << >> and <{ }>,
+ even in attribute values.
+
+X Add macro expansion capability.
+
+X Switch to lexical scoping, instead of dynamic, for macro expansion.
+ I.e., macros ought to carry around closures.
+
+X Fix this:
+
+ ,make-section title =
+ section
+ -- start of section
+ title
+ ,title
+ ,BODY
+ -- end of section
+
+ ,make-section -title=<<Section containing another>>
+ ,make-section -title=<<This is my title>>
+ p <<This is a paragraph.>>
+ p <<And another.>>
+ p <<And so on.>>
+
53 UTF8.hs
@@ -0,0 +1,53 @@
+-- $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 Bits
+import Char
+import 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"
49 XmlString.hs
@@ -0,0 +1,49 @@
+-- | Strings representing XML content.
+--
+-- CVS: $Id: XmlString.hs,v 1.4 2005/01/27 14:43:52 thor Exp $
+--
+-- Copyright (C) 2003 Tom Moertel <tom@moertel.com>
+-- 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
<