pypackage: Merge 2.6.2
authorClaudio Luck <claudio.luck@pep.foundation>
Wed, 18 Mar 2020 19:20:01 +0100
changeset 58 a218553807ab
parent 57 2f4ad3800a3f (diff)
parent 40 432ab62b2537 (current diff)
child 59 ca8b9c6eb602
pypackage: Merge 2.6.2
yml2/backend.py
yml2/pyPEG.py
yml2/yml2.py
yml2/yml2c.py
yml2/yml2proc.py
yml2/yslt.yml2
yml2c
yml2proc
--- a/Makefile	Mon Nov 04 11:38:34 2019 +0100
+++ b/Makefile	Wed Mar 18 19:20:01 2020 +0100
@@ -1,5 +1,7 @@
 YML_PATH=
 YML2C=yml2c
+PKGVER=$(shell python setup.py -V)
+DEBVER=1
 
 all: homepage
 
@@ -16,5 +18,18 @@
 %.html: %.en.yhtml2 heading.en.yhtml2 homepage.en.yhtml2
 	$(YML2C) $< -o $@
 
+.PHONY: deb
+deb:  YML2_$(PKGVER).orig.tar.gz python-yml2_$(PKGVER)-$(DEBVER)_all.deb
+
+YML2_$(PKGVER).orig.tar.gz:
+	python setup.py sdist
+	mv -f dist/YML2-$(PKGVER).tar.gz YML2_$(PKGVER).orig.tar.gz
+
+python-yml2_$(PKGVER)-$(DEBVER)_all.deb:
+	python setup.py --command-packages=stdeb.command bdist_deb
+	mv -f deb_dist/python-yml2_$(PKGVER)-$(DEBVER)_all.deb .
+
 clean:
 	rm -f *.html *.pyc *.pyo
