Introducing Pyxis, the readable alternative to XML for Eiffel configuration files

by Finnian Reilly (modified: 2012 Jan 10)


Although XML was designed to be a human readable data description language it does not compare well to other data description languages in terms of clarity and readability. This is why there were many grumbles when the old LACE syntax for Eiffel project files was done away with in favor of ECF XML.

Pyxis was invented during the Autumn of 2010 as a data description language that has the readability of LACE but is structurally equivalent to XML and thus easily convertible to XML. Although it's main purpose was to notate Eiffel configuration files in a more readable form, it can in fact be used to represent any XML document.

The move to XML configuration files was justified by the claim that it should never be necessary to edit the files by hand because of the completeness of the EiffelStudio project properties editor. Personally I much prefer to edit a file that has a reasonably clear and terse syntax and once I had the Pyxis to XML conversion tool working well enough I started using it for all my Eiffel projects. The EiffelStudio properties editor is still occasionally useful if I have forgotten the keywords for some construct. After examining the XML output I then translate it into Pyxis.

The syntax is inspired by the design of the Python programming language and the name "Pyxis" as an acronym stands for PYthonic Xml Ideal Source. One of the main characteristics of Python is the elimination of statement block terminators by relying entirely on tabbed indentation. In Pyxis, tree elements are represented by an identifier followed by a colon. Nested elements appear underneath the parent element indented by one tab stop relative to the parent. Closing elements are obsolete.

One nice feature of Pyxis is the elimination of the need to put quotes around every attribute value. Quotes are optional if the attribute value conforms to one of the following patterns:

  1. Is a decimal number
  2. Is a natural number
  3. Is a Python identifier

A happy consequence of this is that it makes possible syntax highlighting for common attribute values like: unix; windows; true; false; standard, etc.

Syntax rules

The syntax rules for Pyxis are outlined below with reference to the following real world example of an Eiffel configuration file taken from the Eiffel Loop libraries:

Example 1: audio-file.ecf.pyx

pyxis-doc: version = 1.0; encoding = "ISO-8859-1" # Pyxis as an acronym stands for: Pythonic XML ideal source. # The word 'pyxis' is also a Latin transliteration of a Greek word for a type of pottery used by women to hold # cosmetics, trinkets or jewellery. system: xmlns = "" xmlns.xsi = "" xsi.schemaLocation = "" name = eiffel_loop_audio_files uuid = "229b770e-09aa-11df-87c3-c377311e3588" library_target = eiffel_loop_audio_files target: name = eiffel_loop_audio_files description: """ Audio processing and ID3 library. ( """ option: namespace = "Eiffel-Loop.Library.Audio-files"; trace = false; debug = false; warning = true; syntax = standard assertions: precondition = true; postcondition = true; check = true; invariant = true root: all_classes = true # For Windows: Internal WaveOut API callback function. external_include: location = "$(EIFFEL_LOOP)/C_library/audio/source" condition: platform: value = windows external_object: location = "$(EIFFEL_LOOP)/C_library/audio/spec/$(ISE_PLATFORM)/el_audio.lib" condition: platform: value = windows external_object: location = "WinMM.lib" condition: platform: value = windows # libid3 and libid3tag externals external_include: location = "$(EIFFEL_LOOP)/contrib/C/libid3tag-0.15.1b/include" # For Windows: libid3 and libid3tag externals external_include: location = "$(EIFFEL_LOOP)/contrib/C++/id3lib/include -DID3LIB_LINKOPTION=1 -DWIN32 -EHsc" condition: platform: value = windows external_object: location = "$(EIFFEL_LOOP)/contrib/C/libid3tag-0.15.1b/spec/$(ISE_PLATFORM)/id3tag.lib" condition: platform: value = windows external_object: location = "$(EIFFEL_LOOP)/contrib/C++/id3lib/spec/$(ISE_PLATFORM)/id3.lib" condition: platform: value = windows external_object: location="$(ISE_LIBRARY)/library/vision2/spec/$ISE_C_COMPILER/$ISE_PLATFORM/lib/zlib.lib" condition: platform: value = windows # For Unix: libid3 and libid3tag externals external_include: location = "$(EIFFEL_LOOP)/contrib/C++/id3lib/include" condition: platform: value = unix external_object: location = '-L"$(EIFFEL_LOOP)/contrib/C++/id3lib/spec/$(ISE_PLATFORM)" -lid3' condition: platform: value = unix external_object: location = '-L"$(EIFFEL_LOOP)/contrib/C/libid3tag-0.15.1b/spec/$(ISE_PLATFORM)" -lid3tag' description: """ WARNING: subtle bug Only link against a static library. If linked against a shared object library GetRawText() routine will return null for utf8 strings. """ condition: platform: value = unix external_object: location = "-lz" condition: platform: value = unix library: name = base; location = "$ISE_LIBRARY/library/base/base.ecf" # Eiffel Loop library: name = eiffel_loop; location = "base.ecf" cluster: name = audio_files; location = "multimedia/audio"; recursive = true option: namespace = "Audio-files" file_rule: exclude: "/laabhair$" "/eyeD3$" file_rule: exclude: "/player$" condition: platform: value = unix cluster: name = wel_x_constants; location = "graphic/toolkit/wel-x/constants" file_rule: exclude: "/el_.*\.e" include: "/el_mm_system_constants.*\.e" condition: platform: value = windows

Document header

The document header in lines 1 to 2 is equivalent to the following XML:<?xml version = "1.0" encoding = "ISO-8859-1"?>

Element attribute assignments

Element attribute assignments are indented by one tab stop relative to the enclosing element. Assignments on the same line must be separated by a semicolon. Assignments listed across multiple lines must all be indented to the same tab stop. White space around the equal sign is optional but recommended for readability.

