Case Study CS00003: Building HTML table code

The problem: Put tabular text output into HTML table code

For CS00002, I hand coded the HTML to produce the program output. You can't just cut and paste into web pages because the information gets raveled into an incoherent string of words with no shape or structure. As it turns out, Glee is a pretty neat language for wrapping this output with HTML tags making it presentable. This case study illustrates how I used software sculpture to create a program producing from the raw text the HTML output I need for browser rendering. The rendered output is shown here.

TABLE OF CONTENTS:

The Cock and the Pearl    The Frog and the Ox
The Wolf and the Lamb    Androcles
The Dog and the Shadow    The Bat, the Birds, and the Beasts
...    
   ...
The Wind and the Sun    The Buffoon and the Countryman
Hercules and the Waggoner    The Old Woman and the Wine-Jar
The Man, the Boy, and the Donkey    The Fox and the Goat

The solution:

  1. Extract the tablular information
  2. Break tabular entries into sequences of sequences
  3. Decorate the output by prepending the sequences with appropriate HTML tags.

Note: You can cut and paste these code fragments into the code pane of the Glee interpreter and experiment as you go along to see the actual operations live.

The Glee code:

Adapted from Case Study CS00002: Extract the table text

$$ Code for reading from the file (See CS00002):
13#asc =>nl; "TABLE"=> begin; "Aesop"=> end;
"C:\GLEE\Website\CaseStudies\AesopsFables.txt"#file[] => t;
t @->>& begin; t ->>& end => t;
t ~>(13 10 #asc) =>t;
t \~ => t; t <- => title; t <- _1 => t;

$$ Define the tag variables we need
'  <tr>'=>rb; '</tr>' =>re; '    <td>'=>cb; '</td>' => ce;
'<p>'=>pb; '</p>'=>pe; '<br>'=>br; '&nbsp '> =>sp;

$$ Define the program that does each row of raw text
'p1'#pgm{
  x \43=>x;
  rb nl
    cb (x[1]< ~~)ce nl
    cb (sp *-> 3)(x[2]< ~~) ce re
  nl};