+	rm -f YML2_$(PKGVER).orig.tar.gz
+	rm -f python-yml2_$(PKGVER)-$(DEBVER)_all.deb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,144 @@
+## What is YML?
+
+Well, it's the idea not to need to define a grammar first when you want to use a Domain Specific Language. For that purpose, YML is being translated into XML. Let's make an example.
+
+Everything which comes close to a C like language, parses without a grammar definition:
+
+This:
+
+    template< class T > T max(T a, T b);
+
+Parses to:
+
+    <?xml version='1.0' encoding='UTF-8'?>
+    <template>
+      <generic>
+        <class/>
+        <T/>
+      </generic>
+      <T>
+        <max>
+          <parm>
+            <T/>
+            <a/>
+          </parm>
+          <parm>
+            <T/>
+            <b/>
+          </parm>
+        </max>
+      </T>
+    </template>
+
+Instead of defining grammars, you test out and play around until the results are matching your needs. If the resulting tree does not fit what you're expecting, change it by patching the grammar with `decl`:
+
+This:
+
+    module A {
+        interface B {
+            attribute long n;
+        };
+    };
+
+Parses to:
+
+    <?xml version='1.0' encoding='UTF-8'?>
+    <module>
+      <A>
+        <interface>
+          <B>
+            <attribute>
+              <long>
+                <n/>
+              </long>
+            </attribute>
+          </B>
+        </interface>
+      </A>
+    </module>
+
+This does not look like what we want. So we tell YML that we have a module name after the module, an interface name after the interface and type and name after the attribute:
+
+This:
+
+    decl module @name;
+    decl interface @name;
+    decl attribute @type @name;
+
+    module A {
+        interface B {
+            attribute long n;
+        };
+    };
+
+Parses to:
+
+    <?xml version='1.0' encoding='UTF-8'?>
+    <module name="A">
+      <interface name="B">
+        <attribute type="long" name="n"/>
+      </interface>
+    </module>
+
+What can I do with YML?
+
+With YML you can:
+
+  * use a C-like DSL without writing a grammar first
+  * generate code out of this DSL using YSLT
+  * generate code out of UML using YSLT on XMI
+  * generate code out of any XML based language like SVG using YSLT
+  * define a wiki like language in just a few lines like YHTML does
+  * replace bad designed and complicated XML languages with simpler C-like ones
+  * ... and much more.
+
+How it works: 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:
+
+  * the information, that “list of goods” is an attribute named `name`, while `Goods` is the text value of a tag
+  * `title` shout be written out as `columnTitle`
+
+How to do that? Let's invent a simple definition language for that information:
+
+    decl list(name);
+    decl title alias columnTitle;
--- a/backend.py	Mon Nov 04 11:38:34 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,844 +0,0 @@
-# 2.6.1 backend
-
-# written by VB.
-
-import re, codecs
-import fileinput
-import sys, traceback, os
-from xml.sax.saxutils import escape, quoteattr
-from copy import copy, deepcopy
-from glob import glob
-from pyPEG import code, parse, parseLine, u, Symbol
-from yml2 import ymlCStyle, comment, _inner
-
-ymlFunc, pointers, pythonFunc = {}, {}, {}
-in_ns = ""
-operator = []
-included = ""
-includePath = []
-emitlinenumbers = False
-encoding = "utf-8"
-
-first = True
-enable_tracing = False
-
-ymlFunc["decl"] = "#error"
-ymlFunc["define"] = "#error"
-ymlFunc["operator"] = "#error"
-
-def clearAll():
-    global ymlFunc, pointers, pythonFunc, in_ns, operator, included
-    ymlFunc, pointers, pythonFunc = {}, {}, {}
-    in_ns = ""
-    operator = []
-    included = ""
-
-lq = re.compile(r"\|(\>*)(.*)")
-sq = re.compile(r"(\d*)\>(.*)")
-ts = re.compile(r'(\|\|(?P<inds>\>*)\s*\n(?P<text1>.*?)\n(?P<base>\s*)\|\|)|("""(?P<text2>.*?)""")|(\>\>(?P<text3>.*?)\>\>)', re.S)
-tq = re.compile(r"(\]|\<)\s*(.*)")
-bq = re.compile(r"\`(.*?)\`", re.S)
-bqq = re.compile(r"\s*\`\`(.*)")
-all = re.compile(r".*", re.S)
-
-line = 1
-
-def pointer(name):
-    try:
-        return u(pointers[name[1:]])
-    except:
-        if name == "*_trace_info":
-            return '""'
-        if included:
-            raise LookupError("in " + included + ":" + u(line) + ": pointer " + name)
-        else:
-            raise LookupError("in " + u(line) + ": pointer " + name)
-
-def evalPython(expr):
-    try:
-        result = eval(u(expr), pythonFunc)
-        if type(result) is bytes:
-            return codecs.decode(result, encoding)
-        else:
-            return result
-    except:
-        name, parm, tb = sys.exc_info()
-        msg = "in python expression: " + u(parm)
-        if name is SyntaxError:
-            tbl = traceback.format_exception(name, parm, tb)
-            msg += "\n" + tbl[-3] + tbl[-2]
-        else:
-            msg += ": " + expr + "\n"
-        if included:
-            raise name("in " + included + ":" + u(line) + ": " + msg)
-        else:
-            raise name("in " + u(line) + ": " + msg)
-    
-def execPython(script):
-    try:
-        if type(script) is str:
-            exec(script, pythonFunc)
-        else:
-            exec(codecs.decode(script, encoding), pythonFunc)
-    except:
-        name, parm, tb = sys.exc_info()
-        msg = "in python script: " + u(parm)
-        if name is SyntaxError:
-            tbl = traceback.format_exception(name, parm, tb)
-            msg += "\n" + tbl[-3] + tbl[-2]
-        else:
-            msg += ": " + expr + "\n"
-        if included:
-            raise name("in " + included + ":" + u(line) + ": " + msg)
-        else:
-            raise name("in " + u(line) + ": " + msg)
-
-def textOut(text):
-    if not text:
-        return ""
-    if type(text) is not str:
-        text = codecs.decode(text, encoding)
-    text = text.replace(r'\"', r'\\"')
-    text = '"""' + text.replace('"', r'\"') + '"""'
-    try:
-        textFunc = ymlFunc["text"]
-        parms = ['text', ('parm', [text])]
-        c, result = textFunc(parms)
-        if c:
-            if type(textFunc.alias) is str:
-                result += "</" + textFunc.alias + ">"
-            else:
-                result += "</" + codecs.decode(textFunc.alias, encoding) + ">"
-        return result
-    except:
-        return escape(eval(text))
-
-def strRepl(text):
-    if not text:
-        return ""
-    if type(text) is not str:
-        text = codecs.decode(text, encoding)
-    text = text.replace(r'\"', r'\\"')
-    text = '"""' + text.replace('"', r'\"') + '"""'
-    if type(text) is str:
-        return escape(eval(text))
-
-def applyMacros(macros, text):
-    result = text
-    for key, value in macros.items():
-        result = result.replace(key, value)
-    return result
-
-class YF:
-    def __init__(self, name):
-        self.name = name
-        self.parms = []
-        self.descends = []
-        self.values = {}
-        self.content = None
-        self.pointers = {}
-        self.macros = {}
-        if in_ns:
-            self.alias = in_ns + ":" + name.replace("_", "-")
-        else:
-            self.alias = name.replace("_", "-")
-        pythonFunc["yml_" + name] = self
-        if emitlinenumbers:
-            self.values["yml:declared"] = u(line)
-
-    def copy(self, newName):
-        yf = YF(newName)
-        yf.parms.extend(self.parms)
-        yf.descends.extend(self.descends)
-        yf.values = self.values.copy()
-        yf.content = self.content
-        yf.pointers = self.pointers.copy()
-        yf.macros = self.macros.copy()
-        yf.alias = self.alias
-        return yf
-
-    def patch(self, second):
-        self.parms.extend(second.parms)
-        self.descends.extend(second.descends)
-        self.values.update(second.values)
-        if second.content:
-            self.content = second.content
-        self.pointers.update(second.pointers)
-        self.macros.update(second.macros)
-
-    def __call__(self, called_with, hasContent = False, avoidTag = False):
-        global pointers
-        parms = []
-        vals = {}
-        if self.pointers:
-            pointers.update(self.pointers)
-   
-        for data in called_with:
-            if type(data) is tuple or type(data) is Symbol:
-                if data[0] == "parm":
-                    l = data[1]
-                    parm = l[0]
-                    if parm[0] == "*":
-                        parm = pointer(parm)
-                    if len(l) == 1:
-                        if type(parm) is tuple or type(parm) is Symbol:
-                            if parm[0] == "pyExp":
-                                val = evalPython(parm[1][0])
-                                parms.append(val)
-                        else:
-                            parms.append(evalPython((parm)))
-                    else:
-                        if type(parm) is tuple or type(parm) is Symbol:
-                            if parm[0] == "pyExp":
-                                parm = evalPython(parm[1][0])
-                        val = l[1]
-                        if type(val) is tuple or type(val) is Symbol:
-                            if val[0] == "pyExp":
-                                val = evalPython(val[1][0])
-                        if val[0] == "*":
-                            val = pointer(val)
-                        if u(val)[0] == '"' or u(val)[0] == "'":
-                            vals[parm] = evalPython(u(val))
-                        else:
-                            vals[parm] = u(val)
-                elif data[0] == "content":
-                    hasContent = True
-
-        if enable_tracing:
-            text = u(parms) + ", " + u(vals)
-            pointers["_trace_info"] = '"' + u(line) + ": " + u(self.name) + " " + text.replace('"', '#') + '"'
-
-        if emitlinenumbers:
-            global first
-            if first:
-                vals["xmlns:yml"] = "http://fdik.org/yml"
-                first = False
-            vals["yml:called"] = u(line)
-        return self.xml(parms, vals, hasContent, avoidTag)
-
-    def addParm(self, parm):
-        if parm[0] == "%":
-            for i in range(len(self.parms)):
-                if self.parms[i][0] != "%":
-                    self.parms.insert(i, parm)
-                    return
-        self.parms.append(parm)
-
-    def addDescend(self, desc):
-        if desc[0] == "+" or desc[0] == "@":
-            self.descends.append(desc[1:])
-        else:
-            self.descends.append(desc)
-
-    def addValue(self, parm, value):
-        if type(value) is str or type(value) is str:
-            if value[0] != "'" and value[0] != '"':
-                self.values[parm] = u(value)
-            else:
-                self.values[parm] = u(evalPython(value))
-        else:
-            self.values[parm] = u(evalPython(u(value)))
-
-    def xml(self, callParms, callValues, hasContent, avoidTag = False):
-        global pointers
-        extraContent = ""
-        if self.content:
-            hasContent = True
-        resultParms = self.values.copy()
-        macros = self.macros.copy()
-        toDelete = [ key for key in resultParms.keys() ]
-        for key in toDelete:
-            if key[0] == "*":
-                del resultParms[key]
-        for key, value in callValues.items():
-            if key[0] == "%":
-                macros[key] = value
-            else:
-                resultParms[key] = value
-        i = 0
-        for cp in callParms:
-            if i < len(self.parms):
-                if self.parms[i][0] == "*":
-                    cp = u(cp)
-                    if "'" in cp:
-                        pointers[self.parms[i][1:]] = '"' + cp + '"'
-                    else:
-                        pointers[self.parms[i][1:]] = "'" + cp + "'"
-                elif self.parms[i][0] == "%":
-                    macros[self.parms[i]] = u(cp)
-                else:
-                    resultParms[self.parms[i]] = cp
-            else:
-                extraContent += u(cp)
-                hasContent = True
-            i += 1
-        result = ""
-        for p, v in resultParms.items():
-            if p[0] == "'" or p[0] == '"':
-                p = eval(p)
-            result += " "+ p + "=" + quoteattr(applyMacros(macros, u(v)))
-        if hasContent:
-            if avoidTag:
-                return True, strRepl(extraContent)
-            else:
-                return True, "<" + self.alias + result + ">" + strRepl(extraContent)
-        else:
-            if avoidTag:
-                return False, ""
-            else:
-                return False, "<" + self.alias + result + "/>"
-
-def replaceContent(tree, subtree):
-    n = 0
-    while n < len(tree):
-        obj = tree[n]
-        if obj[0] == "func":
-            l = obj[1]
-            if l[0] == "content":
-                d = 1
-                if subtree:
-                    for el in subtree:
-                        tree.insert(n+d, el)
-                        d += 1
-                del tree[n]
-                n += d
-            else:
-                try:
-                    if l[-1][0] == "content":
-                        replaceContent(l[-1][1], subtree)
-                except: pass
-        elif obj[0] == "funclist":
-            replaceContent(obj[1], subtree)
-        n += 1
-    return tree
-
-def executeCmd(text):
-    if type(text) is not str:
-        text = codecs.decode(text, encoding)
-    for (regex, pattern) in operator:
-        match = re.search(regex, text)
-        while match:
-            cmd = pattern
-            opt = match.groups()
-            for i in range(len(opt)):
-                cmd = cmd.replace("%" + u(i+1), opt[i])
-            text = text[:match.start()] + "`" + cmd + "`"+ text[match.end():]
-            match = re.search(regex, text)
-
-    result = ""
-    m = re.search(bq, text)
-    while text and m:
-        cmd  = m.group(1)
-        head = textOut(text[:m.start()])
-        text = text[m.end():]
-        try:
-            r, rest = parseLine(cmd, _inner, [], True, comment)
-            if rest: raise SyntaxError(cmd)
-        except SyntaxError:
-            if included:
-                raise SyntaxError("in " + included + ":" + u(line) + ": syntax error in executing command: " + cmd.strip())
-            else:
-                raise SyntaxError("in " + u(line) + ": syntax error in executing command: " + cmd.strip())
-        inner = _finish(r)
-        result += head + inner
-        m = re.search(bq, text)
-    result += textOut(text)
-
-    return result
-
-def codegen(obj):
-    global in_ns, pointers, line, included
-    ctype = obj[0]
-
-    if type(obj) is code:
-        return obj
-
-    try:
-        if ctype.line: line = ctype.line
-    except: pass
-
-    if ctype == "empty":
-        return code("")
-
-    if ctype == "in_ns":
-        in_ns = obj[1][0]
-        subtree = obj[1]
-        for sel in subtree:
-            codegen(sel)
-        in_ns = ""
-        return code("")
-
-    elif ctype == "decl":
-        name = ""
-        for data in obj[1]:
-            if type(data) is str:
-                name = data
-                try:
-                    yf = ymlFunc[name]
-                    yf.alias
-                except:
-                    ymlFunc[name] = YF(name)
-                    yf = ymlFunc[name]
-                    if in_ns:
-                        yf.alias = in_ns + ":" + name
-                        if not enable_tracing:
-                            if in_ns == "xsl" and (name == "debug" or name=="assert" or name[:7]=="_trace_"):
-                                yf.alias = "-"
-                                yf.addParm("skip1")
-                                yf.addParm("skip2")
-                                break
-            elif type(data) is tuple or type(data) is Symbol:
-                if data[0] == "base":
-                    base = data[1][0]
-                    try:
-                        yf = ymlFunc[name] = ymlFunc[base].copy(name)
-                    except KeyError:
-                        if included:
-                            raise KeyError("in " + included + ":" + u(line) + ": " + base + " as base for " + name)
-                        else:
-                            raise KeyError("in " + u(line) + ": " + base + " as base for " + name)
-                elif data[0] == "shape":
-                    shape = ymlFunc[data[1]]
-                    try:
-                        yf = ymlFunc[name]
-                        yf.patch(shape)
-                    except KeyError:
-                        if included:
-                            raise KeyError("in " + included + ":" + u(line) + ": " + base + " as shape for " + name)
-                        else:
-                            raise KeyError("in " + u(line) + ": " + base + " as shape for " + name)
-                elif data[0] == "descend":
-                    yf.addDescend(data[1])
-                elif data[0] == "declParm":
-                    l = data[1]
-                    parmName = l[0]
-                    if len(l)==1:
-                        yf.addParm(parmName)
-                    else:
-                        value = l[1]
-                        if parmName[0] != "%":
-                            yf.addValue(parmName, value)
-                        if parmName[0] == "*":
-                            yf.pointers[parmName[1:]] = value
-                            yf.addParm(parmName)
-                        elif parmName[0] == "%":
-                            if type(value) is str:
-                                yf.macros[parmName] = u(evalPython(value))
-                            else:
-                                yf.macros[parmName] = u(evalPython(u(value)))
-                            yf.addParm(parmName)
-                elif data[0] == "alias":
-                    if in_ns:
-                        yf.alias = in_ns + ":" + data[1][0]
-                    else:
-                        yf.alias = data[1][0]
-                elif data[0] == "content":
-                    yf.content = data[1]
-
-        return code("")
-
-    elif ctype == "funclist":
-        result = ""
-        for f in obj[1]:
-            result += codegen(f)
-        return code(result)
-
-    elif ctype == "parentheses":
-        if len(obj[1]):
-            return codegen(('func', ['_parentheses', ('content', [obj[1][0]])]))
-        else:
-            return ""
-
-    elif ctype == "fparm":
-        if len(obj[1]):
-            return codegen(('func', ['_parm', ('content', [obj[1][0]])]))
-        else:
-            return ""
-
-    elif ctype == "generic":
-        return codegen(('func', ['_generic', ('content', [obj[1][0]])]))
-
-    elif ctype == "xbase":
-        return codegen(('func', ['_base', ('content', [obj[1][0]])]))
-
-    elif ctype == "func":
-        avoidTag = False
-        name = obj[1][0]
-
-        if name == "decl":
-            if ymlFunc[name] == "#error":
-                if included:
-                    raise SyntaxError("in " + included + ":" + u(line) + ": syntax error in decl statement")
-                else:
-                    raise SyntaxError("in " + u(line) + ": syntax error in decl statement")
-        if name == "define" or name == "operator":
-            if ymlFunc[name] == "#error":
-                if included:
-                    raise SyntaxError("in " + included + ":" + u(line) + ": syntax error in define statement")
-                else:
-                    raise SyntaxError("in " + u(line) + ": syntax error in define statement")
-
-        if name[0] == "&":
-            avoidTag = True
-            name = name[1:]
-        hasContent = False
-
-        if len(name) > 2:
-            if name[0:2] == "**":
-                return code(eval(''+pointer(name[1:])))
-
-        if name[0] == "*":
-            name = eval(pointer(name))
-            if name[0] == "&":
-                avoidTag = True
-                name = name[1:]
-
-        try:
-            ymlFunc[name]
-        except KeyError:
-            try:
-                if ymlFunc["_"].alias != "-":
-                    return codegen(('func', ['_', ('content', [('funclist', [obj])])]))
-                else:
-                    ymlFunc[name] = copy(ymlFunc["_"])
-                    ymlFunc[name].alias = name.replace("_", "-")
-                    return codegen(obj)
-            except KeyError:
-                ymlFunc[name] = YF(name)
-        
-        if ymlFunc[name].alias == "-": avoidTag = True
-
-        to_add = []
-        if len(ymlFunc[name].descends):
-            if obj[1][-1][0] != 'content':
-                if included:
-                    raise KeyError("in " + included + ":" + u(line) + ": " + name + " has descending attributes, but no descendants are following")
-                else:
-                    raise KeyError("in " + u(line) + ": " + name + " has descending attributes, but no descendants are following")
-
-            def first_func(obj):
-                if type(obj) is tuple or type(obj) is Symbol:
-                    if obj[0] == 'func':
-                        return obj
-                    elif obj[0] == 'funclist':
-                        return first_func(obj[1])
-                    elif obj[0] == 'content':
-                        return first_func(obj[1])
-                    else:
-                        return None
-                elif type(obj) == list:
-                    for e in obj:
-                        f = first_func(e)
-                        if f: return f
-                    return None
-
-            def copy_without_first_func(o, found = False):
-                c = []
-                for obj in o:
-                    if found:
-                        c.append(obj)
-                    else:
-                        if obj[0] == 'func':
-                            if obj[1][-1][0] == 'content':
-                                c.extend( obj[1][-1][1] )
-                            found = True
-                        else:
-                            c.append( ( obj[0], copy_without_first_func(obj[1], False ) ) )
-                return c
-
-            def get_parms(obj):
-                result = []
-                for e in obj[1]:
-                    if type(e) is tuple or type(e) is Symbol:
-                        if e[0] == "parm":
-                            result.append( e )
-                return result
-
-            try:
-                add_params = get_parms(obj)
-                for e in obj[1][-1][1]:
-                    c = e[1]
-                    for dname in ymlFunc[name].descends:
-                        f, c = first_func(c), copy_without_first_func(c)
-                        if dname[0] == "*":
-                            pointers[dname[1:]] = "'" + f[1][0] + "'"
-                        else:
-                            add_params.append( ('parm', [dname, "'" + f[1][0] + "'"]) )
-                        try:
-                            add_params.extend( get_parms(f) )
-                        except: pass
-
-                    new_things = [ e[1][0] ]
-                    new_things.extend( add_params )
-                    new_things.append( ('content', c) )
-                    
-                    to_add.append( ('func', new_things ) )
-            except:
-                if included:
-                    raise KeyError("in " + included + ":" + u(line) + ": " + name + " has descending attributes, and too less descendants are following")
-                else:
-                    raise KeyError("in " + u(line) + ": " + name + " has descending attributes, and too less descendants are following")
-
-        if not to_add:
-            to_add = ( obj, )
-
-        complete = ""
-
-        for obj in to_add:
-            subtree = None
-            try:
-                if obj[1][-1][0] == "content":
-                    subtree = obj[1][-1][1]
-            except: pass
-     
-            if ymlFunc[name].content:
-                hasContent = True
-                treetemplate = deepcopy(ymlFunc[name].content)
-                subtree = replaceContent(treetemplate, subtree)
-
-            if subtree:
-                hasContent = True
-
-            hasContent, result = ymlFunc[name](obj[1], hasContent, avoidTag)
-
-            if subtree:
-                for sel in subtree:
-                    result += codegen(sel)
-
-            if hasContent and not(avoidTag):
-                result += "</" + ymlFunc[name].alias + ">"
-
-            complete += result
-
-        return code(complete)
-
-    elif ctype == "textsection":
-        result = ''
-        ll = obj[1].splitlines()
-        space = len(ll[-1]) - 2
-        for l in ll[1:-1]:
-            m = re.match(bqq, l)
-            if m:
-                cmd = m.group(1)
-                try:
-                    r, x = parseLine(cmd, _inner, [], True, comment)
-                    if x: raise SyntaxError(cmd)
-                    result += _finish(r)
-                except SyntaxError:
-                    if included:
-                        raise SyntaxError("in " + included + ":" + u(line) + ": syntax error in executing command: " + cmd.strip())
-                    else:
-                        raise SyntaxError("in " + u(line) + ": syntax error in executing command: " + cmd.strip())
-            else:
-                result += codegen(Symbol('lineQuote', '| ' + l[space:]))
-        return code(result)
-
-    elif ctype == "textsectionu":
-        result = ''
-        ll = obj[1].splitlines()
-        space = len(ll[-1]) - 2
-        for l in ll[1:-1]:
-            m = re.match(bqq, l)
-            if m:
-                cmd = m.group(1)
-                try:
-                    r, x = parseLine(cmd, _inner, [], True, comment)
-                    if x: raise SyntaxError(cmd)
-                    result += _finish(r)
-                except SyntaxError:
-                    if included:
-                        raise SyntaxError("in " + included + ":" + u(line) + ": syntax error in executing command: " + cmd.strip())
-                    else:
-                        raise SyntaxError("in " + u(line) + ": syntax error in executing command: " + cmd.strip())
-            else:
-                if result != '': result += ' '
-                result += codegen(Symbol('quote', ['> ' + l[space:]]))
-        return code(result)
-
-    elif ctype == "lineQuote" or ctype == "quote":
-        m, text, base, inds = None, "", 0, 0
-
-        if ctype == "lineQuote":
-            text = obj[1]
-            m = lq.match(text)
-            if m:
-                inds = len(m.group(1))
-                text = m.group(2)[1:]
-            else: inds = 0
-        elif ctype == "quote":
-            inds = -1
-            text = obj[1][0]
-            m = sq.match(text)
-            if m:
-                if m.group(1):
-                    inds = int(m.group(1))
-                text = m.group(2)[1:]
-            else:
-                if type(text) is str:
-                    text = u(evalPython(text))
-
-        ind = ""
-        if inds > -1:
-            try:
-                cmd = evalPython("indent(" + u(inds) + ")")
-                result, rest = parseLine(u(cmd), _inner, [], True, comment)
-                if rest:
-                    raise SyntaxError()
-                ind = _finish(result)
-            except: pass
-        
-        if ctype == "lineQuote": text += "\n"
-
-        hasTextFunc = False
-        try:
-            ymlFunc["text"]
-            hasTextFunc = True
-        except: pass
-
-        text = executeCmd(text)
-        return code(ind + text) 
-
-    elif ctype == "tagQuote":
-        m = tq.match(obj[1])
-        if m.group(1) == "<":
-            return code("<" + m.group(2))
-        else:
-            return code(m.group(2))
-
-    elif ctype == "operator":
-        operator.append((re.compile(evalPython(obj[1][0])), obj[1][1]))
-        return code("")
-
-    elif ctype == "constant":
-        name = obj[1][0]
-        if name[0] == "*":
-            name = name[1:]
-        value = obj[1][1]
-        pointers[name] = value
-        return code("")
-
-    elif ctype == "include":
-        reverse = False
-        ktext, kxml = False, False
-        for arg in obj[1]:
-            if type(arg) is tuple or type(arg) is Symbol:
-                if arg[0] == "reverse":
-                    reverse = True
-                elif arg[0] == "ktext":
-                    ktext = True
-                elif arg[0] == "kxml":
-                    kxml = True
-            elif type(arg) is str:
-                filemask = arg
-
-        if filemask[0] == '/' or filemask[0] == '.':
-            files = sorted(glob(filemask))
-        else:
-            files = []
-            for directory in includePath:
-                path = os.path.join(directory, filemask)
-                files.extend(sorted(glob(path)))
-
-        if files and reverse:
-            files = files[-1::-1]
-
-        if not(files):
-            if included:
-                raise IOError("in " + included + ":" + u(line) + ": include file(s) '" + filemask + "' not found")
-            else:
-                raise IOError("in " + u(line) + ": include file(s) '" + filemask + "' not found")
-
-        includeFile = fileinput.input(files, mode="rU", openhook=fileinput.hook_encoded(encoding))
-        _included = included
-        if ktext or kxml:
-            text = ""
-            for line in includeFile:
-                included = includeFile.filename()
-                if kxml:
-                    if (not line[:6] == '<?xml ') and (not line[:6] == '<?XML '):
-                        text += line
-                else:
-                    text += executeCmd(line)
-            included = _included
-            return code(text)
-        else:
-            result = parse(ymlCStyle(), includeFile, True, comment)
-            included = u(filemask)
-            x = _finish(result)
-            included = _included
-            return code(x)
-
-    elif ctype == "pyExp":
-        exp = obj[1][0]
-        cmd = evalPython(exp)
-        result, rest = parseLine(u(cmd), _inner, [], True, comment)
-        if rest:
-            raise SyntaxError(cmd)
-        return code(_finish(result))
-
-    elif ctype == "pythonCall":
-        parms = []
-        data = obj[1]
-        for p in data:
-            if type(p) is str:
-                name = p
-            elif type(p) is tuple or type(p) is Symbol:
-                ptype = p[0]
-                if ptype == "parm":
-                    if p[1][0][0] == "*":
-                        parms.append(pointer(p[1][0]))
-                    else:
-                        parms.append(p[1][0])
-        if len(parms) == 0:
-            exp = name + "()"
-        elif len(parms) == 1:
-            exp = name + "(" + u(parms[0]) + ")"
-        else:
-            exp = name + u(tuple(parms))
-        cmd = evalPython(exp)
-        result, rest = parseLine(u(cmd), _inner, [], True, comment)
-        if rest:
-            raise SyntaxError()
-        return code(_finish(result))
-
-    else:
-        return code("")
-
-def _finish(tree):
-    result = ""
-    python = ""
-
-    for el in tree:
-        if el[0] == "python":
-            if el[1][0][:2] == "!!":
-                python += el[1][0][2:-2]
-            else:
-                python += el[1][0][1:] + "\n"
-            continue
-        else:
-            if python:
-                execPython(python)
-                python = ""
-
-        try:
-            result += codegen(el)
-        except RuntimeError:
-            if included:
-                raise RuntimeError("in " + included + ":" + u(line))
-            else:
-                raise RuntimeError("in " + u(line))
-
-    if python:
-        execPython(python)
-
-    return result
-
-def finish(tree):
-    global first
-    first = True
-    return _finish(tree)
-
-ymlFunc["_parentheses"] = YF("parms")
-ymlFunc["_parm"] = YF("parm")
-ymlFunc["_generic"] = YF("generic")
-ymlFunc["_base"] = YF("base")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/changelog	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,5 @@
+yml2 (5.8-1) unstable; urgency=low
+
+  * source package automatically created by stdeb 0.8.5
+
+ -- Volker Birk <vb@pep.foundation>  Tue, 04 Sep 2018 17:29:10 +0200
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/compat	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,1 @@
+9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/control	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,18 @@
+Source: yml2
+Maintainer: Volker Birk <vb@pep.foundation>
+Section: python
+Priority: optional
+Build-Depends: dh-python, python-setuptools (>= 0.6b3), python-all (>= 2.6.6-3), debhelper (>= 9)
+Standards-Version: 3.9.6
+Homepage: https://pep.foundation/dev/repos/yml2
+
+Package: python-yml2
+Architecture: all
+Depends: ${misc:Depends}, ${python:Depends}
+Description: YML 2 compiler
+ .
+ YML2 caters the idea not to need to define a grammar first when you want to
+ use a Domain Specific Language. For that purpose, YML is being translated into
+ XML. Let's make an example.
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/rules	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+# This file was automatically generated by stdeb 0.8.5 at
+# Tue, 04 Sep 2018 17:29:10 +0200
+export PYBUILD_NAME=yml2
+%:
+	dh $@ --with python2 --buildsystem=pybuild
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/source/format	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,1 @@
+3.0 (quilt)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/watch	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,4 @@
+# please also check http://pypi.debian.net/YML2/watch
+version=3
+opts=uversionmangle=s/(rc|a|b|c)/~$1/ \
+http://pypi.debian.net/YML2/YML2-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
\ No newline at end of file
--- a/manpage.yml2	Mon Nov 04 11:38:34 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-decl manpage(xmlns="http://docbook.org/ns/docbook", version="5.0", *title) alias article { title *title; content; };
--- a/pyPEG.py	Mon Nov 04 11:38:34 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,351 +0,0 @@
-# YPL parser 1.6
-# adapted for Python 3.x
-
-# written by VB.
-
-import re
-import sys, codecs
-
-class keyword(str): pass
-class code(str): pass
-class ignore(object):
-    def __init__(self, regex_text, *args):
-        self.regex = re.compile(regex_text, *args)
-
-class _and(object):
-    def __init__(self, something):
-        self.obj = something
-
-class _not(_and): pass
-
-class Name(str):
-    def __init__(self, *args):
-        self.line = 0
-        self.file = ""
-
-class Symbol(list):
-    def __init__(self, name, what):
-        self.__name__ = name
-        self.append(name)
-        self.what = what
-        self.append(what)
-    def __call__(self):
-        return self.what
-    def __str__(self):
-        return 'Symbol(' + repr(self.__name__) + ', ' + repr(self.what) + ')'
-    def __repr__(self):
-        return str(self)
-
-word_regex = re.compile(r"\w+")
-rest_regex = re.compile(r".*")
-
-print_trace = False
-
-def u(text):
-    if isinstance(text, BaseException):
-        text = text.args[0]
-    if type(text) is str:
-        return text
-    if isinstance(text, bytes):
-        if sys.stdin.encoding:
-            return codecs.decode(text, sys.stdin.encoding)
-        else:
-            return codecs.decode(text, "utf-8")
-    return str(text)
-
-def skip(skipper, text, skipWS, skipComments):
-    if skipWS:
-        t = text.lstrip()
-    else:
-        t = text
-    if skipComments:
-        try:
-            while True:
-                skip, t = skipper.parseLine(t, skipComments, [], skipWS, None)
-                if skipWS:
-                    t = t.lstrip()
-        except: pass
-    return t
-
-class parser(object):
-    def __init__(self, another = False, p = False):
-        self.restlen = -1 
-        if not(another):
-            self.skipper = parser(True, p)
-            self.skipper.packrat = p
-        else:
-            self.skipper = self
-        self.lines = None
-        self.textlen = 0
-        self.memory = {}
-        self.packrat = p
-
-    # parseLine():
-    #   textline:       text to parse
-    #   pattern:        pyPEG language description
-    #   resultSoFar:    parsing result so far (default: blank list [])
-    #   skipWS:         Flag if whitespace should be skipped (default: True)
-    #   skipComments:   Python functions returning pyPEG for matching comments
-    #   
-    #   returns:        pyAST, textrest
-    #
-    #   raises:         SyntaxError(reason) if textline is detected not being in language
-    #                   described by pattern
-    #
-    #                   SyntaxError(reason) if pattern is an illegal language description
-
-    def parseLine(self, textline, pattern, resultSoFar = [], skipWS = True, skipComments = None):
-        name = None
-        _textline = textline
-        _pattern = pattern
-
-        def R(result, text):
-            if __debug__:
-                if print_trace:
-                    try:
-                        if _pattern.__name__ != "comment":
-                            sys.stderr.write("match: " + _pattern.__name__ + "\n")
-                    except: pass
-
-            if self.restlen == -1:
-                self.restlen = len(text)
-            else:
-                self.restlen = min(self.restlen, len(text))
-            res = resultSoFar
-            if name and result:
-                name.line = self.lineNo()
-                res.append(Symbol(name, result))
-            elif name:
-                name.line = self.lineNo()
-                res.append(Symbol(name, []))
-            elif result:
-                if type(result) is type([]):
-                    res.extend(result)
-                else:
-                    res.extend([result])
-            if self.packrat:
-                self.memory[(len(_textline), id(_pattern))] = (res, text)
-            return res, text
-
-        def syntaxError():
-            if self.packrat:
-                self.memory[(len(_textline), id(_pattern))] = False
-            raise SyntaxError()
-
-        if self.packrat:
-            try:
-                result = self.memory[(len(textline), id(pattern))]
-                if result:
-                    return result
-                else:
-                    raise SyntaxError()
-            except: pass
-
-        if callable(pattern):
-            if __debug__:
-                if print_trace:
-                    try:
-                        if pattern.__name__ != "comment":
-                            sys.stderr.write("testing with " + pattern.__name__ + ": " + textline[:40] + "\n")
-                    except: pass
-
-            if pattern.__name__[0] != "_":
-                name = Name(pattern.__name__)
-
-            pattern = pattern()
-            if callable(pattern):
-                pattern = (pattern,)
-
-        text = skip(self.skipper, textline, skipWS, skipComments)
-
-        pattern_type = type(pattern)
-
-        if pattern_type is str:
-            if text[:len(pattern)] == pattern:
-                text = skip(self.skipper, text[len(pattern):], skipWS, skipComments)
-                return R(None, text)
-            else:
-                syntaxError()
-
-        elif pattern_type is keyword:
-            m = word_regex.match(text)
-            if m:
-                if m.group(0) == pattern:
-                    text = skip(self.skipper, text[len(pattern):], skipWS, skipComments)
-                    return R(None, text)
-                else:
-                    syntaxError()
-            else:
-                syntaxError()
-
-        elif pattern_type is _not:
-            try:
-                r, t = self.parseLine(text, pattern.obj, [], skipWS, skipComments)
-            except:
-                return resultSoFar, textline
-            syntaxError()
-
-        elif pattern_type is _and:
-            r, t = self.parseLine(text, pattern.obj, [], skipWS, skipComments)
-            return resultSoFar, textline
-
-        elif pattern_type is type(word_regex) or pattern_type is ignore:
-            if pattern_type is ignore:
-                pattern = pattern.regex
-            m = pattern.match(text)
-            if m:
-                text = skip(self.skipper, text[len(m.group(0)):], skipWS, skipComments)
-                if pattern_type is ignore:
-                    return R(None, text)
-                else:
-                    return R(m.group(0), text)
-            else:
-                syntaxError()
-
-        elif pattern_type is tuple:
-            result = []
-            n = 1
-            for p in pattern:
-                if type(p) is type(0):
-                    n = p
-                else:
-                    if n>0:
-                        for i in range(n):
-                            result, text = self.parseLine(text, p, result, skipWS, skipComments)
-                    elif n==0:
-                        if text == "":
-                            pass
-                        else:
-                            try:
-                                newResult, newText = self.parseLine(text, p, result, skipWS, skipComments)
-                                result, text = newResult, newText
-                            except SyntaxError:
-                                pass
-                    elif n<0:
-                        found = False
-                        while True:
-                            try:
-                                newResult, newText = self.parseLine(text, p, result, skipWS, skipComments)
-                                result, text, found = newResult, newText, True
-                            except SyntaxError:
-                                break
-                        if n == -2 and not(found):
-                            syntaxError()
-                    n = 1
-            return R(result, text)
-
-        elif pattern_type is list:
-            result = []
-            found = False
-            for p in pattern:
-                try:
-                    result, text = self.parseLine(text, p, result, skipWS, skipComments)
-                    found = True
-                except SyntaxError:
-                    pass
-                if found:
-                    break
-            if found:
-                return R(result, text)
-            else:
-                syntaxError()
-
-        else:
-            raise SyntaxError("illegal type in grammar: " + u(pattern_type))
-
-    def lineNo(self):
-        if not(self.lines): return ""
-        if self.restlen == -1: return ""
-        parsed = self.textlen - self.restlen
-
-        left, right = 0, len(self.lines)
-
-        while True:
-            mid = int((right + left) / 2)
-            if self.lines[mid][0] <= parsed:
-                try:
-                    if self.lines[mid + 1][0] >= parsed:
-                        try:
-                            return u(self.lines[mid + 1][1]) + ":" + u(self.lines[mid + 1][2])
-                        except:
-                            return ""
-                    else:
-                        left = mid + 1
-                except:
-                    try:
-                        return u(self.lines[mid + 1][1]) + ":" + u(self.lines[mid + 1][2])
-                    except:
-                        return ""
-            else:
-                right = mid - 1
-            if left > right:
-                return ""
-
-# plain module API
-
-def parseLine(textline, pattern, resultSoFar = [], skipWS = True, skipComments = None, packrat = False):
-    p = parser(p=packrat)
-    text = skip(p.skipper, textline, skipWS, skipComments)
-    ast, text = p.parseLine(text, pattern, resultSoFar, skipWS, skipComments)
-    return ast, text
-
-# parse():
-#   language:       pyPEG language description
-#   lineSource:     a fileinput.FileInput object
-#   skipWS:         Flag if whitespace should be skipped (default: True)
-#   skipComments:   Python function which returns pyPEG for matching comments
-#   packrat:        use memoization
-#   lineCount:      add line number information to AST
-#   
-#   returns:        pyAST
-#
-#   raises:         SyntaxError(reason), if a parsed line is not in language
-#                   SyntaxError(reason), if the language description is illegal
-
-def parse(language, lineSource, skipWS = True, skipComments = None, packrat = False, lineCount = True):
-    lines, lineNo = [], 0
-
-    while callable(language):
-        language = language()
-
-    orig, ld = "", 0
-    for line in lineSource:
-        if lineSource.isfirstline():
-            ld = 1
-        else:
-            ld += 1
-        lines.append((len(orig), lineSource.filename(), lineSource.lineno() - 1))
-        orig += u(line)
-
-    textlen = len(orig)
-
-    try:
-        p = parser(p=packrat)
-        p.textlen = len(orig)
-        if lineCount:
-            p.lines = lines
-        else:
-            p.line = None
-        text = skip(p.skipper, orig, skipWS, skipComments)
-        result, text = p.parseLine(text, language, [], skipWS, skipComments)
-        if text:
-            raise SyntaxError()
-
-    except SyntaxError as msg:
-        parsed = textlen - p.restlen
-        textlen = 0
-        nn, lineNo, file = 0, 0, ""
-        for n, ld, l in lines:
-            if n >= parsed:
-                break
-            else:
-                lineNo = l
-                nn += 1
-                file = ld
-
-        lineNo += 1
-        nn -= 1
-        lineCont = orig.splitlines()[nn]
-        raise SyntaxError("syntax error in " + u(file) + ":" + u(lineNo) + ": " + lineCont)
-
-    return result
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/setup.py	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+
+import sys
+from setuptools import setup
+
+sys.path.insert(0, '.')
+from yml2 import yml2c
+caption = yml2c.__doc__.split('\n')[0]
+short_desc, version = map(lambda s: s.strip(), caption.split('version', 1))
+
+with open('README.md', 'r') as fh:
+    long_desc = fh.read().strip()
+
+setup(
+        name='YML2',
+        version=version,
+        description=short_desc,
+        long_description=long_desc,
+        author="Volker Birk",
+        author_email="vb@pep.foundation",
+        url="https://pep.foundation/dev/repos/yml2",
+        zip_safe=False,
+        packages=["yml2"],
+        install_requires=['lxml'],
+        package_data = {
+            '': ['gpl-2.0.txt', '*.css', '*.yhtml2'],
+            'yml2': ['*.yml2', '*.ysl2'],
+        },
+        entry_points = {
+            'console_scripts': [
+                'yml2c=yml2.yml2c:main',
+                'yml2proc=yml2.yml2proc:main'
+            ],
+        }
+    )
+
--- a/standardlib.ysl2	Mon Nov 04 11:38:34 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-// YML2 standardlib version 2.5.8
-
-function "yml:hex2dec" {
-    param "hex";
-    param "_result", 0;
-    
-    const "hd", "substring($hex, 1, 1)";
-    const "a", """translate($hd, 'ABCDEFabcdef123456789',
-        '123456123456000000000')""";
-
-    const "d" choose {
-        when "$a>0" value "$a + 9";
-        otherwise value "$hd";
-    }
-
-    choose {
-        when "string-length($hex) = 1"
-            value "$_result * 16 + $d";
-        otherwise call "yml:hex2dec"
-            with "hex", "substring($hex, 2, 8)", 
-            with "_result", "$_result * 16 + $d";
-    }
-}
-
-function "yml:dec2hex" {
-    param "dec";
-    param "bits", !16**7!;
-
-    const "v", "floor($dec div $bits)";
-    value "substring('0123456789abcdef', $v + 1, 1)";
-
-    if "$bits > 1" call "yml:dec2hex"
-        with "dec", "$dec - $bits * $v",
-        with "bits", "$bits div 16";
-}
-
-def "yml:dec2hex" {
-    param "dec";
-    param "digits", 8;
-        
-    result call "yml:dec2hex" with "dec", "$dec", with "bits", "math:power(16, $digits - 1)";
-}
-
-def "yml:hex2dec" {
-    param "hex";
-    result call "yml:hex2dec" with "hex", "$hex";
-}
-
-def "yml:lcase" {
-    param "text", "''";
-    result "translate($text, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')";
-}
-
-def "yml:ucase" {
-    param "text", "''";
-    result "translate($text, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')";
-}
-
-def "yml:mixedCase" {
-    param "text", "''";
-    result "concat(yml:lcase(substring($text,1,1)),substring($text,2))";
-}
-
-def "yml:capit" {
-    param "text", "''";
-    result "concat(yml:ucase(substring($text,1,1)),substring($text,2))";
-}
-
--- a/xml2yml.ysl2	Mon Nov 04 11:38:34 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-// xml2yml2.ysl2 version 2.4.4
-
-include yslt.yml2
-
-decl textstylesheet is estylesheet(*output="text") {
-    output *output;
-    const "space", !"'" + " " * 200 + "'"!;
-    param "autoindent", 4;
-    content;
-}, tstylesheet is textstylesheet;
-
-define operator "†" as call "convert" with "tag", "name()";
-
-tstylesheet {
-    function "printNS"
-        for "*/namespace::*[local-name()!='xml']" {
-            > xmlns
-            if "not(local-name()='')"
-                > :«local-name()»
-            > ="«.»"
-            if "position()<last()" > , 
-        }
-    
-    template "/" {
-        if "count(*/namespace::*)>1" {
-            | decl «name(*)»(`call "printNS"`);
-            |
-        }
-        apply "*", 0;
-    }
-
-    template "text()" {
-        param "text", "normalize-space()";
-
-        if "string-length($text)>0" choose {
-            when "contains($text,'\\n')" choose {
-                when "string-length(substring-before($text,'\\n'))<string-length($text)-1" choose {
-                    when "substring($text,string-length($text),1)='\\n'"
-                        > \n||\n«$text»||\n\n
-                    otherwise > > «str:replace($text,'\\n','\\\\n')»\n
-                }
-                otherwise > | «$text»\n
-            }
-            otherwise > > `copy "."`\n
-        }
-    }
-
-    template "*" {
-        0>
-        call "convert" with "tag", "name()";
-        apply "@*";
-
-        choose {
-            when "count(*)=0 and string-length(normalize-space())=0"
-                > ;\n
-            when "count(*)=1 and string-length(normalize-space())=0" {
-                > \n
-                apply "*";
-            }
-            when "count(*)=0 and string-length(normalize-space())>0" {
-                >  
-                apply "text()";
-            }
-            otherwise {
-                >  {\n
-                for "*|text()" {
-                    if "local-name()=''"
-                        if "string-length(normalize-space())>0"
-                            1>
-                    apply ".";
-                }
-                | }
-            }
-        }
-    }
-
-    template "@*" {
-        >  «name()»="«.»"
-        if "position()<last()" > ,
-    }
-
-    function "convert" {
-        param "tag";
-
-        > «translate($tag, '-', '_')»
-    }
-}
--- a/yml2.py	Mon Nov 04 11:38:34 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-# YML 2.6.0 language definition
-
-# written by VB.
-
-import re
-from pyPEG import keyword, _and, _not
-
-# pyPEG:
-#
-#   basestring:     terminal symbol (characters)
-#   keyword:        terminal symbol (keyword)
-#   matchobj:       terminal symbols (regex, use for scanning symbols)
-#   function:       named non-terminal symbol, recursive definition
-#                   if you don't want naming in output, precede name with an underscore
-#   tuple:          production sequence
-#   integer:        count in production sequence:
-#                    0: following element is optional
-#                   -1: following element can be omitted or repeated endless
-#                   -2: following element is required and can be repeated endless
-#   list:           options, choose one of them
-#   _not:           next element in production sequence is matched only if this would not
-#   _and:           next element in production sequence is matched only if this would, too
-
-newSyntax = True
-
-def oldSyntax():
-    global newSyntax
-    newSyntax = False
-
-def _if(cond, val):
-    if cond:
-        return val
-    else:
-        return ()
-
-def listing(x):     return x, -1, (",", x)
-r = re.compile
-
-comment = [r(r"//.*"), r(r"/\*.*?\*/", re.S)]
-_symbol = r"(?=\D)\w(\w|:)*"
-symbol = r(_symbol, re.U)
-pointer = r(r"\*" + _symbol, re.U)
-ppointer = r(r"\*\*" + _symbol, re.U)
-macro = r(r"\%" + _symbol, re.U)
-reference = r(r"\&" + _symbol, re.U)
-
-NameStartChar = r''':|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]'''
-NameChar = NameStartChar + r'''|-|\.|[0-9]|\u00B7|[\u0300-\u036F]|[\u203F-\u2040]'''
-
-_xmlSymbol = "(" + NameStartChar + ")(" + NameChar + ")*"
-xmlSymbol = r(_xmlSymbol)
-aliasSymbol = r(r"-|(" + _xmlSymbol + r")")
-
-literal = [r(r'""".*?"""', re.S), r(r"'''.*?'''", re.S), r(r"""0x[a-f0-9]+|-?\d+\.\d*|-?\.\d+|-?\d+|".*?"|'.*?'""")]
-filename = [("'", r(r"[^']*"), "'"), ('"', r(r'[^"]*'), '"'), r(r"[^\s;]+")]
-ws = r(r"\s+", re.U)
-
-def pyExp():        return "!", r(r"(!=|\\!|[^!])+"), "!"
-value = [literal, pyExp]
-
-def tagQuote():     return r(r"\].*|\<.*?\>")
-def lineQuote():    return r(r"\|.*")
-def quote():        return [r(r"\d*>.*"), (literal, 0, [";", "."])]
-def parm():         return [([xmlSymbol, pyExp, pointer, macro], "=", [value, pointer, symbol]), value, pointer]
-def parm_eq():      return [xmlSymbol, pyExp, pointer, macro], "=", [value, pointer, symbol]
-parm_eq.__name__ = "parm"
-_func = [symbol, ppointer, pointer, reference], _if(newSyntax, (-1, ("[", listing(parm), "]"))), 0, ("(", listing(parm), ")"), 0, listing(parm), -1, parm_eq
-def pythonCall():   return keyword("python"), _func, [";", "."]
-def declParm():     return [pointer, macro, xmlSymbol], 0, ("=", [literal, symbol])
-def alias():        return keyword("alias"), aliasSymbol
-def descend():      return r(r"[+@*]" + _symbol, re.U)
-def base():         return keyword("is"), symbol
-def shape():        return symbol
-def decl():         return symbol, 0, base, 0, ("<", listing(shape), ">"), -1, descend, _if(newSyntax, (-1, ("[", 0, listing(declParm), "]"))), 0, ("(", 0, listing(declParm), ")"), 0, alias, 0, content
-def python():       return [r(r"!!.*?!!", re.S), r(r"!.*")]
-def operator():     return 0, keyword("define"), keyword("operator"), literal, keyword("as"), r(r".*")
-def constant():     return 0, keyword("define"), [pointer, symbol], "=", literal, 0, [";", "."]
-def in_ns():        return keyword("in"), xmlSymbol, [_decl, ("{", -2, _decl, "}")]
-_decl = keyword("decl"), listing(decl), [";", "."]
-def textsection():  return r(r'(\|\|(\>*)(.*?)\|\|(\>*))\s*$', re.S | re.M)
-def textsectionu(): return r(r'(\>\>.*?\>\>)', re.S)
-def include():      return keyword("include"), 0, reverse, 0, [ktext, kxml], filename, 0, [";", "."]
-def func():         return _func, 0, content
-def funclist():     return listing(func)
-_cmd = funclist, 0, [";", "."]
-_inner = [include, textsection, textsectionu, pythonCall, _cmd, quote, lineQuote, tagQuote, pyExp]
-_cc = "{", -1, _inner, "}"
-def content_plain(): return [ (_l, 0, _p, 0, _b, 0, _cc), (_p, 0, _b, 0, _cc), (_b, 0, _cc), _cc ]
-content_plain.__name__ = "content"
-def func_plain():   return _func, 0, content_plain
-func_plain.__name__ = "func"
-def flist_plain():  return -2, func_plain
-flist_plain.__name__ = "funclist"
-def xbase():        return flist_plain
-def generic():      return flist_plain
-def subscript():    return flist_plain
-def parentheses():  return "(", 0, funclist, ")"
-def fparm():        return flist_plain
-
-_l = _if(newSyntax, ("<", listing(generic), ">"))
-_p = _if(not newSyntax, parentheses), _if(newSyntax, ("(", 0, listing(fparm), ")"))
-_b = (":", listing(xbase))
-_c = [_inner, _cc]
-
-def content():      return [ (_l, 0, _p, 0, _b, 0, _c), (_p, 0, _b, 0, _c), (_b, 0, _c), _c ]
-def reverse():      return keyword("reverse")
-def ktext():        return keyword("text")
-def kxml():         return keyword("xml")
-def ymlCStyle():    return -1, [_decl, in_ns, include, python, operator, constant, tagQuote, lineQuote, quote, _cmd]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yml2/backend.py	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,844 @@
+# 2.6.1 backend
+
+# written by VB.
+
+import re, codecs
+import fileinput
+import sys, traceback, os
+from xml.sax.saxutils import escape, quoteattr
+from copy import copy, deepcopy
+from glob import glob
+from pyPEG import code, parse, parseLine, u, Symbol
+from yml2 import ymlCStyle, comment, _inner
+
+ymlFunc, pointers, pythonFunc = {}, {}, {}
+in_ns = ""
+operator = []
+included = ""
+includePath = []
+emitlinenumbers = False
+encoding = "utf-8"
+
+first = True
+enable_tracing = False
+
+ymlFunc["decl"] = "#error"
+ymlFunc["define"] = "#error"
+ymlFunc["operator"] = "#error"
+
+def clearAll():
+    global ymlFunc, pointers, pythonFunc, in_ns, operator, included
+    ymlFunc, pointers, pythonFunc = {}, {}, {}
+    in_ns = ""
+    operator = []
+    included = ""
+
+lq = re.compile(r"\|(\>*)(.*)")
+sq = re.compile(r"(\d*)\>(.*)")
+ts = re.compile(r'(\|\|(?P<inds>\>*)\s*\n(?P<text1>.*?)\n(?P<base>\s*)\|\|)|("""(?P<text2>.*?)""")|(\>\>(?P<text3>.*?)\>\>)', re.S)
+tq = re.compile(r"(\]|\<)\s*(.*)")
+bq = re.compile(r"\`(.*?)\`", re.S)
+bqq = re.compile(r"\s*\`\`(.*)")
+all = re.compile(r".*", re.S)
+
+line = 1
+
+def pointer(name):
+    try:
+        return u(pointers[name[1:]])
+    except:
+        if name == "*_trace_info":
+            return '""'
+        if included:
+            raise LookupError("in " + included + ":" + u(line) + ": pointer " + name)
+        else:
+            raise LookupError("in " + u(line) + ": pointer " + name)
+
+def evalPython(expr):
+    try:
+        result = eval(u(expr), pythonFunc)
+        if type(result) is bytes:
+            return codecs.decode(result, encoding)
+        else:
+            return result
+    except:
+        name, parm, tb = sys.exc_info()
+        msg = "in python expression: " + u(parm)
+        if name is SyntaxError:
+            tbl = traceback.format_exception(name, parm, tb)
+            msg += "\n" + tbl[-3] + tbl[-2]
+        else:
+            msg += ": " + expr + "\n"
+        if included:
+            raise name("in " + included + ":" + u(line) + ": " + msg)
+        else:
+            raise name("in " + u(line) + ": " + msg)
+    
+def execPython(script):
+    try:
+        if type(script) is str:
+            exec(script, pythonFunc)
+        else:
+            exec(codecs.decode(script, encoding), pythonFunc)
+    except:
+        name, parm, tb = sys.exc_info()
+        msg = "in python script: " + u(parm)
+        if name is SyntaxError:
+            tbl = traceback.format_exception(name, parm, tb)
+            msg += "\n" + tbl[-3] + tbl[-2]
+        else:
+            msg += ": " + expr + "\n"
+        if included:
+            raise name("in " + included + ":" + u(line) + ": " + msg)
+        else:
+            raise name("in " + u(line) + ": " + msg)
+
+def textOut(text):
+    if not text:
+        return ""
+    if type(text) is not str:
+        text = codecs.decode(text, encoding)
+    text = text.replace(r'\"', r'\\"')
+    text = '"""' + text.replace('"', r'\"') + '"""'
+    try:
+        textFunc = ymlFunc["text"]
+        parms = ['text', ('parm', [text])]
+        c, result = textFunc(parms)
+        if c:
+            if type(textFunc.alias) is str:
+                result += "</" + textFunc.alias + ">"
+            else:
+                result += "</" + codecs.decode(textFunc.alias, encoding) + ">"
+        return result
+    except:
+        return escape(eval(text))
+
+def strRepl(text):
+    if not text:
+        return ""
+    if type(text) is not str:
+        text = codecs.decode(text, encoding)
+    text = text.replace(r'\"', r'\\"')
+    text = '"""' + text.replace('"', r'\"') + '"""'
+    if type(text) is str:
+        return escape(eval(text))
+
+def applyMacros(macros, text):
+    result = text
+    for key, value in macros.items():
+        result = result.replace(key, value)
+    return result
+
+class YF:
+    def __init__(self, name):
+        self.name = name
+        self.parms = []
+        self.descends = []
+        self.values = {}
+        self.content = None
+        self.pointers = {}
+        self.macros = {}
+        if in_ns:
+            self.alias = in_ns + ":" + name.replace("_", "-")
+        else:
+            self.alias = name.replace("_", "-")
+        pythonFunc["yml_" + name] = self
+        if emitlinenumbers:
+            self.values["yml:declared"] = u(line)
+
+    def copy(self, newName):
+        yf = YF(newName)
+        yf.parms.extend(self.parms)
+        yf.descends.extend(self.descends)
+        yf.values = self.values.copy()
+        yf.content = self.content
+        yf.pointers = self.pointers.copy()
+        yf.macros = self.macros.copy()
+        yf.alias = self.alias
+        return yf
+
+    def patch(self, second):
+        self.parms.extend(second.parms)
+        self.descends.extend(second.descends)
+        self.values.update(second.values)
+        if second.content:
+            self.content = second.content
+        self.pointers.update(second.pointers)
+        self.macros.update(second.macros)
+
+    def __call__(self, called_with, hasContent = False, avoidTag = False):
+        global pointers
+        parms = []
+        vals = {}
+        if self.pointers:
+            pointers.update(self.pointers)
+   
+        for data in called_with:
+            if type(data) is tuple or type(data) is Symbol:
+                if data[0] == "parm":
+                    l = data[1]
+                    parm = l[0]
+                    if parm[0] == "*":
+                        parm = pointer(parm)
+                    if len(l) == 1:
+                        if type(parm) is tuple or type(parm) is Symbol:
+                            if parm[0] == "pyExp":
+                                val = evalPython(parm[1][0])
+                                parms.append(val)
+                        else:
+                            parms.append(evalPython((parm)))
+                    else:
+                        if type(parm) is tuple or type(parm) is Symbol:
+                            if parm[0] == "pyExp":
+                                parm = evalPython(parm[1][0])
+                        val = l[1]
+                        if type(val) is tuple or type(val) is Symbol:
+                            if val[0] == "pyExp":
+                                val = evalPython(val[1][0])
+                        if val[0] == "*":
+                            val = pointer(val)
+                        if u(val)[0] == '"' or u(val)[0] == "'":
+                            vals[parm] = evalPython(u(val))
+                        else:
+                            vals[parm] = u(val)
+                elif data[0] == "content":
+                    hasContent = True
+
+        if enable_tracing:
+            text = u(parms) + ", " + u(vals)
+            pointers["_trace_info"] = '"' + u(line) + ": " + u(self.name) + " " + text.replace('"', '#') + '"'
+
+        if emitlinenumbers:
+            global first
+            if first:
+                vals["xmlns:yml"] = "http://fdik.org/yml"
+                first = False
+            vals["yml:called"] = u(line)
+        return self.xml(parms, vals, hasContent, avoidTag)
+
+    def addParm(self, parm):
+        if parm[0] == "%":
+            for i in range(len(self.parms)):
+                if self.parms[i][0] != "%":
+                    self.parms.insert(i, parm)
+                    return
+        self.parms.append(parm)
+
+    def addDescend(self, desc):
+        if desc[0] == "+" or desc[0] == "@":
+            self.descends.append(desc[1:])
+        else:
+            self.descends.append(desc)
+
+    def addValue(self, parm, value):
+        if type(value) is str or type(value) is str:
+            if value[0] != "'" and value[0] != '"':
+                self.values[parm] = u(value)
+            else:
+                self.values[parm] = u(evalPython(value))
+        else:
+            self.values[parm] = u(evalPython(u(value)))
+
+    def xml(self, callParms, callValues, hasContent, avoidTag = False):
+        global pointers
+        extraContent = ""
+        if self.content:
+            hasContent = True
+        resultParms = self.values.copy()
+        macros = self.macros.copy()
+        toDelete = [ key for key in resultParms.keys() ]
+        for key in toDelete:
+            if key[0] == "*":
+                del resultParms[key]
+        for key, value in callValues.items():
+            if key[0] == "%":
+                macros[key] = value
+            else:
+                resultParms[key] = value
+        i = 0
+        for cp in callParms:
+            if i < len(self.parms):
+                if self.parms[i][0] == "*":
+                    cp = u(cp)
+                    if "'" in cp:
+                        pointers[self.parms[i][1:]] = '"' + cp + '"'
+                    else:
+                        pointers[self.parms[i][1:]] = "'" + cp + "'"
+                elif self.parms[i][0] == "%":
+                    macros[self.parms[i]] = u(cp)
+                else:
+                    resultParms[self.parms[i]] = cp
+            else:
+                extraContent += u(cp)
+                hasContent = True
+            i += 1
+        result = ""
+        for p, v in resultParms.items():
+            if p[0] == "'" or p[0] == '"':
+                p = eval(p)
+            result += " "+ p + "=" + quoteattr(applyMacros(macros, u(v)))
+        if hasContent:
+            if avoidTag:
+                return True, strRepl(extraContent)
+            else:
+                return True, "<" + self.alias + result + ">" + strRepl(extraContent)
+        else:
+            if avoidTag:
+                return False, ""
+            else:
+                return False, "<" + self.alias + result + "/>"
+
+def replaceContent(tree, subtree):
+    n = 0
+    while n < len(tree):
+        obj = tree[n]
+        if obj[0] == "func":
+            l = obj[1]
+            if l[0] == "content":
+                d = 1
+                if subtree:
+                    for el in subtree:
+                        tree.insert(n+d, el)
+                        d += 1
+                del tree[n]
+                n += d
+            else:
+                try:
+                    if l[-1][0] == "content":
+                        replaceContent(l[-1][1], subtree)
+                except: pass
+        elif obj[0] == "funclist":
+            replaceContent(obj[1], subtree)
+        n += 1
+    return tree
+
+def executeCmd(text):
+    if type(text) is not str:
+        text = codecs.decode(text, encoding)
+    for (regex, pattern) in operator:
+        match = re.search(regex, text)
+        while match:
+            cmd = pattern
+            opt = match.groups()
+            for i in range(len(opt)):
+                cmd = cmd.replace("%" + u(i+1), opt[i])
+            text = text[:match.start()] + "`" + cmd + "`"+ text[match.end():]
+            match = re.search(regex, text)
+
+    result = ""
+    m = re.search(bq, text)
+    while text and m:
+        cmd  = m.group(1)
+        head = textOut(text[:m.start()])
+        text = text[m.end():]
+        try:
+            r, rest = parseLine(cmd, _inner, [], True, comment)
+            if rest: raise SyntaxError(cmd)
+        except SyntaxError:
+            if included:
+                raise SyntaxError("in " + included + ":" + u(line) + ": syntax error in executing command: " + cmd.strip())
+            else:
+                raise SyntaxError("in " + u(line) + ": syntax error in executing command: " + cmd.strip())
+        inner = _finish(r)
+        result += head + inner
+        m = re.search(bq, text)
+    result += textOut(text)
+
+    return result
+
+def codegen(obj):
+    global in_ns, pointers, line, included
+    ctype = obj[0]
+
+    if type(obj) is code:
+        return obj
+
+    try:
+        if ctype.line: line = ctype.line
+    except: pass
+
+    if ctype == "empty":
+        return code("")
+
+    if ctype == "in_ns":
+        in_ns = obj[1][0]
+        subtree = obj[1]
+        for sel in subtree:
+            codegen(sel)
+        in_ns = ""
+        return code("")
+
+    elif ctype == "decl":
+        name = ""
+        for data in obj[1]:
+            if type(data) is str:
+                name = data
+                try:
+                    yf = ymlFunc[name]
+                    yf.alias
+                except:
+                    ymlFunc[name] = YF(name)
+                    yf = ymlFunc[name]
+                    if in_ns:
+                        yf.alias = in_ns + ":" + name
+                        if not enable_tracing:
+                            if in_ns == "xsl" and (name == "debug" or name=="assert" or name[:7]=="_trace_"):
+                                yf.alias = "-"
+                                yf.addParm("skip1")
+                                yf.addParm("skip2")
+                                break
+            elif type(data) is tuple or type(data) is Symbol:
+                if data[0] == "base":
+                    base = data[1][0]
+                    try:
+                        yf = ymlFunc[name] = ymlFunc[base].copy(name)
+                    except KeyError:
+                        if included:
+                            raise KeyError("in " + included + ":" + u(line) + ": " + base + " as base for " + name)
+                        else:
+                            raise KeyError("in " + u(line) + ": " + base + " as base for " + name)
+                elif data[0] == "shape":
+                    shape = ymlFunc[data[1]]
+                    try:
+                        yf = ymlFunc[name]
+                        yf.patch(shape)
+                    except KeyError:
+                        if included:
+                            raise KeyError("in " + included + ":" + u(line) + ": " + base + " as shape for " + name)
+                        else:
+                            raise KeyError("in " + u(line) + ": " + base + " as shape for " + name)
+                elif data[0] == "descend":
+                    yf.addDescend(data[1])
+                elif data[0] == "declParm":
+                    l = data[1]
+                    parmName = l[0]
+                    if len(l)==1:
+                        yf.addParm(parmName)
+                    else:
+                        value = l[1]
+                        if parmName[0] != "%":
+                            yf.addValue(parmName, value)
+                        if parmName[0] == "*":
+                            yf.pointers[parmName[1:]] = value
+                            yf.addParm(parmName)
+                        elif parmName[0] == "%":
+                            if type(value) is str:
+                                yf.macros[parmName] = u(evalPython(value))
+                            else:
+                                yf.macros[parmName] = u(evalPython(u(value)))
+                            yf.addParm(parmName)
+                elif data[0] == "alias":
+                    if in_ns:
+                        yf.alias = in_ns + ":" + data[1][0]
+                    else:
+                        yf.alias = data[1][0]
+                elif data[0] == "content":
+                    yf.content = data[1]
+
+        return code("")
+
+    elif ctype == "funclist":
+        result = ""
+        for f in obj[1]:
+            result += codegen(f)
+        return code(result)
+
+    elif ctype == "parentheses":
+        if len(obj[1]):
+            return codegen(('func', ['_parentheses', ('content', [obj[1][0]])]))
+        else:
+            return ""
+
+    elif ctype == "fparm":
+        if len(obj[1]):
+            return codegen(('func', ['_parm', ('content', [obj[1][0]])]))
+        else:
+            return ""
+
+    elif ctype == "generic":
+        return codegen(('func', ['_generic', ('content', [obj[1][0]])]))
+
+    elif ctype == "xbase":
+        return codegen(('func', ['_base', ('content', [obj[1][0]])]))
+
+    elif ctype == "func":
+        avoidTag = False
+        name = obj[1][0]
+
+        if name == "decl":
+            if ymlFunc[name] == "#error":
+                if included:
+                    raise SyntaxError("in " + included + ":" + u(line) + ": syntax error in decl statement")
+                else:
+                    raise SyntaxError("in " + u(line) + ": syntax error in decl statement")
+        if name == "define" or name == "operator":
+            if ymlFunc[name] == "#error":
+                if included:
+                    raise SyntaxError("in " + included + ":" + u(line) + ": syntax error in define statement")
+                else:
+                    raise SyntaxError("in " + u(line) + ": syntax error in define statement")
+
+        if name[0] == "&":
+            avoidTag = True
+            name = name[1:]
+        hasContent = False
+
+        if len(name) > 2:
+            if name[0:2] == "**":
+                return code(eval(''+pointer(name[1:])))
+
+        if name[0] == "*":
+            name = eval(pointer(name))
+            if name[0] == "&":
+                avoidTag = True
+                name = name[1:]
+
+        try:
+            ymlFunc[name]
+        except KeyError:
+            try:
+                if ymlFunc["_"].alias != "-":
+                    return codegen(('func', ['_', ('content', [('funclist', [obj])])]))
+                else:
+                    ymlFunc[name] = copy(ymlFunc["_"])
+                    ymlFunc[name].alias = name.replace("_", "-")
+                    return codegen(obj)
+            except KeyError:
+                ymlFunc[name] = YF(name)
+        
+        if ymlFunc[name].alias == "-": avoidTag = True
+
+        to_add = []
+        if len(ymlFunc[name].descends):
+            if obj[1][-1][0] != 'content':
+                if included:
+                    raise KeyError("in " + included + ":" + u(line) + ": " + name + " has descending attributes, but no descendants are following")
+                else:
+                    raise KeyError("in " + u(line) + ": " + name + " has descending attributes, but no descendants are following")
+
+            def first_func(obj):
+                if type(obj) is tuple or type(obj) is Symbol:
+                    if obj[0] == 'func':
+                        return obj
+                    elif obj[0] == 'funclist':
+                        return first_func(obj[1])
+                    elif obj[0] == 'content':
+                        return first_func(obj[1])
+                    else:
+                        return None
+                elif type(obj) == list:
+                    for e in obj:
+                        f = first_func(e)
+                        if f: return f
+                    return None
+
+            def copy_without_first_func(o, found = False):
+                c = []
+                for obj in o:
+                    if found:
+                        c.append(obj)
+                    else:
+                        if obj[0] == 'func':
+                            if obj[1][-1][0] == 'content':
+                                c.extend( obj[1][-1][1] )
+                            found = True
+                        else:
+                            c.append( ( obj[0], copy_without_first_func(obj[1], False ) ) )
+                return c
+
+            def get_parms(obj):
+                result = []
+                for e in obj[1]:
+                    if type(e) is tuple or type(e) is Symbol:
+                        if e[0] == "parm":
+                            result.append( e )
+                return result
+
+            try:
+                add_params = get_parms(obj)
+                for e in obj[1][-1][1]:
+                    c = e[1]
+                    for dname in ymlFunc[name].descends:
+                        f, c = first_func(c), copy_without_first_func(c)
+                        if dname[0] == "*":
+                            pointers[dname[1:]] = "'" + f[1][0] + "'"
+                        else:
+                            add_params.append( ('parm', [dname, "'" + f[1][0] + "'"]) )
+                        try:
+                            add_params.extend( get_parms(f) )
+                        except: pass
+
+                    new_things = [ e[1][0] ]
+                    new_things.extend( add_params )
+                    new_things.append( ('content', c) )
+                    
+                    to_add.append( ('func', new_things ) )
+            except:
+                if included:
+                    raise KeyError("in " + included + ":" + u(line) + ": " + name + " has descending attributes, and too less descendants are following")
+                else:
+                    raise KeyError("in " + u(line) + ": " + name + " has descending attributes, and too less descendants are following")
+
+        if not to_add:
+            to_add = ( obj, )
+
+        complete = ""
+
+        for obj in to_add:
+            subtree = None
+            try:
+                if obj[1][-1][0] == "content":
+                    subtree = obj[1][-1][1]
+            except: pass
+     
+            if ymlFunc[name].content:
+                hasContent = True
+                treetemplate = deepcopy(ymlFunc[name].content)
+                subtree = replaceContent(treetemplate, subtree)
+
+            if subtree:
+                hasContent = True
+
+            hasContent, result = ymlFunc[name](obj[1], hasContent, avoidTag)
+
+            if subtree:
+                for sel in subtree:
+                    result += codegen(sel)
+
+            if hasContent and not(avoidTag):
+                result += "</" + ymlFunc[name].alias + ">"
+
+            complete += result
+
+        return code(complete)
+
+    elif ctype == "textsection":
+        result = ''
+        ll = obj[1].splitlines()
+        space = len(ll[-1]) - 2
+        for l in ll[1:-1]:
+            m = re.match(bqq, l)
+            if m:
+                cmd = m.group(1)
+                try:
+                    r, x = parseLine(cmd, _inner, [], True, comment)
+                    if x: raise SyntaxError(cmd)
+                    result += _finish(r)
+                except SyntaxError:
+                    if included:
+                        raise SyntaxError("in " + included + ":" + u(line) + ": syntax error in executing command: " + cmd.strip())
+                    else:
+                        raise SyntaxError("in " + u(line) + ": syntax error in executing command: " + cmd.strip())
+            else:
+                result += codegen(Symbol('lineQuote', '| ' + l[space:]))
+        return code(result)
+
+    elif ctype == "textsectionu":
+        result = ''
+        ll = obj[1].splitlines()
+        space = len(ll[-1]) - 2
+        for l in ll[1:-1]:
+            m = re.match(bqq, l)
+            if m:
+                cmd = m.group(1)
+                try:
+                    r, x = parseLine(cmd, _inner, [], True, comment)
+                    if x: raise SyntaxError(cmd)
+                    result += _finish(r)
+                except SyntaxError:
+                    if included:
+                        raise SyntaxError("in " + included + ":" + u(line) + ": syntax error in executing command: " + cmd.strip())
+                    else:
+                        raise SyntaxError("in " + u(line) + ": syntax error in executing command: " + cmd.strip())
+            else:
+                if result != '': result += ' '
+                result += codegen(Symbol('quote', ['> ' + l[space:]]))
+        return code(result)
+
+    elif ctype == "lineQuote" or ctype == "quote":
+        m, text, base, inds = None, "", 0, 0
+
+        if ctype == "lineQuote":
+            text = obj[1]
+            m = lq.match(text)
+            if m:
+                inds = len(m.group(1))
+                text = m.group(2)[1:]
+            else: inds = 0
+        elif ctype == "quote":
+            inds = -1
+            text = obj[1][0]
+            m = sq.match(text)
+            if m:
+                if m.group(1):
+                    inds = int(m.group(1))
+                text = m.group(2)[1:]
+            else:
+                if type(text) is str:
+                    text = u(evalPython(text))
+
+        ind = ""
+        if inds > -1:
+            try:
+                cmd = evalPython("indent(" + u(inds) + ")")
+                result, rest = parseLine(u(cmd), _inner, [], True, comment)
+                if rest:
+                    raise SyntaxError()
+                ind = _finish(result)
+            except: pass
+        
+        if ctype == "lineQuote": text += "\n"
+
+        hasTextFunc = False
+        try:
+            ymlFunc["text"]
+            hasTextFunc = True
+        except: pass
+
+        text = executeCmd(text)
+        return code(ind + text) 
+
+    elif ctype == "tagQuote":
+        m = tq.match(obj[1])
+        if m.group(1) == "<":
+            return code("<" + m.group(2))
+        else:
+            return code(m.group(2))
+
+    elif ctype == "operator":
+        operator.append((re.compile(evalPython(obj[1][0])), obj[1][1]))
+        return code("")
+
+    elif ctype == "constant":
+        name = obj[1][0]
+        if name[0] == "*":
+            name = name[1:]
+        value = obj[1][1]
+        pointers[name] = value
+        return code("")
+
+    elif ctype == "include":
+        reverse = False
+        ktext, kxml = False, False
+        for arg in obj[1]:
+            if type(arg) is tuple or type(arg) is Symbol:
+                if arg[0] == "reverse":
+                    reverse = True
+                elif arg[0] == "ktext":
+                    ktext = True
+                elif arg[0] == "kxml":
+                    kxml = True
+            elif type(arg) is str:
+                filemask = arg
+
+        if filemask[0] == '/' or filemask[0] == '.':
+            files = sorted(glob(filemask))
+        else:
+            files = []
+            for directory in includePath:
+                path = os.path.join(directory, filemask)
+                files.extend(sorted(glob(path)))
+
+        if files and reverse:
+            files = files[-1::-1]
+
+        if not(files):
+            if included:
+                raise IOError("in " + included + ":" + u(line) + ": include file(s) '" + filemask + "' not found")
+            else:
+                raise IOError("in " + u(line) + ": include file(s) '" + filemask + "' not found")
+
+        includeFile = fileinput.input(files, mode="rU", openhook=fileinput.hook_encoded(encoding))
+        _included = included
+        if ktext or kxml:
+            text = ""
+            for line in includeFile:
+                included = includeFile.filename()
+                if kxml:
+                    if (not line[:6] == '<?xml ') and (not line[:6] == '<?XML '):
+                        text += line
+                else:
+                    text += executeCmd(line)
+            included = _included
+            return code(text)
+        else:
+            result = parse(ymlCStyle(), includeFile, True, comment)
+            included = u(filemask)
+            x = _finish(result)
+            included = _included
+            return code(x)
+
+    elif ctype == "pyExp":
+        exp = obj[1][0]
+        cmd = evalPython(exp)
+        result, rest = parseLine(u(cmd), _inner, [], True, comment)
+        if rest:
+            raise SyntaxError(cmd)
+        return code(_finish(result))
+
+    elif ctype == "pythonCall":
+        parms = []
+        data = obj[1]
+        for p in data:
+            if type(p) is str:
+                name = p
+            elif type(p) is tuple or type(p) is Symbol:
+                ptype = p[0]
+                if ptype == "parm":
+                    if p[1][0][0] == "*":
+                        parms.append(pointer(p[1][0]))
+                    else:
+                        parms.append(p[1][0])
+        if len(parms) == 0:
+            exp = name + "()"
+        elif len(parms) == 1:
+            exp = name + "(" + u(parms[0]) + ")"
+        else:
+            exp = name + u(tuple(parms))
+        cmd = evalPython(exp)
+        result, rest = parseLine(u(cmd), _inner, [], True, comment)
+        if rest:
+            raise SyntaxError()
+        return code(_finish(result))
+
+    else:
+        return code("")
+
+def _finish(tree):
+    result = ""
+    python = ""
+
+    for el in tree:
+        if el[0] == "python":
+            if el[1][0][:2] == "!!":
+                python += el[1][0][2:-2]
+            else:
+                python += el[1][0][1:] + "\n"
+            continue
+        else:
+            if python:
+                execPython(python)
+                python = ""
+
+        try:
+            result += codegen(el)
+        except RuntimeError:
+            if included:
+                raise RuntimeError("in " + included + ":" + u(line))
+            else:
+                raise RuntimeError("in " + u(line))
+
+    if python:
+        execPython(python)
+
+    return result
+
+def finish(tree):
+    global first
+    first = True
+    return _finish(tree)
+
+ymlFunc["_parentheses"] = YF("parms")
+ymlFunc["_parm"] = YF("parm")
+ymlFunc["_generic"] = YF("generic")
+ymlFunc["_base"] = YF("base")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yml2/manpage.yml2	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,1 @@
+decl manpage(xmlns="http://docbook.org/ns/docbook", version="5.0", *title) alias article { title *title; content; };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yml2/pyPEG.py	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,351 @@
+# YPL parser 1.6
+# adapted for Python 3.x
+
+# written by VB.
+
+import re
+import sys, codecs
+
+class keyword(str): pass
+class code(str): pass
+class ignore(object):
+    def __init__(self, regex_text, *args):
+        self.regex = re.compile(regex_text, *args)
+
+class _and(object):
+    def __init__(self, something):
+        self.obj = something
+
+class _not(_and): pass
+
+class Name(str):
+    def __init__(self, *args):
+        self.line = 0
+        self.file = ""
+
+class Symbol(list):
+    def __init__(self, name, what):
+        self.__name__ = name
+        self.append(name)
+        self.what = what
+        self.append(what)
+    def __call__(self):
+        return self.what
+    def __str__(self):
+        return 'Symbol(' + repr(self.__name__) + ', ' + repr(self.what) + ')'
+    def __repr__(self):
+        return str(self)
+
+word_regex = re.compile(r"\w+")
+rest_regex = re.compile(r".*")
+
+print_trace = False
+
+def u(text):
+    if isinstance(text, BaseException):
+        text = text.args[0]
+    if type(text) is str:
+        return text
+    if isinstance(text, bytes):
+        if sys.stdin.encoding:
+            return codecs.decode(text, sys.stdin.encoding)
+        else:
+            return codecs.decode(text, "utf-8")
+    return str(text)
+
+def skip(skipper, text, skipWS, skipComments):
+    if skipWS:
+        t = text.lstrip()
+    else:
+        t = text
+    if skipComments:
+        try:
+            while True:
+                skip, t = skipper.parseLine(t, skipComments, [], skipWS, None)
+                if skipWS:
+                    t = t.lstrip()
+        except: pass
+    return t
+
+class parser(object):
+    def __init__(self, another = False, p = False):
+        self.restlen = -1 
+        if not(another):
+            self.skipper = parser(True, p)
+            self.skipper.packrat = p
+        else:
+            self.skipper = self
+        self.lines = None
+        self.textlen = 0
+        self.memory = {}
+        self.packrat = p
+
+    # parseLine():
+    #   textline:       text to parse
+    #   pattern:        pyPEG language description
+    #   resultSoFar:    parsing result so far (default: blank list [])
+    #   skipWS:         Flag if whitespace should be skipped (default: True)
+    #   skipComments:   Python functions returning pyPEG for matching comments
+    #   
+    #   returns:        pyAST, textrest
+    #
+    #   raises:         SyntaxError(reason) if textline is detected not being in language
+    #                   described by pattern
+    #
+    #                   SyntaxError(reason) if pattern is an illegal language description
+
+    def parseLine(self, textline, pattern, resultSoFar = [], skipWS = True, skipComments = None):
+        name = None
+        _textline = textline
+        _pattern = pattern
+
+        def R(result, text):
+            if __debug__:
+                if print_trace:
+                    try:
+                        if _pattern.__name__ != "comment":
+                            sys.stderr.write("match: " + _pattern.__name__ + "\n")
+                    except: pass
+
+            if self.restlen == -1:
+                self.restlen = len(text)
+            else:
+                self.restlen = min(self.restlen, len(text))
+            res = resultSoFar
+            if name and result:
+                name.line = self.lineNo()
+                res.append(Symbol(name, result))
+            elif name:
+                name.line = self.lineNo()
+                res.append(Symbol(name, []))
+            elif result:
+                if type(result) is type([]):
+                    res.extend(result)
+                else:
+                    res.extend([result])
+            if self.packrat:
+                self.memory[(len(_textline), id(_pattern))] = (res, text)
+            return res, text
+
+        def syntaxError():
+            if self.packrat:
+                self.memory[(len(_textline), id(_pattern))] = False
+            raise SyntaxError()
+
+        if self.packrat:
+            try:
+                result = self.memory[(len(textline), id(pattern))]
+                if result:
+                    return result
+                else:
+                    raise SyntaxError()
+            except: pass
+
+        if callable(pattern):
+            if __debug__:
+                if print_trace:
+                    try:
+                        if pattern.__name__ != "comment":
+                            sys.stderr.write("testing with " + pattern.__name__ + ": " + textline[:40] + "\n")
+                    except: pass
+
+            if pattern.__name__[0] != "_":
+                name = Name(pattern.__name__)
+
+            pattern = pattern()
+            if callable(pattern):
+                pattern = (pattern,)
+
+        text = skip(self.skipper, textline, skipWS, skipComments)
+
+        pattern_type = type(pattern)
+
+        if pattern_type is str:
+            if text[:len(pattern)] == pattern:
+                text = skip(self.skipper, text[len(pattern):], skipWS, skipComments)
+                return R(None, text)
+            else:
+                syntaxError()
+
+        elif pattern_type is keyword:
+            m = word_regex.match(text)
+            if m:
+                if m.group(0) == pattern:
+                    text = skip(self.skipper, text[len(pattern):], skipWS, skipComments)
+                    return R(None, text)
+                else:
+                    syntaxError()
+            else:
+                syntaxError()
+
+        elif pattern_type is _not:
+            try:
+                r, t = self.parseLine(text, pattern.obj, [], skipWS, skipComments)
+            except:
+                return resultSoFar, textline
+            syntaxError()
+
+        elif pattern_type is _and:
+            r, t = self.parseLine(text, pattern.obj, [], skipWS, skipComments)
+            return resultSoFar, textline
+
+        elif pattern_type is type(word_regex) or pattern_type is ignore:
+            if pattern_type is ignore:
+                pattern = pattern.regex
+            m = pattern.match(text)
+            if m:
+                text = skip(self.skipper, text[len(m.group(0)):], skipWS, skipComments)
+                if pattern_type is ignore:
+                    return R(None, text)
+                else:
+                    return R(m.group(0), text)
+            else:
+                syntaxError()
+
+        elif pattern_type is tuple:
+            result = []
+            n = 1
+            for p in pattern:
+                if type(p) is type(0):
+                    n = p
+                else:
+                    if n>0:
+                        for i in range(n):
+                            result, text = self.parseLine(text, p, result, skipWS, skipComments)
+                    elif n==0:
+                        if text == "":
+                            pass
+                        else:
+                            try:
+                                newResult, newText = self.parseLine(text, p, result, skipWS, skipComments)
+                                result, text = newResult, newText
+                            except SyntaxError:
+                                pass
+                    elif n<0:
+                        found = False
+                        while True:
+                            try:
+                                newResult, newText = self.parseLine(text, p, result, skipWS, skipComments)
+                                result, text, found = newResult, newText, True
+                            except SyntaxError:
+                                break
+                        if n == -2 and not(found):
+                            syntaxError()
+                    n = 1
+            return R(result, text)
+
+        elif pattern_type is list:
+            result = []
+            found = False
+            for p in pattern:
+                try:
+                    result, text = self.parseLine(text, p, result, skipWS, skipComments)
+                    found = True
+                except SyntaxError:
+                    pass
+                if found:
+                    break
+            if found:
+                return R(result, text)
+            else:
+                syntaxError()
+
+        else:
+            raise SyntaxError("illegal type in grammar: " + u(pattern_type))
+
+    def lineNo(self):
+        if not(self.lines): return ""
+        if self.restlen == -1: return ""
+        parsed = self.textlen - self.restlen
+
+        left, right = 0, len(self.lines)
+
+        while True:
+            mid = int((right + left) / 2)
+            if self.lines[mid][0] <= parsed:
+                try:
+                    if self.lines[mid + 1][0] >= parsed:
+                        try:
+                            return u(self.lines[mid + 1][1]) + ":" + u(self.lines[mid + 1][2])
+                        except:
+                            return ""
+                    else:
+                        left = mid + 1
+                except:
+                    try:
+                        return u(self.lines[mid + 1][1]) + ":" + u(self.lines[mid + 1][2])
+                    except:
+                        return ""
+            else:
+                right = mid - 1
+            if left > right:
+                return ""
+
+# plain module API
+
+def parseLine(textline, pattern, resultSoFar = [], skipWS = True, skipComments = None, packrat = False):
+    p = parser(p=packrat)
+    text = skip(p.skipper, textline, skipWS, skipComments)
+    ast, text = p.parseLine(text, pattern, resultSoFar, skipWS, skipComments)
+    return ast, text
+
+# parse():
+#   language:       pyPEG language description
+#   lineSource:     a fileinput.FileInput object
+#   skipWS:         Flag if whitespace should be skipped (default: True)
+#   skipComments:   Python function which returns pyPEG for matching comments
+#   packrat:        use memoization
+#   lineCount:      add line number information to AST
+#   
+#   returns:        pyAST
+#
+#   raises:         SyntaxError(reason), if a parsed line is not in language
+#                   SyntaxError(reason), if the language description is illegal
+
+def parse(language, lineSource, skipWS = True, skipComments = None, packrat = False, lineCount = True):
+    lines, lineNo = [], 0
+
+    while callable(language):
+        language = language()
+
+    orig, ld = "", 0
+    for line in lineSource:
+        if lineSource.isfirstline():
+            ld = 1
+        else:
+            ld += 1
+        lines.append((len(orig), lineSource.filename(), lineSource.lineno() - 1))
+        orig += u(line)
+
+    textlen = len(orig)
+
+    try:
+        p = parser(p=packrat)
+        p.textlen = len(orig)
+        if lineCount:
+            p.lines = lines
+        else:
+            p.line = None
+        text = skip(p.skipper, orig, skipWS, skipComments)
+        result, text = p.parseLine(text, language, [], skipWS, skipComments)
+        if text:
+            raise SyntaxError()
+
+    except SyntaxError as msg:
+        parsed = textlen - p.restlen
+        textlen = 0
+        nn, lineNo, file = 0, 0, ""
+        for n, ld, l in lines:
+            if n >= parsed:
+                break
+            else:
+                lineNo = l
+                nn += 1
+                file = ld
+
+        lineNo += 1
+        nn -= 1
+        lineCont = orig.splitlines()[nn]
+        raise SyntaxError("syntax error in " + u(file) + ":" + u(lineNo) + ": " + lineCont)
+
+    return result
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yml2/standardlib.ysl2	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,68 @@
+// YML2 standardlib version 2.5.8
+
+function "yml:hex2dec" {
+    param "hex";
+    param "_result", 0;
+    
+    const "hd", "substring($hex, 1, 1)";
+    const "a", """translate($hd, 'ABCDEFabcdef123456789',
+        '123456123456000000000')""";
+
+    const "d" choose {
+        when "$a>0" value "$a + 9";
+        otherwise value "$hd";
+    }
+
+    choose {
+        when "string-length($hex) = 1"
+            value "$_result * 16 + $d";
+        otherwise call "yml:hex2dec"
+            with "hex", "substring($hex, 2, 8)", 
+            with "_result", "$_result * 16 + $d";
+    }
+}
+
+function "yml:dec2hex" {
+    param "dec";
+    param "bits", !16**7!;
+
+    const "v", "floor($dec div $bits)";
+    value "substring('0123456789abcdef', $v + 1, 1)";
+
+    if "$bits > 1" call "yml:dec2hex"
+        with "dec", "$dec - $bits * $v",
+        with "bits", "$bits div 16";
+}
+
+def "yml:dec2hex" {
+    param "dec";
+    param "digits", 8;
+        
+    result call "yml:dec2hex" with "dec", "$dec", with "bits", "math:power(16, $digits - 1)";
+}
+
+def "yml:hex2dec" {
+    param "hex";
+    result call "yml:hex2dec" with "hex", "$hex";
+}
+
+def "yml:lcase" {
+    param "text", "''";
+    result "translate($text, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')";
+}
+
+def "yml:ucase" {
+    param "text", "''";
+    result "translate($text, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')";
+}
+
+def "yml:mixedCase" {
+    param "text", "''";
+    result "concat(yml:lcase(substring($text,1,1)),substring($text,2))";
+}
+
+def "yml:capit" {
+    param "text", "''";
+    result "concat(yml:ucase(substring($text,1,1)),substring($text,2))";
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yml2/xml2yml.ysl2	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,87 @@
+// xml2yml2.ysl2 version 2.4.4
+
+include yslt.yml2
+
+decl textstylesheet is estylesheet(*output="text") {
+    output *output;
+    const "space", !"'" + " " * 200 + "'"!;
+    param "autoindent", 4;
+    content;
+}, tstylesheet is textstylesheet;
+
+define operator "†" as call "convert" with "tag", "name()";
+
+tstylesheet {
+    function "printNS"
+        for "*/namespace::*[local-name()!='xml']" {
+            > xmlns
+            if "not(local-name()='')"
+                > :«local-name()»
+            > ="«.»"
+            if "position()<last()" > , 
+        }
+    
+    template "/" {
+        if "count(*/namespace::*)>1" {
+            | decl «name(*)»(`call "printNS"`);
+            |
+        }
+        apply "*", 0;
+    }
+
+    template "text()" {
+        param "text", "normalize-space()";
+
+        if "string-length($text)>0" choose {
+            when "contains($text,'\\n')" choose {
+                when "string-length(substring-before($text,'\\n'))<string-length($text)-1" choose {
+                    when "substring($text,string-length($text),1)='\\n'"
+                        > \n||\n«$text»||\n\n
+                    otherwise > > «str:replace($text,'\\n','\\\\n')»\n
+                }
+                otherwise > | «$text»\n
+            }
+            otherwise > > `copy "."`\n
+        }
+    }
+
+    template "*" {
+        0>
+        call "convert" with "tag", "name()";
+        apply "@*";
+
+        choose {
+            when "count(*)=0 and string-length(normalize-space())=0"
+                > ;\n
+            when "count(*)=1 and string-length(normalize-space())=0" {
+                > \n
+                apply "*";
+            }
+            when "count(*)=0 and string-length(normalize-space())>0" {
+                >  
+                apply "text()";
+            }
+            otherwise {
+                >  {\n
+                for "*|text()" {
+                    if "local-name()=''"
+                        if "string-length(normalize-space())>0"
+                            1>
+                    apply ".";
+                }
+                | }
+            }
+        }
+    }
+
+    template "@*" {
+        >  «name()»="«.»"
+        if "position()<last()" > ,
+    }
+
+    function "convert" {
+        param "tag";
+
+        > «translate($tag, '-', '_')»
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yml2/yml2.py	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,109 @@
+# YML 2.6.0 language definition
+
+# written by VB.
+
+import re
+from pyPEG import keyword, _and, _not
+
+# pyPEG:
+#
+#   basestring:     terminal symbol (characters)
+#   keyword:        terminal symbol (keyword)
+#   matchobj:       terminal symbols (regex, use for scanning symbols)
+#   function:       named non-terminal symbol, recursive definition
+#                   if you don't want naming in output, precede name with an underscore
+#   tuple:          production sequence
+#   integer:        count in production sequence:
+#                    0: following element is optional
+#                   -1: following element can be omitted or repeated endless
+#                   -2: following element is required and can be repeated endless
+#   list:           options, choose one of them
+#   _not:           next element in production sequence is matched only if this would not
+#   _and:           next element in production sequence is matched only if this would, too
+
+newSyntax = True
+
+def oldSyntax():
+    global newSyntax
+    newSyntax = False
+
+def _if(cond, val):
+    if cond:
+        return val
+    else:
+        return ()
+
+def listing(x):     return x, -1, (",", x)
+r = re.compile
+
+comment = [r(r"//.*"), r(r"/\*.*?\*/", re.S)]
+_symbol = r"(?=\D)\w(\w|:)*"
+symbol = r(_symbol, re.U)
+pointer = r(r"\*" + _symbol, re.U)
+ppointer = r(r"\*\*" + _symbol, re.U)
+macro = r(r"\%" + _symbol, re.U)
+reference = r(r"\&" + _symbol, re.U)
+
+NameStartChar = r''':|[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]'''
+NameChar = NameStartChar + r'''|-|\.|[0-9]|\u00B7|[\u0300-\u036F]|[\u203F-\u2040]'''
+
+_xmlSymbol = "(" + NameStartChar + ")(" + NameChar + ")*"
+xmlSymbol = r(_xmlSymbol)
+aliasSymbol = r(r"-|(" + _xmlSymbol + r")")
+
+literal = [r(r'""".*?"""', re.S), r(r"'''.*?'''", re.S), r(r"""0x[a-f0-9]+|-?\d+\.\d*|-?\.\d+|-?\d+|".*?"|'.*?'""")]
+filename = [("'", r(r"[^']*"), "'"), ('"', r(r'[^"]*'), '"'), r(r"[^\s;]+")]
+ws = r(r"\s+", re.U)
+
+def pyExp():        return "!", r(r"(!=|\\!|[^!])+"), "!"
+value = [literal, pyExp]
+
+def tagQuote():     return r(r"\].*|\<.*?\>")
+def lineQuote():    return r(r"\|.*")
+def quote():        return [r(r"\d*>.*"), (literal, 0, [";", "."])]
+def parm():         return [([xmlSymbol, pyExp, pointer, macro], "=", [value, pointer, symbol]), value, pointer]
+def parm_eq():      return [xmlSymbol, pyExp, pointer, macro], "=", [value, pointer, symbol]
+parm_eq.__name__ = "parm"
+_func = [symbol, ppointer, pointer, reference], _if(newSyntax, (-1, ("[", listing(parm), "]"))), 0, ("(", listing(parm), ")"), 0, listing(parm), -1, parm_eq
+def pythonCall():   return keyword("python"), _func, [";", "."]
+def declParm():     return [pointer, macro, xmlSymbol], 0, ("=", [literal, symbol])
+def alias():        return keyword("alias"), aliasSymbol
+def descend():      return r(r"[+@*]" + _symbol, re.U)
+def base():         return keyword("is"), symbol
+def shape():        return symbol
+def decl():         return symbol, 0, base, 0, ("<", listing(shape), ">"), -1, descend, _if(newSyntax, (-1, ("[", 0, listing(declParm), "]"))), 0, ("(", 0, listing(declParm), ")"), 0, alias, 0, content
+def python():       return [r(r"!!.*?!!", re.S), r(r"!.*")]
+def operator():     return 0, keyword("define"), keyword("operator"), literal, keyword("as"), r(r".*")
+def constant():     return 0, keyword("define"), [pointer, symbol], "=", literal, 0, [";", "."]
+def in_ns():        return keyword("in"), xmlSymbol, [_decl, ("{", -2, _decl, "}")]
+_decl = keyword("decl"), listing(decl), [";", "."]
+def textsection():  return r(r'(\|\|(\>*)(.*?)\|\|(\>*))\s*$', re.S | re.M)
+def textsectionu(): return r(r'(\>\>.*?\>\>)', re.S)
+def include():      return keyword("include"), 0, reverse, 0, [ktext, kxml], filename, 0, [";", "."]
+def func():         return _func, 0, content
+def funclist():     return listing(func)
+_cmd = funclist, 0, [";", "."]
+_inner = [include, textsection, textsectionu, pythonCall, _cmd, quote, lineQuote, tagQuote, pyExp]
+_cc = "{", -1, _inner, "}"
+def content_plain(): return [ (_l, 0, _p, 0, _b, 0, _cc), (_p, 0, _b, 0, _cc), (_b, 0, _cc), _cc ]
+content_plain.__name__ = "content"
+def func_plain():   return _func, 0, content_plain
+func_plain.__name__ = "func"
+def flist_plain():  return -2, func_plain
+flist_plain.__name__ = "funclist"
+def xbase():        return flist_plain
+def generic():      return flist_plain
+def subscript():    return flist_plain
+def parentheses():  return "(", 0, funclist, ")"
+def fparm():        return flist_plain
+
+_l = _if(newSyntax, ("<", listing(generic), ">"))
+_p = _if(not newSyntax, parentheses), _if(newSyntax, ("(", 0, listing(fparm), ")"))
+_b = (":", listing(xbase))
+_c = [_inner, _cc]
+
+def content():      return [ (_l, 0, _p, 0, _b, 0, _c), (_p, 0, _b, 0, _c), (_b, 0, _c), _c ]
+def reverse():      return keyword("reverse")
+def ktext():        return keyword("text")
+def kxml():         return keyword("xml")
+def ymlCStyle():    return -1, [_decl, in_ns, include, python, operator, constant, tagQuote, lineQuote, quote, _cmd]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yml2/yml2c.py	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+# vim: set fileencoding=utf-8 :
+
+"""\
+YML 2 compiler version 6.2
+Copyleft (c), 2009-2019, Volker Birk  http://fdik.org/yml/
+
+"""
+
+import sys, os, codecs, locale
+import fileinput, unicodedata
+from optparse import OptionParser
+
+from pyPEG import parse, u
+from yml2 import ymlCStyle, comment, oldSyntax
+import backend
+
+def printInfo(option, opt_str, value, parser):
+    sys.stdout.write(__doc__)
+    sys.exit(0)
+
+def w(msg):
+    if isinstance(msg, BaseException):
+        msg = str(msg) + "\n"
+    if type(msg) is bytes:
+        msg = codecs.encode(msg, sys.stderr.encoding)
+    sys.stderr.write(msg)
+
+def main():
+    optParser = OptionParser()
+    optParser.add_option("-C", "--old-syntax", action="store_true", dest="old_syntax",
+            help="syntax of YML 2 version 1.x (compatibility mode)", default=False)
+    optParser.add_option("-D", "--emit-linenumbers", action="store_true", dest="emitlinenumbers",
+            help="emit line numbers into the resulting XML for debugging purposes", default=False)
+    optParser.add_option("-E", "--encoding", dest="encoding", metavar="ENCODING", default=locale.getdefaultlocale()[1],
+            help="encoding of input files (default to locale)")
+    optParser.add_option("-I", "--include", dest="includePathText", metavar="INCLUDE_PATH",
+            help="precede YML_PATH by a colon separated INCLUDE_PATH to search for include files")
+    optParser.add_option("-m", "--omit-empty-parm-tags", action="store_true", dest="omitemptyparm",
+            help="does nothing (only there for compatibility reasons)", default=False)
+    optParser.add_option("-n", "--normalization", dest="normalization", metavar="NORMALIZATION", default="NFC",
+            help="Unicode normalization (none, NFD, NFKD, NFC, NFKC, FCD, default is NFC)")
+    optParser.add_option("-o", "--output", dest="outputFile", metavar="FILE",
+            help="place output in file FILE")
+    optParser.add_option("-p", "--parse-only", action="store_true", dest="parseonly",
+            help="parse only, then output pyAST as text to stdout", default=False)
+    optParser.add_option("-V", "--version", action="callback", callback=printInfo, help="show version info")
+    (options, args) = optParser.parse_args()
+
+    if options.old_syntax:
+        oldSyntax()
+
+    if options.emitlinenumbers:
+        backend.emitlinenumbers = True
+
+    backend.encoding = options.encoding
+
+    try:
+        if options.includePathText:
+            backend.includePath = options.includePathText.split(':')
+
+        dirs = os.environ.get('YML_PATH', '.').split(':')
+        backend.includePath.extend(dirs)
+
+        files = fileinput.input(args, mode="rU", openhook=fileinput.hook_encoded(options.encoding))
+
+        ymlC = ymlCStyle()
+        result = parse(ymlC, files, True, comment, packrat=True)
+
+        if options.parseonly:
+            print(result)
+        else:
+            result = backend.finish(result)
+            if options.normalization != "none":
+                result = unicodedata.normalize(options.normalization, result)
+
+            if options.outputFile and options.outputFile != "-":
+                outfile = open(options.outputFile, "wb")
+                outfile.write(codecs.encode(result, options.encoding))
+                outfile.close()
+            else:
+                sys.stdout.buffer.write(codecs.encode(result, options.encoding))
+                print()
+
+    except KeyboardInterrupt:
+        w("\n")
+        sys.exit(1)
+    except KeyError as msg:
+        w("not found: " + u(msg) + "\n")
+        sys.exit(4)
+    except LookupError as msg:
+        w("not found: " + u(msg) + "\n")
+        sys.exit(4)
+    except Exception as msg:
+        w(msg)
+        sys.exit(5)
+
+
+if __name__ == "__main__":
+    sys.exit(main())
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yml2/yml2proc.py	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,248 @@
+#!/usr/bin/env python3
+# vim: set fileencoding=utf-8 :
+
+"""\
+YML/YSLT 2 processor version 6.2
+Copyleft (c), 2009-2019 Volker Birk  http://fdik.org/yml/
+
+"""
+
+import sys, os, codecs, locale
+import fileinput, unicodedata
+from optparse import OptionParser
+
+try:
+    from lxml import etree
+except:
+    sys.stderr.write("This program needs lxml, see http://codespeak.net/lxml/\n")
+    sys.exit(1)
+
+from yml2 import ymlCStyle, comment, oldSyntax
+from pyPEG import parse, u
+import backend
+
+def printInfo(option, opt_str, value, parser):
+    sys.stdout.write(__doc__)
+    sys.exit(0)
+
+class YMLAssert(Exception): pass
+
+def w(msg):
+    if isinstance(msg, BaseException):
+        try:
+            msg = str(msg) + "\n"
+        except:
+            msg = u(msg) + "\n"
+    sys.stderr.write(msg)
+
+def main():
+    optParser = OptionParser()
+    optParser.add_option("-C", "--old-syntax", action="store_true", dest="old_syntax",
+            help="syntax of YML 2 version 1.x (compatibility mode)", default=False)
+    optParser.add_option("-D", "--emit-linenumbers", action="store_true", dest="emitlinenumbers",
+            help="emit line numbers into the resulting XML for debugging purposes", default=False)
+    optParser.add_option("--debug", action="store_true", dest="trace",
+            help="switch on tracing to stderr", default=False)
+    optParser.add_option("-d", "--paramdict", dest="params", metavar="PARAMS",
+            help="call X/YSLT script with dictionary PARAMS as parameters")
+    optParser.add_option("-e", "--xpath", dest="xpath", metavar="XPATH",
+            help="execute XPath expression XPATH and print result")
+    optParser.add_option("-E", "--encoding", dest="encoding", metavar="ENCODING", default=locale.getdefaultlocale()[1],
+            help="encoding of input files (default to locale)")
+    optParser.add_option("-I", "--include", dest="includePathText", metavar="INCLUDE_PATH",
+            help="precede YML_PATH by a colon separated INCLUDE_PATH to search for include files")
+    optParser.add_option("-m", "--omit-empty-parm-tags", action="store_true", dest="omitemptyparm",
+            help="does nothing (only there for compatibility reasons)", default=False)
+    optParser.add_option("-M", "--empty-input-document", action="store_true", dest="emptyinput",
+            help="use an empty input document", default=False)
+    optParser.add_option("-n", "--normalization", dest="normalization", metavar="NORMALIZATION", default="NFC",
+            help="Unicode normalization (none, NFD, NFKD, NFC, NFKC, FCD, default is NFC)")
+    optParser.add_option("-o", "--output", dest="outputFile", metavar="FILE",
+            help="place output in file FILE")
+    optParser.add_option("-p", "--parse-only", action="store_true", dest="parseonly",
+            help="parse only, then output pyAST as text to stdout", default=False)
+    optParser.add_option("-P", "--pretty", action="store_true", default=False,
+            help="pretty print output adding whitespace")
+    optParser.add_option("-s", "--stringparamdict", dest="stringparams", metavar="STRINGPARAMS",
+            help="call X/YSLT script with dictionary STRINGPARAMS as string parameters")
+    optParser.add_option("-x", "--xml", action="store_true", default=False,
+            help="input document is XML already")
+    optParser.add_option("-X", "--xslt", dest="xslt", metavar="XSLTSCRIPT",
+            help="execute XSLT script XSLTSCRIPT")
+    optParser.add_option("-y", "--yslt", dest="yslt", metavar="YSLTSCRIPT",
+            help="execute YSLT script YSLTSCRIPT")
+    optParser.add_option("-Y", "--xml2yml", action="store_true", default=False,
+            help="convert XML to normalized YML code")
+    optParser.add_option("-V", "--version", action="callback", callback=printInfo, help="show version info and exit")
+    (options, args) = optParser.parse_args()
+    
+    if options.old_syntax:
+        oldSyntax()
+    
+    if options.trace:
+        backend.enable_tracing = True
+    
+    if options.emitlinenumbers:
+        backend.emitlinenumbers = True
+    
+    if options.includePathText:
+        backend.includePath = options.includePathText.split(':')
+    
+    backend.encoding = options.encoding
+    
+    dirs = os.environ.get('YML_PATH', '.').split(':')
+    backend.includePath.extend(dirs)
+    
+    if options.xml2yml:
+        for directory in backend.includePath:
+            try:
+                name = directory + "/xml2yml.ysl2"
+                f = open(name, "r")
+                f.close()
+                break
+            except:
+                pass
+    
+        options.yslt = name
+        options.xml = True
+    
+    if  (options.xslt and options.yslt) or (options.xslt and options.xpath) or (options.yslt and options.xpath):
+        sys.stderr.write("Cannot combine --xpath, --xslt and --yslt params\n")
+        sys.exit(1)
+    
+    try:
+        ymlC = ymlCStyle()
+    
+        rtext = ""
+    
+        if not options.emptyinput:
+            files = fileinput.input(args, mode="rU", openhook=fileinput.hook_encoded(options.encoding))
+    
+            if options.xml:
+                rtext = ""
+                for line in files:
+                    rtext += line
+            else:
+                result = parse(ymlC, files, True, comment)
+                if options.parseonly:
+                    print(result)
+                    sys.exit(0)
+                else:
+                    rtext = backend.finish(result)
+    
+        if not rtext:
+            rtext = "<empty/>"
+    
+        def ymldebug(context, text):
+            if options.trace:
+                sys.stderr.write("Debug: " + codecs.encode(u(text), options.encoding) + "\n")
+            return ""
+    
+        def ymlassert(context, value, msg):
+            if options.trace:
+                if not value:
+                    raise YMLAssert(msg)
+            return ""
+    
+        ymlns = etree.FunctionNamespace("http://fdik.org/yml")
+        ymlns.prefix = "yml"
+        ymlns['debug'] = ymldebug
+        ymlns['assert'] = ymlassert
+    
+        if options.xpath:
+            tree = etree.fromstring(rtext)
+            ltree = tree.xpath(codecs.decode(options.xpath, options.encoding))
+            rtext = ""
+            try:
+                for rtree in ltree:
+                    rtext += etree.tostring(rtree, pretty_print=options.pretty, encoding=unicode)
+            except:
+                rtext = ltree
+    
+        elif options.yslt or options.xslt:
+            params = {}
+    
+            if options.yslt:
+                backend.clearAll()
+                yscript = fileinput.input(options.yslt, mode="rU", openhook=fileinput.hook_encoded(options.encoding))
+                yresult = parse(ymlC, yscript, True, comment)
+                ytext = backend.finish(yresult)
+            else:
+                yscript = fileinput.input(options.xslt, mode="rU")
+                ytext = ""
+                for line in yscript:
+                    ytext += line
+    
+            doc = etree.fromstring(rtext)
+    
+            xsltree = etree.XML(ytext, base_url=os.path.abspath(yscript.filename()))
+            transform = etree.XSLT(xsltree)
+            
+            if options.params:
+                params = eval(options.params)
+                for key, value in params.iteritems():
+                    if type(value) is not str:
+                        params[key] = u(value)
+            if options.stringparams:
+                for key, value in eval(options.stringparams).iteritems():
+                    params[key] = "'" + u(value) + "'"
+    
+            rresult = transform(doc, **params)
+            # lxml is somewhat buggy
+            try:
+                rtext = u(rresult)
+            except:
+                rtext = etree.tostring(rresult, encoding=unicode)
+                if not rtext:
+                    rtext = codecs.decode(str(rresult), "utf-8")
+    
+        if options.normalization != "none":
+            rtext = unicodedata.normalize(options.normalization, rtext)
+    
+        if options.pretty:
+            plaintext = etree.tostring(etree.fromstring(rtext), pretty_print=True, xml_declaration=True, encoding=options.encoding)
+        else:
+            if isinstance(rtext, str):
+                plaintext = codecs.encode(rtext, options.encoding)
+            else:
+                plaintext = rtext
+    
+        try:
+            if plaintext[-1] == "\n":
+                plaintext = plaintext[:-1]
+        except: pass
+    
+        if options.outputFile and options.outputFile != "-":
+            outfile = open(options.outputFile, "wb")
+            outfile.write(plaintext)
+            outfile.close()
+        else:
+            sys.stdout.buffer.write(plaintext)
+            if not options.pretty:
+                print()
+    
+    except KeyboardInterrupt:
+        w("\n")
+        sys.exit(1)
+    except YMLAssert as msg:
+        w("YML Assertion failed: " + u(msg) + "\n")
+        sys.exit(2)
+    except KeyError as msg:
+        w("not found: " + u(msg) + "\n")
+        sys.exit(4)
+    except LookupError as msg:
+        w("not found: " + u(msg) + "\n")
+        sys.exit(4)
+    except etree.XMLSyntaxError as e:
+        log = e.error_log.filter_from_level(etree.ErrorLevels.FATAL)
+        for entry in log:
+            w("XML error: " + u(entry.message) + "\n")
+        sys.exit(5)
+    except Exception as msg:
+        w(msg)
+        sys.exit(5)
+
+
+if __name__ == "__main__":
+    sys.exit(main())
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yml2/yslt.yml2	Wed Mar 18 19:20:01 2020 +0100
@@ -0,0 +1,101 @@
+// YSLT version 2.6.2
+
+!!
+def indent(level):
+    return "value 'substring($space, 1, $_indent+" + str(level) + "*$autoindent)';"
+!!
+
+in exsl decl document(href, method) alias document;
+in func decl def(name) alias function, result(select);
+
+decl debug_off(exclude-result-prefixes="yml");
+
+in xsl {
+    decl _trace_param(%text, name="yml:trace", select='yml:debug("* %text")') alias param;
+    decl _trace_with is _trace_param alias with-param;
+
+    decl param(name, select);
+    decl const(name, select) alias variable, variable is const;
+    decl output(method), key(name, match, use);
+    decl value(select) alias value-of, copy(select) alias copy-of;
+
+    decl indent is value(%level, select='substring($space, 1, $_indent + (%level) * $autoindent)');
+
+    decl stylesheet(
+        *output="xml",
+        version="1.0",
+        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    ) {
+        output *output;
+        const "space", !"'" + " " * 200 + "'"!;
+        param "autoindent", 4;
+        content;
+    };
+
+    decl estylesheet is stylesheet (
+        xmlns:exsl='http://exslt.org/common',
+        xmlns:math='http://exslt.org/math',
+        xmlns:func='http://exslt.org/functions',
+        xmlns:str='http://exslt.org/strings',
+        xmlns:dyn='http://exslt.org/dynamic',
+        xmlns:set='http://exslt.org/sets',
+        xmlns:sets='http://exslt.org/sets',
+        xmlns:date='http://exslt.org/dates-and-times',
+        xmlns:yml='http://fdik.org/yml',
+        extension-element-prefixes='exsl func str dyn set sets math date yml'
+    );
+
+    decl textstylesheet is estylesheet(*output="text") {
+        output *output;
+        const "space", !"'" + " " * 200 + "'"!;
+        param "autoindent", 4;
+        xsl:template match="text()";
+        content;
+    }, tstylesheet is textstylesheet;
+
+    decl template(match) {
+        _trace_param *_trace_info;
+        param "_indent", 0;
+        content;
+    };
+
+    decl function(name) alias template {
+        _trace_param *_trace_info;
+        param "_indent", 0;
+        content;
+    };
+   
+    decl call(name) alias call-template {
+        _trace_with *_trace_info;
+        content;
+    };
+
+    decl namespace_alias(stylesheet-prefix, result-prefix);
+    decl text, raw(disable-output-escaping='yes') alias text;
+
+    decl with(name, select) alias with-param;
+    decl withIndent(%level, name="_indent", select='$_indent + (%level) * $autoindent') alias with-param;
+
+    decl apply(select, *indent=1) alias apply-templates {
+        _trace_with *_trace_info;
+        withIndent *indent;
+        content;
+    };
+
+    decl choose, when(test), otherwise;
+    decl if(test);
+    decl for(select) alias for-each, foreach is for;
+    decl element(name, namespace);
+    decl attrib(name, namespace) alias attribute, attrib_set(name) alias attribute-set;
+    decl processing(name) alias processing-instruction;
+    decl comment;
+    decl number(value), sort(select), decimal_format(name, decimal-separator=".", grouping-separator=",");
+    decl import(href), fallback;
+    decl message, error is message(terminate='yes'), warning is message(terminate='no');
+
+    decl debug(%text, select='yml:debug(%text)') alias value-of;
+    decl assert(%test, %msg="''", select='yml:assert(%test,%msg)') alias value-of;
+}
+
+define operator "«(.*?)»" as value "%1";
+
--- a/yml2c	Mon Nov 04 11:38:34 2019 +0100
+++ b/yml2c	Wed Mar 18 19:20:01 2020 +0100
@@ -1,95 +1,1 @@
-#!/usr/bin/env python3
-# vim: set fileencoding=utf-8 :
-
-"""\
-YML 2 compiler version 6.2
-Copyleft (c), 2009-2019, Volker Birk  http://fdik.org/yml/
-
-"""
-
-import sys, os, codecs, locale
-import fileinput, unicodedata
-from optparse import OptionParser
-
-from pyPEG import parse, u
-from yml2 import ymlCStyle, comment, oldSyntax
-import backend
-
-def printInfo(option, opt_str, value, parser):
-    sys.stdout.write(__doc__)
-    sys.exit(0)
-
-def w(msg):
-    if isinstance(msg, BaseException):
-        msg = str(msg) + "\n"
-    if type(msg) is bytes:
-        msg = codecs.encode(msg, sys.stderr.encoding)
-    sys.stderr.write(msg)
-
-optParser = OptionParser()
-optParser.add_option("-C", "--old-syntax", action="store_true", dest="old_syntax",
-        help="syntax of YML 2 version 1.x (compatibility mode)", default=False)
-optParser.add_option("-D", "--emit-linenumbers", action="store_true", dest="emitlinenumbers",
-        help="emit line numbers into the resulting XML for debugging purposes", default=False)
-optParser.add_option("-E", "--encoding", dest="encoding", metavar="ENCODING", default=locale.getdefaultlocale()[1],
-        help="encoding of input files (default to locale)")
-optParser.add_option("-I", "--include", dest="includePathText", metavar="INCLUDE_PATH",
-        help="precede YML_PATH by a colon separated INCLUDE_PATH to search for include files")
-optParser.add_option("-m", "--omit-empty-parm-tags", action="store_true", dest="omitemptyparm",
-        help="does nothing (only there for compatibility reasons)", default=False)
-optParser.add_option("-n", "--normalization", dest="normalization", metavar="NORMALIZATION", default="NFC",
-        help="Unicode normalization (none, NFD, NFKD, NFC, NFKC, FCD, default is NFC)")
-optParser.add_option("-o", "--output", dest="outputFile", metavar="FILE",
-        help="place output in file FILE")
-optParser.add_option("-p", "--parse-only", action="store_true", dest="parseonly",
-        help="parse only, then output pyAST as text to stdout", default=False)
-optParser.add_option("-V", "--version", action="callback", callback=printInfo, help="show version info")
-(options, args) = optParser.parse_args()
-
-if options.old_syntax:
-    oldSyntax()
-
-if options.emitlinenumbers:
-    backend.emitlinenumbers = True
-
-backend.encoding = options.encoding
-
-try:
-    if options.includePathText:
-        backend.includePath = options.includePathText.split(':')
-
-    dirs = os.environ.get('YML_PATH', '.').split(':')
-    backend.includePath.extend(dirs)
-
-    files = fileinput.input(args, mode="rU", openhook=fileinput.hook_encoded(options.encoding))
-
-    ymlC = ymlCStyle()
-    result = parse(ymlC, files, True, comment, packrat=True)
-
-    if options.parseonly:
-        print(result)
-    else:
-        result = backend.finish(result)
-        if options.normalization != "none":
-            result = unicodedata.normalize(options.normalization, result)
-
-        if options.outputFile and options.outputFile != "-":
-            outfile = open(options.outputFile, "wb")
-            outfile.write(codecs.encode(result, options.encoding))
-            outfile.close()
-        else:
-            sys.stdout.buffer.write(codecs.encode(result, options.encoding))
-            print()
-
-except KeyboardInterrupt:
-    w("\n")
-    sys.exit(1)
-except KeyError as msg:
-    w("not found: " + u(msg) + "\n")
-    sys.exit(4)
-except LookupError as msg:
-    w("not found: " + u(msg) + "\n")
-    sys.exit(4)
-except Exception as msg:
-    w(msg)
-    sys.exit(5)
+yml2/yml2c.py
\ No newline at end of file
--- a/yml2proc	Mon Nov 04 11:38:34 2019 +0100
+++ b/yml2proc	Wed Mar 18 19:20:01 2020 +0100
@@ -1,242 +1,1 @@
-#!/usr/bin/env python3
-# vim: set fileencoding=utf-8 :
-
-"""\
-YML/YSLT 2 processor version 6.2
-Copyleft (c), 2009-2019 Volker Birk  http://fdik.org/yml/
-
-"""
-
-import sys, os, codecs, locale
-import fileinput, unicodedata
-from optparse import OptionParser
-
-try:
-    from lxml import etree
-except:
-    sys.stderr.write("This program needs lxml, see http://codespeak.net/lxml/\n")
-    sys.exit(1)
-
-from yml2 import ymlCStyle, comment, oldSyntax
-from pyPEG import parse, u
-import backend
-
-def printInfo(option, opt_str, value, parser):
-    sys.stdout.write(__doc__)
-    sys.exit(0)
-
-class YMLAssert(Exception): pass
-
-def w(msg):
-    if isinstance(msg, BaseException):
-        try:
-            msg = str(msg) + "\n"
-        except:
-            msg = u(msg) + "\n"
-    sys.stderr.write(msg)
-
-optParser = OptionParser()
-optParser.add_option("-C", "--old-syntax", action="store_true", dest="old_syntax",
-        help="syntax of YML 2 version 1.x (compatibility mode)", default=False)
-optParser.add_option("-D", "--emit-linenumbers", action="store_true", dest="emitlinenumbers",
-        help="emit line numbers into the resulting XML for debugging purposes", default=False)
-optParser.add_option("--debug", action="store_true", dest="trace",
-        help="switch on tracing to stderr", default=False)
-optParser.add_option("-d", "--paramdict", dest="params", metavar="PARAMS",
-        help="call X/YSLT script with dictionary PARAMS as parameters")
-optParser.add_option("-e", "--xpath", dest="xpath", metavar="XPATH",
-        help="execute XPath expression XPATH and print result")
-optParser.add_option("-E", "--encoding", dest="encoding", metavar="ENCODING", default=locale.getdefaultlocale()[1],
-        help="encoding of input files (default to locale)")
-optParser.add_option("-I", "--include", dest="includePathText", metavar="INCLUDE_PATH",
-        help="precede YML_PATH by a colon separated INCLUDE_PATH to search for include files")
-optParser.add_option("-m", "--omit-empty-parm-tags", action="store_true", dest="omitemptyparm",
-        help="does nothing (only there for compatibility reasons)", default=False)
-optParser.add_option("-M", "--empty-input-document", action="store_true", dest="emptyinput",
-        help="use an empty input document", default=False)
-optParser.add_option("-n", "--normalization", dest="normalization", metavar="NORMALIZATION", default="NFC",
-        help="Unicode normalization (none, NFD, NFKD, NFC, NFKC, FCD, default is NFC)")
-optParser.add_option("-o", "--output", dest="outputFile", metavar="FILE",
-        help="place output in file FILE")
-optParser.add_option("-p", "--parse-only", action="store_true", dest="parseonly",
-        help="parse only, then output pyAST as text to stdout", default=False)
-optParser.add_option("-P", "--pretty", action="store_true", default=False,
-        help="pretty print output adding whitespace")
-optParser.add_option("-s", "--stringparamdict", dest="stringparams", metavar="STRINGPARAMS",
-        help="call X/YSLT script with dictionary STRINGPARAMS as string parameters")
-optParser.add_option("-x", "--xml", action="store_true", default=False,
-        help="input document is XML already")
-optParser.add_option("-X", "--xslt", dest="xslt", metavar="XSLTSCRIPT",
-        help="execute XSLT script XSLTSCRIPT")
-optParser.add_option("-y", "--yslt", dest="yslt", metavar="YSLTSCRIPT",
-        help="execute YSLT script YSLTSCRIPT")
-optParser.add_option("-Y", "--xml2yml", action="store_true", default=False,
-        help="convert XML to normalized YML code")
-optParser.add_option("-V", "--version", action="callback", callback=printInfo, help="show version info and exit")
-(options, args) = optParser.parse_args()
-
-if options.old_syntax:
-    oldSyntax()
-
-if options.trace:
-    backend.enable_tracing = True
-
-if options.emitlinenumbers:
-    backend.emitlinenumbers = True
-
-if options.includePathText:
-    backend.includePath = options.includePathText.split(':')
-
-backend.encoding = options.encoding
-
-dirs = os.environ.get('YML_PATH', '.').split(':')
-backend.includePath.extend(dirs)
-
-if options.xml2yml:
-    for directory in backend.includePath:
-        try:
-            name = directory + "/xml2yml.ysl2"
-            f = open(name, "r")
-            f.close()
-            break
-        except:
-            pass
-
-    options.yslt = name
-    options.xml = True
-
-if  (options.xslt and options.yslt) or (options.xslt and options.xpath) or (options.yslt and options.xpath):
-    sys.stderr.write("Cannot combine --xpath, --xslt and --yslt params\n")
-    sys.exit(1)
-
-try:
-    ymlC = ymlCStyle()
-
-    rtext = ""
-
-    if not options.emptyinput:
-        files = fileinput.input(args, mode="rU", openhook=fileinput.hook_encoded(options.encoding))
-
-        if options.xml:
-            rtext = ""
-            for line in files:
-                rtext += line
-        else:
-            result = parse(ymlC, files, True, comment)
-            if options.parseonly:
-                print(result)
-                sys.exit(0)
-            else:
-                rtext = backend.finish(result)
-
-    if not rtext:
-        rtext = "<empty/>"
-
-    def ymldebug(context, text):
-        if options.trace:
-            sys.stderr.write("Debug: " + codecs.encode(u(text), options.encoding) + "\n")
-        return ""
-
-    def ymlassert(context, value, msg):
-        if options.trace:
-            if not value:
-                raise YMLAssert(msg)
-        return ""
-
-    ymlns = etree.FunctionNamespace("http://fdik.org/yml")
-    ymlns.prefix = "yml"
-    ymlns['debug'] = ymldebug
-    ymlns['assert'] = ymlassert
-
-    if options.xpath:
-        tree = etree.fromstring(rtext)
-        ltree = tree.xpath(codecs.decode(options.xpath, options.encoding))
-        rtext = ""
-        try:
-            for rtree in ltree:
-                rtext += etree.tostring(rtree, pretty_print=options.pretty, encoding=unicode)
-        except:
-            rtext = ltree
-
-    elif options.yslt or options.xslt:
-        params = {}
-
-        if options.yslt:
-            backend.clearAll()
-            yscript = fileinput.input(options.yslt, mode="rU", openhook=fileinput.hook_encoded(options.encoding))
-            yresult = parse(ymlC, yscript, True, comment)
-            ytext = backend.finish(yresult)
-        else:
-            yscript = fileinput.input(options.xslt, mode="rU")
-            ytext = ""
-            for line in yscript:
-                ytext += line
-
-        doc = etree.fromstring(rtext)
-
-        xsltree = etree.XML(ytext, base_url=os.path.abspath(yscript.filename()))
-        transform = etree.XSLT(xsltree)
-        
-        if options.params:
-            params = eval(options.params)
-            for key, value in params.iteritems():
-                if type(value) is not str:
-                    params[key] = u(value)
-        if options.stringparams:
-            for key, value in eval(options.stringparams).iteritems():
-                params[key] = "'" + u(value) + "'"
-
-        rresult = transform(doc, **params)
-        # lxml is somewhat buggy
-        try:
-            rtext = u(rresult)
-        except:
-            rtext = etree.tostring(rresult, encoding=unicode)
-            if not rtext:
-                rtext = codecs.decode(str(rresult), "utf-8")
-
-    if options.normalization != "none":
-        rtext = unicodedata.normalize(options.normalization, rtext)
-
-    if options.pretty:
-        plaintext = etree.tostring(etree.fromstring(rtext), pretty_print=True, xml_declaration=True, encoding=options.encoding)
-    else:
-        if isinstance(rtext, str):
-            plaintext = codecs.encode(rtext, options.encoding)
-        else:
-            plaintext = rtext
-
-    try:
-        if plaintext[-1] == "\n":
-            plaintext = plaintext[:-1]
-    except: pass
-
-    if options.outputFile and options.outputFile != "-":
-        outfile = open(options.outputFile, "wb")
-        outfile.write(plaintext)
-        outfile.close()
-    else:
-        sys.stdout.buffer.write(plaintext)
-        if not options.pretty:
-            print()
-
-except KeyboardInterrupt:
-    w("\n")
-    sys.exit(1)
-except YMLAssert as msg:
-    w("YML Assertion failed: " + u(msg) + "\n")
-    sys.exit(2)
-except KeyError as msg:
-    w("not found: " + u(msg) + "\n")
-    sys.exit(4)
-except LookupError as msg:
-    w("not found: " + u(msg) + "\n")
-    sys.exit(4)
-except etree.XMLSyntaxError as e:
-    log = e.error_log.filter_from_level(etree.ErrorLevels.FATAL)
-    for entry in log:
-        w("XML error: " + u(entry.message) + "\n")
-    sys.exit(5)
-except Exception as msg:
-    w(msg)
-    sys.exit(5)
+yml2/yml2proc.py
\ No newline at end of file
--- a/yslt.yml2	Mon Nov 04 11:38:34 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-// YSLT version 2.6.2
-
-!!
-def indent(level):
-    return "value 'substring($space, 1, $_indent+" + str(level) + "*$autoindent)';"
-!!
-
-in exsl decl document(href, method) alias document;
-in func decl def(name) alias function, result(select);
-
-decl debug_off(exclude-result-prefixes="yml");
-
-in xsl {
-    decl _trace_param(%text, name="yml:trace", select='yml:debug("* %text")') alias param;
-    decl _trace_with is _trace_param alias with-param;
-
-    decl param(name, select);
-    decl const(name, select) alias variable, variable is const;
-    decl output(method), key(name, match, use);
-    decl value(select) alias value-of, copy(select) alias copy-of;
-
-    decl indent is value(%level, select='substring($space, 1, $_indent + (%level) * $autoindent)');
-
-    decl stylesheet(
-        *output="xml",
-        version="1.0",
-        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-    ) {
-        output *output;
-        const "space", !"'" + " " * 200 + "'"!;
-        param "autoindent", 4;
-        content;
-    };
-
-    decl estylesheet is stylesheet (
-        xmlns:exsl='http://exslt.org/common',
-        xmlns:math='http://exslt.org/math',
-        xmlns:func='http://exslt.org/functions',
-        xmlns:str='http://exslt.org/strings',
-        xmlns:dyn='http://exslt.org/dynamic',
-        xmlns:set='http://exslt.org/sets',
-        xmlns:sets='http://exslt.org/sets',
-        xmlns:date='http://exslt.org/dates-and-times',
-        xmlns:yml='http://fdik.org/yml',
-        extension-element-prefixes='exsl func str dyn set sets math date yml'
-    );
-
-    decl textstylesheet is estylesheet(*output="text") {
-        output *output;
-        const "space", !"'" + " " * 200 + "'"!;
-        param "autoindent", 4;
-        xsl:template match="text()";
-        content;
-    }, tstylesheet is textstylesheet;
-
-    decl template(match) {
-        _trace_param *_trace_info;
-        param "_indent", 0;
-        content;
-    };
-
-    decl function(name) alias template {
-        _trace_param *_trace_info;
-        param "_indent", 0;
-        content;
-    };
-   
-    decl call(name) alias call-template {
-        _trace_with *_trace_info;
-        content;
-    };
-
-    decl namespace_alias(stylesheet-prefix, result-prefix);
-    decl text, raw(disable-output-escaping='yes') alias text;
-
-    decl with(name, select) alias with-param;
-    decl withIndent(%level, name="_indent", select='$_indent + (%level) * $autoindent') alias with-param;
-
-    decl apply(select, *indent=1) alias apply-templates {
-        _trace_with *_trace_info;
-        withIndent *indent;
-        content;
-    };
-
-    decl choose, when(test), otherwise;
-    decl if(test);
-    decl for(select) alias for-each, foreach is for;
-    decl element(name, namespace);
-    decl attrib(name, namespace) alias attribute, attrib_set(name) alias attribute-set;
-    decl processing(name) alias processing-instruction;
-    decl comment;
-    decl number(value), sort(select), decimal_format(name, decimal-separator=".", grouping-separator=",");
-    decl import(href), fallback;
-    decl message, error is message(terminate='yes'), warning is message(terminate='no');
-
-    decl debug(%text, select='yml:debug(%text)') alias value-of;
-    decl assert(%test, %msg="''", select='yml:assert(%test,%msg)') alias value-of;
-}
-
-define operator "«(.*?)»" as value "%1";
-