Attribute values

Attribute value quotes are optional if the value conforms to one of the following patterns:

  1. Is a decimal number
  2. Is a natural number
  3. Is a Python identifier

Quote marks can be either single or double. The backslash character can be used to escape a quote.Single quotes are particularly useful when using the -L gcc option as the path argument can be quoted using unescaped double quotes. (See line 89)

Element text nodes

Element text should be quoted on the next line to the element marker and indented by one tab stop relative to the element name. Quotes can be either single or double. Three double quotes indicates a verbatim string. Quotes inside a verbatim string do not need to be escaped. (See lines 98 to 100)Repeated elements with text can be abbreviated by omitting subsequent element names. Lines 127-128 in the example are an abbreviated version of the following.

file_rule: exclude: "/laabhair$" exclude: "/eyeD3$"

Escape characters

The following escape characters are recognized in double or single quoted strings:

  1. '\t': tabulation
  2. '\n': line feed
  3. '\: single quote
  4. "\"": double quote


Comments are indicated by the hash symbol. In the accompanying conversion tool, consecutive comment lines are amalgamated into a single XML comment.


Namespaces are referred to using object member dot notation instead of the XML namespace colon. (See lines 10-11)

Example 2: translations.xml.pyx

This example shows the application of Pyxis to an XML internationalization table. Eiffel Loop contains a module which make use of such a table. Lines 150, 153, 161, 164 show the usefulness of single quoted strings to contain unescaped double quotes. The escape characters \t and \n are used in lines 161, 164, 172, 175 to represent tab and newline respectively.pyxis-doc: version = 1.0; encoding = "ISO-8859-1" translation_tables: default_translation: lang = en group: name = "New passphrase dialog" item: id = "Passphrases are different" translation: lang = de "Passphrasen sind unterschiedlich" translation: lang = en "$id" item: id = "Enter a passphrase" translation: lang = de 'Geben sie ein passphrase für "$NAME" tagebuch' translation: lang = en 'Enter a passphrase for "$NAME" journal' group: name = "Dialogs general" item: id = "Delete journal" translation: lang = de 'Löschen tagebuch: "$S"\nSind sie sicher?' translation: lang = en 'Delete journal: "$S"\nAre you sure?' group: name = "Menus" item: id = "&New entry" translation: lang = de "&Neuer eintrag\tCtrl-T" translation: lang = en "&New entry\tCtrl-T"

Implementation in Eiffel Loop

A parser for Pyxis documents is included as part of an Eiffel Loop module for processing XML documents using xpaths assigned to agents. Pyxis sources can be used interchangeably with XML sources. The module also contains an XML printer for converting parse events into XML text. This forms the basis of a tool for converting Pyxis documents to XML documents.

Conversion tool

An alpha version of the Pyxis conversion tool is available for 64 bit Windows and Linux. (Tested Windows 7 and Ubuntu Linux 10.10)


el_toolkit -pyxis_to_xml -in <file name>
Note that el_toolkit is a "swiss army knife tool". The pyxis_to_xml option selects the conversion tool.The tool assumes that the input has a ".pyx" extension. The output is named by stripping the pyx extension from the input name. For Eiffel configuration files it is recommend to name the pyx file as follows:<base name>.ecf.pyx
Error handling is minimal so it is best to scan the text carefully before converting. Python programmers should have very little difficulty getting used to the tool in it's current form.

Quoted string conversion

Pyxis escape characters are rendered in XML as the following entities:

  1. \t -> &#9;
  2. \n -> &#10;
  3. \" -> &quot;

Also the characters '&', '<' and '>' are escaped in XML.


el_toolkit 64-bit Windows(Tested Windows 7)

el_toolkit 64-bit Linux(Tested Ubuntu Linux 10.10)

  • Emmanuel Stapf (5 years ago 3/1/2012)

    A quick comment, wouldn't it make more sense to use the Eiffel verbatim string syntax instead of using triple double quote? Do you have a conversion from XML to PYX?

    • Finnian Reilly (5 years ago 3/1/2012)

      verbatim syntax

      I figure that most Eiffel programmers are familiar with Python and since I started using Python block conventions with colons at the end of lines and use of indentation I felt it was better to be consistent in style and make it as much like Python as possible.

      • Finnian Reilly (5 years ago 3/1/2012)

        general purpose

        Also I wanted it to be a general purpose language, not just for ECF files. There maybe people who will want to use it who haven't even heard of Eiffel.

      • Colin Adams (5 years ago 3/1/2012)

        I'm not familiar with Python

        and I suspect the majority of the Eiffel programmers I know aren't either.

        Nevertheless, this doesn't seem important to me. The .pyx format looks nice and easy to read.

        I don't write ECF files myself, as at work we have a utility that generates them for us. I do have to occaisio0nally tweak them. Usually I can use the project dialog, but there are occasions when I need to right-click on the settings icon to bring up the external editor. For me, this is emacs. So it would be useful to have a pyx-mode for emacs. I don't suppose you have one?

        • Finnian Reilly (5 years ago 3/1/2012)

          text editor mode

          Hi Colin, I haven't got around to making a Pyxis syntax config for any specific editor. But Pyxis is enough like Python that you can use Python mode and I think emacs has one. I use gedit on Linux and TextPad on Windows. For the time being I find Python mode good enough. I can see the quoted strings highlighted.

    • Finnian Reilly (5 years ago 3/1/2012)

      xml to pyxis

      As yet I don't have a converter in this direction

  • Finnian Reilly (5 years ago 3/1/2012)

    Other platform variations

    Sorry if the provided exes don't suit your platform. I hope to make a release of Eiffel Loop in the near future so you can compile your own.