Page: (cpl)Top, Next: Keyword Index, Up: CPLcode.net
Documentation for the CPL programming language and its compiler, March 2021. Printable and citable version can be found at arxiv.org/abs/2012.12143 . CPL = Concealed Pointer Lookup CPL = Custom Parameter Lists CPL = Consistent Procedure Linkage CPL = Compiled Programming Language CPL = Conceived by Paolo Luchini
Page: (cpl)Keyword Index, Next: Installation, Prev: Top, Up: Top

Keyword index

Page: (cpl)Installation, Next: Syntax, Prev: Keyword Index, Up: Top


This is CPL, a high-level programming language designed and developed by Paolo Luchini. Copyright 1993-2021 Paolo Luchini CPLcode.net The files that compose the CPL system are located in their own directory, and can be shared among users. This directory is initially named /usr/local/fri in a shared installation or ~/fri in a single user's home, but may be renamed if needed. None of the tools require root privileges nor write permission; it suffices for them to be readable and executable by every interested user. Documentation in info format can be read through the infocpl script. Typing infocpl <keyword> brings you help about the given CPL keyword. A CPL source file is a list of statements, and references other files through USE statements. Consistency is enforced. Compilation is achieved under most circumstances by the single command makecpl <file>. If the curl command is available (on Debian, apt install curl), makecpl also checks for updates. The interactive CPL interpreter (if installed) accepts any CPL expression, statement or system command for immediate execution, either on the command line or in a full-screen editor (F1 key, or edit <file>). Type icpl in a terminal to try it, and copy the icpl.desktop shortcut to your Desktop.
Page: (cpl)automatic updates, Next: multi-user, Prev: Installation, Up: Installation

Automatic updates

The makecpl script checks for updates to itself if the curl program is installed in the system (on Debian, apt install curl) and the network is available. This transparent background check at most takes place once a day, and only while makecpl is being run. If a new version is found, you are offered to upgrade to it. In case the answer is no, you won't be asked again and you will have to download the update manually from the CPLcode.net website. If the answer is later (or any other key), you will be prompted again on the next day. This functionality can be disabled, if undesired, by editing the makecpl script and turning ENABLE_UPDATES to NO.
Page: (cpl)multi-user, Next: Q&A, Prev: automatic updates, Up: Installation

Installation for multiple users

If you unpack the CPL archive while you have write access to the /usr/local/ directory (generally this requires root privileges), the archive gets expanded in /usr/local/fri/ with symbolic links in /usr/local/bin/. This is the simplest way to make CPL simultaneously available to all users of the system. If you have no access to /usr/local/, or prefer to avoid root privileges even if you can have them, a slightly more complex alternative is to go through the normal single-user installation, make sure that your fri directory and all files in it have read permission for the interested group, and then have every other interested user create their own symbolic links by cd-ing to your fri directory and running ./linksetup.
Page: (cpl)Q&A, Next: Syntax, Prev: multi-user, Up: Installation

Common problems and their solutions

makecpl: command not found

If the initial setup did not report any errors, and $HOME/fri/makecpl or /usr/local/fri/makecpl do work, this is a PATH problem. CPL setup makes commands available to the unix shell by placing symbolic links in either $HOME/bin or /usr/local/bin. Please check that these links have been created and are within reach of your PATH. If you have to modify the PATH manually, you may find it easier to put $HOME/fri or /usr/local/fri there. Please see your shell's instructions for how to modify the PATH variable.

My program compiles but does not execute

Is . in your PATH? Many Linux distributions do not by default have your current working directory in the set of automatically searched file paths. Solutions: 1) prefix ./ to the name of your executable, as in ./Helloworld 2) modify your PATH environment variable (look up instructions for your shell) 3) use icpl as your shell (either icpl Helloworld or just icpl and then Helloworld). icpl will automatically compile and execute <yourprogram> if a more recent <yourprogram>.cpl file is found in the same directory, or interpret <yourprogram>.cpl if the .cpl suffix is included.

My program crashes without any error messages

Always put USE rtchecks as the first line of your main program during development. rtchecks transparently adds a number of runtime checks and error messages at the expense of execution speed. You will remove it once your program is debugged.

I want to access icpl from my Desktop

Drag and drop the interactive CPL icon from wherever your fri directory is (typically, /usr/local/fri or $HOME/fri) to your Desktop.

I want to receive automatic updates

Starting with version 2020-12-16, makecpl will automatically offer updates when they become available (see automatic updates). If you see an update on the website for which you did not get a reminder while running makecpl, please check that the curl command is installed (on Debian, apt install curl) and that you have write access to the parent of the fri/ directory. In case you would rather disable this feature, edit the makecpl script and turn ENABLE_UPDATES to NO.

I want to use icpl and/or edit remotely

icpl is perfectly compatible with ssh. Just ssh into your remote system where CPL is installed, and give the icpl command there. Non-Linux ssh clients are ok too. All special and arrow keys have alphabetic equivalents for the case they may be missing or hard to combine on your keyboard. In a pinch, you can log in from your smartphone with ConnectBot or similar.

On my system, edit pops up a different editor

The edit command is used as an alias by more than one editor, including vim and other vi derivatives. You can always access the icpl editor by its unaliased name icpl -edit (with a space), or as edit within icpl, or you can modify your PATH so the edit link to icpl takes precedence.

On my system, edit used to pop up a different editor

If having edit pop up a different editor is your preferred behaviour, just delete the bin/edit symbolic link. You will still be able to access the icpl editor by one of the other methods specified above.

My .cpl files get rejected when sent as email attachments

Some email servers automatically reject files with the .cpl extension because this extension is shared with MS Windows Control PaneL files. This behaviour is unfortunate but we cannot change the server policy; a possible remedy is to rename your files to another (e.g., .txt) extension before sending, and rename them back on the receiving end.
Page: (cpl)Syntax, Next: Functions, Prev: Installation, Up: Top

The CPL programming language syntax

CPL = Concealed Pointer Lookup CPL = Custom Parameter Lists CPL = Consistent Procedure Linkage CPL = Computer Programming Language CPL = Conceived by Paolo Luchini A CPL Source file is composed of nested blocks of statements which take effect in a hierarchical top-down order, much like Pascal or Modula-2. Statements are composed of keywords (Keyword index), identifiers and separators. User-declared identifiers can contain alphabetic and numeric characters plus _ and can be of any length. Upper and lower case are considered distinct. The first character of an identifier must be non-numeric. Separators can be spaces, tabs or comments (Comments) in any number and sequence; at least one separator is required between contiguous words that could otherwise be mistaken for a single identifier. Indenting is advised, to highlight statement blocks, but not enforced. Statements within a block are delimited by either newline or ;, which allows for multi-statement lines without requiring (but permitting) a ; when the statement ends at the end of a line. Multi-line statements can be broken wherever the statement is incomplete: if the given line does not make sense as is, the compiler will automatically treat newline as a separator and try to complete the statement on the next line. Continuation can also be explicitly requested wherever a separator is allowed by putting a \ as the last character before newline. Round, square and curly brackets can be used interchangeably wherever a parenthesis is needed, in either declarations, statements or expressions. (Parentheses) Subroutine and function declarations (Functions) are on the same footing as all other VARIABLE, CONSTANT and TYPE declarations. Contrary to many other languages, declarations (Declarations) and executable statements (Statements) may be freely intermixed in the program flow. The scope of a declaration extends from the position where the declaration appears down to the end of the containing block. It is recommended that declarations appear as close as possible to the position where the declared item is used for the first time, as this enables the compiler to catch a larger number of errors automatically. In most situations, a variable can be declared and initialized in a single statement. CPL has Custom Parameter Lists, meaning that not just commas but any non-ambiguous single character or character sequence may be used as parameter separators in function calls. (Custom Parameter Lists:Functions) Whereas commas will still be the commonest choice for short parameter lists, a remindful choice of separators can make long parameter lists both more understandable and more thoroughly checked by the compiler. Parameters may also be OPTIONAL, and be specified by name rather than by position. Loops and all other flow control statements (Control) implicitly define new blocks within them, just as subroutine declarations do. A block can also be explicitly delimited as a MODULE. A block can contain every declaration that the outermost block can, including further subroutines and modules, but the declared items cease to exist on exit from the block.
Page: (cpl)Comments, Next: Parentheses, Prev: Syntax, Up: Syntax


Comments delimited by ! without a following bracket extend to the end of line. Comments delimited by !( ... !) or ![ ... !] or !{ ... !} can be inserted wherever a space can, and can extend over a few characters or over several lines. The compiler will still match !bracket pairs within comments, and consider the comment finished only when all opened parentheses have been closed.
Page: (cpl)Parentheses, Next: C preprocessor, Prev: Comments, Up: Syntax


Round, square and curly brackets can all be used interchangeably wherever a parenthesis is needed, but the compiler requires that every opening bracket is closed by a matching bracket of the same kind, just as in mathematics. Alternating the three kinds of bracket, while not mandatory, makes for easier reading by the programmer and more effective error catching by the compiler. When round brackets are used in the statement prototypes contained in this manual, it is always understood that the other two styles may be used as well. In such prototypes square brackets denote optional items.
Page: (cpl)C preprocessor, Next: Source file, Prev: Parentheses, Up: Syntax

Macro definition and conditional compilation

C preprocessor #define and #if constructs (:(/usr/share/info/cpp).) are used in a CPL program to define macros and alter the flow of compilation. For file inclusion see USE, INCLUDE, and C interface.
Page: (cpl)Source file, Next: MODULE, Prev: C preprocessor, Up: Syntax

Source files

A CPL program file directly consists of a sequential block of declarations and statements, some of which may in turn contain other blocks, without any need to mark the beginning and end of the complete program other than by the beginning and end of the file itself. The standard ending for CPL source files is .cpl, and is automatically added by the compiler if omitted. Though this ending is unambiguous in unix, it does have a small inconvenience in being banned from some email services because of malicious exploitation in Microsoft Windows Control PaneL files. If you encounter this problem, just rename your .cpl files to some other ending before sending them as email attachments.
Page: (cpl)USE, Next: INCLUDE, Prev: Source file, Up: Source file

Separately compiled program segments

Separately compiled source files can be used with the statement USE <filename> .cpl is automatically appended if <filename> contains no dot. In addition, the directory of the USEd file becomes the present working directory for other references nested in it. The USEd file is logically treated as if it were inserted in the program sequence in place of the USE statement, i.e. just as if INCLUDE were in place. However, in reality the file is either compiled into a separate object file or, if a more recent object file of the same name already exists, not compiled at all. In the latter case the CPL compiler reads the outermost block only from the USEd source file and skips the inner block contents altogether, generating the same effect as a Modula-2 definition file or a C header file. In case actual headers must be generated in order to save space (or to distribute a library without source code) it is possible to USE a source file with empty subroutine bodies, since the compiler would not read the subroutine bodies anyway, provided the object file is artificially given a more recent date (for instance, through (∗manpages∗)touch) If this is not done, subroutines will be recompiled empty, with obviously unwanted results. In an interpreted program (icpl) just as well, USE loads a separately compiled module, whose subroutines can then be executed at compiled speed, and executes its main body, if any, before passing control back to the console.
Page: (cpl)INCLUDE, Next: C interface, Prev: USE, Up: Source file

File inclusion

Files included with INCLUDE filename[(name=value [,name=value])] are treated as if they appeared in the place of the INCLUDE statement during compilation, and are not separately compiled (as in USE). Nonetheless, the directory of the INCLUDEd file becomes the present working directory for other references nested in it. If the optional arguments are present, name becomes an alias for value within the scope of the INCLUDEd file only.
Page: (cpl)C interface, Next: C SECTION, Prev: INCLUDE, Up: Source file

C interface

The directive #include file or #include <file> in addition to being copied into the generated C code has a special meaning. file is expected to contain a C header or program, which is scanned for declarations by the CPL compiler. The declarations and prototypes contained in the C file thus become transparently available to the including CPL program. An include directory path (similar to the -I C compiler directive) can be specified with #includedir path The name of the corresponding object file or other linking options can be transmitted to the linker with #link linking options A C code excerpt may also appear directly in the CPL source file if encapsulated in a C SECTION. The standard libc headers stdlib.h stdio.h fcntl.h math.h limits.h string.h setjmp.h errno.h signal.h are pre-included in all CPL programs and need not (but may) be included again.
Page: (cpl)C SECTION, Next: FORTRAN interface, Prev: C interface, Up: Source file


C code excerpts can be inserted in a CPL program through the statement C SECTION <C code> END C SECTION This has the same effect as recalling with #include a file that contains <C code>. That is, the identifiers declared in the C SECTION become transparently available to the CPL program. As an alternative, C code may also be inserted between <∗ and ∗> as: <∗ C code ∗> In this case, anything that appears between <∗ and ∗> is copied verbatim in the generated C code without any intervention of the CPL compiler. It is up to the programmer to ensure that the resulting code makes sense, so this construction should only be used as a last resort.
Page: (cpl)FORTRAN interface, Next: MODULE, Prev: C SECTION, Up: Source file

Use of FORTRAN subroutine calls

An interface to FORTRAN precompiled subroutines is provided by the statements FORTRANCALL <subrname> ( <parameters> ) and <type> FORTRANFUNCTION <functionname> ( <parameters> ) These have the following effects: <subrname> or <functionname> is copied verbatim into the generated C source, with an appended underscore; all arguments are passed by reference, according to FORTRAN convention; any arrays are replaced by a reference to their first element. The user must take care of the fact that FORTRAN has multidimensional array indices in the reverse order with respect to both C and cpl. Arrays to be passed as such arguments should be declared and used accordingly. For (e.g. LAPACK) library calls that require the number of elements used in actual memory storage for the rows of a matrix as an argument, the pseudofunction STRIDEOF( <ARRAY> ) provides the argument to be passed. Note that argument type checking is suppressed during a FORTRANCALL (as the necessary information is not available). Type checking can be restored, however, by wrapping every FORTRANCALL in a suitable INLINE SUBROUTINE. The name of the object file containing the FORTRAN compiled subroutines must be transmitted to the linker with #link linking options together with any other linking options required (e.g., -lg2c to provide the FORTRAN run time library, if g77 was used as the FORTRAN compiler).
Page: (cpl)MODULE, Next: Functions, Prev: Source file, Up: Syntax

Separately scoped program block

A block of code can be explicitly delimited as MODULE name <block> END name or MODULE <block> END MODULE This construction limits the visibility of variables declared inside the MODULE, and is the basis for information hiding. It replaces, and should be preferred to, called-once-only subroutines. An important difference is, however, that variables declared in a MODULE are static and retain their value across subroutine calls, even if they can only be accessed by subroutines declared in the MODULE itself. Notice also that separate source files recalled with either USE or INCLUDE are not separately scoped unless they are enclosed in a MODULE. Control may be transferred to the end of a named MODULE, just as to the end of a SUBROUTINE, by EXIT. In order to be exported outside, a function declared inside a module must also be declared before the MODULE with the keyword FOLLOWS. Variables or constants can be simply declared outside a MODULE and assigned a value inside it as necessary.
Page: (cpl)Functions, Next: Declarations, Prev: Syntax, Up: Top

Functions and Subroutines

Functions (FUNCTION) and subroutines (SUBROUTINE) have a long and a short declaration form. In long form, a subroutine declaration reads SUBROUTINE name( <parameter declarations> ) <block> END name Correspondingly a function, i.e. a subroutine that returns a value and may appear inside an expression, is declared as <type> FUNCTION name( <parameter declarations> ) <block> END name In short form, a function whose body is composed of a single expression may also be declared as <type> FUNCTION name( <parameter declarations> ) = <expression> A short syntax is also available for subroutines SUBROUTINE name( <parameter declarations> ) = <single-line block> where <single-line block> is a sequence of statements separated by semicolons and terminating at the first newline. CPL has Custom Parameter lists, meaning that not just commas but any non-ambiguous single character or character sequence may be used as parameter separator. In parameter declarations non-alphanumeric separators can be specified verbatim, whereas separators that could be mistaken for identifiers are to be enclosed in (either single or double) quotes. In subroutine and function calls the quotes are omitted, and the compiler enforces conforming separators. Whereas commas will still be the commonest choice for short parameter lists, the adoption of remindful separators can make long parameter lists both more understandable and more thoroughly checked by the compiler. Formal parameter declarations otherwise follow the same syntax as normal variable declarations, and are distinguished both by their position within the parenthesis following the subroutine or function name and by separator matching. Round, square or curly brackets can be used, just as everywhere else (Parentheses). However an important difference exists: whereas general variables are by default VARIABLE, i.e. can be assigned a value multiple times, and must be explicitly declared CONSTANT to become inalterable, formal parameters are by default CONSTANT and only become VARIABLE if explicitly declared so. Parameters can be OPTIONAL, and be specified by name rather than by position. A subroutine returns at the END of the subroutine block or at the EXIT statement. Within functions a variable named RESULT is implicitly declared with the type of the function's result, and can be used just as any other local variable of that type within the body of the function; the function returns the value of the variable RESULT at the time of exit. The declared subroutine is visible from the point of its declaration to the end of the enclosing block, just as all other declarations. This includes the subroutine body itself, and recursive calls are thus automatically allowed.
Page: (cpl)SUBROUTINE, Next: FUNCTION, Prev: Functions, Up: Functions

Subroutine declarations

In long form, a subroutine declaration reads SUBROUTINE name( <parameter declarations> ) <block> END name In short form: SUBROUTINE name( <parameter declarations> ) = <single-line block> where <single-line block> is a sequence of statements separated by semicolons and terminated by the first newline. The keyword SUBROUTINE is also used to declare variables of Subroutine type.
Page: (cpl)FUNCTION, Next: INLINE, Prev: SUBROUTINE, Up: Functions

Function declarations

In long form, a function declaration reads <type> FUNCTION name( <parameter declarations> ) <block> END name In short form, a function whose result can be written as a single expression may also be declared as <type> FUNCTION name( <parameter declarations> ) = <expression> A third alternate syntax, similar to the one used for variables of Subroutine type, has the result type denoted by -> following the parameter declaration: FUNCTION name( <parameter declarations> )-><type> <block> END name When this syntax is used, the result type may be, or contain, an ARRAY type with dimensions depending on the formal parameters themselves (see array parameters). Any formal parameter as well may depend on the formal parameters that precede it. For example: FUNCTION massagearray[ARRAY(∗) OF REAL v]->ARRAY(v.LO..v.HI) OF REAL . Within a function's body, a variable named RESULT is implicitly declared with the type of the function's result, and can be used just as any other local variable of that type within the body of the function; the function returns the value of the variable RESULT at the time of exit. All primitive and compound types are allowed for the result of a function. The statement RETURN <value> is a shorthand for RESULT=<value>; EXIT name. The keyword FUNCTION is also used to declare variables of Subroutine type. The statement RETURN is also used in interactive session.
Page: (cpl)INLINE, Next: FOLLOWS, Prev: FUNCTION, Up: Functions

Inline functions and subroutines

The keyword INLINE prefixed to a SUBROUTINE or FUNCTION declaration (in either long or short form) generates a compile-time macro that is inserted in place of each call to the subroutine. See also INLINE LOOP.
Page: (cpl)FOLLOWS, Next: OPTIONAL, Prev: INLINE, Up: Functions

Function prototypes

In case the prototype of a subroutine must be declared in advance, the construction SUBROUTINE name( <parameter declarations> ) FOLLOWS (or its equivalent for functions) can be used to make the subroutine callable before its body appears. This prototype declaration is only really needed for mutually referencing subroutines (as in Pascal or C) or for subroutines whose body is hidden in a subsequent MODULE. A prototype using FOLLOWS may also be defined for a CONSTANT.
Page: (cpl)OPTIONAL, Next: nested functions, Prev: FOLLOWS, Up: Functions

Optional parameters

The parameter list of a function or subroutine can include optional items. In the function declaration, optional parameters must appear after all ordinary positional parameters (if there are any), separated by the keyword OPTIONAL. Each optional parameter must be assigned a default value in its declaration. Example: SUBROUTINE test(INTEGER x; OPTIONAL INTEGER y=0,z=3; REAL w=3.14) Within the body of the function, optional parameters obey the same rules as ordinary formal parameters; on entry they acquire the value of each corresponding actual parameter if one is specified in the calling statement, or their default value otherwise. In the calling statement, optional parameters follow all standard positional parameters and can appear in any order (or not appear at all); they are identified by name rather than by position. In the above example, test(3,w=1E2,y=1) is a possible calling statement. Optional parameters cannot be used to disambiguate overloaded function names. (See overloading.)
Page: (cpl)nested functions, Next: overloading, Prev: OPTIONAL, Up: Functions

Nested functions

Functions and subroutines exist on a par with variable Declarations and can appear anywhere in a block, another function included. Nested functions are therefore an integral part of CPL. Like all other block contexts, they have access to variables (and functions) declared before them, in the same or in an enclosing block. The present implementation of nested functions translates to nested functions in the generated C code, and is therefore only compatible with C compilers that handle nested functions as an extended feature (gcc is one of them).
Page: (cpl)overloading, Next: array parameters, Prev: nested functions, Up: Functions

Function name overloading

Function overloading is an inherent feature of CPL. Since type checking is enforced on function arguments, functions and subroutines of the same name but different (not OPTIONAL) arguments can be distinguished from each other, and are transparently handled. This also works for runtime specified types: see DYNAMIC.
Page: (cpl)array parameters, Next: RESULT, Prev: overloading, Up: Functions

Passing array parameters and their dimensions

Functions that accept array parameters of variable dimensions are a necessity that is handled differently in different languages, especially when error checking is desired. In CPL they are handled as follows. The special dimension ∗ can be used to declare a POINTER TO ARRAY that implicitly passes the bounds of the ARRAY together with its address (see ARRAY). This is the most compact notation, as in e.g. REAL FUNCTION NORM[ARRAY(∗) OF REAL v]=SUM v(i)^2 FOR ALL i (same as the builtin NORM function). When this notation is used, the index bounds of the actual parameter are accessible in the function's body by the LO and HI builtins (in the above example, these would be v.LO and v.HI), and its number of elements by the LENGTH function, just as for all ARRAYs. A formal parameter OF type ARRAY(∗) also accepts a variable of its base type as the actual parameter, and transforms it into an ARRAY with dimension (0..0). For more elaborate needs, for instance when two arrays must have equal dimensions, CPL allows INTEGER formal parameters to be used in the declaration of subsequent compound parameters. An example would be SUBROUTINE dosomething[INTEGER n; ARRAY(1..n) OF REAL a,b,c] ..... END dosomething Within the body of such a subroutine or function, a, b and c are all assumed to have the same dimensions 1..n; this is similar to the convention adopted in languages (like C) that treat arrays like pointers with no special provision, but with the difference that in CPL the call of such a subroutine verifies that the actual parameters indeed have the specified dimensions at run time, and throws an error if they don't and rtchecks is on. The same is true if the dimension is not simply the n parameter but an expression involving n, or if the dimension is extracted from the bounds of a previous array as in SUBROUTINE dosomething[ARRAY(∗) OF REAL a; ARRAY(a.LO..a.HI) OF REAL b,c] ..... END dosomething Additionally, the return type of a FUNCTION can as well be an ARRAY involving a formal parameter in its dimensions. An example is: FUNCTION dosomething[ARRAY(∗) OF REAL a,b]->ARRAY(a.LO..b.HI) OF REAL ..... END dosomething After this declaration the dimensions of the return type are known to the compiler, and can either be checked by rtchecks or used to allocate a suitable ARRAY CONSTANT.
Page: (cpl)RESULT, Next: Declarations, Prev: array parameters, Up: Functions
Formal name of the result of a FUNCTION inside of the function's body. RESULT is implicitly declared as a variable of the type indicated in the FUNCTION's declaration and may be used and assigned like any other variable. On exit it becomes the function's value returned to the calling program.
Page: (cpl)Declarations, Next: Statements, Prev: Functions, Up: Top

Variables, types and constants

