YML - Why a Markup Language?!
Especially the XSLT programmer can benefit from YML. Usually many attributes of XSLT are annoying in programming praxis:
In short, it's ugly ;-)
Usually the result is, that many programmers are avoiding XSLT as a programming language. It's a pity, because for processing XML data, it can do much more than "formatting" as "stylesheets".
The idea of YSLT now is to supply a simple language, which programmers want to use, to make the features of XSLT accessible to a broader base of people.
YSLT can be used much simpler; it's just a Y Language for XSLT. I'm using it for my blog, here you can download YBlog2, a simple blogging software in YSLT.
Here you can find the YSLT specification.
In YSLT, the hello world program reads like this:
include yslt.yml2
textstylesheet template "/" | hello, world
The include line includes the YSLT Y Language declarations. The second line generates an XSLT hello world program. You can generate it using:
% yml2c -o hello.xsl hello.ysl2
This results in the following program:
<xsl:stylesheet xmlns:func="http://exslt.org/functions"
xmlns:dyn="http://exslt.org/dynamic" xmlns:str="http://exslt.org/strings"
xmlns:math="http://exslt.org/math"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
extension-element-prefixes="exsl func str dyn set math"
xmlns:set="http://exslt.org/sets" version="1.0"
xmlns:exsl="http://exslt.org/common"><xsl:output method="text"/>
<xsl:variable name="space" select="' '"/>
<xsl:param name="autoindent" select="4"/><xsl:template match="/">
<xsl:param name="_indent" select="0"/><xsl:value-of
select="substring($space, 1, $_indent+0*$autoindent)"/>hello, world
</xsl:template></xsl:stylesheet>
You can execute this program with any XSL processor, for example the Free Software xsltproc.
% yml2c -o hello.xsl hello.ysl2
% echo '<empty/>' > empty.xml
% xsltproc hello.xsl empty.xml
hello, world
% _
Or you can just use yml2proc:
% echo empty | yml2proc -y hello.ysl2
hello, world
% _
Because YSLT is just a Y Language for XSLT, you can do anything with YSLT you can do with XSLT in just nicer syntax ;-) To read what XSLT is all about, I recommend the official W3C documentation for XSLT.
So this document is just an beginners guide, if you want to understand XSLT completely, better read the W3C stuff.
Lovers of Lisp or Haskell will not see any problems. But many programmers are used to have a programming language with a procedural imperative paradigma like Java, C/C++ or Python. Why should they use a functional language?
Actually, if a functional language is practical or not, depends of what's to do - "the right tool for the task", one could say.
Because processing XML data means traversing a (document) tree, recursive algorithms are a clear advantage. And this is the reason, why choosing a functional language is a good choice for that job.
It's a little bit like with SQL - for it's job, it's excellent, but no-one wants to write a complete application in SQL (and also not in one of the Turing-complete SQL extension languages like PL/SQL).
Of course, you can use YSLT as you would use XSLT. Let's say, you have data in XML documents, and you want to have an excerpt out of this data. This is a common task, comparable to SELECT data out of an SQL database. But we have no database, we have something like this file customers.yml2:
decl customer(*id, *name) { id *id, name *name };
list {
customer 23, "Kurt Meier";
customer 42, "Lieschen Schmidt";
}
Let's say, we want to output this into an XHTML document, where the list is showed as a table. Then we would do the following:
The XHTML document should be like the following and include the list in the body:
include yslt.yml2
stylesheet {
template "/" html {
head title "Customer List";
body apply "list";
}
In the example above, stylesheet declares the main program. Then we define a template, which is executed automatically starting reading the root '/' of the document tree of the XML document we're processing.
Using the XML document as input, we're creating this HTML tree:
<html>
<head>
<title>Customer List</title>
</head>
<body>
<!-- ... ->
</body>
</html>
How do we create the list? Well, let's use a table with customers:
template "list" table apply "customer";
What to do per customer? Well, generate a table row with two columns, one for id and one for name:
template "customer" tr {
td value "id";
td value "name";
}
}
That was it. We now can run our small program:
% yml2proc -y customer.ysl2 -x customer.xml -o customer.html -P
The result looks like this:
% cat customer.html
<?xml version="1.0"?>
<html>
<head>
<title>Customer List</title>
</head>
<body>
<table>
<tr>
<td>23</td>
<td>Kurt Meier</td>
</tr>
<tr>
<td>42</td>
<td>Lieschen Schmidt</td>
</tr>
</table>
</body>
</html>
% _
Here you can download the complete program.
Well, now for something real ;-) This is a very common task: somebody models with an UML class diagram, and you want to have SQL DDL, which generates a matching database structure.
Let's go:
First, lets use a stylesheet, which declares the needed XMI namespaces:
include yslt.yml2
tstylesheet xmlns:uml="http://schema.omg.org/spec/UML/2.1",
xmlns:xmi="http://schema.omg.org/spec/XMI/2.1" {
Now, search the Model for all content:
template "/" apply "xmi:XMI/uml:Model/packagedElement", 0;
We're translating UML Packages into underscore separated prefixes:
template "packagedElement[@xmi:type='uml:Package']" {
param "name", "''";
if "$name=''" apply "packagedElement", 0 { with "name", "@name"; }
if "$name!=''" apply "packagedElement", 0 { with "name", "concat($name, '_', @name)"; }
}
Each Class is represented by a table in the database:
template "packagedElement[@xmi:type='uml:Class']" {
param "name";
| CREATE TABLE $name_@name (
apply "ownedAttribute";
| );
}
Finally, for each different data type for an attribute we're outputting different fields with different types:
template "ownedAttribute[@xmi:type='uml:Property' and type/@xmi:type='uml:PrimitiveType']" {
0> @name
choose {
when "type/@href='http://schema.omg.org/spec/UML/2.1/uml.xml#String'"
> VARCHAR
// [...] for other types, extend when clauses
}
if "position()!=last()" > ,
text "\n";
}
}
Our little sample only supports VARCHAR, but it is an easy game to play to complete that.
Here you can download the XMI 2 DDL compiler sample. I used BOUML to create a small UML sample file as XMI 2.1.
To compile that, use:
% yml2proc -y xmi2ddl.ysl2 -x demo.xmi
CREATE TABLE demo_Customer (
id VARCHAR,
name VARCHAR
);
% _
Because YSLT just generates XSLT programs, it will be a good idea to read the XSLT Documentation as well as the XPath Documentation.
In the following, you find a List of YSLT Functions and a List of YSLT Operators.
Generates the <xsl:apply-templates /> tag. The *indent pointer gives the number of indention levels to add (default: 1) for the Indention System.
Generates the <xsl:attribute /> tag.
Generates the <call-template /> tag. Used to call a function().
Generates the <xsl:choose /> tag. Use in a choose() ... when()... otherwise()... structure.
Generates the <xsl:comment /> tag.
Generates the <xsl:variable /> tag.
Generates the <xsl:copy-of /> tag.
Generates the EXSLT <func:funcion /> tag.
Generates the EXSLT <exsl:document /> tag.
Generates the <xsl:element /> tag.
Generates the <xsl:message /> tag with attribute terminate set to "yes".
Does the same as stylesheet(), but additionally declares the EXSLT functions of the groups exsl, func, str, dyn, set and math and declares the corresponding name spaces.
Generates the <xsl:for-each /> tag.
Same as for().
Generates the <xsl:template /> tag. Used by calling with call().
Generates the <xsl:if /> tag.
Generates the <xsl:import /> tag.
Generates the <xsl:key /> tag.
Generates the <xsl:message /> tag.
Generates the <xsl:otherwise /> tag. Use in a choose() ... when()... otherwise()... structure.
Generates the <xsl:output /> tag.
Generates the <xsl:param /> tag.
Generates the <xsl:processing-instruction /> tag.
Generates the <xsl:text /> tag. Sets the attribute disable-output-escaping to "yes".
Generates the EXSLT <func:result /> tag.
Generates the XSLT <stylesheet /> tag. Additionally generates an <output /> tag in the body, with attribute method set to the value of the pointer *output (default: "xml").
The content you're giving is placed in the body after the <output /> tag.
The version attribute is set to "1.0" and XML namespace xsl is correctly defined.
In short: use for a stylesheet, just give the output type as parameter, if you don't want to to generate XML but HTML ("html") oder plain text ("text").
stylesheet() additionally generates tags for the Indention System.
Generates the <xsl:template /> tag. Additionally generates tags for the Indention System.
Generate the <xsl:text /> tag.
Same as estylesheet(), but *output is now "text", that means the stylesheet outputs plain text.
Same as textstylesheet().
Generates the <xsl:value-of /> tag.
Generates the <xsl:message /> tag with attribute terminate set to "no".
Generates the <xsl:when /> tag. Use in a choose() ... when()... otherwise()... structure.
Generates the <xsl:with-param /> tag.
Generate YSLT Function Call value('...').