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 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:
Temerature today is 10% above normal. => Temperature today is 10% above normal.
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). Macros (see section Macros) 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 holes.
Hashes are ordered collections of arbitrary values indexed by arbitrary scalars, i.e. they establish a so-called key/value mapping.
Macros are what most programming languages call functions. A macro in
chpp
is a body of text which can be parameterized by formal
arguments which are implicitly assigned the values of the actual
arguments in a local scope upon invocation. Thus, having defined a macro
%define(hello,name,Gruess Dich Gott %name!)
calling the macro with the argument Heinz
%hello(Heinz)
is semantically equivalent to
%locals(name,%<name=Heinz>Gruess Dich Gott %name!)
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 the variable name,
e.g. %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 variable name
within angle brackets, e.g. %<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.
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}
.
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.
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 by-value 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 it. 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 assigning 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
subscribable, 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 always copies the assigned value. In other words, there are no references or pointers. However, see section Planned but not yet Implemented Features.
chpp
uses dynamic scoping (see, however, section Planned but not yet Implemented Features). This means that a variable is visible from the time its
declaration is executed until the time its scope level ceases to
exist. This also means that the scope of a variable is not
immediately evident from the source of the program.
A new scope level is created when a macro is called and when the macro
locals
is executed. A macro call introduces a new scope level
which contains all arguments to the macro, while local
creates a
new scope level with arbitrary variable names, which are initialized to
be the empty string. The scope which is the one last created is called
the innermost scope, while the outermost scope, which is always
available, is referred to as the global scope.
Upon variable access all scopes starting with the innermost scope are tested for containing a variable with that name and, if one is found, that variable is used. If no scope containing a variable with that name is found, an error message is produced.
When a value is assigned to a variable, each scope, string with the innermost scope is searched for a variable bearing that name and, if one is found, the value is assigned to the variable in that scope. If the variable is not contained in any scope, a new variable is created in the global scope.
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 double-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
Go to the first, previous, next, last section, table of contents.