Variables, types and constants are denoted by identifiers introduced through the relevant declarations as follows. Identifiers cannot be duplicated as long as a previous declaration is in effect (in either the same or an enclosing block). An identifier which was not previously declared in the same or an enclosing block and does not coincide with a CPL keyword (see Keyword index) is denoted as a newid below. A general declaration has the form: [<constorvar>] <type declarator> <vardeclaration> [, <vardeclaration>] where <vardeclaration> denotes newid[<postfix modifier>][= <value>] and <constorvar> may be either CONSTANT or VARIABLE or none. CPL is a type-checking language and requires that every VARIABLE be declared before it is used. It can be initialized at the same time by the optional = <value> field. However, the declaration of CONSTANTs and TYPEs may be implicit: newid = <value> declares a CONSTANT, whose value cannot be modified later in the program and whose type is the type of the r.h.s. newid = <compound type> declares a TYPE identifier to denote the compound type on the r.h.s. A newid used as the running index of a FOR loop is implicitly declared CONSTANT in the scope of the loop.
Page: (cpl)CONSTANT, Next: VARIABLE, Prev: Declarations, Up: Declarations


The word CONSTANT in Declarations specifies that the declared item cannot be modified. Thus a CONSTANT is not the same thing as a compile-time constant, that is an expression whose value can be known at compile time, but more generally a value which cannot appear on the left side of an assignment or have its address assigned to a POINTER. The value of the CONSTANT is usually specified in the declaration itself but occasionally later. When this happens the constant declaration takes the role of a constant prototype, and may be optionally followed by the keyword FOLLOWS. (For instance, a constant may be declared outside a MODULE, but its value only be known inside the module itself; or the value may be READ as input.) If the constant is not initialized in the declaration itself, it is understood that it will be assigned once only in the course of its block. In alternative, a statement of the form newid = value implicitly declares a CONSTANT whose type and value are the type and value of the r.h.s. (see, however, CONSTANT:Concealed Pointer Lookup if the address of the r.h.s. is desired instead). If the r.h.s. of a CONSTANT declaration is known at compile time (what is denoted as a compile-time constant), the new identifier becomes just an alias for it and no storage is assigned. This also applies to STRINGs. An alias for any expression, even one which does not represent a compile-time constant, can always be defined through the statement newid == expression (see Delayed assignment). Subroutine formal parameters are by default CONSTANT, with the actual parameter as their value, and must be explicitly declared VARIABLE if they are to be reassigned within the subroutine's body.
Page: (cpl)VARIABLE, Next: BOOLEAN, Prev: CONSTANT, Up: Declarations


The word VARIABLE in Declarations specifies that the declared item is a regular variable and can be modified. In fact, this is the default and the word VARIABLE is optional in ordinary declarations. However, subroutine formal parameters are by default CONSTANT, with the actual parameters as values, and must be explicitly declared VARIABLE if they are to be reassigned within the subroutine's body. Notice that the value assigned to a VARIABLE parameter is invisible after the subroutine returns. In order to change the value of variables which exist outside the subroutine, the corresponding formal parameter must be declared a POINTER. Indeed VARIABLE behaviour of a formal parameter is seldom needed, and making CONSTANT the default improves error catching. When a VARIABLE identifier is used in an expression, it initially stands for the variable's address in memory, and thus can be used wherever a POINTER is expected; the same identifier gets automatically converted to the variable's value where needed through Concealed Pointer Lookup.
Page: (cpl)BOOLEAN, Next: CHAR, Prev: VARIABLE, Up: Declarations


Primitive type declarator for boolean logical variables translated into C type int. (Declarations) BOOLEAN values are denoted as YES or TRUE and NO or FALSE, and written as Y and N. On reading any complete word starting with T, Y, t or y is read as YES, any word starting with F, N, f, n as NO.
Page: (cpl)CHAR, Next: INTEGER, Prev: BOOLEAN, Up: Declarations


Primitive type declarator for character variables translated into C type char. (Declarations) Where needed, a single-character STRING literal is implicitly converted to a CHAR constant, which is in turn implicitly converted to an INTEGER ASCII code. INTEGER to CHAR conversion must be explicit (CHAR:Builtin).
Page: (cpl)INTEGER, Next: REAL, Prev: CHAR, Up: Declarations


Primitive type declarator for integer variables, translated into C type int. Can be implicitly converted to REAL. REAL to INTEGER conversion must be explicit (INTEGER:Builtin). See also INTEGER operator.
Page: (cpl)REAL, Next: SINGLE, Prev: INTEGER, Up: Declarations


Primitive type declarator for real variables, translated into C type double. See also REAL operator, REAL:Builtin.
Page: (cpl)SINGLE, Next: TYPE, Prev: REAL, Up: Declarations


Primitive type declarator for single-precision real variables, translated into C type float. Can be implicitly converted to REAL. REAL to SINGLE conversion must be explicit (SINGLE:Builtin).
Page: (cpl)TYPE, Next: STRUCTURE, Prev: REAL, Up: Declarations

Declaration of new type identifiers

A new type identifier can be declared as either TYPE newid = <compound type> or just newid = <compound type> The new identifier can be used in subsequent declarations in the place of the r.h.s. The TYPE of an already declared variable or expression can always be recovered from the pseudo-function TYPEOF(<variable>) which can appear wherever a type name can. (E.g., it can be assigned to a type identifier or directly used to declare further variables or in the construction of a compound type.) When applied to a POINTER or STORED type, this function returns the underlying base type. In order to test whether a variable is of a given type the IS comparison operator is available. See also Type identity. The memory size in bytes of a type or variable (useful when using the C interface or to estimate the memory requirements of the program) can be obtained from the pseudo-function SIZEOF(<typeorvariable>)
Page: (cpl)ENUM, Next: STRUCTURE, Prev: TYPE, Up: Declarations

Enumerated type

Not really a compound type, ENUM (much like C enum) variables take a limited set of explicitly enumerated values. The syntax of this type declaration is ENUM(<id1>,<id2>,...) and can directly declare variables or be assigned to a new type name like all other TYPEs. <id1>, <id2>, etc. are thereby declared as constants to which variables of the given type can be set or compared. The purpose of ENUM variables is to flag a specified set of alternatives, while forbidding out-of-range values which could arise if INTEGERs were used for this purpose. No other operations than assignment and comparison are defined on them.
Page: (cpl)STRUCTURE, Next: ARRAY, Prev: ENUM, Up: Declarations


The type declarator for structures is STRUCTURE(<field declarations>) Field declarations follow exactly the same syntax as general variable declarations. For instance, to declare a structure containing one integer field named i and two real fields named x and y, one would write: STRUCTURE(INTEGER i; REAL x,y) Structure elements are selected by either the traditional dot notation structure.field or the selector-function notation field(structure) Conversely, any function of a single argument can be called either as function(argument) or as argument.function (familiar to those who are accustomed to object-oriented languages). A structure declaration may also contain one or more anonymous fields. If a type declarator appears alone, without any following variable declaration, an anonymous field is declared to which the given structure can be implicitly converted. For instance, after the declaration: TYPE t1=STRUCTURE(REAL a,b) STRUCTURE(t1; INTEGER n) var var includes a field of type t1 of which var.a denotes the a field. Anonymous structure fields are the CPL way of defining objects that inherit the structure of other more general objects and add their own specific fields. When these objects are accessed as arguments to functions or subroutines, the appropriate function among those with the same name and different arguments is automatically selected. See also overloading, DYNAMIC.
Page: (cpl)variable size, Next: ARRAY, Prev: STRUCTURE, Up: STRUCTURE

Variable size STRUCTUREs

STRUCTUREs may be defined that contain ARRAYs the size of which will only be known at run time. Such STRUCTURE TYPEs are declared with one or more INTEGER CONSTANTs among their fields. When a variable of the given type is allocated, either by using the name assigned to the TYPE in a Declaration or in a NEW statement, the values following the type name in parentheses (like arguments of a function) are assigned to these CONSTANTs by order, and are simultaneously used to allocate a chunk of memory of the appropriate size. Being CONSTANTs, these fields cannot be altered in the subsequent life of the allocate STRUCTURE. Example: TYPE twoarrays=STRUCTURE[REAL x; INTEGER CONSTANT n POINTER TO twoarrays next ARRAY(1..n) OF INTEGER k; ARRAY(-n..2∗n) OF REAL data] ............. twoarrays(8) mytwoarrays mytwoarrays.next=NEW twoarrays(2∗mytwoarrays.n) Notice that this syntax is intentionally similar to the syntax for FUNCTIONs that contain ARRAYs of runtime-specified size as arguments. Compare array parameters. In fact, the set of parameters of a given function may usefully be thought of as a structure, although one that is invisible to the programmer and only exists as a conceptual model.
Page: (cpl)ARRAY, Next: STRUCTURED, Prev: STRUCTURE, Up: Declarations


The prefix type declarator for arrays is ARRAY(dimension[,dimension]) OF <type> The postfix declarator is just (dimension[,dimension]). Each dimension is specified either as <lower bound>..<upper bound> or as <upper bound> only, in which case the lower bound is taken to be zero. Specifying multiple dimensions, like in ARRAY(dimension1,dimension2) OF, is a shorthand for ARRAY(dimension1) OF ARRAY(dimension2) OF Therefore the order in which the elements of the array are stored in memory is similar to C and Pascal and different from FORTRAN. Variable dimensions are allowed wherever constant dimensions are; the compiler automatically decides for static or dynamic allocation of memory space as needed. Arrays can contain elements of any primitive or compound type and can in turn appear as fields in structures or be pointed at by pointers. When an ARRAY is assigned in a single = statement to another of the same base type and number of elements, its lower bound is implicitly realigned. A similar realignment takes place for function arguments. Assigning to a different base type triggers a compilation error. Mismatch in the number of elements is flagged by rtchecks. The special dimension ∗ can only appear in the declaration of an ARRAY formal parameter or of a POINTER TO ARRAY, and declares a special type of pointer that remembers the current dimensions of the array together with its address. A formal parameter or pointer of this type can be dynamically assigned arrays of different length and still provide runtime range checking and FOR ALL looping when requested. The actual upper and lower bounds of an array index can always be recovered through the functions HI and LO, and the total number of elements through the function LENGTH. In the case of multidimensional arrays, these functions apply to the first index only; subarray selection can be used in order to extract the bounds of other indices. Alternatively, LO1, LO2, LO3 and HI1, HI2, HI3 can be used to refer to the first three indices. ARRAY elements are accessed through INTEGER indices in brackets as usual, e.g.: REAL arr(1..8,2..9) WRITE arr(4,5) All three kinds of bracket are allowed (Parentheses). It should be noted that the above declaration is a shorthand for ARRAY(1..8) OF ARRAY(2..9) OF REAL arr Therefore, a notation such as arr(4) is allowed, and has type ARRAY(2..9) OF REAL. This is a particular case of a subarray. By the same token, the second line might also have been written as WRITE arr(4)(5)
Page: (cpl)HI, Next: LO, Prev: ARRAY, Up: ARRAY
HI: upper bound of an array index The function HI applied to an ARRAY returns the upper bound of its first index. HI1 is a synonym for HI; HI2 and HI3 return the upper bound of the second and third index respectively. Further indices can be accessed by subarray selection. The argument of HI is implicit (i.e., HI may be written without any argument as if WITH were in action) when HI is used inside the index itself as in arr(HI-3) or in the specification of a FOR loop.
Page: (cpl)LO, Next: LENGTH, Prev: HI, Up: ARRAY
LO: lower bound of an array index The function LO applied to an ARRAY returns the lower bound of its first index. LO1 is a synonym for LO; LO2 and LO3 return the lower bound of the second and third index respectively. Further indices can be accessed by subarray selection. The argument of LO is implicit (i.e., LO may be written without any argument as if WITH were in action) when LO is used inside the index itself as in arr(LO+2) or in the specification of a FOR loop.
Page: (cpl)LENGTH, Prev: LO, Up: ARRAY
LENGTH: number of elements of an array The function LENGTH applied to an ARRAY returns its number of elements. LENGTH is equivalent to HI-LO+1. In a multidimensional ARRAY, the number of values of the first index only is returned.
Page: (cpl)STRING, Next: STRUCTURED, Prev: ARRAY, Up: Declarations


