h3mm3's blog

Stories from behind the keyboard

  • RSS
  • Twitter

In a previous post I introduced X2ml, a lightweight library that enables you to produce XML code using strings and overloaded operators. Compared to the state-of-the-art XElement, using operators gives you a better programming experience, since you can avoid nested constructor calls in favor of simple operand-operator concatenation.

Operators and operands

In the following code, slash '/' and percentage '%' operators are used to create the first child (H1) and it's siblings, respectively; star '*' is used to generate attributes or text values:

var x = X.X2ml;
var xExpression = x["div"] 
                     * "@id:main"
                   / "h1" 
                     * "Hello X2ml!"
                   % "p" 
                     * "@class:intro"
                     * "This is vanilla HTML."
                   % "img" 
                     * "@src='x2ml.png'";

var htmlString = xExpression.ToXmlString();

Slash '/' operator is defined in the X2ml.X sealed class as following

public static X operator /(X x, string child)

Other operators are defined in a similar way: each one takes an X object as first operand and returns a reference to an X object. Since every X2ml binary operator is defined such a way, once you have established the very first operand as an instance of X, you can combine any other string in cascade, obtaining each time an X object as a partial result.

The operand x["div"] in the previous sample is used to generate the first instance of X, enabling the cascading invocation of X2ml operators.

So far so good. What's with the very first declaration? X.X2ml is a readonly property that returns a singleton object of type X2ml.X.X2. The indexer defined in X2 is a factory method and returns new X objects named like the string index. For instance, in the previous sample, x["div"] is logically equivalent to the following code:

return new X { Name = "div", Type = XType.Element };

Why only three operators?

X2ml.X declares (overloads) only three operators: star, slash and percentage. They are, in effect, the only highest precedence binary overloadable operators (they are known as the "multiplicative operators" since they are commonly used as mulipication, division and remainder symbols among scalar .Net types).

Using highest precedence (and "same" precedence) operators is crucial in order to let X2ml do its job, since the order in which the compiler generates IL code invocations must not be messed up. Messing up this order could lead the compiler to translate inner expressions before resolving the previous ones, and this could eventually result in a type mismatch. For instance, let's use some parenthesis to alter the expression precedence:

//1. Here expression parsing flows left-to-right:
var expr1 = x["table"] / "tr" / "td";
//2. Here parsing order is altered using parenthesis
var expr2 = x["table"] / ("tr" / "td");

In the second expression, the compiler evaluates first the expression ("tr" / "td") and produces a compilation error, since the division operator is not defined in the domain Strings×Strings. The first expression, on the contrary, gives no error, since each operator is being applied in X's×Strings.

No comments: