Tutorial (in spe) and Reference

last updated 3 February 1999 for version 0.3.5

Mark Probst (schani@unix.cslab.tuwien.ac.at)


Introduction

chpp is a preprocessor. Therefore, its main purpose is to modify input text by including other input files and by macro expansion. Programs performing these tasks already exist, among them the popular C Preprocessor (cpp) and m4, which have proven to be convenient and valuable tools suitable for a variety of tasks. The motivation for chpp is thus questionable.

What distinguishes chpp from at least the two programs mentioned above are mainly two features:

  • chpp is non-intrusive. This means that you can take your favourite text and it is very unlikely that it will be changed when piped through chpp. Due to this feature it is pretty easy to start using chpp since you can just start writing your text and need not concern yourself with chpp sitting in the background changing it for no obvious reason.
  • chpp is not just a package for performing simple macro expansion, but can indeed be considered a full-fledged programming language. Most importantly, it provides support for complex data structures, namely lists and hashes (associative arrays), which can be nested arbitrarily.

chpp consists of two parts which could, in some sense, be regarded as two separate passes over the input file. This is not entirely true, though, since the two parts are intertwined and cannot be separated. The first part, which performs command processing, is similar to what cpp does. It allows the inclusion of other files, simple macro definitions and conditional inclusion and exclusion of parts of the input. The second part does macro processing and is the actual workhorse and core of chpp.

Although macro processing on its own could do anything that can be accomplished with commands, the latter are not only easier to use but also easier to read and therefore improve clarity.

What chpp stands for

chpp does not stand for Chakotay Preprocessor.

Uses for chpp

chpp can be used very well as

  • a preprocessor for HTML (see section Web Site). This was, by the way, our original motivation for chpp (see section The History of chpp).
  • a CGI scripting language. chpp will become even more attractive for this kind of application when the planned database interface (see section Planned but not yet Implemented Features) is available.
  • a generator of Quake(tm) configuration files.
  • a producer of funny sentences (see section Tautogen).
  • and a lot more...

What chpp should not be used for

chpp should, due to its nature, not be used for

  • interactive applications.
  • applications where text output is only a secondary function.
  • applications which require speed. Unfortunately, chpp is not a fast program. However, this may change in the future (see section Planned but not yet Implemented Features).

Planned but not yet Implemented Features

We will, in some of the next releases, improve the performance of chpp by yet another factor of two for typical applications.

It will be possible to write new built-in macros in C, which chpp will load dynamically at run-time (see section Extending chpp).

We would also like to add a few extension packages to chpp, which will make it more suitable for and easier to apply to various applications, most notably CGI scripting, HTML generation and database interfacing.

Obtaining chpp

chpp is available for free download on the world-wide-web. Point your web-browser at @url{http://chakotay.ml.org/} (or @url{http://chakotay.ml.org/chpp/} if you are using an ancient browser). From there you can download the latest version of chpp.

The History of chpp

Not much here, yet.

Problems and Known Bugs

chpp is far from error-free in its current version. It works quite reliably now when not stressed too far, though. The biggest problem is currently that chpp's error handling is insufficient. chpp does not report some errors, while others cause it to crash or to hang up in an endless loop. We do, of course, plan to change this in the future.

Reporting Bugs

Before you tell us about a bug, please check that you are using the latest version of chpp (see section Obtaining chpp). If this is not the case, please upgrade and try again.

Please do not report bugs that concern chpp's error handling. We know that it is unreliable and buggy and we will change that. You should, however, report any incident where chpp does anything wrong with a script which you believe is correct. In such a case, please write us an email containing the script that causes chpp to fail and any additional files that your script needs. Better yet, try to narrow down the bug to the smallest script possible. Do also include information on the configuration you ran chpp on, especially if the bug only happens to show up with this configuration. The email address you should send your bug-report to is @email{chpp@unix.cslab.tuwien.ac.at}.

Licence and Warranty

chpp is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

chpp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with chpp; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

The Authors

chpp was designed and implemented by Heinz Deinhart (@email{heinz@unix.cslab.tuwien.ac.at}) and Mark Probst (@email{schani@unix.cslab.tuwien.ac.at}). Check out the chpp homepage (see section Obtaining chpp) for more information.

Acknowledgements

Our thanks go to Tatjana Svizensky for proofreading (an older edition of) this manual and to Herbert P@"otzl for donating the hash-table functions, which we were too lazy, erm, too busy to implement, and for not reading the examples chapter of this document.

A Guided Tour through chpp

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'
)\

Invoking chpp

To get a quick overview of chpp command line syntax, just type

chpp --help

The general chpp syntax is

chpp [option ...] [filename ...]

chpp reads and processes all specified files in sequential order, as if they were one file. If no file is specified, chpp reads from standard input. Output is written to standard output if not otherwise specified.

The following is a summary and description of all available chpp options:

--version
Prints out the version number of the invoked chpp.
--help
Prints out a summary of chpp command line syntax.
--output filename
-o filename
Specifies that output should be written to file filename.
--include-dir dir
-I dir
Adds dir to the list of standard include directories.
-D name=value
Defines the chpp variable name with the value value.
--generate-dependencies
-M
Generates a dependency list suitable for make like cpp.

Language Reference

Files processed with chpp are passed through two stages, which are, however, not sequential in nature but can rather be viewed as coroutines. The first stage processes commands (see section Commands). Command processing is a sequential process, i.e. no loops or recursions occur. The second stage is macro processing (see section The Meta-Char) and allows for loops as well as for recursion.

Commands