Type declaration STRING is a synonim for ARRAY(∗) OF CHAR, and can be used wherever the last can. In particular, a STRING can be subscripted or subarray extracted, and its bounds and length are available through functions LO, HI, and LENGTH. STRING valued constants (which are always indexed starting from 0) can be generated either at compile time, as quoted strings of characters, or dynamically through String concatenation, and can either be given a name through implicit:CONSTANT. declaration or passed as actual parameters to function calls and further string concatenations. Quoted strings of characters, also known as literals, are delimited by either double or single quotes, in matching pairs. A single quote may appear inside a literal delimited by double quotes and vice versa. In addition, C escape sequences are literals, but contrary to C must be unquoted. The empty string is also a valid (zero-character-long) STRING value. Finally, in bash-like syntax, multi-line literals may be introduced by << DELIMITER and ended by DELIMITER alone on a new line, where DELIMITER can be any word that is not to be part of the literal itself. Where appropriate, a single-character literal is implicitly converted to a CHAR constant, which is in turn implicitly converted to an INTEGER ASCII code. Strings may also be concatenated among themselves and with variables, see conversion:String. Standard C functions returning a char∗ ((/usr/share/info/libc) String and Array Utilities) are also interpreted as generating a STRING value. Just as for all C interface calls, it is the programmer's responsibility to ensure that such C library functions are used consistently and to free any possible malloc-ated space where appropriate. As an experimental feature, and an exception to the general rule for ARRAY(∗), STRING VARIABLEs may also be declared and assigned a value.
Page: (cpl)STRUCTURED, Next: POINTER, Prev: STRING, Up: Declarations


A structure (STRUCTURE) of like elements that may also be accessed as a linear array (ARRAY) of such. Its type declarator is STRUCTURED ARRAY(<field declarations>) OF <type> where <field declarations>, separated by commas, are either single newids or newids followed by (possibly multiple) array dimensions. For example, STRUCTURED ARRAY(a,b,c(1..4,1..2)) OF REAL stra defines a STRUCTURE named stra with fields a, b of type REAL and c of type ARRAY(1..4,1..2) OF REAL. At the same time, stra may also be accessed as an ARRAY(0..9) OF REAL, with automatically determined bounds. This is particularly useful when the new type has to be subjected to ARRAY operations. For instance, one can define a Vector type with fields x,y,z and then perform vector operations on variables of this type.
Page: (cpl)POINTER, Next: INTO, Prev: ARRAY, Up: Declarations


The type declarator for pointers is POINTER TO (prefix) or ^ (postfix) (Postfix). For example POINTER TO INTEGER a,b; INTEGER c and INTEGER a^,b^,c are equivalent declarations. The notation for pointer dereferencing is described in Concealed Pointer Lookup. A null pointer is denoted by the predefined constant NULL <type> which translates to the C constant NULL (generally the address 0) with type <type> if this is a POINTER type, or POINTER TO <type> otherwise.

Uncommitted POINTERs

A pointer can also be declared with reference to a (yet) uncommitted type name, by the notation POINTER TO <newid> It is implied that <newid> will later denote a type identifier. Variables, constants and structure fields of uncommitted pointer type can be declared, assigned and compared to each other like all POINTERs but not dereferenced. They become POINTERs in full rights as soon as a declaration for type <newid> is encountered. Uncommitted pointers acquire a special meaning if used as formal parameters or function results (in which case no explicit declaration of type <newid> shall follow): a formal parameter of uncommitted pointer type can receive an actual parameter of any POINTER type (except POINTER TO ARRAY(∗), which needs special treatment) as argument, thus allowing the coding of subroutines for the manipulation of generic pointers. Type checking will still be enforced based on the equality of type name. Of course, uncommitted pointers of this kind can never be dereferenced within the body of the subroutine itself. See also Memory allocation:NEW, Object-oriented features:DYNAMIC.
Page: (cpl)INTO, Next: DYNAMIC, Prev: POINTER, Up: Declarations

Pointer arithmetics

Pointer arithmetics is the most powerful feature C has in common with assembly language. It is, however, also one of its weakest points as far as type checking is concerned. For these reasons CPL only allows restricted forms of pointer arithmetics. One of these is subarray extraction. The other is the type declarator POINTER INTO <array> which defines a pointer that can be decremented or incremented provided it stays within the bounds of ARRAY <array>. Arithmetics on such a pointer is allowed just as if it were an ordinary INTEGER; dereferencing is not implicit, as for ordinary POINTERS would be, but is obtained by using it as an index into its base array. For instance: REAL R(10) R(5)=0.5 POINTER INTO R ip ip=3 ip=ip+2 WRITE R(ip) In other words, code is written just as if an INTEGER index were used, but the program is compiled to use a pointer instead. This can sometimes improve performance, especially in short loops that perform few repetitive operations.
Page: (cpl)DYNAMIC, Next: IS, Prev: INTO, Up: Declarations

Object-oriented features

CPL is not an object-oriented language in the sense that functions are supposed to be declared within object class declarations, and intentionally so. Object-like behaviour is achieved through overloaded function names (Function:overloading.) and implicit type conversion (anonymous:STRUCTURE). In addition, situations where the type of an object is not fixed at compile time are catered for by dynamic pointers, pointers that can reference and provide type checking for objects whose type will only be known at run time. These are declared by the type declarator DYNAMIC POINTER A function call in which such a pointer appears is transparently switched at run time to the function defined for the type of the actual arguments, or an error is thrown if no such function exists. Pointers to variables of any type except ARRAY(∗) and STORED can be assigned to a DYNAMIC POINTER. If the scope of assignment is to be restricted, the declaration becomes DYNAMIC POINTER TO <type declarator> In this case only types which can be implicitly converted to <type declarator> are acceptable for the variable pointed to. The actual type a dynamic pointer points to can be tested by the IS operator.
Page: (cpl)IS, Next: Postfix, Prev: DYNAMIC, Up: DYNAMIC

Type comparison

The actual type of the variable pointed to by a dynamic pointer (or, for that matter, of any variable) can be tested by the BOOLEAN expression <pointer> IS <type> For instance, in DYNAMIC POINTER dyn ............ IF dyn IS INTEGER THEN ............ END IF the IF block is executed only if the dynamic pointer dyn points at run time to a variable of INTEGER type. In addition, the IS test has the side effect of casting a dynamic pointer to an ordinary pointer of the tested type when the test succeeds, thus allowing its use as a regular variable. That is, in the above example dyn is implicitly converted to a POINTER TO INTEGER for the scope of the IF block. A restricted dynamic pointer may always be implicitly converted to its base type, as compatibility is enforced at compile time.
Page: (cpl)Postfix, Next: Subroutine type, Prev: DYNAMIC, Up: Declarations

Postfix type modifiers

A postfix ^ denotes either pointer dereferencing (^:Concealed Pointer Lookup) or a short form of the POINTER declaration. For instance INTEGER a^,b^ is a shorthand for POINTER TO INTEGER a,b A postfix form of the ARRAY declaration ARRAY(1..10) OF REAL a,b,c is REAL a(1..10),b(1..10),c(1..10) When both a postfix modifier and a prefix type declarator are simultaneously present in a declaration, they represent the compound type that is obtained by prepending the postfix modifier to the type declarator. For example STRUCTURE(INTEGER x,y) A^(10) is equivalent to POINTER TO ARRAY(10) OF STRUCTURE(INTEGER x,y) A
Page: (cpl)Subroutine type, Next: Type identity, Prev: Postfix, Up: Declarations

Variable call to a subroutine or function

A subroutine variable may be specified through the type declarator SUBROUTINE( <parameter declarations> ) A function variable may be specified through the type declarator FUNCTION( <parameter declarations> )-><type> or else <type> FUNCTION( <parameter declarations> ) the first form being mandatory when the result <type> is a pointer type and thus ambiguity can arise. These type declarators can be used, just like all other type declarators, to declare variables and formal parameters, or as a part of new compound types. In addition to being assigned or compared to each other, subroutine variables can be assigned the name of a compatible subroutine as their value or as the actual parameter, and can later be called with the same syntax as normal (constant) subroutines and functions.
Page: (cpl)Type identity, Prev: Subroutine type, Up: Declarations

Type identity

When are two VARIABLEs of the same TYPE? Pascal-like strongly typed languages only consider variables of the same type when they are declared by the same typename or they appear in the same compound declaration. In other words, repeated compound declarations are not considered the same type even when they are equal, just as are not considered the same type two different typenames with similar definitions. C, on the other hand, handles typenames as bare aliases, and compares their definitions directly. None of these languages has runtime-dimensioned arrays. CPL takes the C approach when possible, and compares type definitions, but explicitly considers two different typenames different even when they correspond to the same declaration. Generally, it is good practice to declare variables that must be of the same type in a single declaration, or through a single typename, or through TYPEOF. This is particularly true with runtime-dimensioned arrays (ARRAYs whose bounds are specified by a VARIABLE and are only known at run time). In fact, since a VARIABLE may take on different values in different parts of the program, multiple declarations of this type are necessarily considered different. In order to test whether a variable is of a given type the IS comparison operator is available.
Page: (cpl)Statements, Next: Control, Prev: Declarations, Up: Top

Executable statements and expressions

Page: (cpl)Expressions, Next: Assignment, Prev: Statements, Up: Statements


Page: (cpl)INTEGER operator, Next: REAL operator, Prev: Expressions, Up: Expressions

INTEGER operators: + - ∗ DIV MOD

DIV is the INTEGER division operator, and produces a rounded-down INTEGER quotient. / is reserved for REALs, and produces a REAL result even when the operands are INTEGERs. MOD is the modulo infix operator. If a and b are INTEGERs, a MOD b represents the remainder of the integer division of a by b. Notice on the truncation of integer division: CPL requires that a MOD b must always be a positive number, regardless of the sign of a/b. This is so that modular arithmetics can be applied to negative as well as to positive numbers, for example in array index manipulations. Since b∗(a DIV b) + a MOD b = a, consistently a DIV b must be rounded towards minus infinity, or a DIV b = FLOOR(a/b). Care should be taken when porting programs from languages that truncate negative division, such as FORTRAN or C. Operators following the C convention of truncating integer division and signed modulo are available, if needed, as CDIV and CMOD respectively.
Page: (cpl)REAL operator, Next: Comparison, Prev: INTEGER operator, Up: Expressions

REAL operators: + - ∗ / ^ (or ∗∗)

An infix ^ (or ∗∗) is the exponentiation operator. Postfix ^ is the pointer dereferencing operator. Also prefix ^:Concealed Pointer Lookup.
Page: (cpl)Comparison, Next: BOOLEAN ops, Prev: REAL operator, Up: Expressions

Comparison: > >= < <= = # IS

The equality and inequality operators apply to any type, including pointers. Since pointers are implicitly dereferenced (Concealed Pointer Lookup), an ambiguity may arise as to whether the values or addresses of the two sides must be compared. The comparison generally takes place at the lowest dereferencing level at which types match, but the comparison of two constant addresses is not allowed (so that A = B, where A and B are two simple variables, denotes the comparison of the values of A and B rather than their addresses). Explicit type cast or pointer dereferencing may at times be necessary, and is always advised, where ambiguities exist. The type-testing operator IS is described in Object-oriented features:DYNAMIC.
Page: (cpl)BOOLEAN ops, Next: Conditional expressions, Prev: Comparison, Up: Expressions

BOOLEAN operators

Page: (cpl)AND, Next: OR, Prev: BOOLEAN ops, Up: BOOLEAN ops
AND: boolean AND operator Takes precedence over OR but not NOT. AND may also appear in a FOR clause (FOR AND) and in READ.
Page: (cpl)OR, Next: NOT, Prev: AND, Up: BOOLEAN ops
OR: boolean OR operator Has lower precedence than either AND or NOT. OR may also appear in a READ statement.
Page: (cpl)NOT, Prev: OR, Up: BOOLEAN ops
NOT: boolean NOT operator Has higher precedence than either AND or OR.
Page: (cpl)Conditional expressions, Next: Bitwise, Prev: BOOLEAN ops, Up: Expressions

