Go to the first, previous, next, last section, table of contents.


Examples

This chapter gives a few more practical examples of the use of chpp, illustrating a few of its many possible applications.

Song Lyrics

Let us assume you have gathered a collection of song lyrics by various performers as text files. You wish to generate not only HTML files for all the songs but also an index page for each performer which contains, sorted by album, hyperlinks to all the song HTML files.

The song files you have collected all look like this:

Gloria

I try to sing this song.
I try to stand up.
But I can't find my feet.
I try, I try to speak up.
But only in you I'm complete.
...

Furthermore, assume you have created a directory for each performer containing a directory for each album where the song files reside. The names of the directories not necessarily match the corresponding performer's name or the album's, as these often contain spaces, which are uncommon in file names. The same applies to the song file names. Thus, our first task is to somehow associate the names of the performers, albums and songs with the song files. We have a bonus for the song names since these are included in the song files themselves (in the first line). For simplicity we will include this information in the files themselves, namely as HTML comments containing assignments in chpp syntax. To modify the files, we will use the following chpp script:

#include files.chh
%<file=%fopen(%SONGFILENAME)>\
%<songname=%sremovews(%fgets(%file))>\
<!-- %%<song=%songname>%%<album=%ALBUM>%%<perf=%PERF> -->
%frest(%file)\
%fclose(%file)\

