YML - Why a Markup Language?!

Any time a formal language is created for computing, a compromise has to be found: whether the language is perfect for the computer but terrible for the human or vice versa. XML is very good for the computer ;-)

Using XML for the reasons mentioned above, but for programming? "Why a Markup Language?!" was what I was shouting some time playing around with a code generator in XSLT. That gave the idea.

Replacing angle brackets with some Python

Just writing down what I wanted to have instead of XML for a sample:

<list name="List of goods">
    <head>
        <columTitle>
            Goods
        </columnTitle>
        <columnTitle>
            Price
        </columnTitle>
    </head>
    <row>
        <value>
            Beer
        </value>
        <value>
            20
        </value>
    </row>
    <row>
        <value>
            Wine
        </value>
        <value>
            30
        </value>
    </row>
</list>

Something like that should be more easy, say, like this:

list("List of goods") {
    head {
        title "Goods"
        title "Price"
    }
    row {
        value "Beer"
        value 20
    }
    row {
        value "Wine"
        value 30
    }
}

Y Languages

The latter is what I call an Y language - a language specified in YML. How could this be achieved? Well, what's to do? To have the required information, how to build XML from the script above, we need:

How to do that? Let's invent a simple definition language for that information:

decl list("name") alias list
decl head() alias head
decl row() alias row
decl title("=>body") alias columnTitle
decl value("=>body") alias value

These four lines have all information we need. If you declare a parameter "name", then it's clear, that what's going in there as value will be attached to an attribute "name".

The alias keyword will connect the declared function with an XML tag name.

The notation =>body means, that what's given here as a parameter, will be put into the value of a tag directly. Because <=body is default, you may drop this part. The alias keyword is optional, if tag name and function name match. The following declarations are equivalent to the ones above:

decl list("name")
decl head
decl row
decl title alias columnTitle
decl value

The difference between =>body and <=body is, that <=body does not move a given attribute into the body, if the attribute text contains a '=' sign. The reason for doing so is, that you can simply add plain attributes to "empty tags" using the syntax i.e.: decl div alias div in YHTML and then calling it with manually set attributes, i.e. div 'style="color=red;"' If you don't want this behaviour, you should declare using =>body.

Here you can download the complete list sample.

Hint: Every declared function generates a Python function named yml_ plus the function name. decl head() alias head for example defines a Python function yml_head().

Some Features

Function Calls

It does not matter, if you're calling your function using parantheses or without, or if you want to add a semicolon to the end of line.

So these statements are all equal:

foo "hello, world"
foo("hello, world")
foo("hello, world");

Hint: What comes behind the function name (or in the parantheses) is just a comma separated list of Python expressions. That means, text(' ' * 10) will give you a string of ten spaces as first parameter of function text.

Sub Trees

YML sub trees are opened and closed with braces:

foo {
    bar {
        something();
    }
}

XML Escaping for attribute values

YML automatically escapes text of attribute values.

If you don't want this, use x(value) as attribute value. This prevents YML from escaping your text.

// text escaped
text("> hello, world <")

// text not escaped
text(x('this is "real"'))

Quoting Operators

Three different quoting operators implement different functionality:

Quote >

The '>' operator quotes into text nodes, doing XML escaping of text. An example:

> this text will be put into a text node and these angle brackets <> will be quoted

Line Quote |

The '|' operator does the same as the '>' operator, adding a new line character to the text node.

Additionally, it can be used to implement an indention system, see YSLT below.

Then it's used together with additional '>' symbols showing the grade of indention:

| not indented
|> single indent
|>> double indent
(...)

Inserting Commands

Just like with a Unix shell, you can insert statements into text by using back quotes:

| Click `a("http://fdik.org/yml/", "this link")`, please!

For abbreviation purposes, you can define a short cut insert (see YSLT below).

Here you can have a default tag for inserts using the « » operators. In YSLT you can use them for xsl:value-of tags, for example.

The following two lines are equivalent in YSLT:

| Gimme some «$value» hereby.
| Gimme some `value("$value")` hereby.

They are, because there is a define special_command_name as value command in the YSLT definition file.

Hint: If you're running into troubles because of character set problems with the '»' symbols, then use these two lines on top of your YSLT script:

define special_command_left as «
define special_command_right as »

My samples all use iso_8859-15 charset, if you're using something else you may need that.

Quote Through ]

The Apple ][ prompt operator just quotes through directly into XML what it gets.

If the first non whitespace character of a line is a '<', then quote through is applied automatically.

This is the preferred way to output XML tags directly in YML:

<output me="directly" />

] <!--
]     add some comment, which then appears in XML
] -->