Conditional expressions

IF <BOOLEAN> THEN <expr1> ELSE <expr2> is an expression that takes the value of <expr1> if <BOOLEAN> is true and <expr2> if false. <expr1> and <expr2> must evaluate to values of the same type (or implicitly convertible to the same type) and this is the type of the result, which can be embedded in a larger expression like any other operator. Notice that the ELSE part is mandatory, as otherwise the value would be undefined. See also the IF statement.
Page: (cpl)Bitwise, Next: String, Prev: Conditional expressions, Up: Expressions


The C-like notation & | >> << is also available for BITAND BITOR RSHIFTED LSHIFTED. ^ and ~ are reserved as the exponentiation:REAL operator. and the placeholder:Assignment.
Page: (cpl)String, Next: Looping operator, Prev: Bitwise, Up: Expressions

String concatenation

STRINGs written one after the other with no intervening operator (whitespace is allowed, but newlines must be escaped), either in literal (quoted) form or through their names, are concatenated into a single STRING. If all of the STRINGs are literals (i.e., known at compile time), the result will also be a literal, otherwise it will be a dynamically allocated ARRAY(∗) OF CHAR which is transparently freed at the end of the enclosing code block.

String conversion

In addition to STRINGs, values of different type may appear in a concatenation, provided the first item is an actual STRING (possibly ); they are then implicitly converted to strings just as if they appeared in a WRITE statement. Please pay attention that a numeric parenthesis that is concatenated after a string will be interpreted as an ARRAY subscript, since the STRING is a legitimate ARRAY OF CHAR, unless space is placed between the string and the parenthesis. Conversion of a string into a numeric value must be handled explicitly through (/usr/share/info/libc)String. When a string literal is defined in << DELIMITER notation (STRING), a variable included as DELIMITER <variable> DELIMITER is concatenated within the string being defined just as above (whereas DELIMITER <newline> ends the definition).
Page: (cpl)Looping operator, Next: Builtin, Prev: String, Up: Expressions

Looping operators

The four operators SUM PRODUCT MAX MIN embody a loop to denote respectively the sum, product, maximum and minimum of an expression over a running index. ARGMAX and ARGMIN act like MAX and MIN but return the value of the index. They are constructed as SUM <expression> FOR <for clause> and return a value of the same TYPE as <expression>. <for clause> can be of any form that is allowed in a FOR loop. For example: WRITE (MAX SIN(x) FOR x=0. TO 2 BY 0.5)+(PRODUCT arr(n) FOR ALL n) See also MAX, MIN.
Page: (cpl)Builtin, Next: subarray, Prev: Looping operator, Up: Expressions
Page: (cpl)ABS, Next: CEILING, Prev: Builtin, Up: Builtin
ABS: absolute value The function ABS returns the absolute value of an INTEGER, REAL or COMPLEX scalar number, or an ARRAY of those.
Page: (cpl)CEILING, Next: FLOOR, Prev: ABS, Up: Builtin
CEILING: larger or equal integer The function CEILING returns the larger or equal INTEGER to a given REAL value.
Page: (cpl)FLOOR, Next: MAX, Prev: CEILING, Up: Builtin
FLOOR: lesser or equal integer The function FLOOR returns the lesser or equal INTEGER to a given REAL value.
Page: (cpl)MAX, Next: MAXABS, Prev: ABS, Up: Builtin
MAX: maximum The function MAX(argument [,argument]) accepts any number of scalar arguments and returns their maximum. The function MAX(array) returns the maximum of the elements of the array. The function ARGMAX(array) returns the first index where the maximum is found. MAX or ARGMAX without a following bracket is a Looping operator.
Page: (cpl)MAXABS, Next: MIN, Prev: MAX, Up: Builtin
MAXABS: maximum absolute value The function MAXABS(array) returns the maximum absolute value of the elements of the array.
Page: (cpl)MIN, Next: NORM, Prev: MAXABS, Up: Builtin
MIN: minimum The function MIN(argument [,argument]) accepts any number of scalar arguments and returns their minimum. The function MIN(array) returns the minimum of the elements of the array. The function ARGMIN(array) returns the first index where the minimum is found. MIN or ARGMIN without a following bracket is a Looping operator.
Page: (cpl)NORM, Next: RAND, Prev: MIN, Up: Builtin
NORM: squared absolute value The function NORM returns the squared absolute value of either a number or a whole array.
Page: (cpl)RAND, Next: GAUSS, Prev: MIN, Up: Builtin
RAND: uniformly distributed in (0,1) random REAL number The function RAND takes no argument. It is based on the INTEGER C library function rand, whose seed can be changed with srand ((∗manpages∗)rand).
Page: (cpl)GAUSS, Next: ROUND, Prev: RAND, Up: Builtin
GAUSS: gaussian distributed random REAL number with variance 1 The function GAUSS takes no argument. It is based on RAND, and the analytic formula (see Knuth, The Art of Computer Programming, chapter on random numbers): GAUSS = SQRT{-2∗LOG[RAND()]}∗COS[PI∗RAND()]
Page: (cpl)ROUND, Prev: GAUSS, Up: Builtin
ROUND: nearest integer The function ROUND returns the nearest INTEGER to a given REAL value.
Page: (cpl)subarray, Next: compound index, Prev: Builtin, Up: Expressions

Subarray selection

A portion of an array can be selected by specifying new dimensions. For example, if the declaration is REAL arr(1..10), the notation arr(2..5) denotes an array formed by the elements from 2 to 5 of array arr (which has larger dimensions). The whole range can also be selected, by the special dimension ∗. For instance, if the declaration is REAL arr(1..7,-2..2,3..8), arr(2..5,∗,6) denotes a two-dimensional array formed by selecting the range 2..5 of the first index and the whole range of the second index of array arr, with the third index set equal to 6. The most general subarray selection can also specify an offset and one or more strides. The notation for this is: arr(a+b1∗(<newdim1>)[+b2∗(<newdim2>)...]) where a, b1, b2... are INTEGERs and <newdim> is either a subrange l..h or ∗. The meaning of this notation is that the subarray must be formed of those elements of the original array arr whose index is the specified linear function of the new index or indices. The latter are either constrained in the explicitly indicated range l..h or in the maximum admissible range ∗. In fact, a general linear expression may be specified, with terms in any order and the possible occurrence of parentheses. Another form of subarray selection is index permutation, which in the particular case of a two-dimensional array becomes matrix transposition. arr(∗<n>,...) denotes the array obtained from arr by making the <n>-th index become the first and shifting forward all the others. Thus the transpose of a matrix A is A(∗2,∗), or just A(∗2); the alternative notation TRANSPOSED(A) is also provided. <n> is currently limited to 2 or 3. Finally, a subarray can be extracted from an ARRAY of STRUCTUREs. If the declaration is, for instance, ARRAY(1..10) OF STRUCTURE(INTEGER i; REAL r) arr then by a straightforward notation arr.r is the ARRAY(1..10) OF REAL composed of all the r fields of each element of arr. Thus, for instance, arr(5).r and arr.r(5) denote the same REAL field. arr.r is an ARRAY in its own right; it can be copied, passed as a parameter or subjected to further subarray selections. It should be noted that all subarray selections are just address manipulations: no element of the array is ever moved and the selected subarray can appear on the left as well as on the right side of an assignment and can be passed as a POINTER TO ARRAY(∗) parameter to a function or subroutine. No computational penalty is incurred because of the sheer size of either the original or the selected array, but on the other hand any modifications of the subarray also affect the original. All subarray selections can also be applied to STORED ARRAYs and STRUCTURED ARRAYs.
Page: (cpl)compound index, Next: ARRAY operations, Prev: subarray, Up: Expressions

Compound index

This CPL address manipulation allows a multidimensional ARRAY to be accessed sequentially through a single index, for instance in order to apply linear algebra to it. The syntax for this operation is a sequence of as many ∗ as indices are to be compounded. For example if V is defined as ARRAY(0..4,1..5,-7..7,0..2) OF REAL V then V(∗∗∗∗) is a onedimensional view of the same array (as opposed to V(∗,∗,∗,∗) which represents its original arrangement). By the same token V(∗,∗∗,∗) is the three-dimensional array obtained by compounding the two central indices into one. Bounds of a compound index can be retrieved by the :LO. and :HI. functions as usual. Compound indices are restricted to arrays (or portions of them) that are contiguous in memory. This generally precludes their use after subarray operations that permute, stride, or restrict the range of indices.
Page: (cpl)ARRAY operations, Next: Einstein convention, Prev: compound index, Up: Expressions

Linear-space ARRAY operations

Linear-space operations are supported on arrays. That is, congruent ARRAYs of any number of dimensions can be added to or subtracted from each other and multiplied or divided by a scalar inside an expression, as well as appear on the left side of an assignment. The scalar product of two congruent arrays is denoted by the infix operator | . This is defined as the sum over all dimensions of the products of corresponding elements. (Except for (complex)) The constant 0 (but not any other constant) can be assigned as a value to a whole array. Some Builtin functions operate on whole arrays. A larger set of vector and matrix algebra operations are supported by the rbmat:(matrix). and cbmat:(matrix). libraries. Matrix transposition is provided as the TRANSPOSED:subarray. operation. All ARRAY operations can also be applied to STORED ARRAYs and STRUCTURED ARRAYs.
Page: (cpl)Einstein convention, Next: Assignment, Prev: ARRAY operations, Up: Expressions

Implicit looping over array indices

When indices in an expression are prefixed with a $ character, the Einstein convention is implied. In a contraction or in a product the Einstein index must appear exactly twice, and the result will be summed over ALL values of this index; in a sum or in an assignment, Einstein indices must appear exactly once in each and all terms including the l.h.s., and the assignment will be repeated over ALL values of these indices. For example: C($i,$j)=A($i,$j,$m)∗B($i,$m,$j)+D($j,$i) Footnote: the euro symbol would have been appropriate to match the e of Einstein, but it is not part of the 7-bit ascii codes so we replaced it by a currency that is (the $).
Page: (cpl)Assignment, Next: Deferred assignment, Prev: Expressions, Up: Statements


Assignment is performed by a single = sign. The l.h.s. must be a POINTER, and the r.h.s. is required to be a value of the TYPE pointed to or to be implicitly convertible to it (Concealed Pointer Lookup). Compound variables can be assigned as a whole if types match. Owing to their omnicomprehensive role, assignment and equality/inequality tests are the only situations in which pointer dereferencing must be explicit. The left side of an assignment will never be implicitly dereferenced, and must be dereferenced by a postfix ^ if assignment of the value pointed to is desired. For increased clarity, the type of the object to be assigned may also be specified as in (after the declaration REAL a^,b) REAL a=b equivalent to a^=b. On the right-hand side of an assignment the symbol ~ can be used as a placeholder for the left hand. For example: counter = ~ + 2 listelement = ~.next A(i,j,k) = 3 ∗ LOG(~) + gamma(~)
Page: (cpl)Deferred assignment, Next: Concealed Pointer Lookup, Prev: Assignment, Up: Statements

Deferred assignment

When an assignment is performed by a double == sign, no statement is generated in place. The l.h.s. is instead defined to be an alias for the r.h.s., and the assignment will be performed (and error messages issued, if any) at the location where this alias is first used. Deferred assignment permits the appearance on the r.h.s. of yet undefined variables and functions, provided these will be defined before the l.h.s. is actually used. It also allows compile-time constants to be given symbolic names and still be recognized as such. This construction is further extended in the (symbolic) library.
Page: (cpl)Concealed Pointer Lookup, Next: WITH, Prev: Deferred assignment, Up: Statements

Implicit type conversion

