chpp
chpp
chpp
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.
chpp
stands for
chpp
does not stand for Chakotay Preprocessor.
chpp
chpp
can be used very well as
chpp
(see section The History of chpp
).
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.
chpp
should not be used for
chpp
should, due to its nature, not be used for
chpp
is not a
fast program. However, this may change in the future
(see section 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.
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
.
chpp
Not much here, yet.
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.
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 the bug down 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}.
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.
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.
Our thanks go to Tatjana Svizensky for proofreading 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.
chpp
This chapter gives a few more practical examples of the use of
chpp
, illustrating a few of its many possible applications.
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,%®s))\ %<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.
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,%®s))\ %<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
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,%®s)!=-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,%®s)>%[mp!=-1], %<current=%replacesubstring(%current,%mp,%slength(%regs[0]), %some(%regs[1]))> )\ %current%'\n\n' )\
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
chpp
.
--help
chpp
command line syntax.
--output filename
-o filename
--include-dir dir
-I dir
-D name=value
chpp
variable name with the value value.
--generate-dependencies
-M
make
like cpp
.
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.
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.
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.
chpp
).
If the file is not found, an error message is produced.
endif
.
endif
.
endif
.
endd
.
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.
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 are strings of arbitrary length (including the length 0).
Lists are ordered collections of arbitrary values indexed by consecutive numbers starting at 0. It follows that lists cannot have gaps.
Hashes are ordered collections of arbitrary values indexed by arbitrary scalars, i.e. they establish a so-called key/value mapping.
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. define
d macros) are usually called macros.
In order to be able to retain values for subsequent use it is necessary to store them in 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.
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}
.
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:
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.
chpp
user, as it employs garbage collection to free
unused memory.
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 "
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 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.
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:
locals
expression. The new environment is set
up to contain bindings for all the variables mentioned as parameters to
locals
. 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 locals
expression.
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,%locals(c,%<c=0>%lambda(%<c=%[c+1]>%c)))\ %<counter=%newcounter()>\ %counter() %counter() %counter() => 1 2 3The parent environment for the environments of the closure invocations is the environment created by
locals
, 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.
for
, foreach
and foreachkey
expressions (see section Special Forms).
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
!
, ~
, -
@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
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 \'
.
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
chpp
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.
%<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
else
,alternative]])
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
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%' ') =>
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
.
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
.
%list()
returns an empty
list while %list(%")
returns a list with one element, which is
the empty string.
%<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')
%<lst=%list(a,b,c)>%ldelete(%&lst,1)%encode(%lst) => %list(%'a',%'c')
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')
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')
1
, if the key key is contained in the hash
hash. Otherwise, returns 0
.
-1
. The
file is opened for reading if mode is omitted or is r
. The
file is opened for writing if mode is w
.
-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
:
%pipe(/bin/sh,-c,ls a*)
uid
gid
size
blksize
blocks
atime
mtime
ctime
All times are given in seconds since January 1, 1970, 00:00:00 GMT. For
detailed description of these values see stat(2)
.
-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,%®s) %regs[1] => 14 jpg
%ssplit(%regex,%string)
is equivalent to
%ssplit(%regex,%string,%lambda(a,b,c,%b))
Example:
%encode(%ssplit(:+,foo::bar:rules)) => %list(%'foo',%'bar',%'rules')
%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')
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
Examples:
%sgsub(ei,HEINZI Deinzi,!,i) => H!NZI D!nzi %sgsub(a+,abaacaaadaaaa,%lambda(r,%slength(%r[0]))) => 1b2c3d4
%substring(0123456789,3) => 3456789 %substring(0123456789,-3) => 789 %substring(0123456789,2,3) => 234 %substring(0123456789,2,-5) => 234
strcmp()
C function with string1
and string2.
%snumber(34,2) => 100010 %snumber(-255,16) => -ff
%<regs=%list()>\ %void(%smatch(%'\.([^.]*)$',alittlepicture.jpg,%®s))%regs[1] => jpg
chpp
does not output anything.
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
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
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
1
if the name name is bound in the current
environment. If not, returns 0
.
%apply(%lambda(a,b,c,my args are %a %b %c),%list(1,2,3)) => my args are 1 2 3
1
if expr is FALSE and 0
if expr is TRUE.
%shexencode(hello world!) => 68656C6C6F20776F726C6421
shexencode
to a string.
%shexdecode(68656C6C6F20776F726C6421) => hello world!
1
if output is enabled, 0
otherwise. Output can be
enabled and disabled with the macro outputenable
.
1
if chpp
was started to generate dependencies (option
--generate-dependencies
), 0
otherwise.
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.
%listSearch(%list(a,bb,ccc,dddd),%lambda(e,%[%slength(%e)>=3])) => 2
equal
to value. Example:
%listIndexOf(%list(a,b,c,d),b) => 1
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')
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)
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))
%listJoin(:,%list(the,quick,brown,fox)) => the:quick:brown:fox
Time values are represented in chpp
by hashes containing values
for some of the following keys:
year
month
day
hour
minute
second
$
), followed by any of the following
characters is treated specially:
$
$
.
d
1
for
the first day of the month.
m
1
for January.
b
B
Y
H
0
to 23
).
M
0
to 59
).
S
0
to 59
).
Example:
%timeToString($d $B $Y,%hash(day,29,month,12,year,1975)) => 29 December 1975
%encode(%timeFromString($d $B $Y,29 December 1975)) => %hash(%'year',%'1975',%'day',%'29',%'month',%'12')
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
.
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.
timeformat
$H:$M:$S
.
dateformat
$Y-$m-$d
.
datetimeformat
$Y-$m-$d $H:$M:$S
.
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').
type
The package `cgi.chh' provides rudimentary support for CGI scripting.
application/x-www-form-urlencoded
(both GET
and POST
) and multipart/form-data
.