Using XML files for source code

Arjen Markus 2009-09-08: I often use simple commands as a way to configure a program or to read data. [source] then suffices to interpret the configuration or data. I wondered whether it would be possible to use the so popular XML format to do the same. Of course, things are not so straightforward, but here is a first (admittedly playful) attempt.

Take the following XML file:

<?xml version="1.0" encoding="UTF-8"?>
<simple>
<set var="X"/>
<puts text="Value is $var"/>
<puts>"Value is $var"</puts>
<!--
<eval>
    <list>
        <element>puts</element>
        <element>"Value is $var"</element>
    </list>
</eval>
-->
</simple>

The trained eye will recognise a lot of Tcl commands in the guise of tags. That is exactly what is meant. Here is the code to interpret it:

# xmlsource.tcl --
#     Read an XML file and convert it on the fly to Tcl code
#
package require tdom

set infile   [open "example.xml" r]
set contents [read $infile]
close $infile

dom parse $contents code
$code documentElement root

foreach commandNode [$root childNodes] {

    set cmd [$commandNode nodeName]

    if { $cmd eq "set" } {
        foreach arg [$commandNode attributes] {
            set $arg [$commandNode getAttribute $arg]
        }
    } elseif { ![$commandNode hasChildNodes] && [$commandNode nodeType] eq "ELEMENT_NODE" } {
        set argv {}
        foreach arg [$commandNode attributes] {
            append argv [$commandNode getAttribute $arg]
        }
        eval $cmd [list $argv]

    } else {
        if { [$commandNode nodeType] eq "ELEMENT_NODE" && [llength [$commandNode childNodes]] == 1 } {
            #
            # Assume simple command
            #
            set cmd  [$commandNode nodeName]
            set argv [[$commandNode childNodes] nodeValue]
            eval $cmd $argv
        } else {
            puts "Complex commands not implemented yet"
        }
    }
}

Run this program and you will see some of the possibilities and the limitations.

Essentially: You can put Tcl commands inside XML files, so that people who are used to handling XML files can continue doing so, without you having to do more than identifying what the most convenient way is to handle the various kinds of commands.

An extension to the above would be:

proc rootFindingMethod {name tolerance} {
    set ::methodUse          $name
    set ::numericalTolerance $tolerance
}

and in the XML file (the interpreting code will have to match the attributes and arguments by name, but that can be made completely generic):

<rootFindingMethod>
    <name>Newton-Raphson</name>
    <tolerance>1.0e-6</tolerance>
</rootFindingMethod>