Headline:Adventures in Coding June 27, 2019
Date:Thursday, June 27, 2019
Posted By:Sean Woods

I have been engaged in a lot of cross disciplinary projects both at home and at work for the past few months. All of them have required herding a lot of objects within the confines of a tree-like structure. And that has brought me, one again, to a concept that has been bubbling in my mind since I was a child: yggdrasil as a data structure.

The concept is that instead of one singular tree structure, each node has the possibility to be the root of an entirely different kind of tree. Each node can also be the connection point between on tree and another tree. And while that sounds completely batty, try mapping CSS attributes to HTML tags with out it. Every tag in HTML can have a parent, it can have children. At the same time, it can draw properties from it's intrinsic being as a particular type of tag. And it can also draw on properties from the content style sheet, which has its own tree structure in parallel.

I have also been banging my head on the proper way to integrate SVG into my html generator. And for SVG, the tags are not html, but a rawer form of XML. But like HTML tags, SVG tags can also defer to a content style sheet. However, unlike HTML 5, where CSS information is banished to the "style" and "class" tag, for SVG any option that isn't reserved for the tag is assumed to be a CSS property.

Enter a third contender: my permanent work in progress practcl. Practcl is a Tcl based build system that allows an interpreter to express a project as a family of TclOO objects. I wasn't running into any pressing issues at this very second, but it occured to me that I had solved a lot of these problems once before while implementing that library.

Last but not least are megawidgets. I finally have had need to implement a new megawidget system, this time based on Clay. I had a megawidget system, called taotk, but it constantly suffered from "first with the worst" type implementations for things. Yes, it had containers, but objects in a container were largely flat. Yes, there were properties, but properties were a one-size fits all that tried hard to do Tk style options and managed to not do "I have a family of objects that need to talk to each other" kind of options. Its main saving grace was that I had the concept of delegation built in early. But after 10 years of coding in it... it really needed a top-down redesign.

What woke me in the dead of the night was a stroke of madness/genius that all of these problems needed a common architecture. A vocabulary that would allow software developers to only have to learn a finite set of node interactions that apply across the entire framework. At the same time make the rules simple enough that an expert system can automate a lot of the grunt work when it comes to developing reports, implementing GUIs, and carrying out the build process for software.

As an experiment this morning, I collected all of my various tree structure implementations and the best of my option handling tools, and I combined them into a single package called clay-yggdrasil. I then went into Cuneiform (my xml/html report generator), ripped out its tree and option handling systems, and replaced it with yggdrasil. I also went into Practcl, and ripped out its tree and option handling systems, and replaced it with yggdrasil. Finally, I went into my new megawidget system and... you guessed it, ripped out it's tree and option handling system and replaced it with yggdrasil.

Minus a few typos that I caught pretty quicky, and a shim I had to add to practcl to allow existing projects to continue to work, everything ran the first time. Literally, I made a fresh build of practcl, dropped it into my build directory, and was minting fresh kits. One of those kits is a project at work that uses the new mega-widget system, and boom, popped up the first time like toast. And then I did some minor surgery on the toadhttpd webserver which also uses cuneiform, and boom, it worked. The first time.

Part of what helped was that all of these tools were using essentially the same implementation, just developed in parallel. The underlying details may have differed slightly, but because I was object-oriented programming, so long as the arguments to the methods were the same, and the output was comparable, everything worked.

In retrospect, this all should not have been that surprising. All of these tools were developed by me, and I put a lot of work into developing software patterns and sticking with them. But on the other hand...

There was a lot of code that changed

What is Cuneiform

Cuneiform is a technique for generating structured data. It's built on the clay framework, so Cuneiform is a bit of a nod to the ancient Mesopotamians who also wrote structured data using clay.

A Cuneiform document is an object that knows it contains very little, but a list of objects within that are actually producing the content. Those objects, in turn, can contain other objects. This page is generated by the Cuneiform library in fact. You can read the source code of this page.

Those objects begetting objects is nice because a document can remain a living thing up until the absolute last second. And when it's finally "go" time, the toplevel object invokes a recursive call to all of its children, who invoke the same call to their children, and so on, each call appending to text buffer that is the final deliverable.

Why is this complexity needed? Because HTML allows for some very strange, strange occurrences. For instance

Yo Dawg! I heard you like tables
So I put a table in your table
So you can lay out
While you lay out
Oh right where were we again?

Under normal conditions, that structure is a complete dumpster fire. But in Cuneiform it's as simple as:

my tag style {                                          ; # Tweak the style sheet
table, th, td {
  border: 1px solid black;
}
}
set otab [my tag table]                                 ; # Start an outer table
set r [$otab row]                                       ; # Add a row
$r column {Yo Dawg!}                                    ; # Add two columns
$r column {I heard you like tables}
set itab [[$r column] tag table]                        ; # Nest a table in the third column
set r [$itab row]                                       ; # Add a row to the inner table
$r column {So I put a table in your table}
set iitab [$r column tag table]                         ; # Embed yet another table, this time as the second column
set r [$itab row]
$r column {So you can lay out}
set r [$itab row]
$r column {While you lay out}
set r [$otab row]                                       ; # Pick back up on the outer table and add a row
$r column colspan 3 css {border 1 background lightblue} content {Oh right where were we again?}

# That's it. Just wash hands and wipe on pants.

What is Ziggurat

Ziggarut is name of my new megawidget framework for Tk. A ziggarut was a massive ancient structure that was the interface between the people and the Gods. And, keeping with the theme, Ziggaruts were also made with clay bricks. It's kind of hard to show Tk in an html blog post, so here is a simple example out of my test scripts:

::cuneiform::tkframe create APP
set f [APP tag row]
$f tag label text {Hello World!}
$f tag label text {Another Element}

set f [APP tag row]
$f tag button text {Press Me!} command {puts {You pressed me}}

# Note that up until now we have not referenced a Tk path!
frame .c
APP tkrender .c
grid .c

Ziggurat is a work in progress, but look for some good things.