The variables SONGFILENAME, ALBUM and PERF must be set via the command-line. We can now convert all files in one album with the following shell-command (assuming we are in the correct directory and that all song files have the extension `.txt'):

$ pwd
/home/schani/lyrics/u2/october
$ for fn in *.txt ; do
>   chpp -DSONGFILENAME=$fn -DALBUM='October' \
>        -DPERF='U2' ../../convert.ch >../$fn
> done

Note that we have generated the modified files in the performer's directory. After we have done this for all albums, we can delete the album directories. The song files now look like this:

<!-- %<song=Gloria>%<album=October>%<perf=U2> -->

I try to sing this song.
I try to stand up.
But I can't find my feet.
I try, I try to speak up.
But only in you I'm complete.
...

We will now generate HTML files for the songs. For simplicity, we will generate a simple HTML file which contains the song text in a <pre> block:

#include files.chh
%<file=%fopen(%SONGFILENAME)>%void(%{%fgets(%file)})\
\
<html> <head>
<title>%song - %album - %perf</title>
</head> <body>
<h1>%song</h1>
<pre>
%frest(%file)\
</pre>
</body>
</html>
%fclose(file)\

Since the information about the song title, album and performer is coded as chpp code in the first line, we can obtain it by just evaluating it and ignoring its result. With this script we can now generate HTML files for all songs of one performer with the following bash command:

$ pwd
/home/schani/lyrics/u2
$ for fn in *.txt ; do
>   chpp -DSONGFILENAME=$fn ../template.ch >${fn%.txt}.html
> done

Finally, we need to generate an index file for each performer containing hyperlinks to all songs. This file is generated by the following chpp script:

#include files.chh
%<albums=%hash()>\
%<file=%fpipe(/bin/sh,-c,ls *.txt)>\
%foreach(songfilename,%ssplit(%'[ \n]+',%sremovews(%frest(file))),
    %<songfile=%fopen(%songfilename)>\
    %void(%{%fgets(%songfile)})\
    %fclose(%songfile)\
    %<regs=%list()>%void(%smatch(%'(.*)\\.',%songfilename,%&regs))\
    %<htmlfile=%regs[1].html>\
    %if(%hcontains(%albums,%album),
        %<albums{%album}{%song}=%htmlfile>
    ,
        %<albums{%album}=%hash(%song,%htmlfile)>
    )\
)\
%fclose(file)\
\
<html>
<head>
<title>%perf</title>
</head>
<body>
<h1>%perf</h1>
%foreach(album,%lsort(%hkeys(%albums)),\
    <h2>%album</h2>%'\n'<blockquote>%'\n'\
    %foreach(song,%lsort(%hkeys(%albums{%album})),\
        <p><a href="%albums{%album}{%song}">%song</a>%'\n'\
    )\
    </blockquote>%'\n'\
)\
</body>
</html>

The first part of the file gets all information about the songs in the current directory. It generates an entry in the hash %albums for each album, which in turn is a hash containing all names of the HTML files indexed by the song names. The second part just iterates through this table, producing an <h2> for each album and an anchor for each song within.

Web Site

Suppose you were to create a web-site with three main pages: News, Tips and Tricks. Each of these pages should have a layout similar to this:

  N   N EEEE W     W  SSS         News
  NN  N E    W     W S
  N N N EEE   W W W   SSS         _Tips_
  N  NN E     W W W      S
  N   N EEEE   W W    SSS         _Tricks_

--------------------------------------------

This is good news!

--------------------------------------------

         News | _Tips_ | _Tricks_

The header consists of a big graphic banner denoting the title of this particular page. On the right side of the banner are three little graphics, each standing for one of the pages. Two of them lead to the other two pages, whereas the one for the actual page is not functional and grayed out.

The footer consists of a textual link bar with two active links and one 'inactive link' (normal text) for the actual page.

Although these three pages are easy to implement with conventional methods, this becomes increasingly difficult when there are more pages or even hierarchical structures of pages. Therefore, we will use this as an example of how to easily create HTML pages with chpp, leaving more sophisticated designs to the gentle reader.

Ideally, we would like, say, the news source file (which we name `news.csml'), as rendered above, to look like this:

#include header.chml

This is good news!

#include footer.chml

This way, we need not be concerned with the design of the header and footer when we work on the pages and we can easily modify the header and footer in one central place, although they apply to our whole web-site.

Now we need a place where we can enter all the names of the main pages of our web-site and associate them with their corresponding files. We call this file `menu.chml':

%addmenuentry(News,news.csml)
%addmenuentry(Tips,tips.csml)
%addmenuentry(Tricks,tricks.csml)

We simply assume that the generated HTML files will have the same base-name as the sources but the extension `.html'. We furthermore assume that for each main-page we have three additional graphics available, namely one containing the large banner and two containing the small icons, where one of the two is grayed out. These images have the format JPEG, hence the extension `.jpg'. Their base-names consist of the base-names of their main-pages followed by one of the suffixes `_l', `_s' and `_s_g', where `l' denotes large, `s' small and `g' gray.

The include-file `header.chml' fulfills two purposes: Firstly, it must process the information of the file `menu.chml' and secondly, it must generate the header of the HTML file. Since the latter depends on the former, we will first focus our attention on processing the information of `menu.chml'. Each main page will be represented by a hash containing the keys filename, name, htmlfilename, imglarge, imgsmall and imgsmallgray, the meanings of which should be obvious. We will collect all these hashes in a list named menu, which will contain these hashes in the order in which they were entered in `menu.chml'. Since `menu.chml' is already in chpp syntax, all we need to do is to define a macro addmenuentry which creates such a hash and appends it to the list menu:

%<menu=%list()>\
%define(addmenuentry,name,filename,
    %<regs=%list()>%void(%smatch(%'(.*)\\.csml$',%filename,%&regs))\
    %<basename=%regs[1]>\
    %lappend(%&menu,
        %hash(filename,%filename,
              name,%name,
              htmlfilename,%basename.html,
              imglarge,%<basename>_l.jpg,
              imgsmall,%<basename>_s.jpg,
              imgsmallgray,%<basename>_s_g.jpg))
)\

Now we can include `menu.chml' for its side-effects:

%void(
#include menu.chml
)\

Finally, for convenience, we define a variable thisentry which contains the hash that applies to the currently processed page:

%<thisentry=%foreach(menuentry,%menu,
    %if(%equal(%menuentry{filename},%mainfilename),%menuentry))>\

Now we are set and can generate the header of the HTML file:

<html>
<head>
<title>%thisentry{name}</title>
</head>
<body>
<table>
<td>
<img src="%thisentry{imglarge}" alt="%thisentry{name}">
<td>
#include choicestrip.chml
</table>
<hr>

The file `choicestrip.chml' generates a vertical table consisting of the small images for the main-pages with links. It is quite simple:

<table border=0 cellspacing=0 cellpadding=0>
%foreach(menuentry,%menu,
    <tr><td>\
    %if(%equal(%menuentry{filename},%thisentry{filename}),
        <img src="%menuentry{imgsmallgray}" alt="%menuentry{name}">
    ,
        <a href="%menuentry{htmlfilename}">\
        <img border=0 src="%menuentry{imgsmall}" alt="%menuentry{name}">\
        </a>
    )
)
</table>

The footer is even more simple: It contains a horizontal rule and a choice-bar:

<hr>
#include choicebar.chml
</body>
</html>

`choicebar.chml' is similar to `choicestrip.chml':

#include list.chh
<h5><center>
%<barentries=%list()>\
%foreach(menuentry,%menu,
    %lappend(%&barentries,
        %if(%equal(%menuentry{filename},%thisentry{filename}),
            %menuentry{name}
        ,
            <a href="%menuentry{htmlfilename}">%menuentry{name}</a>
        )
    )
)\
%listJoin(%' | ',%barentries)
</center></h5>

All we need now is a comfortable way to create all HTML files from the sources. That is what makefiles are for. They have the additional advantage that files are regenerated only if needed, i.e. when one of the files that the file to be created depends on has changed. A makefile for gnumake suitable for our simple purposes would look like this:

CHFILES=header.chml footer.chml choicebar.chml choicestrip.chml

all : news.html tips.html tricks.html

%.html : %.csml
	../../macros -o $ $<

news.html tricks.html tips.html : $(CHFILES)

clean :
	rm -f news.html tips.html tricks.html

Tautogen

Suppose we have a simple context-sensitive grammar, which we want to use to generate sentences, which is quite the opposite of parsing sentences of that grammar. To illustrate this more clearly, let us assume we have a file like this:

--sentence
$subject $verb $object.
$subject, while $gerund $object, $verb $object.
$subject watches $object $gerund $object.

--subject
$person
The $adjective $person

--object
$person

--person
Butthead
Mrs Krabappel
Charlie Brown
Mrs Robinson

--adjective
observant
naive
embarassed

--verb
kisses
kicks
envies

--gerund
holding
zapping
hugging
smashing

The file is separated into several categories containing so-called productions for so-called non-terminals. The first non-terminal is called the start non-terminal, which is, in our case, sentence. We start by randomly picking one of the right-hand-sides of the productions of the start non-terminal (i.e. one of the lines following the introduction of sentence). Now we repeat the following cycle until our string contains no more placeholders (words prefixed by the dollar sign ($)): Replace the first placeholder by the right-hand-side of a randomly picked production for that placeholder.

The process could evolve like this:

$sentence
$subject, while $gerund $object, $verb $object.
The $adjective $person, while $gerund $object, $verb $object.
The naive $person, while $gerund $object, $verb $object.
The naive Charlie Brown, while $gerund $object, $verb $object.
The naive Charlie Brown, while hugging $object, $verb $object.
The naive Charlie Brown, while hugging $person, $verb $object.
The naive Charlie Brown, while hugging Mrs Robinson, $verb $object.
The naive Charlie Brown, while hugging Mrs Robinson, kicks $object.
The naive Charlie Brown, while hugging Mrs Robinson, kicks $person.
The naive Charlie Brown, while hugging Mrs Robinson, kicks Butthead.

It is easy to see that this simple algorithm even allows for recursive grammars.

This example is not a typical application for a pre-processor. It should rather demonstrate that chpp can be used very successfully for tackling problems not within its direct field of application, i.e. that it is suitable for more general problems.

You may have noticed that our grammar file is not in chpp syntax, so we have the choice of either converting it or parsing it at run-time. Since the former has the disadvantage of being more complicated in usage (the grammar would have to be converted each time it is changed) and is not easier to implement than the latter, the choice is obvious.

The first step of our application is reading in the grammar file, which we call `grammar'. Its content will be stored in a hash data, where the keys are the names of the non-terminals and the values are the lists of the right-hand-sides of the corresponding productions.

%<file=%fopen(grammar)>\
%<current=runaway>\
%<data=%hash(runaway,%list())>\
%until(%feof(%file),
    %<line=%sremovews(%fgets(%file))>\
    %<regs=%list()>\
    %if(%[%smatch(%'^--([a-zA-Z0-9_]+)',%line,%&regs)!=-1],
        %<current=%regs[1]>\
        %<data{%current}=%list()>\
        %if(%not(%bound(start)),%<start=%current>)
    ,
        %if(%line,%<data{%current}[%llength(%data{%current})]=%line>)
    )
)\
%fclose(%file)\

We then proceed to define some variables and macros. First, if the variable n, which will denote the number of sentences generated, is not defined (it could be defined on the command-line), it is set to 10:

%if(%not(%bound(n)),%<n=10>)\

The macro some, when called with the name of a non-terminal, returns the right-hand-side of a random production for that non-terminal:

%define(some,nt,%data{%nt}[%random(%llength(%data{%nt}))])\

The generation of the sentences is now a fairly trivial task:

#include strings.chh
%for(i,1,%n,
    %<current=%some(%start)>\
    %<regs=%list()>\
    %while(%<mp=%smatch(%'\\$([a-zA-Z0-9_]+)',%current,%&regs)>%[mp!=-1],
        %<current=%replacesubstring(%current,%mp,%slength(%regs[0]),
                                    %some(%regs[1]))>
    )\
    %current%'\n\n'
)\


Go to the first, previous, next, last section, table of contents.