Type consistency in expressions and assignments is enforced. However some types can be implicitly converted to others. Possible conversions are automatically checked out in the attempt to build a consistent expression. Only if this turns out to be impossible, a compilation error results. The basic rule is that a type can be implicitly converted to one other type only and that there cannot be closed type conversion loops. INTEGERs are implicitly converted to REALs. POINTERs are implicitly converted to the value they point to. Owing to this Concealed Pointer Lookup, it is hardly ever necessary to explicitly dereference pointers. When really wanted, the dereferencing operator is a postfix ^ (as in Pascal). Vice versa, a prefix ^ forces taking the address rather than the value of a variable; this construction too is rarely needed. Notice however that, in the assignment of a previously undeclared CONSTANT, c = var implicitly declares c as a copy of the value of var, whereas c=^var declares c as a POINTER to var.
Page: (cpl)WITH, Next: NEW, Prev: Concealed Pointer Lookup, Up: Statements

Implicit access to structure fields and functions

Like in Pascal or Modula2, a structure may be implicitly selected by the WITH statement. WITH has both a multi-line form: WITH <structure> [,<structure>] [:] <block> [END WITH] and a single-line form: WITH <structure> [,<structure>] : <single-line-block> The multi-line form is recognized from a newline following the <structure> or the optional colon; in this case the scope of the statement extends either to the optional END WITH statement or to the end of the enclosing block; in the second to the end of line. Within the scope of a WITH statement the given structure (or structures) is understood wherever a field name appears alone. For instance, after the declaration STRUCTURE(REAL x,y) v1,v2 writing WITH v1: x=v2.y; y=3 becomes equivalent to v1.x=v2.y; v1.y=3, or to x(v1)=y(v2); y(v1)=3. Given the symmetry of treatment of structure fields and functions by CPL (STRUCTURE), a function argument may also be specified in a WITH clause. Thus, for instance, WITH 3: WRITE SIN is equivalent to WRITE SIN(3). Of course this notation is not very useful for REAL functions, but becomes handy when functions are defined to access user-defined compound types as if they were structures.
Page: (cpl)NEW, Next: FREE, Prev: WITH, Up: Statements

Memory allocation

