Documentation for the CPL programming language and its compiler, September 2024. Printable and citable 2020 version is at arxiv.org/abs/2012.12143 .
CPL = Concealed Pointer Lookup
CPL = Coactive Parameter Lists
CPL = Consistent Procedure Linkage
CPL = Compiler and Programming Language
CPL = Conceived by Paolo Luchini
This is CPL, a high-level programming language designed and developed by Paolo Luchini. Copyright 1993-2024 Paolo Luchini CPLcode.net
The tools 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 cpl info command. cpl info <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 cpl make <file>. If the curl command is installed (on Debian and derivative systems, apt install curl), cpl also checks for updates available.
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 cpl in a terminal to try it, and copy the cpl.desktop shortcut to your Desktop.
The cpl 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 cpl 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 permanently disabled, if undesired, by editing the cpl script and turning ENABLE_UPDATES to NO.
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.
If the initial setup did not report any errors, and $HOME/fri/cpl or /usr/local/fri/cpl do work, this is likely to be a Unix PATH problem. cpl setup makes commands available to the Unix shell by placing symbolic links in either $HOME/bin or $HOME/.local/bin (if installed by a user), or /usr/local/bin (if installed by root). In many Linux distributions these directories are automatically placed in your PATH, but if they aren't, you may need to manually modify your .profile shell configuration. Please see your shell's instructions (man sh) for what is a PATH and how to do so.
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 cpl as your shell (either cpl Helloworld or just cpl and then Helloworld).
Why should I be concerned with compilation, anyway?
If you are more at ease with the single-command execution usually provided by an interpreter, just use cpl as you would your interpreter. Compilation will happen automatically in the background. You can even put #!/usr/bin/env cpl at the top of your CPL source file, and thus make it executable under the unix interpreter convention.
I want to use a specialized C compiler
In order to apply a non-default C compiler to your CPL program, define the CC environment variable, either for the duration of the session as in
export CC=<myspecialCcompiler>
or for the span of a single command:
CC=<myspecialCcompiler> cpl make <myprogram>
With a compiler other than gcc, you may be unable to use nested functions. If you want some of the pre-packaged libraries to be recompiled, just include a symbolic link for each in your local working directory as in the example
ln -s ~/fri/rbmat.cpl .
The original won't be affected.
Installation fails with error messages mentioning fenv
fenv.h is the system C library that deals with floating-point exceptions. The default for most compilers is to ignore floating-point errors (such as overflow or division by zero) and continue with the result value set to nan. The CPL default is to stop the program with an error message. Exceptions can be enabled or disabled using the fenv library, but unfortunately not on all systems. Notably on Apple macOS, compilation failures have been observed. If you encounter this error, you may want to retry the installation with option -Dnofenv, as in sh /tmp/unpack-cpl.sh -Dnofenv. If you just want to ignore floating-point errors for the scope of a single program, cpl make <myprogram> -Dnofenv.
I want a shortcut key to compile my program (or my LaTeX text)
When you are in the full-screen editor (see (editor)full-screen), pressing Run in the menu, or Esc r or Ctrl-r or F2 as shortcuts, compiles and runs the current .cpl file if you are editing one. If you are editing a .tex or a .gnu file, the same command runs it through pdflatex or gnuplot respectively. The command that Run runs can also be customized: if you previously gave an interactive command in the same session, this last command is repeated. Therefore, if your needs are different from the above default, just give a custom command once in the interactive cpl command screen (accessed by pressing Esc ` or F1), revert to editing by pressing Esc ` or F1 again, and to later re-enter your custom command, just press Run or one of its shortcuts as above.
My program crashes without any error messages
Always put USE rtchecks near the beginning of your main program file 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 interactive cpl from a desktop icon
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, cpl automatically offers 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 cpl, 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 cpl script and turn ENABLE_UPDATES from YES to NO.
I want to go back to a previous CPL version
You see an error or an unexpected behaviour of your program which did not occur with a previous CPL version? Your butlast CPL version is conveniently available in fri.bak/ . You can run it by its full pathname (e.g., ~/fri.bak/cpl), or restore it by renaming (mv) fri.bak to fri. You can also install any older CPL version from the CPLcode.net/version-archive . After you do so the older version will be in fri/, whereas your current version will have been moved to fri.bak/; if this is confusing, or if you need more than two versions at the same time, you may want to rename (mv) fri/ or fri.bak/ or both to something else (say, their version date). If you confirm that the newest version misbehaves, don't forget to inform the developer.
I want to use interactive cpl and/or edit remotely
Interactive cpl is perfectly compatible with ssh. Just ssh into your remote system where CPL is installed, and give the cpl 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 even log in from your smartphone using ConnectBot or similar. Alternately, if all you need is the editor, you can access a remote file from a local editor session (see (editor)Load-and-save).
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 cpl editor by its unaliased name cpl edit (with a space), or as edit within interactive cpl, or you can modify your PATH so the edit link to cpl takes precedence.
On my system, edit used to pop up a different editor
If to have 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 cpl editor as cpl edit.
My .cpl source files get rejected when sent as email attachments
A number of popular email servers automatically reject files with the .cpl extension (amid a list of about 50 extensions), even when found in compressed archives, because this extension is shared with MS Windows Control PaneL files. This precaution is overkill insofar as it is based on extension only, but we cannot change the server policy. A possible remedy is to rename your files to another (e.g., .cplcode) extension before sending, and rename them back on the receiving end; the cpl rename command exists for this purpose. Or else do as you would for a large attachment: upload your files to a cloud service and email their shareable link.
CPL = Concealed Pointer Lookup
CPL = Coactive Parameter Lists
CPL = Consistent Procedure Linkage
CPL = Compiler and 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 bracket pairs can be used interchangeably wherever a parenthesis is needed, in either declarations, statements or expressions. (see Parentheses)
Subroutine and function declarations (see 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 Coactive Parameter Lists, meaning that the type definition of a function's parameter list coacts with its name in identifying the function or subroutine. In other words, functions of the same name and different parameter types are allowed and considered distinct by the language. In addition, not just commas but any non-ambiguous single characters or character sequences may be used as parameter separators in function calls, and equally cooperate in disambiguating the function (see Coactive Parameter Lists). 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 can 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.
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.
Round, square and curly bracket pairs can 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. To alternate 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 implied that the other two styles can be used just as well. In such prototypes, square brackets denote optional items.
C preprocessor #define and #if constructs (manpages.debian.org/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.
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 by some email services to avert malicious exploitation of 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, or send them through cloud storage as you would a large attachment.
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. Notice that this implies that further USE or #include (see C Interface) statements nested inside the USEd file are automatically visible in both the remainder of the program and following USEd files. This is different from the usage of C headers, which have to be repeated in every program file that uses them. In order for repeated USE or #include statements to be allowed, even where not required, such repetitions are silently ignored.
The main difference between USE and INCLUDE is that the USEd 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, producing the same effect as a Modula-2 definition file or a C header.
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.debian.org/touch.html). Missing this, empty subroutines will be recompiled with obviously unwanted results.
In an interpreted program (cpl i) 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.
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.
The directive
#include file [link options]
or
#include <file> [link options]
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 that need to be transmitted to the loader can follow (unquoted) the filename on the same line, or alternately be given in a separate
#link link options
directive.
A C code excerpt may also appear embedded in the CPL source file in the form of a C SECTION.
A few common libc headers, namely
unistd.h
stdlib.h
stdio.h
fcntl.h
math.h
limits.h
float.h
string.h
time.h
setjmp.h
errno.h
signal.h
are pre-included in all CPL programs and need not (but may) be included again.
C code excerpts can be inserted amidst 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 C SECTION is scanned by cpl and any identifiers declared in it become transparently available to the CPL program.
Alternately, C code may also be inserted between <∗ and ∗> as in:
<∗ some text ∗>
The text that appears between <∗ and ∗> is copied verbatim into the generated C file without the CPL compiler verifying its syntax or extracting any identifiers. It is up to the programmer to ensure that the resulting code makes sense, thus this construction should only be used as a last resort for non-standard features.
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 ∗ note 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).
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.
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 Coactive Parameter Lists, meaning that the type definition of a function's parameter list coacts with its name in identifying the function or subroutine. In other words, functions of the same name and different parameter types are allowed and considered distinct by the language. In addition, not just commas but any non-ambiguous single characters or character sequences may be used as parameter separators in function calls, and equally cooperate in disambiguating the function. 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 can also be OPTIONAL, and be specified by name rather than by position.
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 bracket pairs can be used, just as everywhere else (see 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.
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.
A function can also be declared implicitly where a function argument is expected, by providing the function body as the actual parameter. See Implicit function declaration.
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.
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 alternative 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 transformarray[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.
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, INLINE:(symbolic).
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.
The parameter list of a function or subroutine can include optional items. In the function declaration, optional parameters must appear after all standard positional parameters (if there are any), separated by the keyword OPTIONAL or by its abbreviation -- . Each optional parameter must be assigned a default value in its declaration. For 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 positional 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 positional parameters (if there are any) and can appear in any order (or not appear at all); they are identified by name or type rather than by position. In <name>=<value> notation,
test(3,w=1E2,y=1)
is a possible calling statement for the above example, equivalent to test(3,y=1,z=3,w=1E2). Alternately, if an optional parameter can be unambiguously identified through its type (a typical use case is when parameters have different ENUM types, or when there is just one optional parameter), <name>= can be omitted (but doesn't have to, for the sake of clarity) and the notation becomes similar to a positional parameter.
Optional parameters by their very nature cannot be used to disambiguate overloaded function names in Coactive Parameter Lists.
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).
Coactive Parameter Lists (aka function overloading) are 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 single or multiple runtime specified types: see DYNAMIC.
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 its 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 previous 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: FunctionsIndex 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.
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.
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.
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.
Primitive type declarator for boolean logical variables translated into C type int. (see 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.
Primitive type declarator for character variables translated into C type char. (see 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 (see CHAR:Builtin).
Primitive type declarator for integer variables, translated into C type int.
Can be implicitly converted to REAL. REAL to INTEGER conversion must be explicit (see INTEGER:Builtin).
See also INTEGER operator.
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 (see SINGLE:Builtin).
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 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>)
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 features:DYNAMIC.).
A structure declaration may also contain one or more anonymous fields.
If a type declarator appears alone in the declaration of a STRUCTURE field, without any following variable name, 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 CPL's way to define 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 Coactive Parameter Lists, DYNAMIC.
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 allocated 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 arguments 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.
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 1. 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.
The special dimension ∗ can only appear in the declaration of an ARRAY formal parameter (array parameters) 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 ALL looping when requested. The actual upper and lower bounds of an array index can always be recovered through functions HI and LO, and the total number of elements through 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 (see 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)
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), in the specification of a FOR loop, or in arithmetic or comparison operations in which the first variable is an index, subrange or POINTER INTO which HI is defined for.
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+3), in the specification of a FOR loop, or in arithmetic or comparison operations in which the first variable is an index, subrange or POINTER INTO which LO is defined for.
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.
INTEGER(<lower bound>..<upper bound>)
is a type declaration that defines a subrange of INTEGERs, much like Pascal's ... . While for most purposes a subrange can work like an INTEGER, its use enables the LO, HI and ALL operators, and range violations are captured by rtchecks. Example:
INTEGER(1..5) i
LOOP FOR ALL i
IF i+1 > HI THEN WRITE i
REPEAT
ENUM (much like Pascal enumerated type or C enum) variables take a finite set of explicitly enumerated labels as 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 an alternative within a predefined set, 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, however LOOP FOR ALL works for ENUM indices.
Type declaration STRING is a synonym 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 double quotes. They can contain any printing character, including UTF-8 or similar multibyte characters if allowed by the chained C compiler, except , newline and null. In addition, C escape sequences are literals, but contrary to C must be placed outside quotes. A character or a newline can be included in a literal as a concatenated escape sequence outside quotes. As an additional bash-like notation, multi-line literals containing quote characters and newlines may alternatively 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. The null character is used for termination and cannot appear anywhere inside a STRING. The empty string is also a valid (zero-character-long) STRING value.
Where appropriate, a single-byte 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∗ (manpages.debian.org/string) are also interpreted as producing a STRING value. Just as for all C library functions, it is the programmer's responsibility to ensure that such functions are used consistently and to free any 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. Memory space for such strings is automatically allocated and freed without the programmer's intervention.
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.
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.
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 repeating operations.
CPL is not an object-oriented language in which functions are supposed to be declared within object class declarations, and intentionally so. Object-like behaviour is achieved with ordinary STRUCTUREs through overloaded function names (see Coactive Parameter Lists) and implicit type conversion of an anonymous STRUCTURE.
In addition, situations where the type of an object is not decided 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 parameters, or an error is thrown if no such function exists.
Pointers to variables of any but ARRAY(∗) and STORED type 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.
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.
A postfix ^ denotes either pointer dereferencing (see ^: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
A subroutine variable may be specified through the type declarator
SUBROUTINE( <argument declarations> )
A function variable may be specified through the type declarator
FUNCTION( <argument declarations> )-><type> or else
<type> FUNCTION( <argument declarations> )
the first form being mandatory when the result has a pointer or array <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.
Where a subroutine or function variable is expected (in an assignment or as an actual parameter to a call expecting a function-type argument), in alternative to the name of an existing function the implicit definition of a function can be passed. Such an implicit declaration is formed by the body of the function as it would appear after the = sign in a (short form:FUNCTION) declaration, with the formal parameters identified by the names declared in their original function-variable declaration. For example,
SUBROUTINE PLOT(FUNCTION(REAL x)->REAL f; REAL x1..x2)
(a declaration actually contained in graphics.cpl or gnuplot.cpl) declares a formal parameter f that takes an argument named x. After this declaration
PLOT SIN(x^2),0..2∗PI
is a valid call and implicitly declares f(x) = SIN(x^2) for the scope of PLOT. For greater clarity, or in rare cases where this syntax may generate confusion, angle brackets can be added as in the alternative syntax
PLOT <SIN(x^2)>,0..2∗PI
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:TYPE. 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.
DIV is the INTEGER division operator, and produces a truncated-towards-minusinfinity INTEGER quotient a DIV b = FLOOR(a/b) (see FLOOR). a/b is reserved for REALs, and produces a REAL result even with INTEGER operands a,b.
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, defined in such a way that (a DIV b)∗b + (a MOD b) always equals a.
Notice on the truncation of integer division: CPL defines a DIV b = FLOOR(a/b) so that a MOD b has the same sign as b independently of the sign of a, and modular arithmetics can be applied to negative as well as to positive a's, for example in array index manipulations. This implies that a MOD b is negative for negative b, but this case has hardly any uses.
Care should be taken when porting programs from languages that truncate negative division towards zero, 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.
The first four apply to numeric types. The = equality and # inequality operators apply to any type, including pointers. Since pointers are implicitly dereferenced (seeConcealed 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. This is the only unavoidable use of dereferencing in CPL.
It can be noticed that no need exists to denote equality by any other symbol than =, because no other meaning is given to = inside an expression. In the C language the need for a separate symbol == arises because = is reserved for assignment even when it occurs inside an expression. This can sometimes make a C compiler accept hard-to-spot typing errors as meaningful code. CPL has no concept of assignment expression, and therefore no confusion can arise. Similarly no confusion can arise when = occurring before an expression actually denotes an assignment (see Assignment), because its alternate interpretation as a hanging comparison is not part of the language. (In C, by contrast, a hanging expression is allowed for its possible side effects, and its value is simply discarded.)
The type-testing operator IS is described in Object-oriented features:DYNAMIC.
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.
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 existing (manpages.debian.org/string) functions.
When a string literal is defined in << DELIMITER notation (see STRING), a variable included as DELIMITER <variable> DELIMITER is concatenated within the string being defined just as above (whereas DELIMITER <newline> ends the definition).
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 in
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)ABS, Next: CEILING, Prev: Builtin, Up: BuiltinIndex ABS: absolute value
Function ABS returns the absolute value of an INTEGER, REAL or COMPLEX number, or of an ARRAY of those.
Page: (cpl)CEILING, Next: FLOOR, Prev: ABS, Up: BuiltinIndex 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: BuiltinIndex 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: BuiltinIndex 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: BuiltinIndex 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: BuiltinIndex 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: BuiltinIndex 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: BuiltinIndex 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.debian.org/rand.html).
Page: (cpl)GAUSS, Next: ROUND, Prev: RAND, Up: BuiltinIndex 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: BuiltinIndex ROUND: nearest integer
The function ROUND returns the nearest INTEGER to a given REAL value.
A portion of an array can be selected by specifying new dimensions. For example, if the original 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 original 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 an argument 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(∗) argument 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.
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.
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)Top)
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.
When an ARRAY is assigned in a compound = statement to another of the same base type and number of elements, its lower bound is implicitly realigned so all the elements are copied. A similar realignment of the lower bound takes place for function arguments, provided that base type and total number of elements match. Assigning to a different base type triggers a compile-time error; mismatch in the number of elements is flagged by rtchecks at run time.
Realignment also takes place for matrix operations:(matrix):.
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 $).
New meanings can be assigned to the standard operators by the declaration
OPERATOR <type> <id> <op> <type> <id> = <expression>
Similar to an INLINE FUNCTION (see INLINE), this statement does not generate any executable code but determines a compile-time equivalence, or more exactly an inline substitution, of the expression on the right for the operator on the left. For instance, after defining a COMPLEX type and a ComplexAddition function, an operator using this function could be defined as
OPERATOR COMPLEX x + COMPLEX y = ComplexAddition(x,y)
The appropriate realization of the operator is chosen, just as for either INLINE or proper FUNCTIONs, based on the type of its operands. The order of precedence of the standard arithmetic operators cannot be altered.
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 (see Concealed Pointer Lookup). Compound variables can be assigned as a whole if types match.
Owing to their all-encompassing 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 the example (after the declaration REAL a^,b):
REAL a=b equivalent to a^=b.
At any position in the expression appearing 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(~)
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)Top library.
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.
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 (see 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.
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.
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 later dereferencing generates a visible runtime exception. If <pointer> is a file descriptor, FREEing it also writes out any previously buffered data.
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.
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.
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 a 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.
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 a 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 as 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.
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, the statement
WRITE 3∗(INTEGER FROM stdin)
can replace
INTEGER temp
READ FROM stdin temp
WRITE TO stdout 3∗temp
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.
Under cpl i, this console input functionality becomes extended: a cpl shell is opened whenever the program is waiting for input from its controlling terminal, and any number of CPL commands can be executed interactively before answering. The first expression given alone on the command line, which cpl would normally print, gets returned as input instead, and the waiting program resumes. This extension is available to either an interpreted or a compiled program, provided the latter is run under interactive cpl.
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.
A random-access binary file typically contains fields and records at fixed addresses, which can be accessed by POSITIONing the file cursor (see 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).
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 (see C interface).
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 (see C interface).
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>.
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>).
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
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
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 in 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
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.
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.
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 zero or negative <step> in either case would generate a non-terminating loop, which is presumably not what is wanted. To obviate this, when <step> is a compile-time constant and is negative, DOWN TO is implied. Therefore
FOR <index> = <ubound> TO <lbound> BY -1
and
FOR <index> = <ubound> DOWN TO <lbound>
are actually equivalent.
Both INTEGER and REAL types are allowed, as well as other types (for instance POINTER:INTO:) for which increment and comparison make sense. <index> may be an already existing variable or be implicitly declared by the loop itself (see Declarations). In the latter case it acquires the type of the loop bounds and behaves like a CONSTANT within the body of the loop. If the index is REAL, the BY specification 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: FORIndex 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: FORIndex LOOP FOR ALL <index> [,<index>]
denotes a loop whose bounds are automatically determined as the bounds of the first ARRAY that <index> appears in, or as the bounds of the index type if a subrange or a ENUM index is used. ALL should preferably be reserved for short loops, and only arrays of the same dimensions must appear. Ambiguous uses of ALL, such that the same index appears in ARRAYs with nonequal dimensions, are flagged by rtchecks.
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: FORIndex
LOOP FOR <number> TIMES
does exactly that, when just a counter and no indexing is needed.
Page: (cpl)IN, Next: EXCEPT, Prev: TIMES, Up: FORIndex
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 (or its subset specified through subarray selection). 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: FORIndex 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: FORIndex 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>).
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.
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.
Sometimes the need arises to program a state machine or automaton, composed of a set of code blocks (states) to be executed in an a priori unknown sequence where each block decides its immediate follower. For such needs CPL provides a multiple-choice loop.
A multiple-choice loop is a hybrid between a CASE and a LOOP, specified as
CASE LOOP OF
label1: <block>
REPEAT <label>
label2: <block>
REPEAT <label>
..............
labeln: <block>
REPEAT <label>
END CASE
At the end of each labelled block (or nested in its interior, typically within an IF or CASE statement), execution may continue at any other label as specified by the REPEAT statement. Each labelled block is its own scope, and local variables cease to exist at the REPEAT (like at an EXIT) statement (but variables of an outer, enclosing scope are, as always, shared). If the end of a block is reached without an intervening REPEAT statement, the CASE LOOP ends.
While this construct may look like unstructured code allowing an arbitrary goto (and is in fact implemented using one), it maintains statements and labels in the same order as, and is thus no less or more structured than, a CASE inside a LOOP:
ENUM(label1,label2,...labeln,exit) repeat=label1
LOOP
next=repeat; repeat=exit
CASE next OF
label1: <block>
repeat=<label>
label2: <block>
repeat=<label>
..............
labeln: <block>
repeat=<label>
END CASE
REPEAT WHILE repeat#exit
In particular notice that jumping to the inside of a labelled block, as opposed to its beginning, is not allowed. The two constructs above are functionally equivalent, but the first is more efficient and easier to write and read.
Page: (cpl)EXIT, Next: END, Prev: CASE, Up: ControlIndex EXIT <name> transfers control past the end of the LOOP, MODULE, FUNCTION or SUBROUTINE named <name>, terminating the corresponding scope and deallocating local variables. EXIT may be nested in an inner scope, such as a further loop or an IF statement.
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.
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.
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.
All tools that act upon a CPL Source file are managed by a shell script named cpl (formerly makecpl).
Usage
Compiler: cpl make|remake|run <program>[.cpl] [<option>]
Interpreter: cpl i [<expression>|<program>[.cpl]]
Editor: cpl edit [<file>]
Info help: cpl info [keyword]
Swap extension (for mail attachments): cpl rename <file list>
Version and usage: cpl version|usage|help (or any unrecognized word)
Abbreviations
cplm == cpl m == cpl make
cpl (with arguments) == cpl run
cpl (with no arguments) == cpli == cpl i
edit == cple == cpl e == cpl edit
cpl ? == cpl info
Backward compatibility
makecpl == cpl make
makecpl -a == cpl remake
makecpl -r == cpl run
icpl == cpl i
infocpl == cpl info
makecpl (with no arguments) == cpl version|usage|help
Description
The basic command
cpl make <program>[.cpl] [<option>]
(or just cplm) is all that is needed to compile and link the source file <program>.cpl into the executable file <program> , which you can then run by its name just like any other executable. As an alternative, the straight cpl run command compiles and then runs <program> in one shot.
cpl's task is achieved by first running the CPL-to-C compiler cpl.fri, 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 <program>.d, and then running make (manpages.debian.org/make.html) on this makefile. Any arguments possibly present on the command line are passed on as options to the designated C compiler and linker, or to <program> itself if run was specified.
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 compiler options are interpreted by cpl.fri itself, in addition to being passed on 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 for this directory can be specified through the environment variable OBJDIR or the -o option.
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
cpl remake <program>
achieves this by deleting the object files first.
cpl followed by an unrecognized word (e.g. version|usage|help)
prints a short version and usage message and exits.
If your system includes the curl command (on Debian you can install it as apt install curl), cpl provides the functionality to transparently poll the CPLcode.net website for updates. See automatic updates.
Page: (cpl)cpl run, Next: cpl.fri, Prev: cpl, Up: cplIndex
cpl run <program>[.cpl] [<argument>]
(or just cpl if no confusion arises) compiles and runs <program> in one shot, or just runs it without re-compiling if an executable is found that is not older than its source code. <argument>'s are passed to <program> itself.
Unix interpreter directive (hashbang)
Taking advantage of the unix interpreter directive, a CPL program that contains either #!/full/path/to/cpl run (with the appropriate full path) or #!/usr/bin/env cpl as its first line is directly executable, while maintaining its compiled form. Under the hood it undergoes automatic recompilation if and when modified.
The CPL compiler cpl.fri is not an executable file, but rather a formal description of the language that is interpreted by the Formal Rule Interpreter (fri)Top. The cpl.fri 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.fri <source.cpl>
On systems where the #! convention does not work as expected, this command must be expanded into
fri cpl.fri <source.cpl>
All this is normally handled inside the cpl driver script; direct use of the cpl.fri command is not needed.
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)Top). 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.
A CPL manual in info format is available through GNU info. Its reading command is encapsulated as cpl info. Typing cpl info with no arguments takes you to the head of the file you are now reading. Typing cpl info <keyword> brings you help about a specific CPL keyword.
mpicpl [make] <program>[.cpl] [options]
allows the compilation of MPI programs. It invokes cpl make with the required options, obtained from mpicc, and follows mpicc's same syntax (manpages.debian.org/mpicc.html).
The interactive CPL interpreter, accepting expressions, statements or commands at a console prompt, is launched by the command
cpl i
(or just cpl when there are no arguments). Interactive cpl'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, thus e.g. 2+2 is equivalent to WRITE 2+2 and outputs 4. In addition when a command is not recognized, it gets passed to the unix shell before giving up. A shell command can also be given explicitly by prefixing it with :. ? brings this manual to you.
An interactive CPL command can alternately be specified on the cpl i command line, as in
cpl i <command>
<command> may need to be quoted if it contains characters that are specially interpreted by the shell. 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 cpl as its first line and turning on the executable permission flag.
The
cpl
command with no arguments launches an interactive cpl session.
cpl i followed by a CPL expression (possibly quoted to avoid interpretation by the shell) calculates and prints that expression.
cpl i 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 cpl i -USE <libraries>. cpl with no arguments is equivalent to cpl i -USE rbmat,cbmat,gnuplot,rtchecks.
When the input of cpl i is redirected (or piped) from an alternate source, commands from that source are interpreted on a line-by-line basis.
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.
Any command that is meaningless to interactive cpl, 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>
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 interactive cpl, 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 of the program are accessible to the run-time generated command.
An interactive cpl 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 interactive cpl to start with. All the global variables and subroutines defined in either the compiled or the interpreted part of the program are accessible to the interactive session.
The inner session thus opened is signalled by an additional > in the command prompt, and can accept any interactive cpl 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.
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.
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 interactive
cpl, see below)
T,t: TROFF. Turn tracing off and continue running as normal.
C-c,I,i: Open an interactive cpl (cpl i) 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
cpl i <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.
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.
INLINE ARRAY(A.LO..A.HI) OF COMPLEX FUNCTION eigenvalues(COMPLEX A(∗,∗))
INLINE ARRAY(A.LO..A.HI) OF COMPLEX FUNCTION eigenvalues(REAL A(∗,∗))
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.
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.
OpenMP is a standard for parallel computation on shared-memory architectures. (see www.openmp.org). It is built-in in many FORTRAN and C compilers, gcc included, and is activated through suitable command-line options. C-style OpenMP directives are transparently available in CPL. For example,
serial:
LOOP FOR i=1 TO 1000000
a(i)=b(i)+c(i)
REPEAT
parallel:
#pragma omp parallel for
LOOP FOR i=1 TO 1000000
a(i)=b(i)+c(i)
REPEAT
Compile the parallel version as cpl make <program> -fopenmp.
Much of the same applies to OpenACC directives, another standard for parallel execution especially on GPUs, or in fact to any #pragma's that are construed similarly.
Implementation notes:
1) If a parallel program is compiled without the -fopenmp option, #pragma is treated as a comment, that is you obtain the serial version of the program. This is intended and conforming to C behaviour.
2) C compiler options, of which -fopenmp is one, must appear after <program> on the command line in order for them to be distinguished from cpl options (see cpl).
3) The omp for directive can only be applied to a basic LOOP FOR construction (see LOOP:FOR.), which gets translated to for in the intermediate C code. Other types of LOOP or DO do not involve a for and are not recognized by OpenMP.
4) If an omp parallel or other directive needs to be applied to a block of code that is not implicitly delimited by the subsequent instruction, this block can be delimited by MODULE ... END MODULE (see MODULE).
5) As an aid to directives that explicitly affect individual variables, if a CPL identifier is recognized in the parenthesized part of a #pragma, it gets translated to the corresponding C identifier; if this identifies a CPL ARRAY, its size and offset are automatically included in an OpenMP-compatible syntax. All other parts of the #pragma are copied verbatim.