$$ Run the code
pb title pe $;
('<table>')nl
  (t <-* 3#ea '   [.x]p1')
  (('...' <- 50) [.x]p1)
  (('...' -> 50)[.x]p1)
  (t *-> 3#ea '   [.x]p1')
('</table>')$;

The Output:
<p>TABLE OF CONTENTS:</p>
<table>
  <tr>
    <td>The Cock and the Pearl</td>
    <td>&nbsp &nbsp &nbsp The Frog and the Ox</td>
  </tr>
  <tr>
    <td>The Wolf and the Lamb</td>
    <td>&nbsp &nbsp &nbsp Androcles</td>
  </tr>
  <tr>
    <td>The Dog and the Shadow</td>
    <td>&nbsp &nbsp &nbsp The Bat, the Birds, and the Beasts</td>
  </tr>
  <tr>
    <td>...</td>
    <td>&nbsp &nbsp &nbsp </td>
  </tr>
  <tr>
    <td> </td>
    <td>&nbsp &nbsp &nbsp ...</td>
  </tr>
  <tr>
    <td>The Wind and the Sun</td>
    <td>&nbsp &nbsp &nbsp The Buffoon and the Countryman</td>
  </tr>
  <tr>
    <td>Hercules and the Waggoner</td>
    <td>&nbsp &nbsp &nbsp The Old Woman and the Wine-Jar</td>
  </tr>
  <tr>
    <td>The Man, the Boy, and the Donkey</td>
    <td>&nbsp&nbsp&nbspThe Fox and the Goat</td>
  </tr>
</table>

The play-by-play:

$$ Code for reading from the file (See CS00002):
13#asc =>nl; "TABLE"=> begin; "Aesop"=> end;
"C:\GLEE\Website\CaseStudies\AesopsFables.txt"#file[] => t;
t @->>& begin; t ->>& end => t;
t *-> (end# -) ~>(13 10 #asc) =>t;
t \~ => t; t <- => title; t <- _1 => t;

In CS00002 I developed this code for extracting the table from the raw text file. I then hand coded the HTML to make the output look presentable on the web page. It's time to automate that process. I have the title line in title and the rows of the table as elements of the sequence t after I run this code fragment.

$$ Find start of right hand column
t[1]< $;
The Cock and the Pearl           The Frog and the Ox
t[1]< ``&'the' $;

3 16 43 56
First, I need to find where the right hand column begins. It starts with the word "the". "The" occurs four times and I want the third one (index position 43 starts the second column).

$$ Confirm break between fields
t[1]< \43 %**$;
Seq[I732R1C2T:K]:
[1]String[I748R1C22:C]The Cock and the Pearl
[2]String[I754R1C19:C]The Frog and the Ox

The \Selection operator followed by an integer breaks the string into a two item sequence. I use %** $Verbose Display to see my progress.

$$ Define the tag variables we need
'  <tr>'=>rb; '</tr>' =>re; '    <td>'=>cb; '</td>' => ce;
'<p>'=>pb; '</p>'=>pe; '<br>'=>br; '&nbsp '> =>sp;

This is fairly self explanatory. I'm creating mnemonics for the character strings I will use to decorate my output. Notice, for the non-blanking space '&nbsp '> =>sp; I use the >Enclose operator so I can treat that string as a sequence element.

'&nbsp '> =>sp %** $;
Seq[I644R1C1T:K]:
[1]String[I619R2C6:C]&nbsp 

sp *-> 3 %** $;
Seq[I693R1C3T:K]:
[1]String[I667R4C6:C]&nbsp 
[2]String[I667R4C6:C]&nbsp 
[3]String[I667R4C6:C]&nbsp 

This illustrates my special handling of non-blanking spaces. By "enclosing" the string, I create a one element sequence. Doing the take 3 gives me three instances of the string (in HTML-land that's three non-blanking spaces). I continue to carve out my solution.

t[1]< \43 => x;
rb nl
  cb (x[1]< ~~)ce nl
  cb (sp *-> 3)(x[2]< ~~) ce
re nl
Yields

  <tr>
    <td>The Cock and the Pearl</td>
    <td>&nbsp &nbsp &nbsp The Frog and the Ox</td>
  </tr>

I can implicitly catenate my strings together to form my compound output. Here I carve out some code to produce the output I want for each line of input. My source data is a sequence of two strings (the left and right columns). I index out each string (i.e. x[1]) and < Expose each string. I use the ~~Remove Extraneous operator (with no right argument) to remove extraneous blanks. This is working just as I want so I need to make it into a program to be applied to each row of the table.

$$ Define the program that does each row of raw text
'p1'#pgm{
  x \43=>x;
  rb nl
    cb (x[1]< ~~)ce nl
    cb (sp *-> 3)(x[2]< ~~) ce re
  nl};

As illustrated here, turning my carving into a working program is trivial. I merely wrap the code in braces {...} which turns it into a Glee block of code. I preceed this block with the #pgm operator preceeded by the string 'p1'naming the block. I can display this program as follows:

p1:stringv $;
Pgm[S722R2C1:K]
Code:{
x \43=>x;
rb nl
  cb (x[1]< ~~)ce nl
  cb (sp *-> 3)(x[2]< ~~) ce
re nl}

Notice, I don't use the %** Verbose string operator because it would invoke p1 before applying the operator. I would get the verbose look at the output rather than at the block's internal makeup. I use the :stringv $; method instead.


t[1..3]#ea'[.x]p1'
<tr>
  <td>The Cock and the Pearl</td>
  <td>   The Frog and the Ox</td>
</tr>
<tr>
  <td>The Wolf and the Lamb</td>
  <td>   Androcles</td>
</tr>
<tr>
  <td>The Dog and the Shadow</td>
  <td>   The Bat, the Birds, and the Beasts</td>
</tr>


Shows that everything is working fine in the program for a three item table. t[1..3]#ea'[.x]p1' delivers up a subset (first 3 elements) of t to the #ea Each operator which takes those elements in turn. It delivers them one at a time to the left of [.x]. When an index is a field, a namespace is created using the left argument to build it. [.x]thus names the left argument "x" and delivers it as the argument to the program p1. Our program works fine for these three elements showing it correct for beginning and ending conditions and for an inner condition. Thus, it should work just fine for the whole table. This gives an example of a software sculpture technique of "reaching out and touching" the data using just enough to prove the case.

$$ Run the code
pb title pe $;
('<table>')nl
  (t<- 3#ea '    [.x]p1')
  (('...' <- 50)  [.x]p1)
  (('...' -> 50) [.x]p1)
  (t *-> 3#ea '    [.x]p1')
('</table>')$;


I now have the whole program that produces the output I want. I can cut and paste this output into my HTML source code and I have the table I want. There is just one other issue in this code I should explain. Since this is an example, I don't want the whole table. I just want the first and last three rows split by an ellipsis (...). I get this effect by using the <-take from front blank filland ->take from back blank filloperators on my sequence of table items. And in the middle, I create two dummy rows. The first has an elipsis in the left column and the second has it in the right column. I get this effect by doing takes from the front and end on the elipsis strings. In the first case '...'<-50 I take from the front of a string with fills on the right. In the second case '...'->50 I take from the right on a string with blank fills on the left. I'm using 50 for the take which must just be bigger than the 46 (43 where the columns split plus the three character elipsis).

This completes the example. To better understand these operators and other things you can do with them, consult the operator pages according to the type of data you see being operated on.