Command processing is line-oriented. It affects only lines which have, as their first non-whitespace character, the command-char (#). All other lines are passed through literally. Another function of command processing is the concatenation of lines: If the last character of a line is the backslash (\), then the backslash, the following newline and all leading whitespace of the next line are ignored.

A line invoking a command consists of optional whitespace at the beginning, the command-char #, optional whitespace, the command name, whitespace and the command arguments (if any). Thus, the following lines are all commands (given that the command names exist):

#abc
    #def arg
 #  ghi too many arguments

while the following lines are not:

this is a line without commands.
although this line contains a # it is not a command.

Comments

The command ! (exclamation mark) is a special case among commands, as it does nothing, independent of its parameters, i.e. can be used to write comments, or, if used in the first line of a file, to specify the command line to be used if the containing file is executed. Thus, this is a "Hello world" program in chpp:

#! /usr/local/bin/chpp
Hello world!

After setting the executable bit for this file, it can be called like any command and will produce the output

Hello world!

Note that the exclamation mark must be followed by whitespace.

Command Reference

Command: include filename
Includes the file filename. If filename is relative, it is first searched for in the directory of the including file, then in the directories contained in the include search path (see section Invoking chpp). If the file is not found, an error message is produced.

Command: define name value
Defines the global variable name to contain whatever value evaluates to.

Command: if condition
Evaluates condition. If its boolean value is FALSE, it skips everything up to the corresponding end.

Command: ifdefined symbol
Command: ifdef symbol
If a variable with the name symbol does not exist, skips everything up to the corresponding end.

Command: ifnotdefined symbol
Command: ifndef symbol
If a variable with the name symbol exists, skips everything up to the corresponding end.

Command: error message
Produces an error message with the text message.

Command: discard
Command: disc
Discards everything up to the corresponding end.

The Meta-Char

The second stage processes everything that is passed through by the first stage. It is called macro processing because its main use is the expansion of macros. There is just one special character for this stage, namely the meta-char (%). Only character sequences beginning with the meta-char are modified by the macro processing stage. All other characters are simply passed through. Since chpp was designed to be non-intrusive, even uses of the meta-char which do not correspond to the uses described in this chapter are copied verbatim. For example:

Temperature today is 10% above average.
=> Temperature today is 10% above average.

In cases where it is absolutely necessary that the meta-char not be interpreted as special, it can be quoted with itself (i.e. %%), yielding one meta-char. Example:

%<heinz=deinz>\
%%heinz evals to %heinz.
=> %heinz evals to deinz.

Data Types

The only primitive type in chpp is the string. Values of that type are referred to as scalars (see section Scalars). Values of any type can be combined arbitrarily to lists (see section Lists) and hashes (see section Hashes). Closures (see section Closures) also form a data type as they can be stored and used, even though they cannot be directly manipulated.

Scalars

Scalars are strings of arbitrary length (including the length 0).

Lists

Lists are ordered collections of arbitrary values indexed by consecutive numbers starting at 0. It follows that lists cannot have gaps.

Hashes

Hashes are ordered collections of arbitrary values indexed by arbitrary scalars, i.e. they establish a so-called key/value mapping.

Closures

A closure is a piece of code associated with an environment in which it is to be executed, as created by lambda (or define, for that matter). Thus, the names macro and closure actually stand for the same thing, although one usually tends to call anonymous macros (i.e. values returned by lambda) closures, whereas named closure (i.e. defined macros) are usually called macros.

Variables

In order to be able to retain values for subsequent use it is necessary to store them in variables.

Accessing Variables

There are two different syntactic forms of variable access, called the short and the long form.

The short form consists of the meta-char followed by an optional ampersand (&) followed by the variable name, e.g. %name or %&name. The variable name is taken as-is, i.e. is not evaluated. The variable name ends with the first char that is not a letter, a digit or the underscore (_). If a variable with the given name does not exist, the whole string is not interpreted as a variable access and is copied verbatim, i.e. %name evaluates to %name if there is no variable with the name name.

The long form consists of the meta-char followed by the optional ampersand and the variable name within angle brackets, e.g. %<name> or %<&name>. The variable name is evaluated before the variable is looked up, making it possible, for example, to use variable names containing right angle brackets: The term %<%'>>>'> accesses the variable >>>. If a variable with the name does not exists, an error message is issued. Note: Although it is possible to use macros to construct variable names (e.g. %<%name>), this feature is deprecated. Please don't use it.

List and Hash Subscription

If the variable is a list or a hash, it can be subscribed by appending the index in brackets or curly braces to the name, in both the short and the long form. In order to access nested data structures, any number of such indexes can be used in one access, for example %name[3]{foo}.

Copies and References

Accessing a variable or a subscript without an ampersand produces a shallow copy of its value, i.e. accessing a list produces a copy of the list containing the elements of the original list. Example:

%<lst1=%list(a,b,c)>%<lst2=%lst1>\
%same(%&lst1,%&lst2) : %same(%&lst1[0],%&lst2[0])
=> 0 : 1

Accessing a variable or subscript with an ampersand produces the same value, i.e. can be used to bind two names to the same value:

%<str1=abc>%<str2=%&str1>\
%same(%&str1,%&str2)
=> 1

There are several important issues to this:

  • Copying values is of course slower than just reusing the same value. chpp's built-in macros, however, are wise enough not to copy their arguments when they don't need to. For example, calling llength never copies its argument.
  • Circular data structures can be built easily. Care has to be taken when processing these structures, or one might end up in an endless loop.
  • Memory management becomes more complicated when arbitrary cross-references in data-structures are allowed. This, however, need not concern the chpp user, as it employs garbage collection to free unused memory.

Macro Invocation

A macro can be invoked by appending, in the short or long form of variable access, to the variable name or subscript a left parenthesis followed by the actual arguments separated by commas followed by a right parenthesis, e.g. %list(a,b). The value that is yielded by the macro invocation cannot be subscribed further, i.e. %list(a,b)[1] is not allowed. However, see section Subscribing Non-Variables for a method to achieve this goal.

Arguments of a macro-call are processed as follows: First, all leading and trailing whitespace from all arguments is removed. Then, the remaining strings are evaluated and the results are passed as arguments to the macro. In order to pass an argument with leading or trailing whitespace to a macro, it must be quoted. For example:

%define(foobar,arg,"%arg")

%foobar(  xyz  )
=> "xyz"
%foobar(    )
=> ""
%foobar(  %'  '  )
=> "  "
%foobar(%'  xyz  ')
=> "  xyz  "

Subscribing Non-Variables

It is possible to subscribe values that are not variables, for example ones that are returned from macros, by using a modified long form of variable access. Instead of the variable name the expression yielding the value enclosed in parentheses is used. Upon evaluation, the expression is evaluated and all following subscriptions are applied to its value. Example:

%<(%list(a,b))[1]>
=> b

Assignment

Assignment syntax is an enhancement of the long form of variable access. The last subscription (or the variable name, if no subscription is used) is followed by an equal sign (=) which is followed by an expression yielding the value to be assigned.

When assignment is attempted to an element of a list which is out of bounds, the list is enlarged. Elements between the formerly last element and the newly added element default to the empty string. Indexes less then 0 are not allowed.

Assigning to a key in a hash which is not part of it, adds the key/value pair to the hash.

It is not possible to assign to a subscript of a value which is not subscribeable, i.e. it is not possible to do %<bar[3]=foo> if bar is not a list. To make bar an empty list, simply do %<bar=%list()>.

Assignment usually changes a binding, be it in an environment or in a list or hash. This means that the sequence

%<value=%list()>%<value=%hash()>

first binds the name value to a newly created list and then rebinds value to a newly created hash, leaving the old list intact. When using the ampersand-form, however, the old value is changed to the new value, which is a destructive process. Example:

%<value=abc>%<ref=%&value>%<&value=123>%ref
=> 123

When an assignment to a variable is executed for which there is no binding, a new binding in the global environment is created for this variable name.

Scoping Rules

chpp uses lexical scoping, using an environmental model, very similar to Scheme's. An environment contains bindings for names to values and a reference to its parent environment. The only environment without parent is the global environment. Execution always takes place in some environment. If a variable name has to be resolved, the current environment is checked for whether it contains a binding for that name. If it does not, its parent is checked, and so on until the global environment is reached. If it does not contain a corresponding binding, the variable name cannot be resolved and an error message is produced.

New environments are created upon several occasions:

  • The execution of locals and let expressions (see section Special Forms). The new environment is set up to contain bindings for all the variables mentioned as parameters to locals or let. The parent environment of the new environment is the environment active at the time of execution of the expression. The environment is active throughout the body of the expression.
  • The execution of a closure (i.e. the value returned by an invocation of lambda or the value bound to a name as a result of an invocation of define). The environment is set up to contain bindings for all the parameters of the closure. The parent environment of this new environment is the environment active at the time of the generation of the closure, i.e. of the invocation of lambda. That makes it possible to do things like this:
    %define(newcounter,%let(c,0,%lambda(%<c=%[c+1]>%c)))\
    %<counter=%newcounter()>\
    %counter() %counter() %counter()
    => 1 2 3
    
    The parent environment for the environments of the closure invocations is the environment created by let, mapping c to 0, which is itself created every time newcounter is executed. Thus, in a way, counter carries a state, namely its private variable c. Had we called newcounter a second time, a second counter would have been created, with its own c, initally set to 0, completely independent of the first.
  • The execution of for, foreach and foreachkey expressions (see section Special Forms).

Arithmetic Expansion

chpp permits the evaluation of arithmetic expressions by enclosing the expression in square brackets ([]) preceded by the meta-char. The expression is first evaluated according to chpp rules and the resulting value is treated as an arithmetic expression which, in turn, yields a number. Whitespace between operators and operands is ignored. The following table is a summary of all available operators together with their arity. They are sorted by precedence, the first line being of the highest precedence. All binary operators evaluate from left to right. All operators have the same meaning as the corresponding operators in the C language.

@multitable @columnfractions .2 .8

  • Operators @tab Arity
  • !, ~, - @tab unary
  • *, /, % @tab binary
  • +, - @tab binary
  • <, >, <=, >= @tab binary
  • ==, != @tab binary
  • & @tab binary
  • ^ @tab binary
  • | @tab binary
  • && @tab binary
  • || @tab binary Precedence of operators can be overridden by using parentheses (()). In order to make arithmetic expressions more readable, it is allowed to refer to the values of variables within an arithmetic expression by writing its name--without a preceding meta-char. Note that subscription and macro invocation using this syntax is not allowed. Some examples:
    %[1+2]
    => 3
    %[1.5+3.3]
    => 4.800000
    %[3==3]
    => 1
    %[3!=3]
    => 0
    %[(1+2)*(3+4)]
    => 21
    %<x=4>%[%x+1]
    => 5
    %<x=4>%[x+1]
    => 5
    

    Quotation

    To prevent some string from evaluation, it can be quoted by enclosing it in a pair of single-quotes ("), preceded by the meta-char. The only special characters after the first double-quote are the quote-char (the backslash, \) and the closing double-quote. The quote-char gives special meanings to some characters following it: an n becomes a newline character and a t is interpreted as a tabulator. All other character preceded by the quote-char stand for themselves. This includes the quote-char and the double-quote, i.e. %'\\\" evaluates to \'.

    Explicit Evaluation

    In order to evaluate a string twice, for example to evaluate the contents of a variable, the string must be enclosed in curly braces ({}), preceded by the meta-char. Example:

    %<a=abc>%<b=%%a>%{%b}
    => abc
    

    Extending chpp

    Special Forms

    Special forms, although syntactically equivalent to macros, differ from macros in that their arguments are not automatically evaluated before they are called. The evaluation of their arguments usually depend on some condition of some other argument.

    Special Form: if (condition,consequent[,alternative])
    Evaluates condition and, if its boolean value is TRUE, evaluates consequent. Otherwise, alternative is evaluated, if specified.

    Special Form: cond ([condition,consequent[,...]])
    Evaluates the conditions one at a time and checks for their boolean value. If a condition with a value of TRUE is encountered, its corresponding consequent is evaluated and its result returned. If no condition evaluates to TRUE, nothing is done. Example:

    %<number=23>\
    %cond(%[number < 10],less than 10,
          %[number < 50],less than 50 but greater than 9,
          else,greater than 49)
    => less than 50 but greater than 9
    

    Special Form: case (string,list,consequent[,...[,else,alternative]])
    Evaluates string to a scalar. Then, one at a time evaluates each list and checks whether string is contained in the resulting list. If it is, the corresponding consequent is evaluated and its result returned. If the else clause is specified and the string is contained in no list, its alternative is evaluated. Otherwise, nothing is done. Example:

    %<number=7>\
    %case(%number,
          %list(0,2,4,6,8),even,
          %list(1,3,5,7,9),odd)
    => odd
    

    Special Form: for (counter,start,stop,[increment,]body)
    Evaluates counter, start, stop and, if specified, increment. The values of start, stop and increment must be integer numbers. If increment is not specified, an increment of @math{1} is used if start is greater than stop, otherwise @math{-1}. Then counts, beginning with start, while the value of the counter is before or equal to stop in the counting direction. For each step, evaluates body in a new environment where counter is bound to the value of the counter.

    A few examples:

    %for(i,1,10,%i%' ')
    => 1 2 3 4 5 6 7 8 9 10
    %for(i,10,1,%i%' ')
    => 10 9 8 7 6 5 4 3 2 1
    %for(i,1,10,2,%i%' ')
    => 1 3 5 7 9
    %for(i,10,1,-2,%i%' ')
    => 10 8 6 4 2
    %for(i,1,10,0,%i%' ')
    error--> increment in for-loop cannot be zero
    %for(i,10,1,1,%i%' ')
    =>
    

    Special Form: foreach (counter,list,body)
    Evaluates counter and list, which must yield a list. Then iterates over this list evaluating body in a new environment in which counter is bound to the current list element.

    Special Form: foreachkey (counter,hash,body)
    Evaluates counter and hash, which must yield a hash. Then iterates over all keys in the hash evaluating body in a new environment in which counter is bound to the current hash key.

    Special Form: while (condition,expr)
    Evaluates condition. If this yields a boolean value of TRUE, expr is evaluated, otherwise the loop is finished. Then repeats the cycle.

    Special Form: until (condition,expr)
    Evaluates condition. If this yields a boolean value of TRUE, the loop is finished, otherwise expr is evaluated. Then repeats the cycle.

    Special Form: dowhile (expr,condition)
    Evaluates expr, then evaluates condition. If this yields a boolean value of TRUE, the cycle is repeated.

    Special Form: dountil (expr,condition)
    Evaluates expr, then evaluates condition. If this yields a boolean value of TRUE, the loop is finished, otherwise the cycle is repeated.

    Special Form: and ([expr[,...]])
    Evaluates the exprs from left to right. For the first expr evaluating to boolean FALSE, returns 0. If all exprs evaluate to boolean TRUE, returns 1. This means that if one expr evaluates to FALSE, all exprs to its right do not get evaluated. If and is called without parameters, it returns 1.

    Special Form: or ([expr[,...]])
    Evaluates the exprs from left to right. For the first expr evaluating to boolean TRUE, returns 1. If all exprs evaluate to boolean FALSE, returns 0. This means that if one expr evaluates to TRUE, all exprs to its right do not get evaluated. If or is called without parameters, it returns 0.

    Special Form: define (name,argname[,argname...],body)
    Defines a macro with the name name and the specified argument names. The body body is not evaluated.

    If the last argname matches the pattern lstname:[lower]:[upper] then the macro takes a variable number of arguments. All argnames but the last are treated as single arguments. The last argname corresponds to a number of arguments not less than lower and not greater than upper. When the macro is called, these arguments are bound to the lstname in the form of a list. If lower is not given, zero arguments for lstname are allowed. If upper is not given, there is no upper limit on the number of arguments.

    Examples:

    %define(mac,a,b,a=%a b=%b)
    

    defines a macro mac taking exactly two arguments.

    %define(mac,a,b,c:2:3,a=%a b=%b c=%encode(%c))
    %mac(1,2,3,4)
    => a=1 b=2 c=%list(%'3',%'4')
    

    defines a macro mac taking four or five arguments.

    %define(mac,a,b,c::3,a=%a b=%b c=%encode(%c))
    

    defines a macro mac taking at most five arguments.

    %define(mac,a,b,c:2:,a=%a b=%b c=%encode(%c))
    

    defines a macro mac taking at least four arguments.

    Special Form: defspecial (name,argname[,argname...],body)
    Defines a special form with the name name and the specified argument names. The body body is not evaluated.

    When a special form is called, its arguments are not evaluated. This is left to the special form. Instead, intermediate code of the arguments is bound to the argnames and body is evaluated. The special form can evaluate any of the arguments any time it wishes to by using the eval macro.

    The special form body is evaluated in an environment whose parent is the environment from which the special form was invoked.

    As an example, we give an implementation of a special form unless which evalutes its second argument only if its first argument evaluates to a boolean value of FALSE:

    %defspecial(unless,cond,body,
      %if(%not(%eval(%cond)),
        %eval(%body)
      )
    )
    

    Note that any side-effects that result from evalutating the second argument do not occur if the first argument evaluates to TRUE:

    %<a=123>
    %unless(1,%<a=xyz>)
    %%a is %a
    => %a is 123
    

    Special Form: lambda (argname[,argname...],body)
    Creates an anonymous macro (closure) with the specified argument names and the body body, which is not evaluated.

    If the last argname matches the pattern lstname:[lower]:[upper] then the closure takes a variable number of arguments. The semantics of this feature are equal to that of define.

    The definition

    %define(mac,a,b,a=%a b=%b)
    

    is semantically equivalent to

    %<mac=%lambda(a,b,a=%a b=%b)>
    

    Special Form: let (name,value[,name,value...],body)
    Evaluates body in a new environment with the local variables names bound to their corresponding values. The variables are bound beginning with the first name from left to right and the bindings are immediately visible to the following value expressions, i.e. the second value is evaluated in an environment where the first name is already bound. This is a behaviour similar to Lisp's let*.

    %let(a,1,b,%[a+1],%%a=%a %%b=%b)
    => %a=1 %b=2
    

    Special Form: locals (symbol[,symbol...],body)
    Evaluates body in a new environment with the specified local variables.

    Macros

    List Operations

    Macro: list ([element,...])
    Returns a list containing all specified elements in the specified order, beginning with index 0. Note that %list() returns an empty list while %list(%") returns a list with one element, which is the empty string.

    Macro: llength (list)
    Returns the number of elements in the list list.

    Macro: linsert (list,index,element)
    Inserts the element element into the list list at index index. If index is greater or equal the length of list, the list is enlarged by appending empty strings. Examples:

    %<lst=%list(a,b,c)>
    
    %linsert(%&lst,1,x)%encode(%lst)
    => %list(%'a',%'x',%'b',%'c')
    %linsert(%&lst,5,y)%encode(%lst)
    => %list(%'a',%'x',%'b',%'c',%",%'y')
    

    Macro: ldelete (list,index)
    Deletes from the list list the element at index index. Example:

    %<lst=%list(a,b,c)>%ldelete(%&lst,1)%encode(%lst)
    => %list(%'a',%'c')
    

    Macro: lsort (list[,comparator])
    Sorts the elements of the list list, which should be strings, according to the order specified by comparator, which must be a closure taking two arguments and returning an integer. A return value of 0 denotes that the two arguments are equal, less than 0 means that the first argument should come before the second, greater than 0 means that the first argument should come after the second. If comparator is omitted, the macro scmp is used. Returns the resulting list. Examples:

    %encode(%lsort(%list(b,c,a)))
    => %list(%'a',%'b',%'c')
    %encode(%lsort(%list(b,c,a),%lambda(a,b,%scmp(%b,%a))))
    => %list(%'c',%'b',%'a')
    

    Macro: lappend (list,value[,value...])
    Appends all values to the list list. Returns nothing, i.e. is called for its side effects, which means that the first parameter should be a list to be modified, not a copy of a list.

    Macro: luniq (list[,comparator])
    Removes all but the first occurrences of all sequential occurrences of the same element in list, where the sameness is determined by the closure comparator, which must accept two arguments and return a boolean value of TRUE if they are equal, FALSE otherwise. If comparator is omitted, seq is used. Returns the modified list.

    %encode(%luniq(%list(a,b,b,c,d,e,e,e,f)))
    => %list(%'a',%'b',%'c',%'d',%'e',%'f')
    

    Hash Operations

    Macro: hash ([key,value,...])
    Returns a hash containing all the specified key/value pairs.

    Macro: hcount (hash)
    Returns the number of key/value pairs in the hash hash.

    Macro: hcontains (hash,key)
    Returns 1, if the key key is contained in the hash hash. Otherwise, returns 0.

    Macro: hkeys (hash)
    Returns a list of all keys in the hash hash in no particular order.

    Macro: hdelete (hash,key)
    Removes the key key from the hash hash. Does nothing if key is not contained in hash.

    %<h=%hash(a,1,b,2,c,3)>
    %hdelete(%&h,b)
    %encode(%h)
    => %hash(%'a',%'1',%'c',%'3')
    

    Environment Operations

    Macro: envThis ()
    Returns the environment which the macro is executed in.

    Macro: envNew ([parent])
    Returns a new environment. If parent is specified, it is used as the parent to the newly created environment. Otherwise, the new environment is a top-level environment, i.e. has no parent.

    Macro: envAdd (env,name,value)
    Adds a binding for the name name to the value value to the environment env.

    File Operations

    Macro: fopen (filename[,mode])
    Opens the file named filename and returns a handle to it. If the file could not be opened, for example because it does not exists, for lack of permission or if mode is illegal, returns -1. mode describes for what purposes the file is opened and must be one of the following:

    r
    Reading.
    w
    Writing.
    a
    Appending.

    If mode is omitted, the file is opened for reading.

    Macro: fpipe (mode,executable[,arg...])
    Forks off a child process, starts the specified executable with the specified parameters. If mode is r, returns a handle for reading the processes standard output. If mode is w, returns a handle for writing to the processes standard input. Returns -1 if something goes wrong. Note that the arguments are not subject to shell expansion, since the process does not start a subshell. If you want shell expansion, you have to start a subshell explicitly. For example, to list all files beginning with an a:

    %fpipe(r,/bin/sh,-c,ls a*)
    

    Macro: fclose (file)
    Closes the file associated with the handle file.

    Macro: fgets (file)
    Reads one line from the file associated with the handle file and returns it. The newline character is included.

    Macro: fputs (file,string)
    Writes string to the file associated with the handle file, which must have been opened for writing.

    Macro: feof (file)
    Returns boolean TRUE if the end of the file associated with the handle file is reached, FALSE otherwise.

    Macro: fstat (filename)
    Returns a hash containing information on the file with name filename. Returns an empty hash if the file does not exist. Otherwise, the hash contains values for the following keys:

    uid
    User ID of owner.
    gid
    Group ID of owner.
    size
    Size in bytes.
    blksize
    Blocksize for filesystem I/O.
    blocks
    Number of blocks allocated.
    atime
    Time of last access.
    mtime
    Time of last modification.
    ctime
    Time of last change.

    All times are given in seconds since January 1, 1970, 00:00:00 GMT. For detailed description of these values see stat(2).

    Macro: fgetwd ()
    Returns the current working directory.

    Macro: fchdir (path)
    Changes the current working directory to path.

    Macro: fglob (pattern)
    Matches the pattern string pattern using shell pattern matching rules. Returns a list of all filenames matched or boolean FALSE if no filenames could be matched or in case of an error.

    %encode(%fglob(/b*))
    => %list(%'/bin',%'/boot')
    

    String Operations

    Macro: smatch (regexp,string[,registers])
    Searches the string string for the first occurrence of the regular expression regexp and returns the index of the first character of this substring. If no match was found, returns -1. If registers is specified, which must be a list, clears it and sets its elements (beginning with @math{1}) to the contents of the corresponding submatches (parts of the regular expression enclosed by parentheses). The element 0 is set to the whole match. For example:

    %<regs=%list()>\
    %smatch(%'\.([^.]*)$',alittlepicture.jpg,%&regs) %regs[1]
    => 14 jpg
    

    Macro: ssplit (regexp,string[,connector])
    Splits the string string into parts separated by the regular expression regexp. Returns a list containing these parts. If connector is specified, the parts that are inserted into the list are generated by invocations of connector. connector is always called with three parameters, the first being the registers of regexp for the match right before the string and the third being the registers of the match right after the string. The second parameter is the string itself. For the very first string, the first parameter is an empty list, whereas for the last string, the third parameter is an empty list. Thus

    %ssplit(%regex,%string)
    

    is equivalent to

    %ssplit(%regex,%string,%lambda(a,b,c,%b))
    

    Example:

    %encode(%ssplit(:+,foo::bar:rules))
    => %list(%'foo',%'bar',%'rules')
    

    Macro: stokenize (regexp,string[,tokener])
    Returns a list containing all substrings of string matching regexp. If tokener is specified, not the whole matches are inserted into the result list, but the results of calling tokener with the respective lists of registers of the matches. This means, that these two calls have the same result:

    %stokenize(%regexp,%string)
    %stokenize(%regexp,%string,%lambda(r,%r[0]))
    

    Examples:

    %encode(%stokenize([a-zA-Z0-9]+,%' a bc d04 d   fsfd, rwe'))
    => %list(%'a',%'bc',%'d04',%'d',%'fsfd',%'rwe')
    %encode(%stokenize(%'-([0-9]+)-',%'  -32- -- 543 -12--43--',
                       %lambda(r,%r[1])))
    => %list(%'32',%'12',%'43')
    

    Macro: sgsub (regexp,string,replacement[,options])
    Replaces all occurrences of the regular expression regexp in string. If replacement is a string, the occurrences are replaced with this string. Otherwise, replacement must be a closure taking one argument and returning a string. In this case, the occurrences are replaced by the result of the closure when called with a list containing the regular expression registers in elements beginning with 1 and the whole match in element 0. options, if specified, should be a string composed of one or more of the following characters:

    i
    Match case insensitively. The default is case-sensitive matching.

    Examples:

    %sgsub(ei,HEINZI Deinzi,!,i)
    => H!NZI D!nzi
    %sgsub(a+,abaacaaadaaaa,%lambda(r,%slength(%r[0])))
    => 1b2c3d4
    

    Macro: sremovews (string)
    Returns a string which results from removing all leading and trailing whitespace from the string string.

    Macro: slength (string)
    Returns the length of the string string.

    Macro: ssub (string,start[,length])
    Returns a substring of the string string. If it is called with two arguments and start is positive, then the substring beginning with index start is returned. If start is negative, the last -start characters are returned. If called with two arguments, start specifies the index of the first character in the substring. If length is positive, it specifies the length of substring. If it is negative, then -length specifies the index of the character following the last character of the substring. Examples:

    %substring(0123456789,3)
    => 3456789
    %substring(0123456789,-3)
    => 789
    %substring(0123456789,2,3)
    => 234
    %substring(0123456789,2,-5)
    => 234
    

    Macro: scmp (string1,string2)
    Returns the result of the strcmp() C function with string1 and string2.

    Macro: schr (code)
    Returns a string with the character with character code code.

    Macro: snumber (number,base)
    Formats the decimal integer number according to the given base base, which must be between 2 and 36 inclusive. Example:

    %snumber(34,2)
    => 100010
    %snumber(-255,16)
    => -ff
    

    Macro: srange (char1,char2)
    Generates a string by concatenating all characters between and including char1 and char2. Example:

    %srange(a,f)
    => abcdef
    

    Macro: smap (src,dest,str)
    Returns a string where all characters of the string str included in src are translated to characters at the same position in dest. src and dest must be strings of the same length. Example:

    %smap(%srange(a,z),%srange(A,Z),Heinzi Deinzi)
    => HEINZI DEINZI
    

    Time Operations

    Time values are represented in chpp by hashes containing values for some of the following keys:

    year
    month
    day
    hour
    minute
    second

    Macro: timeUNIX ()
    Returns the current time in UNIX time_t format.

    %timeUNIX()
    => 917465546
    

    Macro: timeUNIX2Hash (utime)
    Returns a time hash that corresponds to the UNIX time utime.

    %<(%timeUNIX2Hash(917465546)){year}>
    => 1999
    

    Macro: timeHash2UNIX (htime)
    Returns the UNIX time corresponding to the time hash htime. The hash must contain sane values for at least the keys second, minute, hour, day, month and year.

    Miscellaneous

    Macro: void (expr)
    Executes expr but discard the result. Example:

    %<regs=%list()>\
    %void(%smatch(%'\.([^.]*)$',alittlepicture.jpg,%&regs))%regs[1]
    => jpg
    

    Macro: outputenable (flag)
    If the boolean value of flag is TRUE, enables output, otherwise disables it. If output is disabled, everything is evaluated as usual, but chpp does not output anything.

    Macro: depend (dependency[,target])
    Adds the filename dependency to the list of dependencies for the file target. If target is not specified, the name of the output file is used.

    Macro: warning (message)
    Causes chpp to give a warning with the message message.

    Macro: error (message)
    Causes chpp to signal an error with the message message.

    Macro: encode (value)
    Returns a string which, upon evaluation, yields a value equal to value.

    Macro: random (limit)
    Returns a random number in the interval 0 to limit-1. The random number are uniformly distributed.

    Macro: same (val1,val2)
    Returns 1 if val1 and val2 refer to the same value, otherwise 0. Two values are the same if a change in one entails the same change in the other, i.e. if they occupy the same memory location. Examples:

    %<val=abc>%same(%val,%val)
    => 0
    %<val=abc>%same(%&val,%&val)
    => 1
    %<val=abc>%<val2=%&val>%same(%&val,%&val2)
    => 1
    

    Macro: equal (val1,val2)
    Returns 1 if val1 is equal to val2, otherwise 0. Two scalars are equal if the strings they contain are equal. Two lists are equal if they have the same length and contain equal elements. Two hashes are equal if they have the same count and the same keys map to equal values.

    %equal(%list(a,b,c),%list(a,b,c))
    => 1
    %equal(%hash(a,1,b,2,c,3),%hash(c,3,b,2,a,1))
    => 1
    %equal(%list(a,b,c),%list(1,2,3))
    => 0
    

    Macro: typeof (value)
    Returns the type of value. The result can be one of scalar, list, hash, lambda, built-in.

    %typeof(abc)
    => scalar
    %typeof(%list(a,b,c))
    => list
    %typeof(%hash(a,1,b,2,c,3))
    => hash
    %typeof(%lambda(a,%a%a))
    => lambda
    %typeof(%typeof)
    => built-in
    

    Macro: bound (name)
    Returns 1 if the name name is bound in the current environment. If not, returns 0.

    Macro: apply (lambda,arglist)
    Calls lambda with the elements of arglist as arguments.

    %apply(%lambda(a,b,c,my args are %a %b %c),%list(1,2,3))
    => my args are 1 2 3
    

    Macro: eval (code[,env])
    Evaluates the code code in the the environment env or in the local environment if env is not specified.

    %let(a,xyz,%eval(%%a))
    => xyz
    %let(e,%envNew(),%envAdd(%&e,a,xyz)%eval(%%a,%&e))
    => xyz
    

    Macro: not (expr)
    Returns the negation of the boolean value of expr, i.e. returns 1 if expr is FALSE and 0 if expr is TRUE.

    Macro: shexencode (string)
    Translates the bytes of string to a sequence of hexadecimal digits.

    %shexencode(hello world!)
    => 68656C6C6F20776F726C6421
    

    Macro: shexdecode (string)
    Translates a sequence of hexadecimal digits as produced by shexencode to a string.

    %shexdecode(68656C6C6F20776F726C6421)
    => hello world!
    

    Internal Variables

    Variable: outputenabled
    Is 1 if output is enabled, 0 otherwise. Output can be enabled and disabled with the macro outputenable.

    Variable: dependencing
    Is 1 if chpp was started to generate dependencies (option --generate-dependencies), 0 otherwise.

    Variable: mainfilename
    Is set to the filename of the currently executed top-level source file.

    Variable: env
    Is a hash containing all environment variables of the process.

    %env{TERM}
    => xterm
    

    Packages

    `files.chh'

    Macro: frest (file)
    Returns the not yet read contents of the file associated with the handle file.

    Macro: fwholefile (filename)
    Returns the whole contents of the file with the name filename.

    Macro: fneweras (filename1,filename2)
    Returns 1 if the modification time of the file with name filename1 is more recent than that of the file with name filename2 or if the file with name filename2 does not exist. Returns 0 otherwise.

    `strings.chh'

    Macro: replacesubstring (string,start,length,replacement)
    Returns a string resulting from replacing the substring starting at index start with length length of string by the string replacement.

    Macro: strneq (string1,string2)
    Returns a boolean value of TRUE if string1 and string2 are not equal, otherwise TRUE.

    `list.chh'

    Macro: listSearch (list,criterion)
    Returns the index of the first element of list for which the closure criterion, when called with that element as parameter, evaluates to boolean TRUE. Example

    %listSearch(%list(a,bb,ccc,dddd),%lambda(e,%[%slength(%e)>=3]))
    => 2
    

    Macro: listIndexOf (list,value)
    Returns the index of the first element of list which is equal to value. Example:

    %listIndexOf(%list(a,b,c,d),b)
    => 1
    

    Macro: listMap (mapping,list[,list...])
    All lists must have the same length, and mapping must be a closure taking as many arguments as there are lists. listMap creates a new list by applying mapping element-wise to the elements of the lists and storing the results in the corresponding elements of the resulting list. Example:

    %listMap(%lambda(a,b,%[a+b]),%list(2,5,7),%list(4,2,9))
    => %list(%'6',%'7',%'16')
    

    Macro: listLeftAccumulate (accumulator,list,zero)
    If the length of list is 0, returns zero. Otherwise, accumulates all elements of list through accumulator, which must be a closure taking two arguments, in a left-associative way. Examples:

    %listLeftAccumulate(%lambda(a,b,%[a+b]),%list(1,2,3),0)
    => 6
    %listLeftAccumulate(%lambda(a,b,acc%'('%a%','%b%')'),
                        %list(a,b,c),zero)
    => acc(acc(a,b),c)
    

    Macro: listRightAccumulate (accumulator,list,zero)
    If the length of list is 0, returns zero. Otherwise, accumulates all elements of list through accumulator, which must be a closure taking two arguments, in a right-associative way. Examples:

    %listRightAccumulate(%lambda(a,b,acc%'('%a%','%b%')'),
                         %list(a,b,c),zero)
    => acc(a,acc(b,c))
    

    Macro: listJoin (string,list)
    Joins the elements of the list list by inserting between two sequential elements the string string. Example:

    %listJoin(:,%list(the,quick,brown,fox))
    => the:quick:brown:fox
    

    `time.chh'

    Macro: timeNow ()
    Returns a time hash for the current time.

    Macro: timeToString (format,time)
    Converts a time value to a string according to the format string format. Ordinary characters in format are copied verbatim, while the dollar character ($), followed by any of the following characters is treated specially:

    $
    The character $.
    d
    The day of the month as a decimal number, beginning with 1 for the first day of the month.
    m
    The month as a decimal number, beginning with 1 for January.
    b
    The abbreviated month name.
    B
    The full month name.
    Y
    The year as a decimal number.
    H
    The hour as a decimal number (range 0 to 23).
    M
    The minute as a decimal number (range 0 to 59).
    S
    The second as a decimal number (range 0 to 59).

    Example:

    %timeToString($d $B $Y,%hash(day,29,month,12,year,1975))
    => 29 December 1975
    

    Macro: timeFromString (format,string)
    Converts the string string, which must obey the time format format, as described above, to a time value. Example:

    %encode(%timeFromString($d $B $Y,29 December 1975))
    => %hash(%'year',%'1975',%'day',%'29',%'month',%'12')
    

    `sql.chh'

    This section describes chpp's interface to relational databases, called chdbc (chpp Database Connectivity). The interface is a layer above the client libraries for the various database servers, making it thus transparent to the user which database she is using.

    Connections and results are represented by abstract datatypes. When passing a connection or result to a chdbc macro, do always pass the value which was returned by the creating macro, not a copy (i.e. use the reference form of variable access (see section Accessing Variables) to pass connection and result parameters).

    Drivers are currently implemented for mSQL and MySQL. The latter takes a connection hash with keys user and password.

    Macro: sqlConnect (url,hash)
    Opens a connection to the SQL Server with the given url, which needs to be of the form chdbc:drivername://hostname[:port]/dbname/. A valid example would be chdbc:mysql://localhost/test/. hash must be hash containing information required by the driver to connect to the server, e.g. username and password. sqlConnect returns a value representing the connection to the server or a boolean value of FALSE if the connection could not be made.

    Macro: sqlClose (connection)
    Closes the database connection connection.

    Macro: sqlDatabaseInfo (connection)
    Returns a hash containing information about the database. The hash contains values for the following keys, if appropriate for the database:

    timeformat
    Format for time values which can be used in inserts and updates, like $H:$M:$S.
    dateformat
    Format for date values which can be used in inserts and updates, like $Y-$m-$d.
    datetimeformat
    Format for time plus date values which can be used in inserts and updates, like $Y-$m-$d $H:$M:$S.

    Macro: sqlQuery (connection,querystring)
    Performs a query on the database connected to by connection. Returns a value representing the result of the query or a boolean value of FALSE if the query could not be executed.

    Macro: sqlResultData (result)
    Returns the result rows of the query result result, as obtained by sqlQuery, in the form of a list. Each row in this list is represented as a hash whose keys are the column names of the result. Values for columns representing time (time, date and datetime) are automatically converted to chpp's time format (see section `time.chh').

    Macro: sqlResultColumnInfo (result)
    Returns a hash indexed by the column names of the query result result containing information about the columns. Each value contained in the hash is a hash containing values for the following keys:

    type
    Type of the column.

    Macro: sqlResultColumnNames (result)
    Returns a list containing the names of the column of the query result result.

    Macro: sqlUpdate (connection,updatestring)
    Performs an SQL statement contained in the string updatestring changing the data of the database connected to by connection, like an insert or an update.

    `cgi.chh'

    The package `cgi.chh' provides rudimentary support for CGI scripting.

    Macro: cgiGetParameters ()
    Returns a hash containing all parameters passed to the CGI script. Supported encodings are application/x-www-form-urlencoded (both GET and POST) and multipart/form-data.

    `w3lib.chh'

    This package provides rudimentary support for web page building. It requires the program `xli' to parse picture files.

    There is some experimental JavaScript code in `w3lib.chh', but nothing is used by now. If you are a JavaScript guru you might want to code a bit?

    Variable: w3xliExecutable
    If set before `w3lib.chh' is included, it will use this as full path to the `xli' program.

    Macro: w3img (imagename,[arg...])
    Macro: w3image (imagename,[arg...])
    Generates an img tag for imagename. If args are supplied the will be added at the end of the img tag. w3img takes care of dependencies.

    Macro: w3imgX (imagename)
    Macro: w3imgWidth (imagename)
    Returns the width of the image imagename. Takes care of dependencies.

    Macro: w3imgY (imagename)
    Macro: w3imgHeight (imagename)
    Returns the height of the image imagename. Takes care of dependencies.

    Macro: w3rgbcolor (red,green,blue)
    Returns an HTML conforming color value string.

    `w3lib.chh' is intended to be used together with `make'. A simple `Makefile' may look like this:

    # chpp Makefile for web pages
    
    HTML = Main.html News.html Authors.html Manual.html Wizard.html \
           Download.html Links.html
    
    # -- general
    ----------------------------------------------------------------
    
    all : $(HTML)
    
    clean :
            rm -f *.d *.jpgd *.gifd *.pngd *.html core *~
    
    # -- chpp dependencies
    ------------------------------------------------------
    
    %.html : %.csml
            chpp $< > $
    
    %.d : %.csml
            chpp -M -o $(<:.csml=.html) $< > $
    
    # -- includes
    ---------------------------------------------------------------
    
    -include $(HTML:.html=.d)
    

    Macro Index

    a

  • and
  • apply
  • b

  • bound
  • c

  • case
  • cgiGetParameters
  • cond
  • d

  • define, define
  • defspecial
  • depend
  • disc
  • discard
  • dountil
  • dowhile
  • e

  • encode
  • envAdd
  • envNew
  • envThis
  • equal
  • error, error
  • eval
  • f

  • fchdir
  • fclose
  • feof
  • fgets
  • fgetwd
  • fglob
  • fneweras
  • fopen
  • for
  • foreach
  • foreachkey
  • fpipe
  • fputs
  • frest
  • fstat
  • fwholefile
  • h

  • hash
  • hcontains
  • hcount
  • hdelete
  • hkeys
  • i

  • if, if
  • ifdef
  • ifdefined
  • ifndef
  • ifnotdefined
  • include
  • l

  • lambda
  • lappend
  • ldelete
  • let
  • linsert
  • list
  • listIndexOf
  • listJoin
  • listLeftAccumulate
  • listMap
  • listRightAccumulate
  • listSearch
  • llength
  • locals
  • lsort
  • luniq
  • n

  • not
  • o

  • or
  • outputenable
  • r

  • random
  • replacesubstring
  • s

  • same
  • schr
  • scmp
  • sgsub
  • shexdecode
  • shexencode
  • slength
  • smap
  • smatch
  • snumber
  • sqlClose
  • sqlConnect
  • sqlDatabaseInfo
  • sqlQuery
  • sqlResultColumnInfo
  • sqlResultColumnNames
  • sqlResultData
  • sqlUpdate
  • srange
  • sremovews
  • ssplit
  • ssub
  • stokenize
  • strneq
  • t

  • timeFromString
  • timeHash2UNIX
  • timeNow
  • timeToString
  • timeUNIX
  • timeUNIX2Hash
  • typeof
  • u

  • until
  • v

  • void
  • w

  • w3image
  • w3img
  • w3imgHeight
  • w3imgWidth
  • w3imgX
  • w3imgY
  • w3rgbcolor
  • warning
  • while