Importing and Including

Because YML first is translated into Python (what I call Python YML - PYML), you can use simple Python modules together with your YML code:

import module

YML does not support name spaces. Instead, import module is being translated into from module import *.

Additionally, you can include a second YML script file into an existing YML script file at any place using:

include something.yml

Escaping into Python directly - the Escape Operator !

As an alternative, you can insert a Python command into the generated PYML script at any place by using the ! operator:

!print "hello, world"

Comments //

Comments are written like in Java or C++:

// this is a comment

something() { // this is a comment after a tag

After quoting operators, comments are not possible. Instead, they're quoted through:

// the following line you'll find in the output document
> this text is being output // and this too

Hint: Because comments are removed with the lexical analyzer already, you should not use // in parameters when using functions without parantheses. This will cause an error, because everthing behind // will be treated as a comment:

callMe "with // in text parameter"

There is no such problem, when you're using parantheses. This line will work:

callMe("with // in text parameter")

Macros

Macros can be defined using he define keyword, i.e.:

define ABC as "ABCDEFGHIFJKLMNOPQRSTUVWXYZ"
define abc as "abcdefghifjklmnopqrstuvwxyz"

Some macros have special meanings:

quote_text

quote_text is an PYML (Python) expression, how to insert plain text. %n is a placeholder for the inserted text.

YSLT defines it as following:

define quote_text as yml_text("""%n""")

quote_indention

quote_indention is an YML function call, which is called by the | operator for indenting. There is the place holder %n, which is replaced by the indention level.

YSLT defines quote_indention as following:

define quote_indention as indent("%n")

special_command_left and special_command_right

special_command_left and special_command_right are operators, which can be used inside quoted text for special functions. By default they're defined as following:

define special_command_left as «
define special_command_right as »

They're used by YSLT for value() function calls. By YHTML they're used for <strong> text.

special_command_name

special_command_name is the YML function, which is called by using the operators, which were defined with special_command_left and special_command_right.

YSLT - XSLT as Y language

Basic idea

XSLT already is a functional programming language. But it's ugly. So one of the first approaches to use YML was to define YSLT. YSLT is just a Y language for XSLT.

Some samples

Hello, world

The hello world looks plain and simple:

include yslt.yml

stylesheet {
    output "text"

    template("/") {
        > hello, world\n
    }
}

Generating HTML

This sample generates a table view in HTML out of the list sample of the first chapter. I'm using YHTML for it, the same Y language I'm writing this documentation in:

include yhtml.yml
include yslt.yml

stylesheet {
    template ("list") {
        _html {
            head {
                title {
                    > «@name»
                }
                style ("text/css") {
                    comment {
                        > th { border-style:solid; border-width:1; }
                        > td { border-style:dotted; border-width:1; }
                    }
                }
            }
            body {
                table {
                    apply "head"
                    apply "row"
                }
            }
        }
    }

    template ("head") {
        tr {
            apply "columnTitle"
        }
    }

    template ("columnTitle") {
        th {
            > «.»
        }
    }

    template ("row") {
        tr {
            apply "value"
        }
    }

    template ("value") {
        td {
            > «.»
        }
    }
}

This YSLT program you can download here. The result looks like this:

Java code generator in YHTML - a sketch

This works very well - see this sample program, which compiles Java interfaces out of XMI files.

Features

For features, just read the YSLT definition and have a look on the sample above.

Tool Chain

What it is

This documentation was written in YHTML - see the source and the Makefile as well as the YML translator script.

It was mostly generated out of the XML Schema of XHTML using the Default Compiler using YSLT and an YSLT processor. As XML tool chain, I'm using xsltproc and XMLStarlet.

YSLT is using the YPath python module as an addition to XPath and an indention system.

How it works

Because I'm lazy, I'm using Python excessively ;-) I'm just compiling to python code, which then - being executed - outputs XML code.

This is the reason why in the Makefile above the output is again put into a Python interpreter.

Have a look at the YML compiler front end and at the YML compiler back end.

But one thing is left...

Please find vim syntax coloring here. ;-)

To download everything in a package...

... use this one for a compressed tar archive, or this one for a zip file.

License

YML is Free Software. It is under the GNU General Public License 2.0.

Updates

Updates to YML and YSLT I will announce in my blog.

Up to now, there are the following versions:

Valid XHTML 1.0 TransitionalValid CSS!