Runtime memory allocation is achieved either through the statement NEW <pointer> [,<pointer>] or the function NEW <type> The NEW statement allocates (through the `malloc' system call) the space necessary for a variable of the TYPE pointed to by <pointer> and sets <pointer> to its address. The NEW function returns a POINTER to a dynamically allocated variable of TYPE <type>. If applied to a POINTER TO STORED or FILE type, NEW returns a pointer to a temporary file which will be automatically destroyed when freed or at program termination. Dynamically allocated space is retained, independently of subroutine scoping, until it is returned to the system by the FREE statement.
Page: (cpl)FREE, Next: Input/Output, Prev: NEW, Up: Statements

Freeing memory or file allocation

FREE <pointer> [,<pointer>] releases the memory space or file pointed to by each <pointer>, which must have been previously obtained from NEW, OPEN, or CREATE. <pointer> is subsequently zeroed, so that any subsequent dereferencing generates a visible runtime exception. If <pointer> is a file descriptor, FREEing it also writes out any previously buffered data.
Page: (cpl)Input/Output, Next: Control, Prev: NEW, Up: Statements


Text I/O is performed by READ and WRITE instructions on variables of type FILE, which must be declared like ordinary variables and associated with a file before being used for I/O. FILE variables can be implicitly declared if they are CONSTANT and be part of compound types like all other variables. Association to a file is obtained through OPEN and CREATE. A FILE variable is translated to the C type FILE∗, and can be used in C library I/O functions transparently accessed through the C interface. Sequential binary I/O is performed by adding the BINARY keyword to the READ and WRITE instructions. No distinction is made between text and binary files at the filesystem level. Random-access binary I/O is where cpl really differs from other languages; see STORED. A more traditional and lower-level method of random access is described in POSITION.
Page: (cpl)FILE, Next: WRITE, Prev: Input/Output, Up: Input/Output


File descriptor type, translated to C type FILE∗ The predefined C file descriptors stdin, stdout, stderr are also predefined in a CPL program (as are, in fact, most other symbols and functions in the C stdio library; see C interface). The construction FILE OF <type> is used for random-access files (see STORED). Type FILE is equivalent to FILE OF CHAR.
Page: (cpl)WRITE, Next: READ, Prev: FILE, Up: Input/Output


The output statement WRITE is used as follows: WRITE [TO <file>] [BY NAME] [<outputexpr>] [./.] <file> can be a file descriptor, of type FILE, such as returned by OPEN or CREATE, or else a string representing a filename. In the latter case the file is implicitly CREATEd before WRITEing and FREEd afterwards. If TO <file> is omitted, stdout is implied. If <file> is present, WRITE TO may be omitted. <outputexpr> can be <value>[:<format>] or ,. <value> can be of any type except POINTER (which if present undergoes Concealed Pointer Lookup). Compound types such as ARRAY and STRUCTURE are printed in a standard format which is understandable to READ. (If STRUCTURE elements are POINTERs, however, they are printed as the string POINTER and cannot be read back.) If a SUBROUTINE named WRITE is defined with first argument of type FILE and second argument of a user-defined TYPE, it is used to print variables of such type. Primitive types are printed in a default format, unless the numeric part of a C format specifier is included after a colon. The default format is initially the C library's default, but may be changed (down to the end of the enclosing scope) by the declaration DEFAULTFORMAT, e.g. DEFAULTFORMAT 1.15 A , in the sequence of <outputexpr>'s is printed as a tab. <outputexpr>'s may also be written one after the other without any comma, in which case they are just adjoined without any intervening space. Please pay attention that a numeric parenthesis that immediately follows a string will be interpreted as an ARRAY subscript, since the STRING is a legitimate ARRAY OF CHAR, unless space is placed between the string and the parenthesis. By default WRITE prints a newline at the end of its sequence of <outputexpr>'s. The optional symbol ./. suppresses this newline and FLUSHes the output instead. WRITE with no <outputexpr> and no ./. prints just a newline. If the optional BY NAME qualification is present, WRITE prepends each value with the expression from which it is produced, in the form name=value. If the expression is a single identifier, this format can be read back by the statement READ BY NAME. WRITE syntax may also be used to convert any expression into a string. See Stringconversion.
Page: (cpl)READ, Next: FROM, Prev: WRITE, Up: Input/Output


The input statement READ is used as follows: READ [BY NAME] [FROM <file>] [<variable> [<conjunction> <variable>]] <file> can be a file descriptor, of type FILE, such as returned by OPEN or CREATE, or else a string representing a filename. In the latter case the file is implicitly opened before READ is executed and closed afterwards. If FROM <file> is omitted, stdin is assumed. <variable> must be a VARIABLE identifier or a POINTER to a variable, which can be of any type except POINTER. Compound types such as ARRAY and STRUCTURE are also accepted; they are expected to appear in the same format as produced by WRITE <variable> can also be a literal STRING, which is then expected to appear verbatim in the input file. If <variable> is an ARRAY OF CHAR, characters are read until the array is full or a newline appears. In all other cases, blanks, tabs and newlines are automatically skipped. Lines starting with ! are considered comments and skipped. <conjunction> can be , AND or OR. If , or AND is used, the joined variables must both be compulsorily present in the input or an I/O error results. If the conjunction is OR, only one or the other is expected to be present and the READ statement succeeds and yields control as soon as one is found. READ with no variable is also allowed and just blocks until a newline is received, skipping any other characters that precede it. This statement can be used to skip lines in a file or to create a pause in execution until a newline appears in stdin. READ may also appear as a function in a BOOLEAN expression, and should then be pronounced as the past participle of the verb to read. In this form READ is construed just as above, but returns a BOOLEAN value saying whether reading was successful or not and never generates an I/O error. If the optional BY NAME qualifier is present, READ expects to find the value of each variable preceded by its name in the input file in the form name=value.
Page: (cpl)FROM, Next: ASK, Prev: READ, Up: Input/Output


As an auxiliary keyword, FROM is used in READ statements to introduce the FILE data are read from. In addition, the construction <type declarator> FROM <file> represents a value of type <type declarator> read from the file descriptor <file>. This value can directly appear in an expression just as the value of a function can. As an example, WRITE TO stdout 3∗(INTEGER FROM stdin) can replace INTEGER temp READ FROM stdin temp WRITE TO stdout 3∗temp
Page: (cpl)ASK, Next: BINARY, Prev: FROM, Up: Input/Output

Console prompting and reading

ASK [<type>] [literal:] <variable> [, [literal:] <variable>] has the effect that a prompt is automatically written to stderr for each variable and the value of the variable is subsequently read from stdin. The prompt is the optional literal if present or the name of the variable followed by a ? otherwise. Console prompting can be compounded with CONSTANT declaration. If <type> is present, each variable is declared as a CONSTANT before being asked.
Page: (cpl)BINARY, Next: STORED, Prev: ASK, Up: Input/Output

Binary (unformatted) Input/Output

Sequential binary I/O is obtained by appending the word BINARY to either READ or WRITE, as in READ BINARY FROM <file> <variable> [,<variable>] WRITE BINARY TO <file> <outputexpr> [,<outputexpr] In binary I/O no format need, of course, be specified and no newline is automatically added by WRITE statements. For random-access binary I/O the statement POSITION can be used to get and set the reading and writing position in the file. A more powerful and frequently preferable mechanism is provided by the STORED declaration.
Page: (cpl)STORED, Next: OPEN, Prev: BINARY, Up: Input/Output

Random-access files as disk-resident variables

A random-access binary file typically contains fields and records at fixed addresses, which can be accessed by POSITIONing the file cursor (POSITION) before each READ or WRITE BINARY. However, this process requires knowing the address of each field and is quite error-prone. CPL provides a powerful alternative: a pointer to a compound variable residing in a disk file. Records and fields are then defined by just declaring a suitable compound type through the usual STRUCTURE and ARRAY declarations. For this purpose a type declaration prefixed by the keyword STORED is used, as in the example: POINTER TO STORED ARRAY(1..10) OF STRUCTURE(REAL x,y) v which defines a variable named v as the pointer to a random-access file containing 10 records composed of two real numbers each. v is in fact an ordinary file descriptor, which can be obtained from OPEN or CREATE, just like any other file descriptor, or from NEW like any other POINTER. Once v is assigned a file descriptor, reading and writing to it is achieved by just accessing v as if it were a memory POINTER. In the example, since Concealed Pointer Lookup is in effect, v(4).x = 3.14 and WRITE LOG(v(8).y) are valid statements. The rule is simply that anything that could be done if v had been declared as a normal POINTER is still allowed, but the actual storage is in a disk file rather than in memory. Just as for any POINTER, accessing a variable of POINTER TO STORED type without first assigning a file descriptor to it is an error (caught by rtchecks if active). On the contrary, the declaration STORED ARRAY(1..10) OF STRUCTURE(REAL x,y) v (using the same example) actually reserves storage for the variable v, just like the corresponding declaration without the keyword STORED would, by requesting a temporary file through NEW. This temporary file is automatically opened and closed as necessary and cannot be explicitly assigned. When declared with the special dimension ∗, a STORED ARRAY can contain an a-priori undetermined number of records (numbered from 0). The shorthand FILE OF <type> is equivalent to POINTER TO STORED ARRAY(∗) OF <type> The ordinary FILE type declarator is a shorthand for FILE OF CHAR. STORED ARRAYs and POINTER TO STORED ARRAYs with multiple ∗ dimensions are allowed as function formal parameters, just like their non-STORED equivalents. The corresponding actual parameter must be a STORED ARRAY of matching number of dimensions and type (not just an OPENed file).
Page: (cpl)OPEN, Next: CREATE, Prev: STORED, Up: Input/Output

Open an existing file and associate a file descriptor with it

The statement OPEN <fd>, <filename> opens the file descriptor <fd> for the file identified by the string <filename>. <fd> can be any STORED type. The function OPEN(<filename>) returns <fd> as its value. <filename> is opened for both reading and writing, and is created if it does not exist, but it is never truncated. Therefore, it may be written over but any data which are not overwritten will remain there. If a possible file of the same name is to be cleared before writing, CREATE should be used instead. OPEN calls the open system function with options O_RDWR|O_CREAT. For finer control over options, the open or fopen function may be called directly, just as all other libc functions (C interface).
Page: (cpl)CREATE, Next: FLUSH, Prev: OPEN, Up: Input/Output

Create a new, empty file

The statement CREATE <fd>, <filename> opens the file descriptor <fd> for the file identified by the string <filename>. <fd> can be of FILE, or POINTER TO STORED type. The function CREATE(<filename>) returns <fd> as its value. <filename> is opened for both reading and writing, is created if it does not exist, and is truncated to zero length. Therefore, it may be reread after it has been written, but any data in a pre-existing file of the same name are lost. If a possible file of the same name is to be preserved, OPEN should be used instead. CREATE calls the open system function with options O_RDWR|O_CREAT|O_TRUNC. For finer control over options, the open or fopen function may be called directly, just as all other libc functions (C interface).
Page: (cpl)FLUSH, Next: POSITION, Prev: CREATE, Up: Input/Output

Flush a file's write buffer

The statement FLUSH <fd> writes out any buffered data for the file descriptor <fd>.
Page: (cpl)POSITION, Next: INPUTREADY, Prev: FLUSH, Up: Input/Output

Read or set a file's current position

The statement POSITION <fd>, n sets the current position in file descriptor <fd> so the next READ or WRITE statement will take effect at byte number n. The function POSITION(<fd>) returns as an INTEGER the current READ/WRITE position in file descriptor <fd>.
Page: (cpl)INPUTREADY, Next: COMMANDLINE, Prev: POSITION, Up: Input/Output

Test for input ready on a file descriptor or stdin

This function is contained in the inputready library (USE inputready). The BOOLEAN function INPUTREADY(<fd>) returns YES if there are characters available for input on file descriptor <fd>. This is mainly of use when <fd> is a terminal, to check that keys have been pressed before reading. Notice that if the terminal is in line-buffered mode (as by default Unix terminals are), characters become available only after a line feed has been entered. Terminal mode may be altered by CHARbyCHAR. INPUTREADY() is a shorthand for INPUTREADY(stdin). INPUTREADY(<fd>,<time>) waits for input for the given <time> (in seconds, a REAL) before returning NO. INPUTREADY(<time>) is a shorthand for INPUTREADY(stdin,<time>).
Page: (cpl)COMMANDLINE, Prev: INPUTREADY, Up: Builtin

Command-line parameter access

The command line through which the program was launched is available in the predefined ARRAY OF STRING COMMANDLINE (the same that is usually named argv in C programs). Therefore the first word passed after the program name is COMMANDLINE(1), the second is COMMANDLINE(2), etc. The program name itself is COMMANDLINE(0). The index of the last word present, obeying normal ARRAY:HI. convention, is COMMANDLINE.HI
Page: (cpl)Control, Next: makecpl, Prev: Statements, Up: Top

Program flow control statements

Page: (cpl)IF, Next: LOOP, Prev: Control, Up: Control

Conditional execution

IF statements have a single-line form and a multi-line form. (For the additional use of IF inside expressions see Conditional expressions) As in BASIC, single-line IF statements terminate at the end of a line, whereas multi-line IF statements are explicitly terminated by END IF. (END) Multi-line IF statements have the structure: IF <BOOLEAN> THEN <block> END IF or IF <BOOLEAN> THEN <block> <else statement> They are recognized by THEN being the last word on a line. Single-line IF statements have the structure IF <BOOLEAN> THEN <single-line block> or IF <BOOLEAN> THEN <single-line block> END IF or IF <BOOLEAN> THEN <single-line block> <else statement> <single-line block> may contain multiple statements separated by semicolons but no newlines. It may, however contain compound statements (such as a LOOP or another IF statement) that in turn contain newlines in their own body. The optional <else statement> has the structure ELSE <block> END IF or ELSE <single-line block> or ELSE <single-line block> END IF Multiple-choice conditionals are simply obtained by nesting another IF in the <else statement>, e.g. IF <BOOLEAN> THEN <block> ELSE IF <BOOLEAN> THEN <block> ELSE <block> END IF
Page: (cpl)LOOP, Next: DO, Prev: IF, Up: Control


Loops tested at their beginning (which are executed zero or more times) are coded as LOOP <test> <block> REPEAT [LOOP] or LOOP name <test> <block> REPEAT name Loops tested at their end (which are always executed one or more times) are coded as LOOP <block> REPEAT [LOOP] <test> or LOOP name <block> REPEAT name <test> or else by the DO short form. A named loop can be terminated at any point in its body by the EXIT statement. A named loop without <test> is an infinite loop that can only be terminated by EXIT. <test> may be any one of
Page: (cpl)WHILE, Next: UNTIL, Prev: LOOP, Up: LOOP

WHILE loops

The simplest test that can be used to decide whether to iterate a loop is WHILE <BOOLEAN> For instance, DO i=i+1 WHILE i<10 will repeat while the boolean condition is true. WHILE is equivalent to UNTIL NOT.
Page: (cpl)UNTIL, Next: FOR, Prev: WHILE, Up: LOOP

UNTIL loops

The simplest test that can be used to decide whether to terminate a loop is UNTIL <BOOLEAN> For instance, DO i=i+1 UNTIL i>10 will repeat until the boolean condition becomes true. UNTIL is equivalent to WHILE NOT. See also C-style FOR loops.
Page: (cpl)FOR, Next: DO, Prev: UNTIL, Up: LOOP

FOR loops

The <test> for indexed loops is FOR <for clause> where the basic <for clause> can be either <index> = <lbound> TO <ubound> [BY <step>] or <index> = <ubound> DOWN TO <lbound> [BY <step>] <index> is incremented by <step> in each iteration in the first case and decremented in the second. The loop terminates when <index> exceeds <ubound> in the first case or drops below <lbound> in the second. If BY <step> is omitted, BY 1 is assumed. <lbound>, <ubound> and <step> can all be arbitrary expressions; notice that a negative <step> in either case generates a non-terminating loop. Both INTEGER and REAL types are allowed. <index> may be an already existing variable or be implicitly declared by the loop itself (Declarations). In the latter case it acquires the type of the loop bounds and behaves as a CONSTANT within the body of the loop. If the index is REAL, BY cannot be omitted. FOR clauses can appear in a LOOP, DO and Looping operator. In addition to the two basic forms, they can have the following variants:
Page: (cpl)FOR AND, Next: ALL, Prev: FOR, Up: FOR
Two or more FOR loops can be combined in a single <test> through the keyword AND (not to be confused with the logical AND operator), as in the following example: LOOP FOR i1=l1 TO u1 AND i2=l2 TO u2 <block> REPEAT which is equivalent to LOOP FOR i1=l1 TO u1 LOOP FOR i2=l2 TO u2 <block> REPEAT REPEAT
Page: (cpl)ALL, Next: TIMES, Prev: FOR AND, Up: FOR
LOOP FOR ALL <index> [,<index>] denotes a loop whose bounds are automatically determined as the bounds of the first ARRAY that <index> appears in. Its use should preferably be limited to short loops in which only arrays of the same dimensions appear and no index arithmetics are performed. ALL can be combined in a multiple loop with AND (FOR AND), as well as contain multiple indices separated by a comma. In addition, the lower and upper bounds of the first array that the index is used in are also available as LO and HI respectively within the scope of the FOR clause, like in the example: DO WRITE A(i,j,k) FOR ALL i,j AND k=LO+2 TO HI-3
Page: (cpl)TIMES, Next: IN, Prev: ALL, Up: FOR
LOOP FOR <number> TIMES does exactly that, when just a counter and no indexing is needed.
Page: (cpl)IN, Next: EXCEPT, Prev: TIMES, Up: FOR
LOOP FOR <element> IN <array> Here <element> is not an INTEGER, but rather a POINTER to elements of the ARRAY <array>. A LOOP is performed in which <element> runs in succession through all the elements of the array. As a particular case, a constant array can be constructed from a set of elements of the same TYPE enclosed in brackets, as in LOOP FOR n IN (2,5,11)
Page: (cpl)EXCEPT, Next: C-style, Prev: IN, Up: FOR
LOOP FOR <for clause> EXCEPT <condition> [, <condition>] The EXCEPT qualifier added to any of the FOR loop constructions excludes exceptional values of the index from the iteration. <condition> may be either an expression of BOOLEAN type, meaning that the loop is skipped when the <condition> is TRUE, or an expression of the same type as the index, meaning that the given value is excluded.
Page: (cpl)C-style, Prev: EXCEPT, Up: FOR
LOOP FOR <index> = <start> UNTIL <ending condition> BY <incrementing statement> is a C-style FOR clause in which the index is not necessarily a number but can be of any type; for instance, a pointer to a structure. <index> is implicitly declared, if necessary, to have the same type as <start> and initialized to this value. <index> is then incremented by executing <incrementing statement> at the end of each LOOP iteration and treated as a constant for the scope of the loop. The loop repeats until <ending condition> (an expression of BOOLEAN type) becomes TRUE. Either <ending condition> or <incrementing statement> may take an alternative short form. The short form of <ending condition> is an expression of the same type as <start>; this is equivalent to the BOOLEAN <index> = <ending condition>. The short form of <incrementing statement> is a single identifier denoting a function or structure element. This is a shorthand for <index> = <incrementing statement>(<index>).
Page: (cpl)DO, Next: INLINE LOOP, Prev: LOOP, Up: Control

Loops (short form)

A short form for nameless loops tested at the end (which are always executed one or more times) is DO <block> <test> where <test> may be any one of
Page: (cpl)INLINE LOOP, Next: CASE, Prev: DO, Up: Control


The statement INLINE LOOP [name] FOR <element> IN <array> <block> REPEAT [name] expands in line by repeating the loop contents once for each element of <array>, whose dimensions must be known at compile time. See also IN.
Page: (cpl)CASE, Next: CASE LOOP, Prev: INLINE LOOP, Up: Control

Multiple-choice selection

A multiple-choice branch is specified as CASE <INTEGER> OF tag1: <block> tag2: <block> ............ tagn: <block> [ ELSE <block> ] END CASE where <INTEGER> is any expression of type INTEGER and tag1, tag2,... tagn are integer compile-time constants. A list of values separated by commas may also appear as a tag.
Page: (cpl)CASE LOOP, Next: EXIT, Prev: CASE, Up: Control

Multiple-choice loop

A multiple-choice loop is specified as CASE LOOP OF label1: <block> REPEAT <label> label2: <block> REPEAT <label> .............. labeln: <block> REPEAT <label> END CASE At the end (or in the interior, possibly inside an inner IF or CASE statement) of each labelled block, execution may continue at any other label as specified by the REPEAT statement. If the end of a block is reached without an intervening REPEAT statement, the CASE LOOP ends. The CASE LOOP construction is functionally equivalent to a CASE in a LOOP, e.g. ENUM(label1,label2,...labeln,exit) repeat=label1 LOOP WHILE repeat#exit next=repeat; repeat=exit CASE next OF label1: <block> repeat=<label> label2: <block> repeat=<label> .............. labeln: <block> repeat=<label> END CASE REPEAT CASE LOOP maintains statements in the same order, but is easier to read and is implemented without auxiliary variables.
Page: (cpl)EXIT, Next: END, Prev: CASE, Up: Control
EXIT <name> transfers control past the end of the loop, module, function or subroutine labelled <name>. EXIT may be nested in an inner scope, such as a further loop or an IF statement.
Page: (cpl)END, Next: STOP, Prev: EXIT, Up: Control
END <name> terminates the body of a module, function or subroutine named <name>. END IF, END CASE, END WITH, END C SECTION, END FRI SECTION and END TRAP terminate the corresponding statements.
Page: (cpl)STOP, Next: ERROR, Prev: END, Up: Control


Stop the program and exit (unless intercepted by a TRAP). Has the same effect as the SIGINT (^C) signal.
Page: (cpl)ERROR, Next: TRAP, Prev: STOP, Up: Control

Error signal

The command ERROR <outputexpr> writes <outputexpr> to the predefined string variable ERRORMESSAGE and signals an error. Unless a TRAP has been set, this causes the program to exit after writing ERRORMESSAGE to stderr.
Page: (cpl)TRAP, Prev: ERROR, Up: Control

Error handling

An error, either caused by a system signal or by the ERROR statement, normally causes the program to terminate after writing a message to stderr. A trap for errors may be set by the statement TRAP <block> END TRAP or TRAP <literal> <block> END TRAP In the first form all errors are trapped; in the second only those whose error message begins with <literal>. When an error is trapped, <block> gets executed. The error message that would be printed is available in this block in the predefined STRING ERRORMESSAGE At the END TRAP statement, control is passed to the end of the enclosing block, where also the scope of the TRAP terminates like that of all other declarations. Within the body of the TRAP, the exception may be re-raised if necessary by the statement ERROR ERRORMESSAGE An exception caused by SIGINT or STOP is accompanied by an empty ERRORMESSAGE.
Page: (cpl)BY, Next: C SECTION, Prev: BOOLEAN, Up: Control
The auxiliary keyword BY is used in
Page: (cpl)ELSE, Next: END, Prev: DOWN, Up: Control
The auxiliary keyword ELSE is used in
Page: (cpl)NAME, Next: NEW, Prev: MODULE, Up: Input/Output
The auxiliary keyword NAME is used in
Page: (cpl)OF, Next: POINTER, Prev: NOT, Up: Declarations
The auxiliary keyword OF is used in:
Page: (cpl)TO, Next: TRAP, Prev: TIMES, Up: Control
The auxiliary keyword TO is used in:
Page: (cpl)makecpl, Next: cpl, Prev: Control, Up: Top

Compilation tools and their usage

The compilation and linking of a cpl Source file is managed by a shell script named makecpl. For most purposes the single command makecpl [-a] <programtobecompiled>[.cpl] [local dir] [options] is all that is needed to compile and link the source file <programtobecompiled>.cpl into the executable file <programtobecompiled> The task of makecpl is achieved by first running cpl, the CPL-to-C compiler, which compiles the given source and all other files USEd by it into corresponding C programs, and simultaneously lists all of the generated files and any needed pre-existing objects in the makefile <programtobecompiled>.d, and then running make ((make)) on this makefile. Any options possibly present on the makecpl command line are passed on through make to the system C compiler and linker, or to the command-line designated compiler. The default C compiler is make's default; an alternate C compiler can be specified in the CC environment variable or as CC= on the command line. The -D and -I options are interpreted by cpl, in addition to being passed to the C compiler, and provide pre#defined constants and include directories to both. C source and object files are kept in a local subdirectory usually named .cpl (and therefore hidden from listing under unix convention). An alternate name can be specified in the environment variable OBJDIR or on the command line. Only those source files whose corresponding object file does not exist or is older than the source are actually recompiled. In exceptional cases where a full re-compilation may be necessary, the command makecpl -a <programtobecompiled> achieves this by deleting the object files first. remakecpl is another alias for makecpl -a. If your system includes the curl command (on Debian you can install it as apt install curl), makecpl provides the functionality to poll the CPLcode.net website in the background. See automatic updates.
Page: (cpl)cpl, Next: fri, Prev: makecpl, Up: makecpl

The CPL-to-C compiler

The CPL compiler itself is not an executable file, but rather a formal description of the language that is interpreted by the Formal Rule Interpreter fri. The cpl file is made executable by the shell convention of having #!/usr/bin/env fri as its first line. Where this convention is understood, the CPL-to-C compilation command is cpl <source.cpl> On systems where the #! convention does not work as expected, this command must be expanded into fri cpl <source.cpl> All this is normally handled inside the makecpl driver script; direct use of the cpl command is not needed.
Page: (cpl)fri, Next: FRI SECTION, Prev: cpl, Up: makecpl

The Formal Rule Interpreter

The design of most compilers passes through a formal description of the language that is compiled by a parser generator like yacc into a C program. At the heart of CPL's extensibility there is a different structure, where a formal description of the language is never compiled to generate the compiler, but rather interpreted each time a compilation is run. The engine that does this is named fri, the Formal Rule Interpreter. If yacc is a compiler-compiler, fri may be called a compiler-interpreter. In fact, fri is a quite general pattern-matching engine with its own FRI meta-language, and only learns about CPL when it reads the cpl file every time the compiler is invoked. Translators for many other languages can in principle be written in it. The interpreted nature of the CPL compiler, in addition to having allowed a smooth continued development and fine-tuning over the years, has the effect that its syntax can be extended on the fly by the compiled program itself with a few lines of FRI code written in a FRI SECTION. This possibility is used by Library modules in order to define, for instance, complex-number notation, matrix-algebra operations or symbolic manipulations.
Page: (cpl)FRI SECTION, Next: infocpl, Prev: fri, Up: makecpl

Dynamic extension of the CPL language

FRI language excerpts may be inserted at any point of a CPL program block by the statement FRI SECTION <FRI code> END FRI SECTION This has the effect of dynamically adding new statement constructions and for all purposes extending the syntax of the language. (See fri) Like everything else in CPL, the new extensions are available from the point where the FRI SECTION appears through the end of the enclosing block.
Page: (cpl)infocpl, Next: icpl, Prev: FRI SECTION, Up: Top

info reading script

Unless the info file you are reading is registered in the general info tree, the command for reading it is relatively cumbersome. To encapsulate this command, the infocpl script exists. Typing just infocpl brings you to this file. Typing infocpl <keyword> brings you help about a specific CPL keyword.
Page: (cpl)icpl, Next: Library, Prev: infocpl, Up: Top

Interactive CPL

The interactive CPL interpreter, accepting expressions, statements or commands at a console prompt, is launched by the command icpl icpl's syntax is totally compatible with CPL's. Any valid CPL statement is executable at the cpl> prompt. As an extension, when an expression is typed alone at the cpl> prompt a WRITE statement is implied. ? brings you to this manual.
Page: (cpl)icpl commands, Next: Library, Prev: icpl, Up: Top
Page: (cpl)icpl command line, Next: ?, Prev: icpl commands, Up: icpl

The icpl command line

An interactive CPL command can alternately be specified on the icpl command line, as in icpl <command> <command> may need to be quoted if it contains characters that are interpreted by the shell specially. If, either at the console prompt or on the command line, the name of a CPL file is given as a command without the .cpl extension, this source file is automatically compiled as necessary (if it is more recent than its executable), and then executed at compiled speed but with interpreted commands available. If the name of a CPL file is given as a command with the .cpl extension included, it is loaded and executed in the interpreter as when it appears in an INCLUDE statement. Exploiting the unix interpreter convention, a CPL source program can be made directly executable by writing #!/usr/bin/env icpl as its first line and turning on the executable permission flag. ((bash)Shell Scripts) The icpl command with no arguments launches an interactive icpl session. icpl followed by a CPL expression (possibly quoted to avoid interpretation by the shell) calculates and prints that expression. icpl followed by one or more CPL statements executes those statements and exits; however if a - precedes the list of statements, after executing those statements an interactive session is opened. For instance, a chosen set of libraries can be loaded by default in an interactive session, if desired, by defining an alias for icpl -USE <libraries>. icpl with no arguments is equivalent to icpl -USE rbmat,cbmat,gnuplot,rtchecks. Finally, if icpl is executed under a different name (by defining a link to it), that name is executed as a command after icpl -. For instance, setup defines a link to icpl named edit, which is executed as icpl -edit. When the input of icpl is redirected (or piped) from an alternate source, commands from that source are interpreted on a line-by-line basis.
Page: (cpl)?, Next: :, Prev: icpl command line, Up: icpl

The icpl help system

The ? command given at the cpl> command prompt brings you to the Top of this info file. ? followed by any user-declared identifier or system-reserved keyword elicits related help.
Page: (cpl):, Next: $, Prev: ?, Up: icpl

(icpl) Shell commands

Any command that is meaningless to icpl, or not the name of a program to be executed, is passed on to the shell before giving up. Thus most shell commands can be given directly at the cpl> prompt. In case of ambiguity, passing a command to the shell can be forced with :<shell command>
Page: (cpl)$, Next: command interpretation, Prev: :, Up: icpl

(icpl) Last result

Within an icpl expression, the $ symbol stands for the last result printed out by a previous command.
Page: (cpl)command interpretation, Next: interactive session, Prev: $, Up: icpl

(icpl) Command interpretation

A statement contained in a string <string> can be executed just as it were typed at the cpl> prompt by the statement icpl <string> The icpl statement is available not only to interpreted but also to compiled programs, provided the latter are run under icpl, thus enabling the on-the-fly execution of program-generated statements. All the global variables and subroutines defined in either the compiled or the interpreted part are accessible to the run-time generated command.
Page: (cpl)interactive session, Prev: command interpretation, Up: icpl

(icpl) Interactive session

An interactive icpl session can be started in the middle of a running program either by the user typing ctrl-C at the controlling terminal (or any other process giving signal SIGINT), or by the program itself executing the statement icpl The interactive session is available not only to interpreted but also to compiled programs, provided the latter are run under icpl to start with. All the global variables and subroutines defined in either the compiled or the interpreted part are accessible to the interactive session. The inner session thus opened is signalled by an additional > in the command prompt, and can accept any icpl command. Exiting this inner session with exit or quit resumes the suspended program, as also does pressing Enter alone on an empty line. The new commands kill or stop kill the interrupted program and return to the session where it was invoked.
Page: (cpl)Library, Prev: icpl, Next: rtchecks, Up: Top

Ready to USE library of CPL modules

Page: (cpl)rtchecks, Next: (complex), Prev: Library, Up: Library

Run Time bounds Checking

The rtchecks.cpl library does not add any new subroutines or instructions, but modifies already existing ones in such a way as to provide run-time bounds checking. The impact on performance is considerable; therefore rtchecks should only (and always) be used during development and later disabled once the program is deemed to be reliable. In particular: all array indexing and subarray extraction operations are bounds-checked, pointers are initialized to NULL and NULL-pointer dereferencing is trapped, all error messages are modified so as to display the position in the source code where the error occurred.
Page: (cpl)TRACE, Prev: rtchecks, Up: rtchecks

TRACE ON/OFF (also abbreviated TRON/TROFF)

toggles execution tracing. When tracing is on, instructions from the source file are displayed in the upper part of the terminal screen while program output goes to the lower part (unless redirected to a file or to another terminal). Each block (e.g., subroutine or loop) of execution is displayed on a new line, thus the last instruction of each block is simultaneously within sight. Each line is prefixed by its source line number and repetition count. Immediate keypresses (with no Enter) alter tracing as follows: P,p: pause. Pause execution, or resume if paused. Q,q: quit. Interrupt the program and exit (C-c is remapped by icpl, see below) T,t: TROFF. Turn tracing off and continue running as normal. C-c,I,i: icpl. Open an icpl (icpl) console inside the paused program, wherein global variables can be inspected or changed, and functions evaluated. Entering an empty line resumes execution. Only available if the program was run from icpl to start with (as in icpl <program>). V,v: view. Open a vi window at the present line in the source code, which can be explored using all vi commands. Tracing resumes on exit. (vi command :q) <space>,S,s: single step. Advance by a single instruction through the code. <tab>,F,f: finish. Finish the current block and resume tracing from the next instruction of the enclosing block. Any other key displays a single-line reminder of the above.
Page: (cpl)CHARbyCHAR, Next: lapack-eigenvalues, Prev: (symbolic), Up: Library

Live keyboard input

The CHARbyCHAR.cpl library contains the system calls (termios) necessary to activate unbuffered terminal input, so that the input of a single character may be read immediately (normally input is transferred from the terminal to the running program one line at a time, i.e. only when a newline is entered). USEing this library is all that is necessary. Input is then performed as usual.
Page: (cpl)lapack-eigenvalues, Prev: CHARbyCHAR, Next: matlib, Up: Library

CPL wrapper around Lapack eigenvalue routines




In order to use this library you must have the liblapack-dev Debian package (or one equivalent) installed. Please repeat the CPL setup after installing.
Page: (cpl)matlib, Prev: lapack-eigenvalues, Up: Library

Basic matrix-creating functions

The matlib.cpl library is used by the m2cpl script in order to translate simple MATLAB programs into their CPL equivalents. Both matlib and m2cpl are in very preliminary development and only include the most basic functions.