| « Illicit Process Improvement | Announcement: A New, New, New Name! » |
When Mike's coworker left the company for greener pastures, Mike got stuck with maintaining all of his old projects. This wouldn't have been too bad if it weren't for a application that was developed using the BrillantML (as I'll call it). While Brillant ML is technically a programming language, a more apt description is "a few commands surrounded by an XML wrapper." Yes, it really is as horrid as it sounds.
BrillantML is used to create interfaces on a certain type of consumer hardware, and Mike's first experience with it was when he needed to change some of the menu labels around. The first thing he noticed was his predecessor didn't believe in using loops. Anywhere.
If, for example, the previous programmer needed to set nine text labels on the screen to the first nine entries in the test "database," he would do the following:
<sub name="vmenu_init_update">
<action value="
set(@lbl_vmenu_0.text,$$DATA_HomePage[0].menu_item); redraw(@lbl_vmenu_0);
set(@lbl_vmenu_1.text,$$DATA_HomePage[1].menu_item); redraw(@lbl_vmenu_1);
set(@lbl_vmenu_2.text,$$DATA_HomePage[2].menu_item); redraw(@lbl_vmenu_2);
set(@lbl_vmenu_3.text,$$DATA_HomePage[3].menu_item); redraw(@lbl_vmenu_3);
set(@lbl_vmenu_4.text,$$DATA_HomePage[4].menu_item); redraw(@lbl_vmenu_4);
set(@lbl_vmenu_5.text,$$DATA_HomePage[5].menu_item); redraw(@lbl_vmenu_5);
set(@lbl_vmenu_6.text,$$DATA_HomePage[6].menu_item); redraw(@lbl_vmenu_6);
set(@lbl_vmenu_7.text,$$DATA_HomePage[7].menu_item); redraw(@lbl_vmenu_7);
set(@lbl_vmenu_8.text,$$DATA_HomePage[8].menu_item); redraw(@lbl_vmenu_8);
" />
</sub>
Mike thought it was ugly code and saw it as a great opportunity to refactor.
"The first problem I ran into," Mike wrote, "was that BrillantML doesn’t support arrays of labels. Actually, it doesn’t support arrays at all." Fortunately, there was a work-around: DataTables. All he'd have to do was create a BrillantML-compatable datatable, like this....
<?xml version="1.0" encoding="UTF-8"?>
<data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<hmenu_data>
<row>
<hmenu_item>FIRST</hmenu_item>
</row>
<row>
<hmenu_item>SECOND</hmenu_item>
</row>
<row>
<hmenu_item>THIRD</hmenu_item>
</row>
<row>
<hmenu_item>FOURTH</hmenu_item>
</row>
<row>
<hmenu_item>FIFTH</hmenu_item>
</row>
</hmenu_data>
</data>
Of course, he'd also need to create a schema:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:bml="http://initrodeglobal.com/xml/ns/brillantml/data-types"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:element name="data">
<xs:complexType>
<xs:sequence>
<xs:element name="hmenu_data" type="hmenuType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="hmenuType">
<xs:sequence>
<xs:element name="row" minOccurs="1" maxOccurs="unbounded">
<xs:complexType>
<xs:complexContent>
<xs:extension base="rowType">
<xs:sequence>
<xs:element name="hmenu_item" type="bml:mixed-string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="rowType">
<xs:attribute name="number" type="xs:int" use="optional"/>
</xs:complexType>
</xs:schema>
"The important thing here," Mike explained, "is the type definition of hmenu_item in the schema. You'll see it has a type of 'bml:mixed-string'. This is one of the defined types that BrillantML will recognize. However, one of the types that BrillantML won’t recognize is anything related to a label. So, even though you can have label variables, you cannot make an array of labels." Obviously, that makes it challenging to loop through an array of labels.
Undaunted, Mike created a routine that would check the value of the counter in the loop and then set a variable to the label using a set of if/else pairs. "It was easy to reference the loop counter and the variable," he wrote, "since BrillantML doesn’t use local variables, only global variables."
So, after defining the DataTable, and the schema, Mike's final pair of routines looked like this.
<sub name="set_cur_label">
<if>
<test lhs="$i" expr="eq" rhs="0" />
<action value="set($cur_label,@lbl_vmenu_0)"/>
</if>
<elseif>
<test lhs="$i" expr="eq" rhs="1" />
<action value="set($cur_label,@lbl_vmenu_1)"/>
</elseif>
<elseif>
<test lhs="$i" expr="eq" rhs="2" />
<action value="set($cur_label,@lbl_vmenu_2)"/>
</elseif>
... snip ...
<elseif>
<test lhs="$i" expr="eq" rhs="8" />
<action value="set($cur_label,@lbl_vmenu_8)"/>
</elseif>
<else>
<action value="set(@lbl_debug.text,'error: attempt to get label');" />
</else>
</sub>
<sub name="vmenu_init_update">
<action value="set($i,0)"/>
<loop>
<if>
<test lhs="$i" expr="gt" rhs="8" />
<break/>
</if>
<else>
<action value="call(set_cur_label)"/>
<action value="
set($cur_label.text,$$DATA_HomePage[$i].menu_item);
redraw($cur_label);" />
<action value="add($i,1)"/>
</else>
</loop>
</sub>
As you may have noticed, loops in BrillantML are "simplified." Loop tags have no attributes (such as "until" or "while"), instead requiring an if/else statement block. In either block, you have to include a <break/> statement, or risk an infinite loop.
However, when Mike "ran" his "code," all of labels had their text set to the tenth item ($$DATA_HomePage[9].menu_item) in the list. As it turned out, that was a result of another undocumented "feature" of BrillantML: when you set() the value of something, it doesn't actually "set" the "value." It merely "associates" the two. In other words, if you call "set(@lbl_vmenu_2.text,$$DATA_HomePage[$i].menu_item);", lbl_vmenu_2 will always contain the current value of $$DATA_HomePage[$i].menu_item, regardless of whether $i changes or the menu_item changes.
So, after a half day of hacking, Mike undid his check-out and returned the code to the way it was before. He decided to loop no more.
|
You just gotta love those custom-designed languages. No formal definition, no general purpose constructs, only ad-hoc tweaks to what was once no doubt a beautiful static definition of a GUI described in XML. One of the reasons I like C++ (rather than "language of the week X") is that it has a solid background, based on both theory and experience.
But hey, I'm just as guilty: the software I'm working on here also has its own built-in scripting language... In XML... And I add features as needed to support new devices (it is used to interact with various types of lab equipment). But at least it has loops ;-) |
|
Only a fool (or an Idiot) would try to refactor such a code! Is it a code? Looks like total mishmach of stuff. XML Programming languages ROCK MY WORLD!
Captcha: bene. Italian? Nice! |
Re: No Loop For You!
2007-12-12 10:39
•
by
lantastik
(unregistered)
|
Agreed. If I don't understand why something was done the way it was. I don't automatically assume I know better without asking someone first. Contrary to popular belief, asking questions does not make you look stupid. In most cases, it makes you look smarter. |
| « Illicit Process Improvement | Announcement: A New, New, New Name! » |