oo2c
?
oo2c
is the first complete working compiler of the OOC project.
Instead of translating Oberon-2 modules to machine code, it generates code
for the most portable assembler in existence: ANSI-C. The compiler was
initially intended as a prototype backend for OOC, which could then be used
to evaluate and debug the frontend and the optimization modules. However,
it is now a full-fledged development system, and among other things, it is
being used to develop native code OOC backends.
The choice of a high-level language as intermediate code has one distinct
advantage: portability. Given a working ANSI-C compiler, oo2c
runs on most modern Unix workstations; porting it to other platforms is
possible with minimal effort. With oo2c
, programs can be developed
that run without modification on all major Unix brands. Differences
between the systems are masked by the implementation of low-level modules,
which take system peculiarities into account.
The special pre-compiler nature of oo2c
also has its disadvantages.
The user should be aware of these points:
oo2c
depends on an existing ANSI-C compiler. Without such a
compiler, oo2c
cannot be used. In particular, a K&R compiler is not
sufficient. Additionally, the C compiler has to support long identifiers;
typical identifiers in the intermediate code are much longer than the
minimum requirements stated in the ANSI-C specification. This should not
generally be a problem because most C compilers and linkers are packaged
with a C++ compiler, and C++ does not limit the length of identifiers.
oo2c
depends on a third party garbage collector. Because it uses
Hans-J. Boehm's excellent conservative garbage collector, this is not
strictly a disadvantage. But in environments where this garbage collector
does not work, both the compiler and programs build with it are somewhat
hampered (see section Preparing for Installation).
oo2c
is only portable within limits. While
oo2c
emits true ANSI-C code and works with standard C types (structs,
arrays, and the like), it is doing all address calculations for structure
and array accesses itself. There are no field or element selectors in the
code, just plain memory accesses. Therefore, oo2c
requires that all
basic types have the expected size, and that the C compiler arranges
structures and arrays in the expected way. For this reason, there are
different versions of oo2c
for 32 and 64 bit systems.
oo2c
, and then it is translated into an object file by the
C compiler. The second step usually takes much longer than the first, and
therefore, the speed of the C compiler has great influence on the time it
takes to build an object file. The impact of this is lessened by an
intelligent make facility that tries to minimize recompilations.
oo2c
's C output. Doing
such elementary checks by means of a high level language like C has a large
impact on the executable program's size and execution time. For this
reason, not all possible run-time checks are supported; oo2c
does not
support detection of integer overflows, nor most kinds of real overflows.
oo2c
oo2c
can use two add-on packages to expand its own capabilities:
Hans-J. Boehm's conservative garbage collector gc
and GNU
libtool
. The garbage collector extends oo2c
's run-time system
with the ability to free unused heap objects. With the help of
libtool
, oo2c
can create shared and static libraries from a
set of modules.
Although both packages are optional, their use is highly recommended. If
either one of these packages is not available, configure
will abort
with an error. To install oo2c without them, each package must be
explicitly disabled by setting `--disable-gc' or `--disable-libs'.
If gc
is installed as a library on the system, configure
detects this and uses it automatically.
Otherwise, get the garbage collector sources from Boehm's server
http://reality.sgi.com/employees/boehm_mti/gc_source/
and unpack the tar file in oo2c
's top-level directory.
Check the files `README' and `Makefile' in the newly created
subdirectory `gc' in case the gc
package needs some special
adjustments. When installing oo2c
, the garbage collector sources are
detected, and are compiled and installed automatically. Please note:
The garbage collector subdirectory is not affected by running oo2c
's
configure
script; only the environment variable CC
is
overridden when calling make
.
To install oo2c
without a garbage collector, the option
`--disable-gc' must be passed to configure
. The drawback is, of
course, that programs cannot free memory that has been dynamically allocated
using NEW
. For short running programs, which request only a small
amount of memory, this is not a severe problem. However, process size for
long running, memory intensive programs can grow without bound until memory
resources are exhausted.
During the installation process, a long running, memory intensive
program is started: oo2c
is used to compile all of its own
sources in a single run. The process size of such a bootstrap without
garbage collection can grow beyond 100MB. The process might run out of
memory, or you might decide to kill it with Ctrl-C because the
system thrashes wildly. In this case, simply start make
again to
finish the job. oo2c
detects files that have already been
compiled successfully, and does not try to compile them again. This
way, oo2c
can be installed on any system without garbage
collection.
libtool
can be obtained from any GNU server, e.g.
ftp://ftp.gnu.org/pub/gnu/. The package's home page is
http://www.profitpress.com/libtool/.
Unpack the tar file and follow the directions in the file `INSTALL'
to install libtool
on your system. oo2c
's installation
process detects libtool
, and uses it to create a static and, if
possible, a shared library for the OOC standard modules.
The basic way to build and install the oo2c
package is as follows:
oo2c
for your system. If you're using
`csh' on an old version of System V, you might need to type sh
./configure instead to prevent `csh' from trying to execute
configure
itself.
If you want to install oo2c without garbage collector support, run
configure
using the option `--disable-gc', and if libtool
is not available, use `--disable-libs'.
Running configure
takes awhile. While running, it prints some
messages telling which features it is checking for.
configure
created, type make distclean.
If all went well, you should now have a working Oberon-2 compiler. Since ancient times, this joyous event is celebrated by writing a little program saying "Hello World!". Tradition is important, so here is a step-by-step description of how to do it:
MODULE Hello; (* Author: Anonymous; program believed to be in the public domain *) IMPORT Out; BEGIN Out.String ("Hello World!"); Out.Ln END Hello.
oo2c
to make an
executable program, and -v tells it to be more verbose with messages
while doing this. Now, you should have quite a few files in the directory:
`Hello.Mod', `Hello.Sym', `Hello', and some files ending in
`.c', `.h', `.d', or `.o'. See section Initialization Files on how to automatically move these intermediate files into other
directories.
Some systems require unusual options for compiling or linking that the
configure
script cannot address. You can give configure
initial values for variables by setting them in the environment. Using a
Bourne-compatible shell, this can be done at the command line as follows:
CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
Or on systems that have the env
program,
env LDFLAGS=-s ./configure
If gcc
is not available on your system, and cc
is a K&R
compiler, try passing CC=c89 to configure. Some systems provide a
separate ANSI-C compliant compiler under this name.
The values of `CC' (the C compiler), `CFLAGS' (additional command
line options to the C compiler), and `LDFLAGS' (additional linker
flags) determined by configure
are used by oo2c
as its default
settings.
By default, make install installs the package's files in the
directories `/usr/local/bin', `/usr/local/man', and so forth. You
can specify an installation prefix, other than `/usr/local', by giving
configure
the option `--prefix=PATH'.
You can specify separate installation prefixes for architecture specific
files and architecture independent files. If you give configure
the
option `--exec-prefix=PATH', the package uses `PATH' as the
prefix for installing programs and libraries. If only `--exec-prefix'
is set, documentation and other data files will still use the regular
prefix.
In addition, if you use an unusual directory layout, you can give options such as `--bindir=PATH' to specify different values for particular kinds of files. Run configure --help for a list of the directories that can be set, and what kinds of files go in them.
Program names and the name of the installation directory can be changed upon
installation. This is achieved by giving configure
the option
`--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. For
example, after
./configure --prefix=/usr --program-suffix=-1.5.1
make install would install the binaries as `oo2c-1.5.1', `oob-1.5.1', and so on in `/usr/bin', and the rest of the compiler's files are put into `/usr/lib/oo2c-1.5.1'.
For performance reasons, run-time checks and assertions are disabled for all library modules and binaries built during installation.
The drawback to this is that any error in the compiler will most likely show itself as a core dump rather than the usual run-time error message. Likewise, programs that pass invalid parameters to one of the library modules might either crash, show strange results, or even, in some cases, work as expected by sheer luck.
As an example of invalid input giving "expected" results, consider an
unterminated character array (an ARRAY OF CHAR
that is not terminated
by 0X
) passed as an input parameter to one of the procedures of
module Strings
. If run-time checks are enabled, the procedure
stops with an error message when it tries to access an index past the end of
the array. Without run-time checks, it simply continues to process
characters beyond the end of the array until it reaches a 0X
somewhere in memory. Under these circumstances, the procedure's result is
undefined.
To install oo2c
with full run-time checks enabled, remove the whole
section PRAGMAS ... END
from `oo2crc.stage1.mk.in' in the
top-level directory before running configure
. This builds
the library modules and executable programs with run-time checks and assertions
enabled. Please note that this has no affect on foreign modules written in C,
like `Files', `Exception', `Types', and so forth.
A simple way to ensure that a single program, say `foo', is created with full run-time checks is to build it once with oo2c --make --all foo. This compiles all modules imported directly or indirectly by module `foo', including the library modules it uses (see section Invoking the Compiler). Afterwards, the newly created object files take precedence over the ones built during installation, and will be linked into all programs.
The following sections summarize the command line options accepted by the
programs of the oo2c
package (oo2c
, oob
, ooef
,
and oowhereis
). Some options have two equivalent names: one is a
single letter preceded by `-', and the other is a long name preceded by
`--'. Multiple single letter options (unless they take an argument)
can be combined into a single command line word; for example, `-MA' is
equivalent to `-M -A'. The special option `--' denotes the end of
the option list. That is, any argument after `--' is not treated as an
option even if it begins with `-'.
Each of these programs first evaluates the global initialization file, and then parses the command line options from left to right. In the default installation, the global initialization file is `/usr/local/lib/oo2c/oo2crc'. This can be changed by setting the environment variable OOC_CONFIG or using the option `--config'. How an environment variable is set depends on the shell:
bash: export OOC_CONFIG=my_config_file csh : setenv OOC_CONFIG my_config_file
The option `--config <file>' supersedes both the hard coded file name and the value in the environment variable OOC_CONFIG. This option takes effect before the initialization file is read, which is different from all other options. There should be at most one `--config' statement. However, by using the option `--include-config <file>', it is possible to specify additional files to be evaluated for configuration. Specifying the empty string as initialization file (e.g., with `--config ""') disables parsing of all default files.
All programs use the same initialization file. Most of the configuration data pertains just to the compiler, but the `PATHS' section is also utilized by the other tools. `PATHS' defines a list of file patterns and paths describing where the programs should look for files, and destination directories for any files that are created. For details on file name resolution see section Initialization Files. Except for the compiler, all programs accept the option `--help' (or `-h' for short).
The compiler oo2c
performs a number of different functions depending
on the command line options it was invoked with.
The primary functions of the compiler and the options that trigger them are listed below.
oo2c [options] <module>...
oo2c (--make|-M) [options] <module>
oo2c (--make-lib|--install-lib) [options] <module>
libtool
. See section Creating Shared or Static Libraries.
oo2c --makefile <file-name> [options] <module>
make
how to perform the necessary
steps to create an executable program from a set of intermediate C files.
If none of the special flags listed above is present, oo2c
assumes that
all arguments are names of source files and tries to compile them to ANSI-C
code. Compiling a single module in this way can be used to check the syntax of
a module, or selectively force recompilation of modules. If a file name
argument has no directory part, the compiler searches in the directories listed
in the `PATHS' sections of the initialization files (see section Paths of Input and Output Files). Any errors encountered while parsing a source file are written to
`stdout' like this:
In file foo.Mod: <pos>: <num> <error message>
Here <pos> refers to the position of the error in the file (the first
character has position 0), <num> is the error number, and the rest is
a plain text error message. The message format can be changed with the
filter program ooef
(see section Converting Error Messages).
The option `--make' (or `-M' for short) turns a set of Oberon-2 modules into an executable program. The single argument can be either the name of a module or a module's file name. Again, the compiler searches the directories in `PATHS' if the file name has no directory part. The compiler inspects all modules imported directly or indirectly by the given main module. For every module that is compiled, the compiler decides whether it needs to be translated to C or not. A module is compiled (or recompiled) if
In the next step, all necessary object files are generated by invoking the C compiler. Typically, this is the most time consuming part of the translation process. The final step links object files and libraries into an executable program with the same name as the main module. Note that, unlike the Oberon System, there is no notion of commands (i.e., parameterless exported procedures, which can be activated directly by the user). Instead, the module body of the main module specifies the program's execution sequence.
Sometimes, it is desirable to recompile all modules contributing to a
program because, for example, some intermediate files were corrupted or a
different set of options needs to be applied to all modules. In this case,
the option `--all' (or `-A' for short) should be used together
with `--make'. This forces oo2c
to recompile every module, from
scratch, that is part of the program and whose source code is available.
Another option modifying the behaviour of `--make' is
`--no-build', which causes the make process to stop after the C code is
written, but before the C compiler is invoked to create the object files.
With `--makefile <file-name>', a so-called `Makefile' is written
to the specified file. This option takes a single argument: the name of a
module or a file name. The generated file contains the rules necessary to
use the make
utility to build an executable program for the main
module from existing C code. This feature is typically used to transfer a
program to a system on which oo2c
is not installed. By default, the
file contains only rules to compile C code into an executable program.
Specifying `--oc-rules' also writes rules that will run oo2c
and
produce, one at a time, the necessary C files from their Oberon-2 code.
The following command line options can be used with every mode of operation
for oo2c
. The variable names shown with some of the options refer to
the configuration variable associated with that option (see section Option and Pragma Variables).
oo2c
needs a C compiler to translate its ANSI-C code to object files,
and to link object files and libraries into an executable program. The
following options specify the name of the C compiler and the options that
are passed to it. Every command line option modifies the value of a
variable of the initialization section `OPTIONS'. For example, writing
`--cc gcc' in the command line is equivalent to adding the assignment
`cc := "gcc"' to the `OPTIONS' section. The default values for
the various variables are set by configure
when installing the
compiler. With the exception of `gcflags' and `valueStack', the
variables listed here have no effect as long as oo2c
is only called
to translate single modules. They are the building blocks for the argument
list passed to the C compiler when generating an object file, linking a
program or a library, or writing a makefile.
oo2c
supports two mechanisms to allocate space for a procedure's
value parameters of open array type. The first works with a fixed block of
heap memory as auxiliary stack, and the second uses the function
alloca()
to reserve the necessary space on the normal stack. The
advantages of alloca()
are efficiency, elegance, and, in practice,
unlimited stack space. The disadvantage is that it is not an ANSI-C
function, but rather a BSD extension, which is not available on some
systems. The alloca()
variant is chosen by setting `valueStack'
to `-1'.
Specifying a positive integer value for `valueStack' tells oo2c
to allocate a block of heap memory of the given size to store open array
value parameters. The size is then hard coded into the program and is fixed
throughout program execution; this size must be chosen carefully in order to
avoid stack overflows. If the C compiler doesn't support alloca()
,
the default value of `valueStack' is 64KB. All modules contributing to
a program should be compiled using one of these two mechanisms: either for
alloca()
or for the auxiliary stack. It is not recommended that they
be mixed within the same program.
A library file (or simply library) is a type of file archive, which contains a group of object files. Library files may be either static or shared.
oo2c
depends on GNU libtool
for the creation and
installation of libraries. This package is available for a wide variety
of systems, and it handles all system specific details of libraries.
Support for particular systems may vary, but in practice libtool
supports static libraries on all systems, and shared libraries on most
systems. The oo2c
package includes libtool
. If necessary,
it is automatically installed, under the name oolibtool
. If a
version of libtool
already exists on the system, it is used
instead of oolibtool
by default.
Static libraries are used by the link editor, which combines libraries with other object files to create a single executable program.
On the other hand, shared libraries (also called dynamic
libraries) are not stored in the executable program file itself, but are
loaded into memory by the run-time loader just prior to execution of a
program. Only one instance of a shared library needs to be loaded into
memory at any particular time, even when more than one program is using it.
This can be beneficial especially if several programs share a large number
of modules, as it can provide a substantial savings in both memory usage and
disk space. But keep in mind that libtool
, and therefore
oo2c
, does not support shared libraries on all systems.
The `--make-lib' function of oo2c
turns a set of modules into a
library. Just like `--make', it takes a single argument: the name of a
module or a file name. The main module must look like this:
MODULE FooLib [LIBRARY "foo" "1:2:3"; INTERFACE "C"]; IMPORT ...; END FooLib.
This creates a library `foo' with version number `1.2.3' from all
modules imported directly or indirectly by module `FooLib'. Modules
that are already a part of another library are not be included in
`foo'. Both a shared library and a static library are built, or, if
the system does not support shared libraries, just the static version. For
information about the version number and how it has to be maintained, see
section `Library Interface Versions' in Libtool Manual.
To install a newly created library use `--install-lib'. This command
takes the same arguments as `--make-lib', and invokes
libtool
to install the library in the target directory.
Subsequent import of any module that is part of the library causes
oo2c
to link against the library file instead of the module's object
file. Note that recompilation of such a module, either through an explicit
command or during a make, will undo this. The compiled module and all
modules importing it will then be linked against the object files.
Linking against libraries that haven't yet been moved to their final
destination is slightly tricky. Some systems encode the absolute path to
the shared library directly in the executable program, and libtool
has to do some special magic to produce a program that works both before
and after the libraries have been installed.
To link against uninstalled libraries, the option `--libtool-link' must
be used. This modifies the linker invocation to link against libtool
libraries from the current directory (for example `libfoo.la'), instead
of the library itself. The resulting command string is then passed to
libtool
.
If uninstalled shared libraries are involved, libtool
places the
executable program in the directory `./.libs', and a wrapper script of
the same name is placed in the current directory. Until the libraries are
installed, only the wrapper script can be executed. If only static
libraries are involved, the program is placed directly in the current
directory. When installing the program, the user should take the file from
`./.libs' if it exists, and the one in the current directory otherwise.
Use of libtool
and the installation path can be adjusted with these
options:
libtool
script, which is used with
`--make-lib' and `--install-lib'. Note that the default value of
`libtoolCmd' depends on how libtool
was installed on the system:
If libtool
was installed prior to the oo2c
package, its path
is used for `libtoolCmd'; otherwise, `libtoolCmd' is set to
oolibtool
.
install
command, the
default prefix is defined to be something like `/usr/bin/install -c -m
644'. Otherwise, oo2c
uses a replacement script with similar
functionality.
libtool
to do final linking. This
option is provided for linking against libraries that have not yet been moved
to their final destination.
There are a number of ways to inspect the inner workings of the compiler
during the optimization steps and in the backend. The following command
switches are specific to oo2c
; it is unlikely that other
implementations of OOC will share even a subset of them.
oo2c
, `proc' and `program' are not supported yet, and (at
the moment) `module' has no noticeable benefits compared to
`gproc'.
Note that the above table is not exhaustive; other undocumented options may exist that are of interest only to the compiler writers.
The location of global files depends on the installation. The file names below assume that the default setting --prefix=/usr/local was used.
The symbol file browser oob
displays the interface definition of a
module as seen by the compiler.
oob [-x|--extended] <module-name>
The program accepts a single argument: the name of a module or a file name, which is stripped of its directory part and any suffix. The symbol file for the resulting module name is located using the `PATHS' section of the configuration data. Once found, the contents of the symbol file are written to `stdout' in a format resembling that of a module.
With option `--extended' (or `-x' for short), the output of
oob
for extended records includes the names of the base types, the
inherited record fields, and the inherited type-bound procedures. Every
definition of a type-bound procedure is listed, even if it is redefined on
subsequent extension levels.
The output differs from a true Oberon-2 module, in particular from the original source code, in a number of ways:
IMPORT
statement lists just those imports of the original module
that appear as part of exported declarations. Imported modules which do not
contribute the module's interface are omitted.
*
is omitted. Read-only exports are
explicitly marked with `-'.
Interface definitions can also be extracted from a module's source text; the command oocn -d <module> extracts the public interface of a module (see section Source Code Analysis and Transformation).
The tool oocn
operates on the source code of Oberon-2 modules to
produce various manipulations of the text. These include the following:
rewriting the source text, converting it into different formats, creating
cross-reference information, and extracting the public interface of the
module. It is invoked like this:
oocn <mode> [options] <module>...
The `<mode>' argument determines the operation performed by
oocn
; for example, `--html' sets the mode to translate to HTML.
No more than one mode can be specified at at time. If no mode is specified,
oocn
produces usage information, which lists all modes and options.
The `[options]' are used in addition to `<mode>' to modify the
behaviour of oocn
.
The `<module>...' argument is a list of one or more modules, or file names of modules. If it is a file name, the directory part and extension are discarded. Modules are then located by means of the `PATHS' section of the compiler's initialization file.
Except for its use of initialization files, oocn
shares no code with
the compiler proper. In particular, oocn
does very limited error
checking. It reports only a small subset of the errors that are detected by
the compiler, and it accepts malformed Oberon-2 sources, as long as the
errors do not interfere with its own operation.
The following is a list of available modes:
IMPORT
relation;
that is, module `Bar' is written before module `Foo' if `Foo'
depends on `Bar'. For example, the invocation oocn --module Foo
lists the names of all modules that are used to build the program
`Foo', writing the name of the most basic module first, and `Foo'
last. With option `--no-internal', the pseudo module `SYSTEM' is
omitted from the output.
Please note: This function implicitly adds the option
`--closure'.
oocn
was called with the option `--closure'.
oo2c
's error messages. The argument `<decl>' is a string
describing a declared object; the string must be the name of a
predefined object, or a module name followed by a (possibly empty)
sequence of names, with the names separated by dots. The selector
`foo.bar' designates the object `bar' in scope `foo',
where `foo' is either
oocn --uses TextRider.Reader.SetEol.i TextRider => In file /usr/local/lib/oo2c/lib/TextRider.Mod: 12345: declaration 12551: use 12597: use 12693: use 12706: useThe above command looks for uses of object `i', which belongs to the type-bound procedure
SetEol
of type Reader
declared in module
`TextRider', within module `TextRider' (and modules it imports).
If the designated object is a type-bound procedure, `--uses' lists the
base definition of the procedure, all of its redefinitions, and all calls to
the type-bound procedure in the scope of the inspected modules.
Example:
oocn --uses Files.Reader.Pos liboo2c => In file lib/Channel.Mod: 11906: base definition In file lib/BinaryRider.Mod: 2462: use In file lib/TextRider.Mod: 9240: use In file backend/ansi-c/lib/PosixFileDescr.Mod: 5963: redefinition In file backend/ansi-c/lib/ProgramArgs.Mod: 2487: redefinition 4175: usePlease note: This function implicitly adds the option `--closure'.
Use the following options to modify the behaviour of each mode:
oocn --html --closure Foowill produce HTML files for all modules contributing to program `Foo'. Please note: For some modes, the option `--closure' is enabled by default.
(**
are not discarded, use option
`--strip-doc-strings' for this.
(**
, from the source text.
oocn
also understands the following additional command line options,
which function in the same way as they do for oo2c
:
Error messages emitted by oo2c
refer to a character position in the
source file. The first character has index `0'. Take, for example,
this slightly faulty program:
MODULE Hello; IMPORT Out; BEGIN Ou.String ("Hello World!") Out.Ln END Hello.
Compiling it with oo2c
produces this output:
In file ./Hello.Mod: 34:197 Undeclared identifier 60:139 `;' expected
File position `34' refers to the first character of `Ou', and `60' to the character to the right of the closing parenthesis. This format is slightly awkward if there is no support from the editor to locate the designated points in the source code.
Because of this awkward format, the filter program ooef
can be used
to transform error messages into a more convenient form. ooef
reads the output of oo2c
from `stdin', and writes a modified
version to `stdout'. Error messages are rewritten, and the rest of the
input is echoed. The filter should be invoked like this:
oo2c ... | ooef [option]
The output format is selected with the following options:
~$ oo2c Hello.Mod | ooef In file ./Hello.Mod: MODULE Hello; IMPORT Out; BEGIN Ou.String ("Hello World!") Out.Ln #-^ # 4: 197 Undeclared identifier #---------------------------^ # 4: 139 `;' expected END Hello.
~$ oo2c Hello.Mod | ooef -l ./Hello.Mod:4:197 Undeclared identifier ./Hello.Mod:4:139 `;' expected
~$ oo2c Hello.Mod | ooef -L ./Hello.Mod:4,3:197 Undeclared identifier ./Hello.Mod:4,29:139 `;' expected
To get the file position of a run-time error, pass the module name and the
error position to ooef
as arguments:
ooef [option] <module> <pos>
In this mode, ooef
does not act as a filter. It generates its output
based on the given command line arguments instead. The module's source file
must be in the standard search path.
Example: Suppose the run-time system reports an index out of range error in module `Strings' at file position 2531. The command ooef Strings 2531 lists the corresponding lines of the file `Strings.Mod', highlighting the instruction whose run-time check failed:
In file /usr/local/lib/oo2c/lib/Strings.Mod: i: INTEGER; BEGIN i := 0; WHILE (stringVal[i] # 0X) DO #--------------------^ # 68: INC (i) END; RETURN i
oowhereis
can be used to locate files given their name. This is a
command line interface to the mechanism used by the compiler to find its
files. Among other things, it is used by the Emacs mode to determine the
file names of modules listed in IMPORT
statements.
The program expects a single argument, the name of a file. If a matching file is found in any of the directories listed in the initialization file, the full path (including the directory part) is written to `stdout'. In this case, the exit code is `0'. Otherwise, nothing is written, and the program returns with an exit code of `1'.
For source files with an associated RCS master file, the name of the working file is returned (unless no working file is checked out and the option `--rcs-master', or `-r', is used). By default, the name of the working file is returned even if this file does not currently exist.
oo2c
's configuration mechanism manages a number of things: options
that control the compiler's workings, pragma variables that are available
during compilation, and the redirection table that specifies the directories
used for file access. This mechanism provides the means to create a
hierarchy of configuration settings: system wide, user based, and project
specific. Initialization files give full access to the configuration
database, whereas command line options provide shortcuts for only some,
albeit often used, settings.
An initialization file is composed of sections; each section is introduced by its keyword, followed by data in a section-dependent formant, and terminated by the keyword `END'. The following sections are known to the compiler: `OPTIONS', `PRAGMAS', `PATHS', `NEWFILES', and `INCLUDE'. An initialization file may contain an arbitrary number of these sections as explained in the rest of this chapter.
Comments are permitted within an initialization file. A comment starts with a `#' character and extends to the end of the line.
Both the `OPTIONS' and the `PRAGMAS' sections define a set of (name,
value) pairs. Names must follow the rules of Oberon-2 identifiers (i.e., a
character followed by a sequence of characters or digits), and the value has to
be a literal value of type boolean, integer, or string. Valid values are as
follows: `TRUE' and `FALSE' for boolean literals, any valid
LONGINT
value for integer literals (including negative numbers), and
strings are delimited by either `"' or `'' and cannot contain
characters below ASCII code `20X'.
The EBNF syntax of these sections is defined as follows:
options_section = "OPTIONS" option_list "END". pragmas_section = "OPTIONS" option_list "END". option_list = [option] { ";" [option]}. option = [ assignment | define ]. assignment = name ":=" value. define = "DEFINE" assignment.
For an example of how this looks in practice, see section An Example Initialization File.
A new variable is defined by prefixing an assignment with the keyword `DEFINE'. No variable of the same name may exist beforehand. The definition also assigns a type to the variable based on the value on the right hand side of the assignment. A variable can be defined only once, but its value can be changed through subsequent assignments.
Any further assignments change the value of an existing variable; the new value has to be of the same type as the old. For example, once a variable is defined to be of type integer, all subsequent assignments can assign only other integer values to it. Note that assignments are valid only for existing variables; that is, it must be a predefined compiler variable or a variable that was previously introduced by a variable definition.
Options and pragmas differ in two points:
oo2c
(or rather, each implementation of OOC),
whereas the entire set of pragma variables is defined for all OOC compilers.
For the complete set of option variables see section Invoking the Compiler. Variable names are listed, where applicable, beside the command line option that can change it. For the built-in pragma variables of the compiler see section Predefined Pragma Variables.
Pragma variables can be set from the command line by using the switch `--pragmas <string>'. For example, specifying
--pragmas "IndexCheck:= FALSE; DerefCheck := FALSE"
has the same effect as adding
PRAGMAS IndexCheck := FALSE; DerefCheck := FALSE END
to an initialization file. The command line option `--options <string>' does the same for `OPTIONS'.
Even though Oberon-2 has only a single name space for module names, it would
be inconvenient if the source code for all modules had to reside within a
single directory. Also, considering the number of intermediate files
oo2c
produces for a single module, the situation could be even worse.
oo2c
provides a way to distribute source code over a number of
directories, and thus "stash away" intermediate files in a more suitable
place. When looking for a particular file, oo2c
searches the
directories defined in the `PATHS' sections of the initialization files.
The `PATHS' section lists a number of wildcard strings, which are used to
determine which directories should be scanned for matching file names.
The syntax of a `PATHS' section is defined like this:
paths_section = "PATHS" {source_pattern} "END". source_pattern = "GET" wildcard_list "FROM" path {path}. wildcard_list = wildcard {[","] wildcard}. wildcard = name | string. path = ( name | string ) [";"].
A wildcard may contain the special characters `*' and `?'. A `*' matches an arbitrary number of characters (including none at all), whereas `?' matches exactly one character. Writing `[RCS]' after a wildcard signals that a file matching the pattern might be under control of the Revision Control System (RCS), and should be checked out if no working file exists.
Any non-absolute path (i.e., all path strings not starting with a `/' or a `~') is interpreted relative to the directory the compiler, or tool, was started from. Whenever a program is invoked from a different directory, these relative path names will then resolve differently. If the intention is to always use the exact same directories for all invocations, all paths in the initialization file must be absolute.
When looking for a particular file, say `foo', the list of patterns is scanned from top to bottom. For every match, the list of directories is tested from left to right. The first directory that contains a file with the requested name is used. If the file cannot be found in any of these directories, the simple file name is used. If RCS support is enabled for `foo', then the files `<dir>/RCS/foo,v' and `<dir>/foo,v' are also searched for in these directories.
Example:
PATHS GET *.Mod [RCS] FROM .; /usr/local/foo GET *.c FROM obj; /usr/local/bar END
This tells oo2c
, when looking for files with the extension
`Mod', search the current directory (`.') and then the directory
`/usr/local/foo'; for files with the extension `c', search in the
`obj' subdirectory (under the current working directory), and then the
directory `/usr/local/bar'.
For instance, when searching for the file `foo.c', the matching pattern is `*.c', and so files `obj/foo.c' and `/usr/local/bar/foo.c' are tested, in that order, for existence. The first match is taken; that is, if `obj/foo.c' exists, then that is the file used. If neither of these files exist, the name `foo.c' is used as a last resort. If this file exists in the current working directory, it is used; otherwise an appropriate error message is written.
As another example, when looking for `Foo.Mod', and the file `./RCS/Foo.Mod,v' exists, but not `./Foo.Mod', then the RCS master `./RCS/Foo.Mod,v' is checked out. Then, the working file `./Foo.Mod' is created, and subsequently used.
Note that, if a module name is passed as argument from the command line, the standard suffix `.Mod' is appended, and the resulting file name is searched for using the above mechanism. Also, if a file name that contains a directory part is used, no searching is done at all; the file name is used exactly as specified.
When trying to decide where to place a generated file (e.g., one of the
numerous intermediate files with C code), oo2c
uses a simplified version
of the mechanism described above. It looks for the first matching wildcard,
and uses the first directory in that list; the newly created file is written to
this directory. It does not matter if the file exists beforehand or not, or if
a file of the same name exists in any of the other listed directories. To use
the above example again, any file ending in `.Mod' would be put into the
current working directory (a hypothetical case since the compiler never writes
a new module), and all files ending in `.c' are placed in the directory
`obj'.
For files that are only written and not subsequently read, it is possible to specify a destination directory in a special section `NEWFILES'. Its syntax is somewhat simpler than `PATHS'.
new_files_section = "NEWFILES" {dest_pattern} "END". dest_pattern = "PUT" wildcard_list "INTO" path.
When writing a new file, the paths in `NEWFILES' take precedence over
the ones in `PATHS'. But when searching for files, `NEWFILES' is
ignored. Because most of the files oo2c
is dealing with are either
input-only or are written out to be used again at a later stage, use of
`NEWFILES' is effectively limited to provide directories for executable
programs. If paths for executable programs are not set in either
`NEWFILES' or `PATHS', they are placed in the current working
directory.
The following example shows how the installation scripts of oo2c
place the listed executable files in the subdirectory `stage2':
Example:
NEWFILES PUT oo2c, oob, oowhereis, ooef, LibSummary, UpdateX11 INTO stage2 END
By default, all programs use the initialization file that was created when the compiler package was installed. The initialization file lists all options needed for the system's C compiler, holds various system specific settings, and specifies paths to the library files installed along with the compiler (see section An Example Initialization File). The global initialization file is shared by all users, unless a particular user provides his own primary initialization file instead. A user can do this by defining an environment variable OOC_CONFIG or by using the command line option `--config' (see section Command Line Options for Compiler and Tools).
More control over the configuration data is allowed by the special initialization file section `INCLUDE'. It is a simple, but efficient, way to add personalized or project specific configuration details to the global default settings. The format of an `INCLUDE' section is simply `INCLUDE <file> END', which causes the entire contents of `<file>' to be processed as if those contents had appeared in place of the `INCLUDE' statement.
For example, the default initialization file contains the following statement to include a user's personalized settings:
INCLUDE ~/.oo2crc END
The file `.oo2crc' from the user's home directory is then parsed just as though it were part of the initialization file at the place of the `INCLUDE' statement. If a user decides that he needs even more control, he could add a line to `~/.oo2crc' like this:
INCLUDE ./.oo2crc_local END
Then, the contents of the file `.oo2crc_local', located in the current working directory, are also added to the configuration database. Provided that the user has a separate working directory for every project, this mechanism allows specification of additional settings, beyond the global and the personalized ones, for each project. Supplementary initialization files should be included after the `OPTIONS' and `PRAGMAS' sections, but before `PATHS'. This way, the specialized files override all option and pragma variables, and their own paths then take precedence over the more global ones.
Option `--include-config <file>' can be used to include initialization files from the command line.
The compiler emits a warning if it cannot find the global initialization file. On the other hand, no message is produced when the compiler fails to find a file listed in an `INCLUDE' section.
The file below is a slightly modified version of the primary initialization file of a vanilla installation after `configure --prefix=/usr' on a Linux system.
OPTIONS verbose := FALSE; errorMessages := "/usr/lib/oo2c/Errors.short"; optimize := FALSE; cc := "gcc"; cflags := "-pipe"; coptflags := "-O2"; ldflags := "-L/usr/lib"; gcflags := "/usr/lib/oo2c/gc.a"; valueStack := -1; libtoolCmd := "oolibtool"; installCmd := "/usr/bin/install -c -m 644 "; libpath := "/usr/lib"; DEFINE LibX11Prefix := " -L/usr/X11R6/lib -lSM -lICE"; DEFINE LibX11Suffix := ""; END PRAGMAS Warnings := FALSE; Assertions := TRUE; Initialize := FALSE; StackCheck := TRUE; ConformantMode := FALSE; CaseSelectCheck := TRUE; IndexCheck := TRUE; RangeCheck := TRUE; DerefCheck := TRUE; FunctResult := TRUE; TypeGuard := TRUE; OverflowCheck := FALSE; IntDivCheck := TRUE; RealOverflowCheck := TRUE; RealDivCheck := TRUE END INCLUDE ~/.oo2crc END PATHS GET *.Mod [RCS] FROM .;/usr/lib/oo2c/lib GET *.Sym, *.Lib FROM .;/usr/lib/oo2c/sym GET *.h, *.d, *.c FROM .;/usr/lib/oo2c/obj;/usr/lib/oo2c/lib GET *.o FROM .;/usr/lib/oo2c/obj END
The paths for error messages and library files are set by configure
when installing the compiler package. Likewise, configure
also
specifies the options for the C compiler, system commands, and external
libraries. In this example, one item was changed manually; gcc
's
optimization flags were moved from `cflags' to `coptflags'. With
the exception of the `PATHS' section, this initialization file reflects
the values hard coded into the compiler during installation.
Unlike many of the other Oberon systems, OOC does not try to provide a
closed development environment or, as in some cases, an entire operating
system. Instead, it provides a set of tools that can be used to write
portable software for a number of operating systems. OOC is meant to
coexist with the target OS and its system libraries. In the case of
oo2c
, this means interfacing with the Unix operating system and its
libraries, which are mostly written in the C programming language.
Access to foreign code (like object files, static or shared libraries, modules, etc.) should be transparent to Oberon-2 modules. Using foreign types, reading and writing to foreign variables, and calling foreign procedures should look exactly like normal Oberon-2 entities. Therefore, all foreign constructs have to be mapped to their Oberon-2 counterparts. That is, each C type is mapped onto an Oberon-2 type, each C function onto an Oberon-2 procedure, and so on. This mapping is accomplished by using special definition modules, which in turn can be imported and used just like any other Oberon-2 module.
Problems can arise when a foreign construct does not map directly onto an Oberon-2 construct. For instance, one construct could carry more information or attributes than the other. An example of this is the Oberon-2 pointer type, which is associated with a type tag (for records) or a length (for open arrays). In C, a pointer is simply an address. So in this case, the mapping must be extended to cover semantic attributes that are not actually defined in the Oberon-2 language (but rather, these are implementation details).
So, the mapping mechanism must provide a way of modifying the semantics of
standard Oberon-2 constructs (because we do not want to modify our
definition of Oberon-2 to include, say, a C-like struct
type). For
instance, a C pointer type is mapped to an Oberon-2 pointer that is marked
as having no associated type tag or length information.
Note that this approach generally works only for languages whose list of features is approximately a subset of those in Oberon-2. For example, complicated constructs like C++ classes cannot be adequately mapped to standard Oberon-2.
Please note: All foreign modules should be considered unsafe, low-level, and system (or compiler) dependent. Any module accessing such a module could also inherit those dependencies, so care must be taken when using any foreign module.
To attach non-standard attributes to Oberon-2 constructs, so-called system flags are introduced. System flags are applied either to the declaration of an object (like variable, procedure, parameter) or to a type constructor (record, array, formal parameter list, etc.). Syntactically, a system flag is defined using a left bracket `[', followed by a implementation-defined statement sequence (which may consist of list of keywords, strings, separators, etc.), and is terminated by a right bracket `]'. Placement of each system flag associates it with a specific Oberon-2 construct.
Example:
MODULE Xutil [INTERFACE "C"]; MODULE Files [FOREIGN "C"; LINK FILE "Files.c" END]; MODULE liboo2c [LIBRARY "oo2c" "0:0:0"; INTERFACE "C"]; TYPE R = RECORD [UNION] ... END; VAR argc- ["_program_argc"]: C.int; PROCEDURE Foo* (VAR status_in_out[NIL_COMPAT]: INTEGER): INTEGER;
Three types of system flags are permitted: module, declaration, and type.
Declaration flags are written to the right of the name and the export mark. Type flags are placed after the keyword starting the type constructor. Procedure declarations are a special case; declaration flags are placed before the procedure name, and type flags are written in front of its formal parameter list.
Example:
PROCEDURE [decl flags...] Foo* [type flags...] (bar: T; ...);
Splitting the system flags in this way is done to allow a syntactical distinction between properties of the object and properties of the object's type. For example, the linkage name of the procedure is an attribute of the object, whereas the procedure's calling convention influences things like assignment compatibility to procedure variables, and is therefore an attribute of the procedure's type.
System flags are not permitted in standard Oberon-2 modules; they are
restricted exclusively to foreign modules. For oo2c
, this means they
can only be used in modules declared `INTERFACE' or `FOREIGN'.
Module flags have the most complex syntax of the system flags. The extended module header is defined like this:
Module = "MODULE" ident [ModuleFlags] ";" ... ModuleFlags = "[" ["LIBRARY" LibName Version ";"] [ "INTERFACE" | "FOREIGN"] CallingConvention {"," ModuleFlag} [ ";" "LINK" LinkSection {";" LinkSection} "END" ] "]". CallingConvention = string. ModuleFlag = "CODE_FILE" | "INIT_FCT" | "GCC_ATTRIB_STDCALL". LinkSection = File | Object | Library. File = "FILE" string [Options]. Object = "OBJ" string. Library = "LIB" string [DependenceList] [Options]. Options = "ADD_OPTION" ident ["," ident]. DependenceList = "(" string {"," string} ")". LibName = string. Version = string.
`INTERFACE' modules are used to access existing C code, and are not
required to follow the Oberon-2 typing and heap conventions
(see section INTERFACE Modules). On the other hand, `FOREIGN' modules
behave exactly like standard Oberon-2 modules, but are implemented in
another language (see section FOREIGN Modules). Both of these describe external
modules; that is, modules containing declarations of various objects whose actual implementation is provided by means such as C code or a system
library. `LIBRARY' headers are used when creating new shared or static
libraries with oo2c
, and are explained in section Creating Shared or Static Libraries.
The "calling convention" string specifies how procedures declared in this
module are to be activated by the compiler. At the moment, the only calling
convention supported for all target operating systems is "C"
. If the
target operating system is `Win32', then the calling convention
"Pascal"
is also available. The default calling convention used is
"C"
for all targets.
The implementation of a module can be taken from a number of sources: an uncompiled C file, an object file, a library, or an arbitrary mix of these input formats. The `LINK' directive specifies which files must be included, and its options are as follows:
oo2c
calls the C
compiler for this file if either the object file `foo.o' is missing or
the source `foo.c' is more recent than `foo.o'. Note that
oo2c
cannot detect if a file included into `foo.c' has changed.
In this case, the user has to remove `foo.o' by hand to trigger a
recompilation of the source file.
If additional options must be passed to the C compiler to translate the
file `foo.c', the necessary arguments can be added to the command
line with `ADD_OPTION'. The keyword `ADD_OPTION' is followed
by one or two (comma separated) identifiers, which refer to oo2c
compiler OPTIONS
variables of string type. The values of the
variables (if they exist) are added to the argument list of the C
compiler invocation.
LIB
option, and are listed in parentheses after
`foo'. This determines the order of `-l...' arguments for the
linker. For example, a library `B' uses definitions from another
library `A', and therefore, it must be declared as `LIB "B"
("A")'. That is, `B' relies on `A' and they are to be linked in
using `-lB -lA'.
To adjust the linker's search path using `ADD_OPTION', the library name is
followed by the keyword `ADD_OPTION' and one or two (comma separated)
identifiers, which refer to oo2c
compiler OPTIONS
variables of
string type. The value of the first identifier is called the prefix
string, and the second is called the suffix string. During compilation,
the prefix string is inserted before the linker argument for the library, and
if a suffix string is present, its value is inserted after the linker argument.
For example, if compiler options are defined as follows:
DEFINE LibX11Prefix := " -L/usr/X11R6/lib -lSM -lICE"; DEFINE LibX11Suffix := "-lsocket -lnsl";Then, the following command may be used to link in the library `X11':
LINK LIB "X11" ADD_OPTION LibX11Prefix, LibX11Suffixwhich translates to the linker flags
-L/usr/X11R6/lib -lSM -lICE -lX11 -lsocket -lnslThe prefix string is usually used to extend the linker's library search path, whereas the suffix string is, in this particular case, filled by the
configure
program with system specific options.
Please note: The file names given after `FILE' or `OBJ' are subject to the usual file search mechanism, unless a directory part is included.
The following flags can be set for a module:
void Foo_init(void)
, which will be called during
program startup as part of the normal module initialization.
The following flags can be applied to type definitions:
union
instead of a struct
.
NEW
cannot be applied to this pointer type.
To allocate storage for a pointer variable of this type, SYSTEM.NEW
or a suitable low-level function of the operating system must be used. This
flag is automatically set if the pointer or the base type is marked with
`NO_DESCRIPTOR', `NO_LENGTH_INFO', or `STATIC_POINTER'.
SYSTEM.VAL(P,SYSTEM.ADR(a))
construction.
Procedure types and procedure declarations inherit the calling convention specified at the beginning of the module. This can be overridden by including a string as part of the system flags of the type, which indicates the procedure's calling convention.
The special parameter `...' is allowed as the last formal parameter of
any procedure declaration without a body part. This parameter corresponds
to variable arguments parameters in C, as used, for example, by
printf()
.
With the extended semantics of parameters, it is necessary to adjust the rules for matching parameter lists: two corresponding parameters are required to share the same system flags in order to match.
oo2c
uses the default calling convention of the target operating
system for standard Oberon-2 procedures. This means that any matching C
function can be assigned to an Oberon-2 procedure variable unless the
function's calling convention has been overidden. The compiler will prevent
an assignment if the calling convention of the procedure variable differs
from that of the procedure.
The following table lists non-standard properties that apply to declarations in external modules:
LEN
cannot be called on this variable. Also, the array
value cannot be passed to a formal open array parameter that expects to get
the length of any open dimensions.
LEN
and cannot be passed to a formal open array
parameter that expects to get the length of any open dimensions.
If such a parameter is used in a normal Oberon-2 procedure, the compiler
cannot create a local copy of the array argument. This means that it cannot
guarantee the normal semantics of value parameters. (Recall that value
parameters normally follow these rules: local modifications to the parameter
stay local, and modifications to the variable that was passed to the
parameter are not reflected in the parameter's local value.)
In this case, instead of the standard behaviour, the compiler treats the
parameter like a read-only variable and prevents local changes to the
parameter's value. However, any changes to the original array
variable, which was passed to the parameter in the first place, are
reflected by the parameter's value. This resembles the semantics of the
keyword const
when applied to C pointer types.
If the array type is marked with `NO_LENGTH_INFO', this flag is
implicitly set.
NIL
can be passed to it.
oo2c
(usually
module name plus two underscores plus declaration name).
oo2c
implementation of Exceptions.PUSHCONTEXT
).
This flag is of use only when the backend is extended to emit code for the
new pseudo procedure.
An interface module describes the Oberon-2 interface to a piece of
existing software. In the case of oo2c
, this can be a file with C
code, an object file, or a library. The existing code is not required to
follow the Oberon-2 typing and heap conventions. Therefore, one must assume
that the semantics of types, variables, and procedures in such a module are
different from their Oberon-2 counterparts.
Names declared in an interface module, with the exception of the module name itself, can contain underscores `_' in place of a character. The default linkage names of variables and procedures are the same as their Oberon-2 names, but without a module prefix. All other names appearing in the module's C header file get a prefix built from the module name followed by two underscores.
None of the record types defined in the module have a type descriptor,
and they cannot be extended. Likewise, array types have no type descriptor
and open arrays carry no length information. Pointer types have no type tag
or length information, meaning that type tests, type guards, and LEN
cannot be applied to them. Parameters are passed without any implicit
arguments containing a type tag or length information.
The special parameter `...' is permitted as last argument in the formal parameter list of the declaration of a procedure or a procedure type. Note that, in this case, the parameter list has to include at least one normal parameter.
The following examples are taken from the Oberon-2 module `X11.Mod', which defines the interface to the X11 library. The module header looks like this:
MODULE X11 [INTERFACE "C"; LINK LIB "X11" ADD_OPTION LibX11Prefix, LibX11Suffix END];
The first example highlights the basic translation process from a C header file to an Oberon-2 module, but be aware that the translation patterns shown are far from exhaustive. The Oberon-2 counterparts to the base C types in the header are taken from the standard module `C.Mod', which also provides two often used character array types.
From C.Mod: char* => TYPE charPtr1d* = POINTER TO ARRAY OF char; char** => TYPE charPtr2d* = POINTER TO ARRAY OF charPtr1d; typedef unsigned long XID; => TYPE XID* = C.longint; #define None 0L => CONST None* = 0; #define NoEventMask 0L #define KeyPressMask (1L<<0) => CONST NoEventMask* = {}; => CONST KeyPressMask* = {0}; #define Bool int => TYPE Bool* = C.int; typedef struct { int depth; /* this depth (Z) of the depth */ int nvisuals; /* number of Visual types at this depth */ Visual *visuals; /* list of visuals possible at this depth */ } Depth; => TYPE DepthPtr* = POINTER TO Depth; Depth* = RECORD depth*: C.int; (* this depth (Z) of the depth *) nvisuals*: C.int; (* number of Visual types at this depth *) visuals*: VisualPtr; (* list of visuals possible at this depth *) END; extern XFontStruct *XLoadQueryFont( Display* display, _Xconst char* name); => PROCEDURE XLoadQueryFont* ( display: DisplayPtr; name: ARRAY OF C.char): XFontStructPtr; extern char *XFetchBytes( Display* display, int* nbytes_return); => PROCEDURE XFetchBytes* ( display: DisplayPtr; VAR nbytes_return: C.int): C.charPtr1d; extern Status XGetAtomNames( Display* dpy, Atom* atoms, int count, char** names_return); => PROCEDURE XGetAtomNames* ( dpy: DisplayPtr; VAR atoms: ARRAY OF Atom; count: C.int; VAR names_return: C.charPtr2d): Status;
Most of this kind of interface conversion is fairly straightforward. The most challenging aspect is the handling of pointer types in parameter lists. A C pointer argument can be translated into a number of things: an Oberon-2 pointer type, a variable parameter of the pointer's base type, an open array parameter, or an array pointer. The decision is made depending on the use of the argument in question. We can only give rough guidelines here.
Take, for example, the C function definition
void f(int *a) {...};
The formal parameter `a' could be either an IN/OUT or an OUT argument, which translates to
PROCEDURE f* (VAR a: C.int) ... (1)
Or, it could be an array parameter, or an array pointer. In that case, it translates to one of the following alternatives:
PROCEDURE f* (VAR a: ARRAY OF C.int) ... (2) PROCEDURE f* (a: POINTER TO ARRAY OF C.int) ... (3) PROCEDURE f* (a: ARRAY OF C.int) ... (4)
The choice depends on how the function `f' accesses the argument
`a', and on whether values passed to this argument are mostly taken
from variables or from the heap. If `*a' is used as simple integer
variable, it translates to (1). But if `a' refers to an integer array,
variant (2) is used, and if it is an integer array stored exclusively on the
heap, number (3) might be used. If the array argument passed to `f' is
not modified by the function, it can be passed as a value array like in (4);
in this case, the C declaration typically adds the prefix const
to
the parameter.
Now, consider a pointer argument of a structured based type `T':
void f(T *a) {...};
which offers one additional translation variant:
PROCEDURE f* (a: POINTER TO T) ...
Again, whether a pointer value or the variable parameter is used depends on the primary source of the argument. If it is taken exclusively from heap objects, the pointer value is more convenient. Otherwise, it has to be defined as variable parameter of type `T'.
Note that the Oberon-2 declarations shown above are not completely accurate
in that it is not possible to use a POINTER
type constructor in a
formal parameter list. Instead, such types have to be declared beforehand
and their name used in the parameter list.
A foreign module describes the interface of a standard Oberon-2 module implemented in a non-standard way, say, in assembler or C. By default, all declarations are assumed to comply to standard Oberon-2 semantics. All types defined in such a module behave exactly like standard Oberon-2 types, and all the normal operations are applicable to objects declared in a foreign module. The user cannot distinguish a foreign module from a normal Oberon-2 module that has been implemented in the conventional way.
Care must be taken when integrating the implementation of a foreign module with the compiler's run-time system. This is typically done by writing an empty Oberon-2 module with the desired interface, compiling it to C code, and then editing the resulting files.
As a recommended guide, the following steps should be used when writing foreign modules:
END
parts). For a module `Foo', the header should
look like this:
MODULE Foo [FOREIGN "C"; LINK FILE "Foo.c" END];The command oocn --filter --mod2foreign Foo.Mod performs the required changes automatically and writes them into the file `Foo.txt' (see section Source Code Analysis and Transformation).
CHAR
, BOOLEAN
, SHORTINT
,
etc.).
For examples of foreign modules, refer to the implementation of the library modules `Signal', `PosixFileDesc', or `Files'.
Some algorithms need large arrays of constant values that cannot be computed
by the program itself. Examples of this are the tables used to approximate
mathematical functions, or the table of hexadecimal digits of pi
used
by the Blowfish encryption algorithm. Because such tables are an integral
part of these algorithms, it is not acceptable to simply read them from
external files.
In Oberon-2, there is basically only one way to create these tables. That is, declare an array variable and write a huge list of assignments to fill the table with the desired values. The problem with this approach is that it is highly inefficient. It usually takes a long time for OOC to compile such a list of assignments, and the generated code is a highly redundant C file that is translated into a large object file.
While it is pointless to undertake any major effort to remedy the former
problem (large tables of constants are too infrequent to make it
worthwhile), it is possible to enhance the code generator to deal with the
latter. Therefore, oo2c
has been extended to detect certain kinds of
constant tables and translate them to array definitions with initialization
parts. However, there are some restrictions:
ASSERT
or HALT
.
An example of this is the module Blowfish(2). In the module body,
it initializes a two-dimensional array of LONGINT
s with over a
thousand elements. All of these assignments are removed by the backend
from the code file `Blowfish.c'. Instead, they appear as part of
the definition of the array variable in `Blowfish.d', reducing the
size of the object file by a significant amount.
Because the implementation of oo2c
is a pre-compiler translating to a
high-level language, some run-time checks are only partial supported, or are
not supported at all. For the complete list of run-time checks see
section Option and Pragma Variables.
oo2c
only detects an overflow when using SHORT()
to
convert a LONGREAL
value to REAL
. Other overflows might
trigger a floating-point exception signal (`SIGFPE') and dump core,
or might go by unnoticed, depending on the system.
oo2c
does not support any overflow checks for integers.
oo2c
cannot detect an overflow of the program stack. If array
parameters are managed on a separate stack (see section C Compiler Options),
enabling this run-time check makes it possible for the program to detect
an overflow of the auxiliary stack.
The error reporting of the math modules `LowReal', `LowLReal', `RealMath', and `LRealMath' is not implemented as described in this reference manual. This is a problem of the math modules, not of the manual. There is no practical reason why the math modules cannot adhere to the specifications; but due to lack of time, correct error reporting has not been implemented yet.
oo2c
comes with a Oberon-2 major mode for the Free Software
Foundation's GNU Emacs, which helps manage Oberon-2 source code. It offers
various keyboard shortcuts to insert skeletons for Oberon-2 constructs,
locate Oberon-2 files and declarations, compile modules, and to display
error messages. It can make the task of writing an Oberon-2 program
considerably easier.
All configuration commands mentioned in the subsequent sections are also listed in the file `o2-default.el'. In the distribution archive, it resides in the `src/tools/emacs' directory. After installation, it can be found in the same directory as the file `oberon2.el'.
To use the Oberon-2 emacs major-mode, add the following lines to your Emacs initialization file (`~/.emacs'):
;; set load-path to incorporate directory with oberon2.el (setq load-path (cons "/usr/local/lib/oo2c" load-path)) ;; auto load oberon2.el if oberon-2-mode is called (autoload 'oberon-2-mode "oberon2" "The Oberon-2 major mode." t) ;; set oberon-2-mode as major mode for all files ending in ".Mod" (setq auto-mode-alist (cons '("\\.Mod$" . oberon-2-mode) auto-mode-alist))
The file `oberon2.el' is installed during installation of OOC, and so it may reside in another location than the path given above; modify the `load-path' setting accordingly. If you are using Emacs version 19.22 or older, replace `"oberon2"' with `"oberon2_18"' in the line `(autoload ...)'. The package `oberon2.el' works only with the `compile.el' package of Emacs 19.28 (or above, I hope). Note that `oberon2_18.el' is an older version of the package, and is no longer maintained; it does not support all the features of the current version.
If you have write permissions to system directories, a simple way to install the Oberon-2 mode for all users on the system is to copy `oberon2.el' into one of the directories of Emacs's load path (e.g., `/usr/lib/emacs/site-lisp'). (To get the list of searched directories on your system, type C-h v load-path in Emacs.) Then, add the above commands for `autoload' and `auto-mode-alist' to the file `default.el'. If no default file exists, you can create one somewhere in Emacs's load path, say, the same directory you used for `oberon2.el'. You could also consider adding the contents of file `o2-default.el' to `default.el'; among other things, it contains the `autoload' commands given above. Assuming that the permissions of both files are correct, all users on the system should have access to the Oberon-2 mode without needing to change any personal initialization files.
The keyboard shortcuts listed below are available for every buffer in Oberon-2 mode. This list is also available online by pressing C-h m in such a buffer.
Indentation:
o2-indent
spaces. With a numeric prefix, indent region.
The variable o2-ident
determines how many spaces are inserted per
indentation level (the default is 2).
Inserting Oberon-2 constructs:
MODULE
outline.
PROCEDURE
outline.
PROCEDURE
outline.
PROCEDURE
.
IF
... THEN
statement.
ELSIF
.
CASE
statement.
WITH
statement.
ELSE
.
|
.
FOR
statement.
WHILE
statement.
REPEAT
... UNTIL
statement.
LOOP
statement.
RECORD
constructor.
Move by procedure headings:
Functions to hide procedure bodies and declarations:
Managing source code:
All the above functions accept a module's alias name (as declared in the
current buffers IMPORT
list) instead of the real module name. For
further information on C-c . or C-c , use C-h k.
Any of the functions that display files in another window or frame do not change the selected window if the C-u prefix is set. For example, C-u C-c 4 . displays the source of the definition of the given identifier in another window, but the current window stays selected and point stays at the current position.
S-mouse-3 (i.e., shift with right mouse button), similar to C-c /, invokes C-u C-c 4 . with the qualified identifier under the mouse cursor as argument and blink to the found position.
Compiling:
Also, please note the following:
o2-indent
spaces or provides identifier completion.
Add `(setq o2-cwd (expand-file-name "dir"))' to `~/.emacs' if you
want Emacs to execute oo2c
, oob
, and so forth in the directory
`dir', regardless of the working directory the editor was started from.
Typically, the most often used function is the one that places the cursor at the next error position after a compile. This is bound to the 2-key sequence C-c ' (or C-c `). To bind this function to a single key, say F8, add one of the following lines to `~/.emacs':
(global-set-key [f8] 'o2-next-error)
which works under X
and recent (>=19) versions of Emacs, or
(global-set-key "[19~" 'o2-next-error)
for text terminals and older versions of Emacs (replace the cryptic string `[19~' by the one that Emacs produces when pressing C-q F8).
If you are working with an X color display, you might try to add the following piece of code to your `~/.emacs' file (for version 19+). It enables the `Font Lock' minor mode for Oberon-2 buffers, colorizing procedure headers, keywords, comments, pragmas, and strings:
(add-hook 'oberon-2-mode-hook (lambda () ;(o2-font-lock-hilit-colors) (turn-on-font-lock))) (setq font-lock-maximum-decoration t)
In case you prefer the color scheme of the `hilit19' package, you
should uncomment the function inside the lambda clause above. Note that
this will change the colors for all buffers with `Font Lock'
enabled, though. `Font Lock' supports different levels of
fontification, with rendering of higher levels taking more time to
display. The value t
selects maximum (and slowest) decoration.
As another option it is possible to enable a pull-down index menu for comfortable navigation in a source module. The index lists all record, procedure, and type-bound procedure declarations of the module. Selecting a menu item will move the cursor to the place of the declaration. This command enables this feature, adding a "Index" pull-down menu to the left of the "Oberon-2" menu:
(add-hook 'oberon-2-mode-hook (lambda () (imenu-add-to-menubar "Index")))
Other possible additions to `~/.emacs' are the commands listed below. They put compilation shells into special frames instead of splitting the current frame. The special frames are of height 10 and placed in the upper right corner of the display; they use a smaller font, menu and scroll bars are disabled, and the frame raises itself to the foreground when the mouse cursor enters it and lowers itself when left by the mouse cursor. Please note: This only works with Emacs versions 19.31 and higher. These settings are included in file `o2-default.el'
(if window-system (let ((o2-frame-params '((height . 10) (unsplittable . t) (menu-bar-lines . 0) (left . (- 0)) (top . 0) (user-position . t) (font . "5x7") (width . 60) (auto-raise . t) (vertical-scroll-bars . nil) (auto-lower . t) (name . "o2-compilation")))) (setq special-display-regexps `(("^\\*o2-compile.*\\*$" . ,o2-frame-params) ("^\\*o2-make.*\\*$" . ,o2-frame-params)))))
Go to the first, previous, next, last section, table of contents.