═══ 1. Disclaimer ═══ This on-line document was generated automatically from its printed version LaTeX source. Some places in the document (especially tables) may look ugly due to the conversion program limits. These problems are being worked on and are supposed to be solved in the final release. ═══ 2. Title Page ═══ [11pt,makeidx,a4]book xTech Development System Native XDS v2.12 for IBM Operating System/2 User's Guide xTech Ltd, 1995 empty page0 XDS software and documentation copyright 1991-1995 xTech Ltd. (xTech). Information in this document is subject to change without notice and does not represent a commitment on the part of xTech. All rights reserved. You may use the enclosed software on a single computer; transfer the software from one computer to another, provided that the software is used on only one computer at a time and that you remove any copies of the software on the computer from which the copies were made; make copies of the software for backup purposes only. XDS software and documentation have been tested and reviewed. Nevertheless, xTech makes no warranty or representation, either express or implied, with respect to the software and documentation included with XDS. In no event will xTech be liable for direct, indirect, special, incidental or consequential damages resulting from any defect in the software or documentation included with this product. In particular, xTech shall have no liability for any programs or data used with this product, including the cost of recovering programs or data. XDS is trademark of xTech Ltd. IBM, Operating System/2, OS/2, Presentation Manager, C Set/2 are trademarks of IBM Corporation. All trademarks and copyrights mentioned in this documentation are the property of their respective owners. ═══ 3. About XDS ═══ ═══ 3.1. Welcome to XDS ═══ xTech development system (XDSTM) is a professional system available for most popular platforms, including IBM PC (PC/MS-DOS, Windows 95, Windows NT, OS/2, Linux and other Unices), Unix workstations (Sun, HP, DEC, MIPS, etc), Macintosh, Amiga, etc. XDS provides a uniform programming environment for all mentioned platforms and allows to design and implement truly portable software. The system contains both Modula-2 and Oberon-2 compilers. These languages are often called ``safe'' and ``modular''. The principle innovation of the language Modula-2 was the module concept, information hiding and separate compilation. Oberon-2 is an object-oriented programming (OOP) language based on Modula-2. With the introduction of object-oriented facilities, extensible project design became much easier. Meanwhile Oberon-2 is quite simple and easy to learn and use, unlike other OOP languages such as C++ or Smalltalk. The XDS Modula-2 compiler implements ISO standard of Modula-2. The ISO standard library set is accessible for both Modula-2 and Oberon-2. XDS is based on a platform-independent front-end for both source languages which performs all syntactic and semantic checks on the source program. The compiler builds an internal representation of the compilation unit in a memory and performs platform-independent analysis and optimizations. After that the compiler emits output code. It can be either a native code for the target platform or a text in the ANSI C language. The ANSI C code generation is available for all platforms. A number of platforms for which a native code compiler is available is constantly extended. Moving to a new language usually means throwing away or rewriting your existing library set which could have been the work of many years. XDS allows the programmer to mix Modula-2, Oberon-2, C, Assembler, and often Pascal and Fortran modules and libraries in a single project. XDS includes standard ISO and PIM libraries along with a set of utility libraries and an interface to the ANSI C library set. XDS compilers for OS/2 produce highly optimized 32-bit code and debug information in the Codeview format. The complete OS/2 32-bit API (including Presentation Manager) support is provided for both Modula-2 and Oberon-2 programs. ═══ 3.2. Conventions used in this manual ═══ ═══ 3.2.1. Language descriptions ═══ Where formal descriptions for language syntax constructions appear, an extended Backus-Naur Formalism (EBNF) is used. These descriptions are set in Courier font. Text= Text [{Text}] | Text. In EBNF, Brackets [ and ] denote optionality of the enclosed expression, braces { and } denote repetition (possibly 0 times), and the line | denotes other possible valid descriptions. Non-terminal symbols start with an upper case letter (e.g. Statement). Terminal symbols either start with a lower case letter (e.g. ident), or are written in all upper case letters (e.g. BEGIN), or are enclosed within quotation marks (e.g. ":="). ═══ 3.2.2. Source code fragments ═══ When fragments of a source code are used for examples or appear within a text they are set in a Courier font. MODULE Example; IMPORT InOut; BEGIN InOut.WriteString("This is an example"); InOut.WriteLn; END Example. ═══ 4. Configuring XDS ═══ ═══ 4.1. System search paths ═══ In order that your operating system should know where to find the executable binary files which constitute the XDS package, you must set your operating system search paths appropriately. See your on-line documentation. ═══ 4.2. Working configuration ═══ The working configuration of XDS includes an utility (xc), that combines Modula-2 and Oberon-2 compilers, and a set of the system files A name of a system file is constructed from the name of the main utility and standard filename extension. If you rename the xc utility, you should also rename all system files.: The appearance of the following table is a known problem and will be improved in the final release. ll xc.red| Search path redirection file (See Redirection file) xc.cfg| Configuration file (See Configuration file) xc.msg| Contains texts of error messages (See Customize XDS messages) When invoked the xc tries to locate the xc.red file in the current directory or in the directory where XDS is placed. Other system files are sought by paths defined by xc.red. If xc.red is not found, or it does not contain paths for a system file, the system file is sought in the current directory or in the directory where XDS is placed. The configuration file contains setting that are relevant for all projects. A project specific settings are defined in a project file (See Project files). A so-called template file is used to simplify the process of building program (See Template files). The configuration and redirection file, project and template files define a working environment for a XDS user. The common features of all these files are described in More about XDS environment. The portable software development is one the main goal of XDS. To achieve the goal not only source text should be portable between various platforms, but the environment also. The XDS introduces a portable notation for file names that may be used in all system files and on the command line. The portable notation combines MS-DOS and Unix notations (file names are case sensitive): [drive letter:] unix file name Examples c:/xds/bin /mnt/users/alex/cur_pro cur_pro/sources Along with the base directory macro (See More about XDS environment) this portable notation allows to write all environment files in a platform independent and position independent manner. ═══ 4.3. XDS memory usage ═══ The XDS compilers are written itself in Oberon-2We use XDS for all our developments.. As any other Oberon programs the compilers use garbage collector to deallocate memory. Most operating systems (such as OS/2, Windows NT and Windows 95) provide virtual memory which can be significantly larger than the physical memory. If the amount of memory used by an Oberon-2 program is larger than the amount of physical memory, the garbage collector is inefficient. Thus, it is important to restrict the amount of memory that can be used by an Oberon-2 program. As a rule, such restrictions are set in the configuration or project file (See HEAPLIMIT and GCTHRESHOLD equations). For the compilers itself, two equations (COMPILERHEAP and COMPILERTHRES) should be used to control the amount of memory to use. These equations are set in the configuration file (xc.cfg). We recommend to set these equations just now according to the amount of the physical memory in your computer: The appearance of the following table is a known problem and will be improved in the final release. c|c|c RAM in megabytes | COMPILERHEAP | COMPILERTHRES 2-8 | 4000000 | 2000000 8-16 | 6000000 | 3000000 16-? | 8000000 | 4000000 It may be necessary to increase COMPILERHEAP if you get the out of memory message. It is very unlikely, if the COMPILERHEAP is set to 8 megabytes. Your compilation unit should be very large to exceed this memory limit. Vice versa, if you see unusually intensive disk activity when compiling your program it may indicate that the value of the COMPILERHEAP equation is too large for your system configuration. ═══ 4.4. Directory hierarchies ═══ XDS gives you complete freedom over where you keep both your source code files and any files which XDS itself creates for further use. It is advisable to work in a project oriented fashion - i.e. have a separate directory hierarchy for each individual project. Due to the re-usable nature of modules written in Modula-2 or Oberon-2, it is wise to keep a separate directory for those files which are to be made available to all projects. We will call such files the library files. We recommend you to have a separate working directory for each project. For example, to create a directory structure for a project called myproj: mkdir myproj Set your current working directory to this new directory cd myproj You will always need subdirectories to store the symbol files and generated code files. We recommend to use the script xdsuser or customized version of it to create all subdirectories and system files (e.g. xc.red). ═══ 4.5. XDS search paths ═══ Upon activation, XDS looks for a file called xc.red - the redirection file. This file defines the paths by which all other files are located. If the redirection file is not found in the current directory it is sought in the directory where XDS executable is placed. ═══ 4.5.1. Redirection file ═══ A redirection file consists of several lines of the formSee also More about XDS environment: pattern = directory {";" directory} It is possible to put comment lines into the redirection file. A comment line should start from "%" symbol. A pattern is a regular expression which all filenames used by XDS will be compared with. A pattern may contain the wildcard symbols '*' and '?', where * stands for any (possibly empty) string, ? stands for any single character. For a full description of extended use of wildcards see Regular expression. A portable notation is used for directory names or paths (See Working configuration). A path may be absolute or relative, i.e. may consist of full names such as /usr/myproj/def or of names relative to the current directory, such as def denoting the directory def which is a subdirectory of the current working directory. A single dot as a pathname represents the current working directory, a double dot represents the parent, i.e. the directory which has the current working directory as a subdirectory. The base directory macro $!can be used in a directory name. It denotes the path to the redirection file. If the redirection file is placed in the /usr/alex directory then $!/sym denotes /usr/alex/sym, while $!/.. denotes /usr directory. For any file, its name is sequentially compared with the patterns of each line. If a match is found, then the file is sought for in the first of the directories listed on that line, then in the second directory and so on until either the file is found, or there are no more directories to search or there are no more patterns to match. If XDS cannot find a file which is needed for correct execution, e.g. a necessary symbol file, then it will terminate with an appropriate error message. When creating a file XDS also uses redirection, and its behavior is determined by the OVERWRITE option. If the option is set and a file is found, then any updates to this file will be re-written to the directory in which it was found. If no file of the same name as the one which XDS needs to create is found or the OVERWRITE option is set off, then the file will be created in the directory which appears first in the search path list appropriate to the filename pattern. If no pattern matching a given filename can be found in the xc.red file, then the file will be read from (or written to) the current working directory. Note: If a pattern matching a given filename is found then XDS will never look into the current directory, unless it is explicitly specified in the search path. The following entry in xc.red would be appropriate for searching for the symbol files (provided a symbol file has a .sym extension. *.sym=sym;/usr/xds/lib/sym;. Using the above redirection file the compiler will first search for symbol files in the directory sym which is a subdirectory of the current working directory; then in the directory storing the XDS library symbol files and then in the current directory. Example of the redirection file: xc.msg = /xds/bin *.mod = mod *.def = def *.ob2 = oberon *.sym = sym; /xds/sym *.obj = obj ═══ 4.5.2. Regular expression ═══ A regular expression is a string containing certain special symbols. These are * denotes an arbitrary sequence of any characters, possibly empty (equivalent to {\000-\377} expression) ? denotes an arbitrary single character; (equivalent to [\000-\377] expression) [characters] denotes one of the listed characters {characters} denotes an arbitrary sequence of the listed characters; \nnn denotes the ASCII character with octal code nnn where n is [0-7]. & denotes the logical operation AND; | denotes the logical operation OR; '136 denotes the logical operation NOT; (..) sets the priority of operations; A sequence of the form a-b used within either [] or {} brackets denotes all ASCII characters from a to b. Examples *.def all files whose extension is .def project.* files whose name is project with an arbitrary extension *.def|*.mod files whose extension is either .def or .mod \{a-z\}*X.def files starting with any sequence of letters, ending in one final "X" and having the extension .def. ═══ 4.6. Options ═══ A rich set of XDS options allows one to control the source language, code generation and internal limits and settings. We distinguish between boolean options (or just options) and equations. An option can be set ON (TRUE) or OFF (FALSE), while an equation value is a string. In this chapter we describe only the syntax of setup directive. The full list of XDS options and equations is provided in the Chapter Compiler Options and Equations. Options and equations may be set in the configuration file (See Configuration file), on the command line (See ???) and in the project file (See Project files). Options may also be set in the source text (See Source code directives). The same syntax of a setup directive is used in the configuration and project file and on the command line. The only difference is that arbitrary spaces are permitted in files, but not on the command line. Option and equation names are case independent. SetupDirective = DeclareOption | DeclareSynonym | SetOption | DeclareEquation | SetEquation DeclareOption = '-' name ':' [ '+' | '-' ] DeclareSynonym = '-' name ':=' name SetOption = '-' name '+'| '-' name '-' DeclareEquation = '-' name '!' [ value ] SetEquation = '-' name '=' [ value ] All options and equations used by XDS are predeclared. The DeclareSynonym directive allows one to use a desirable name (e.g. shorter name) for some option. Examples The appearance of the following table is a known problem and will be improved in the final release. l|p8.0cm Directive | Meaning -m2extensions | M2EXTENSION is set OFF -M2Extensions+ | M2EXTENSION is set ON -debug: | DEBUG is declared and set OFF -DemoVersion:+ | DEMOVERSION is declared and set ON -T:=CheckIndex | the T is declared as synonym to CHECKINDEX -Vers!1.0 | VERS is declared and set to "1.0" -Oberon=o2 | OBERON is set to "o2" Note: The syntax of the setup directive described above was introduced in XDS v2.04. The new syntax is more portable, e.g. it can be used in the MacOS/MPW shell command line. However, the old syntax is still supported to provide backward compatibility: DeclareOption = ':' name [ '+' | '-' ] DeclareSynonym = ':' name '=' name SetOption_on = '+' name | '-' name SetEquation = '#' name ['='] value ═══ 4.7. Configuration file ═══ The configuration file can be used to set the values of options and equations (See Chapter Compiler Options and Equations). Every line in the configuration file can contain only one compiler option or equation setup directive (See Options). Arbitrary spaces are permitted. A character "%" is the comment character; it causes the rest of the line to be discarded. Note: the comment character can not be used when setting an equation. A configuration file can contain several LOOKUP equations, which allows us to change the search paths, defined in the redirection file: -LOOKUP = pattern = directory {";" directory} Example of the configuration file: % this is a comment line % Set equation: - BSDef = df % Set redirection: -lookup = *.mod = mod -lookup = *.sym = sym; c:/xds/sym % Set predeclared options: - RangeCheck - % turn range checks off - M2EXTENSIONS + % allow Modula-2 extensions % Declare new options: -i80486:+ -i80386:- -i80286: % is equal to -80286:- % Declare synonym: -N := checknil -N % disallow NIL checks % end of configuration file In the above example the XDS will search for the files with mod and sym extensions using the search paths defined in the configuration file. The search paths defined in the redirection file will be used for all other extensions. ═══ 4.8. Filename extensions ═══ XDS allows you to define what you want to be the standard extensions to each particular type of file. For instance you may prefer your Oberon-2 source code texts to be extended with a .o2 or a .ob2. It is wise to either use the traditional extensions or at least the extensions which describe the kind of file they refer to, for example, using .def and .mod for Modula-2 modules, .ob2 for Oberon-2 modules etc. It is also wise to keep the extensions the same across all of your projects. Certain other factors must also influence your decisions. By tradition, Oberon-2 pseudo-definition modules (as created by the browser) are extended with a .def. With XDS, this may conflict with the extension used for Modula-2 definition modules. Therefore, by default the browser uses the extension .odf. The following filename extensions are usually defined in the configuration file: The appearance of the following table is a known problem and will be improved in the final release. ll def | extension for Modula-2 definition modules mod | extension for Modula-2 implementation modules oberon | extension for Oberon-2 modules bsdef | extension for Oberon-2 pseudo definition modules code | extension for generated code files sym | extension for symbol files See table ??? for the full list of file extensions. Example (file extension entries in xc.cfg): -def = def -mod = mod -oberon = ob2 -sym = sym ═══ 4.9. More about XDS environment ═══ The XDS user environment consists of  the redirection file  the configuration file  a project file for each project  template files The information provided in this section can be applied to any of these files. Each file is a sequence of lines. The symbol \ at the end of a line denotes the line continuation. The following features may be used in the files:  a portable notation for file names (See Working configuration).  the base directory macro ($!). This macro denotes the directory on which the file containing the macro is placed.  a set of directives, starting from !. The directive has the following syntax (all keywords are case independent): Directive = "!" "NEW" SetOption | SetEquation | "!" "SET" SetOption | SetEquation | "!" "MESSAGE" Expression | "!" "IF" Expression "THEN" | "!" "ELSIF" Expression "THEN" | "!" "ELSE" | "!" "END". SetOption = ident ( "+" | "-" ). SetEquation = ident = { character }. The NEW directive declares a new option or equation. The SET directive changes the value of an option or equation. The MESSAGE directive prints a value of expression. The IF directive allows to process or skip portions of files according the value of expression. Expression = Simple [ Relation Simple ]. Simple = Term { "+" | OR Term }. Relation = "=" | "#" | "<" | ">". Term = Factor { AND Factor }. Factor = "(" Expression ")". | String | NOT Factor | DEFINED name | name. String = "'" { character } "'" | '"' { character } '"'. An operand in an expression is either string or equation name or option name. In the case of equation, the value of equation is used. In the case of option, a string "TRUE" or "FALSE" is used. The operator "+" denotes string concatenation. Relation operators perform string comparison. The NOT operator can be applied to any string with value "TRUE" or "FALSE". The DEFINED operator yields "TRUE" if an option or equation name is declared and "FALSE" otherwise. Example of project file % check project mode !if not defined mode then % by default use debug mode !new mode = debug !end % report the project mode !message "Making project in the " + mode + "mode" % set options according to the mode !if mode = debug then - gendebug+ - checkrange+ !else - gendebug- !fi % specify template file - template = $!/templates/xds.tem !module Main.ob2 ═══ 4.10. Customize XDS messages ═══ The file xc.msgcontains texts of error messages in the form The following is an extract from xc.msg: 001 illegal character 002 comment not closed; started at line %d ... 042 incompatible assignment ... Some messages contain format specifiers for additional arguments. I.e. the above message comment not closed contains %d specifier to print a line number. To use a language other than English for compiler messages it is necessary to translate the text of the messages, whilst preserving the error numbers and the number and order of format specifiers. ═══ 4.11. XDS and your C compiler ═══ XDS allows the C libraries to be used in your projects. Different C compilers use different naming and calling conventions. It may be necessary to configure XDS for your C compiler. See Chapter Configuring XDS for a C Compiler for more details. ═══ 5. Getting Started ═══ In this and following chapters we assume that XDS is properly installed and configured (See Chapter Configuring XDS); the default file extensions are used. Your XDS package contains a script file to create the working directory. It is called xdsuser and placed on the BIN directory. For more information consult your readme.1st file from the XDS on-line documentation. ═══ 5.1. Using the Modula-2 compiler ═══ In the working directory, use a text editor to create a file called hello.mod, containing the following text: MODULE hello; IMPORT InOut; BEGIN InOut.WriteString("Hello World"); InOut.WriteLn; END hello. Type xc hello.mod xc will know that the Modula-2 compiler should be invoked for the source file with the extension .mod. The compiler heading line will appear: Modula-2 version [code generator] "hello.mod" showing which compiler has been invoked (including its version number), which code generator is being used (in square brackets) and its version, and finally the name of the source file it has been asked to compile. Assuming that you have correctly typed the source file, after compilation, the compiler displays errors: 0(0) lines: 15 time: 1.09 showing the number of errors, the number of source lines and compilation time. Note: The XDS compiler reports are user configurable. If the statements above do not appear, check that the DECOR equation value contains `c' (compiler heading) and `r' (report) letters. ═══ 5.2. Using the Oberon-2 compiler ═══ In our bilingual system the Modula-2 source code just shown is also perfectly valid as the Oberon-2 code. XDS allows you to use Modula-2 libraries when programming in Oberon-2 (in our case InOut library). As in Modula-2, this source code in Oberon-2 constitutes a top-level module or program module. Unlike Modula-2, there is no syntactic distinction between a top-level module and any other service module. Oberon-2 compiler must be specifically told that this is a program module by using the option MAIN. Copy the source file to the file hello.ob2 and type: xc hello.ob2 +MAIN The same sequence of reports will occur as that of the Modula-2 compiler, but the Oberon-2 compiler will also report whether a new symbol file was generated or not. It is possible to override the default source file extension: xc hello.mod +O2 +MAIN In this case, the Oberon-2 compiler will be invoked regardless of the file extension. ═══ 5.3. Error reporting ═══ If either compiler detects an error in your code, an error description will be displayed. In most cases a copy of the source line will also be shown with a dollar sign "$" placed directly before the point at which the error occurred. The format by which XDS reports errors is user configurable (See Error message format specification), by default it includes the file name, the line number and column in which the error occurred and an error kind, which can be [E]rror, [W]arning or [F]ault. Example (hello.m 6,33) [E] expected symbol ")" InOut.WriteString("Hello World"$; (hello.m 7,2) [E] expected symbol ";" $InOut.WriteLn; ═══ 5.4. Running a program ═══ After compilation of all modules composing your project you have to link the program. The xdsuser script creates the xl script that can be used to link a simple program. xl hello If your project contains more than one module, we recommend to write a project file (See Project files) and use appropriate template file (See Template files). The following project file contains all necessary settings: % debug ON -gendebug+ -genhistory+ -lineno+ % specify template file -template = xds.tem % specify a name of a linker response file -mkfname = tmp -mkfext = mkf % force generation of the response file -makefile+ % linker command line -link = "xlink @%s",mkfname#mkfext; % main module of the program !module hello.mod It specifies the template file to use (xds.tem), the name of the linker response file (tmp.mkf) and linker command line. After successful compilation of the whole project the compiler creates the linker response file and then executes the command line, specied by the LINK equation. The xds.tem template file defines a template for a linker response file: /sys=C !if gendebug then /debug !end !if defined stacklimit then ! "/stack=%s\n",stacklimit; !else /stack=128K !end ! { main : "/name=%s\n",#>exeext; } ! { main imp oberon : "%s\n",#>objext } ! "%s\n","libxds"#"lib" An iterator of the form { main imp oberon : "%s\n",#>objext } will insert the names of object files for all modules constituting your program. The following invocation xc hello.prj =p will compile modules constituting the project (if required) and then execute the linker. See Template files for the full description of template files. See also the project files, generated by the xdsuser script. ═══ 5.5. Debugging a program ═══ XDS compilers generate debug information in the CodeView format and allows one to use any debugger compatible with this format. However, the postmorten historyfeature of XDS run-time support may be used in may cases instead of debugger. To enable this feature the option LINENOshould be set for all modules in the program and the option GENHISTORYfor the main module of the program; the linker (xlink) should be called with the /debug option. If your program is compiled in this mode, the run-time system will print a stack of procedure calls (a file name and a line number) on abnormal termination of your program. Example MODULE test; PROCEDURE Div(a,b: INTEGER): INTEGER; BEGIN RETURN a DIV b END Div; PROCEDURE Try; VAR res: INTEGER; BEGIN res:=Div(1,0); END Try; BEGIN Try; END test. When this program is running, the exception is raised and the run-time system prints the exception location and a stack of procedure calls. [3] #RTS: No exception handler #6: zero or negative divisor ------------------------------------------------------------ Source file LINE OFFSET PROCEDURE ------------------------------------------------------------ "test.mod" 5 0000000D "test.mod" 11 00000024 "test.mod" 15 00000051 The exception was raised in line 5 of test.mod, the Div procedure was called from line 11, while the Try procedure was called from line 15 (module body). In some cases, the history may contain wrong lines. See History for further details. ═══ 5.6. Optimizing a program ═══ Unlike many other compilers, XDS always produces optimized code. There are no such things in XDS as levels of optimization. It is sometimes the case with other compilers that unoptimized version of the program works properly, but optimized one does not. If a compiler have a dozen of optimization options it may be extremely difficult to debug the compiler itself. A compiler manufacturer has to check all possible combinations of options. It is not the case with XDS. The only option that may disable some of optimizations is the the GENDEBUG option. There are still several ways to control the generated code. First of all you have to choose what is more important for you: fast code or small code. By default, the option SPACE is set off, forcing the compiler to favor the fast code. To get the maximum performance do the following:  turn SPACE off  turn GENDEBUG off  turn ALIGNMENT on  turn run-time checks and overflow checks off It is possible not to turn run-time checks off in the product versions of your programs, because the code generator usually removes redundant checks. An average program with all run-time checks turned off runs only 10-15% faster (the code size is usually significantly smaller). Two options should be used with care:  the PROCINLINE option allows the compiler to expand procedures in-line. As a rule switching the option ON leads to faster but bigger code. However, the effect of this option depends on your programming style (size of procedures, etc).  the NOPTRALIAS option allows the compiler to assume that there is no pointer aliasing, i.e. there are no pointers bounded to non-structure variables. The code quality is better if the option is ON. Example of project file for maximum performance -alignment+ % is unnecessary under Linux -noptralias+ -procinline+ -space -checkindex -checkrange -checknil -ioverflow -coverflow -gendebug -genhistory -lineno !module Foo.mod In some cases, it may be better to set different options for different modules in your program. See dry.mod from XDS samples. ═══ 6. Using XDS ═══ ═══ 6.1. Invoking XDS ═══ The XDS Modula-2 and Oberon-2 compilers are combined together with additional operation modes into a single utility, xc. When invoked without parameters, the utility outputs the help information. xc is invoked from the command line of the following form xc { OPERATION MODE | OPTION | NAME } where NAME for different operation modes is a module name, file name or a project name. See ??? for the full description of operation modes. OPTION is a compiler setup directive (See Options). On the command line all options are applied to all operands. On some platforms it may be necessary to enclose some setup directive in quotation marks, e.g. `enable option' directive under MacOS/MPW shell: xc hello.mod '-checkindex+' See Chapter Compiler Options and Equations for the list of all compiler options and equations. ═══ 6.1.1. Precedence of compiler options ═══ The xc utility can receive its options from the 1. configuration file xc.cfg (See Configuration file) 2. command line (See ???) 3. project file (if present) (See Project files) 4. inline in a source text (not all options can be used there) (See Source code directives) At any point during operation, the last value of an option will be in effect. Thus, if the equation OBERON was set to .ob2 in the configuration file, but then set to .o2 on the command line, then XDS will recognize .o2 as the default Oberon-2 extension. ═══ 6.2. XDS operation modes ═══ XDS has the following operation modes: The appearance of the following table is a known problem and will be improved in the final release. |l|l|Mode | Meaning COMPILE | Compile all modules given on the command line PROJECT | Make all projects given on the command line MAKE | Check dependencies and recompile GEN | Generate makefile for all projects BROWSE | Extract definitions from symbol files HELP | Print help and abort the program Both the PROJECT and MAKE modes have two optional operation submodes: BATCH and ALL. Two auxilary operation submodes - OPTIONS and EQUATIONS can be used to inspect the set of compiler options and equations and their values. From the command line, the compiler mode is set with '=' followed by the required mode. Only the unique portion of a name may be specified. Operation mode names are not case sensitive, thus =PROJECT is equivalent to =p =BROWSE is equivalent to =Bro Operation modes and options can be placed everywhere in the command line. Thus two following invokations are equal to each other: xc =make hello.mod =all -checknil+ xc -checknil+ =a =make hello.mod ═══ 6.2.1. COMPILE mode ═══ xc [=compile] { FILENAME | OPTION } COMPILE is the default mode, and can be invoked simply by supplying xc with a source module(s) to compile. If xc is invoked without a given mode, COMPILE mode is assumed. In order to determine which compiler should be used, xc looks at the extensions of the given source files. The default mapping of extensions is given below : The appearance of the following table is a known problem and will be improved in the final release. lcl .mod |-| Modula-2 implementation module .def |-| Modula-2 definition module .ob2 |-| Oberon-2 module For example: xc hello.mod will invoke the Modula-2 compiler, whilst: xc hello.ob2 will invoke the Oberon-2 compiler. The user is able to reconfigure the extension mapping (See Filename extensions). It is also possible to override this extension mapping from the command line using options M2 and O2 (See ???): xc hello.mod +o2 (* invokes O2 compiler *) xc hello.ob2 +m2 (* invokes M2 compiler *) ═══ 6.2.2. MAKE mode ═══ xc =make [=batch] [=all] { FILENAME | OPTION } In the MAKE mode the compiler calculates module dependencies (using IMPORT clauses) and then recompiles all modules that are necessary. Starting from the files on the command line, it tries to find an Oberon module or a definition and implementaion module for each imported module. It then does the same for each of the imported modules until all modules are located. Note that a search is made for source files only. If a source file is not found, the imported modules will not be appended to the project. Usually, only a Modula-2 program module or an Oberon-2 top-level module should be given on the command line. See section Make strategy for more details. When all modules are gathered, XDS performs an action according to the operation submode. If the BATCH submode is specified, XDS creates a batch file of all necessary compilations, rather than actually calling the compilers and compiling the source code (See BATCH submode). If the ALL submode is specified, all gathered files are recompiled, otherwise XDS calculates conditions for recompilation and recompiles only those files that are necessary. The smart recompilation algorithm is described in Smart recompilation. ═══ 6.2.3. PROJECT mode ═══ xc =project [=batch] [=all] { PROJECTFILE | OPTION } The PROJECT mode is essentially the same as the MAKE mode except that the modules to be `made' are provided in a project file. A project file specifies a set of options and a list of modules. See Project files for further details. As in the MAKE mode, ALL and BATCH submodes can be used. If a file extension of a project file is omitted, XDS will use an extension given by the equation PRJEXT (.prj by default). It may be desirable to compile a single module in the environment specified in the project file. It can be done with the PRJ equation in the COMPILE operation mode. xc -prj=myproject MyModule.mod See also  MAKE operation mode: MAKE mode  Make strategy: Make strategy  Smart recompilation: Smart recompilation ═══ 6.2.4. GEN mode ═══ xc =gen { PROJECTFILE | OPTION } The GEN operation mode allows one to generate a file containing information about your project. The most important usage is to generate a linker response file (See Running a program). This operation mode can also be used to obtain an additional information about your project, e.g. a list of all modules, import lists, etc. A so-called template file is used in this mode. A template file name is determined by the TEMPLATE equation.A template file is a text file, some lines of which are marked with an assigned symbol. All the lines which are not marked are copied to output. The marked lines are processed in a special way (See Template files). XDS will create a file with a name determined by either the compiler option MKFNAME or the project file name. A file name is then concatenated with the extension specified by the equation MKFEXT. ═══ 6.2.5. BROWSE mode ═══ xc =browse { MODULENAME | OPTION } The BROWSE operation mode allows one to generate a pseudo definition module for an Oberon-2 module. In this mode XDS scans the symbol file and produces a Modula-2-like definition module which contains all client-visible objects. The configuration option BSDEF (See Equations) specifies the extension of a generated file. If this option is not set, then the default extension (.odf) will be used. Options BSCLOSURE and BSREDEFINE can be used to control the form of a generated file. Note: the BSTYLE equation (described in Creating a definition) is ignored in this operation mode, and the browse style is always set to DEF. The MAKEDEF option (See Creating a definition) provides an alternative method of producing pseudo definition modules, preserving so-called exported comments if necessary. ═══ 6.2.6. ALL submode ═══ In both PROJECT and MAKE modes, XDS checks the time stamps of the files concerned and recompiles only those files that are necessary. If ALL submode is set, the time stamps are ignored, and all files are compiled. ═══ 6.2.7. BATCH submode ═══ In the BATCH submode, XDS creates a batch file of all necessary compilation, rather than actually calling the compilers and compiling the source code. A batch file is sequence of lines beginning with the compiler name, followed by module names to recompile. XDS will create a batch file with a name determined by either: 1. The compiler option BATNAME (see Equations) 2. The project file name (if given) 3. The name out (if no name can be determined by above). Batch file name is then concatenated with the batch file extension specified by the equation BATEXT (.bat by default). See also  option LONGNAME (???)  equation BATWIDTH (Equations) ═══ 6.2.8. OPTIONS submode ═══ The OPTIONS submode allows you to inspect the values of options which are set in the configuration file, project file and on the command line. It can be used together with COMPILE, MAKE and PROJECT modes. The following invocation will print (to the standard output) the list of all defined options, including all pre-declared options, all options declared in the configuration file, in the project file my.prj and on the command line (xyz option): xc =options -prj=my.prj -xyz:+ In the PROJECT mode options are listed for each project file given on the command line. ═══ 6.2.9. EQUATIONS submode ═══ The EQUATIONS submode allows you to inspect the values of options which are set in the configuration file, project file and on the command line. It can be used together with COMPILE, MAKE and PROJECT modes. ═══ 6.3. Files generated during compilation ═══ When applied to a file which contains a module name, the compilers produce the following files. Modula-2 compiler When applied to a definition module, the Modula-2 compiler produces a symbol file(name.sym). The symbol file contains an information necessary for the compilation of a module which imports a module name. When applied to an implementation module or top level module, the Modula-2 compiler produces an object file (name.obj). Oberon-2 compiler For all compiled modules, the Oberon-2 compiler produces a symbol file (name.sym) and an object file (name.obj). The symbol file(name.sym) contains an information necessary for the compilation of a module which imports a module name. If, during compilation, the compiler needs to overwrite an existing symbol file, it will only do so if the option DEF (see ???) has been set. Examples The appearance of the following table is a known problem and will be improved in the final release. l|l Command Line | Generated files xc Example.def | Example.sym xc Example.mod | Example.obj xc Windows.ob2 +DEF | Windows.sym | Windows.obj ═══ 6.4. Project files ═══ A project file has the following structure: {OPTION} {!module {FILENAME}} where OPTIONs are the compiler setup directives (See Options), defining options and equations that all modules should be compiled with, and FILENAMEs are modules of the project. It should be noted that XDS recursively looks at all the given files for any imported modules. Thus, usually, a project file would consist of one single module, the main program module. At least one module should be specified in a project file. Every line in a project file can contain only one setup directive. A character "%" is the comment character. It causes the rest of the line to be discarded. Note: the comment character can not be used in setting an equation. XDS gives you complete freedom where to set options, equations and redirection directives. However, it is advisable to specify only those settings in the configuration and redirection files which are applied to all your projects, and use project files to specify all project-dependent options and redirection directive. A project file can contain several LOOKUP equations, which allows one to redefine some search paths. Example Project File: -mod = mod -checkindex+ -lookup = *.mod = mod -lookup = *.sym = sym; c:/xds/sym !module hello In the above example, XDS will search files with .mod and .sym extensions using search paths specified in the project file. For all other extensions, search paths, specified in the redirection file or configuration file or on the command line will be used. A project file is specified explicitly in the PROJECT and GEN operation modes. In these cases all options and equations are set and then XDS proceeds the module list to gather all modules constituting a project (See Make strategy). In the MAKE and COMPILE operation mode, a project file can be specified using the PRJ equation. In these cases the module list is ignored, but all options and equations are set. The following command line forces XDS to compile the module hello.mod in the COMPILE operation mode using options and equations specified in the project file hello.prj: xc hello.mod -prj=hello.prj ═══ 6.5. Make strategy ═══ All information presented here concerns MAKE, PROJECT and GEN operation modes. In these modes XDS builds a set of all modules that constitute the project, starting from the modules specified in a project file (PROJECT and GEN) or on a command line (MAKE). The MAKE mode will be used in most of the following examples, but the comments will concern both to the PROJECT and GEN modes. At first, XDS tries to find all given modules according to the following strategy:  If both a filename extension and a path to the a file are specified, it checks if the given file exists. xc =make mod\hello.mod  If a filename extension is specified, the file will try to find a file using search paths. xc =make hello.mod  If a filename extension is not specified, XDS will try to find a file with the Oberon-2 extension, Modula-2 module and definition extensions. xc =make hello An error will be raised if there is more than one source file, e.g. both hello.ob2 and hello.mod files are accessible. Starting from the given files XDS tries to find an Oberon module or a pair - a definition and implementation module for each imported module. It then tries to do the same for each of the imported modules until all modules are located. For all modules XDS checks the correspondence between the file name extensions and a kind of the module. ═══ 6.6. Smart recompilation ═══ In the PROJECT and MAKE mode, XDS offers smart recompilation of all modules in a project which are inconsistent with the available source code files. XDS uses a file modification time to determine which file has been changed. For every module the decision is made (to recompile or not) only after the decision is made for all modules on which it depends. A file is compiled if one or more of the following conditions is true: definition module  the symbol file is missing  the symbol file is present but the modification date is earlier than that of the source file or one of the imported symbol files implementation module  the code file is missing  the code file is present but the file modification date is earlier than that of the source file or one of the imported symbol files (including its own symbol file) program module  the code file is missing  the code file is present but the file modification date is earlier than that of the source file or one of the imported symbol files Oberon-2 module  the symbol file is missing  the symbol file is present but the modification date is earlier than that of one of the imported symbol files  the code file is missing  the code file is present but the file modification date is earlier than that of the source file or one of the imported symbol files When the VERBOSE option is ON, XDS reports why the module is recompiled. Note: if an error occurs when a definition or Oberon-2 module is compiled, all client modules will not be compiled at all. ═══ 6.7. Template files ═══ A template file is used for defining the structure of the file generated in the GEN operation mode or PROJECT mode, if the option MAKEFILE is ON. XDS copies lines from the template file into the output file verbatim, unless the lines are marked as requiring further attention. A single character (attention mark) is specified by the equation ATTENTION(default is '!'). A marked line (or template) has the following format The same syntax is used in the LINK equation. : Template = { Sentence }. Sentence = Item { "," Item } ";" | Iterator. Item = Atom | [ Atom | "^" ] "#" [ Extension ]. Atom = String | name. String = '"' { character } '"' | "'" { character } "'". Extension = [ ">" ] Atom. Iterator = "{" Set ":" { Sentence } "}". Set = { Keyword | String } Keyword = DEF | IMP | OBERON | MAIN | C | HEADER | ASM | OBJ. A name should be the name of equation. Not more than three items may be used in a sentence. A first item in a sentence is a format string, while others are arguments. In the simplest form a template file can be used to output a value of an equation. For example, if a template file contains the line ! "Current project is %s.\n",prj; and the project prj\test.prj is processed, the output will contain the line Current project is prj/test.prj. Note: the following line ! prj; is valid, but will produce unexpected result under MS-DOS or Windows: prj est.prj because \t in the format string will be replaced by tabulator character. Use the following form instead: ! "%s",prj; The "#" operator creates a file name from the namegiven by either equation value or literal string and extension. A file names is build according to XDS search paths. For example, if a path to the XDS library directory is defined: *.lib = \xds\lib the line ! "libxds"#"lib" will produce \xds\lib\libxds.lib If the modifier ">" is specified, XDS assumes that the file with this name is output file and builds its name according to the strategy for output files (See Redirection file). The form in which a name or extension is omitted can be used in an iterator only. Iterators are used to generate a text for all modules from the specified set. Sentences inside the first level brackets are repeated for all modules of the project, while sentences inside the second level are repeated for all imported modules. A set is a sequence of keywords and strings. Each string denotes a specific module, while a keyword denotes all modules of specific kind. The meaning of keywords is the following: The appearance of the following table is a known problem and will be improved in the final release. |l|p7.8cm| Keyword |Meaning DEF | Modula-2 definition module IMP | Modula-2 implementation module MAIN | Modula-2 program module or Oberon module, marked as MAIN OBERON | Oberon module C | C source text HEADER | C header file ASM | assembler source text OBJ | object file The following template file can be used for listing all modules in the project for which source files are available: ! { imp oberon main: "%s ",#; } For example, consider a program module A, which imports modules B and C. B itself imports D. All modules are written in Modula-2. Thus: A / \ B C | D The template given above would generate the following line: A.mod B.mod C.mod D.mod To output both definition and implementation modules, one can write: ! { def : "%s ",#; } ! { imp oberon main: "%s ",#; } Then the output will be: B.def C.def D.def A.mod B.mod C.mod D.mod The next template file can be used for listing all modules in the project together with their import: ! { imp main: "%s\n",#; { def: " %s\n",#; } } The form ^# may be used in the second level iterator to out a current name of the fisrt level iterator. The XDS distribution contains a template file xds.tem which can be used to produce a linker response file (See Running a program). ═══ 7. Compiler Options and Equations ═══ A rich set of XDS options allows one to control the source language, code generated and internal limits and settings. We distinguish between boolean options (or just options) and equations. An option can be set ON (TRUE) or OFF (FALSE), while an equation value is a string. ═══ 7.1. Options ═══ Options control the process of compilation, including language extensions, run-time checks and code generation. An option can be set ON (TRUE) or OFF (FALSE). A compiler setup directive (See Options) is used to set the option value or to declare it. Options may be set in the configuration file (See Configuration file), on the command line (See ???), in the project file (See Project files) or in the source text (See Source code directives). At any point of operation, the last value of an option is in effect. All options are listed in the section Description of options, see also tables ??? (page table:opt:check), ??? (page table:opt:ext), ??? (page table:opt:code) and Options (page table:opt:misc). [htbp] The appearance of the following table is a known problem and will be improved in the final release. |l|l|Option |Meaning ASSERT | enable ASSERT generation CHECKDINDEX | check of dynamic array bounds CHECKDIV | check for a positive divisor | (DIV and MOD) CHECKINDEX | check of static array bounds CHECKNIL | NIL pointer dereference check CHECKPROC | check of a formal procedure call CHECKRANGE | range checks | (range types and enumerations) CHECKSET | range check of set operations CHECKTYPE | dynamic type guards (Oberon-2 only) COVERFLOW | cardinal overflow check IOVERFLOW | integer overflow check Run-time checks [htbp] The appearance of the following table is a known problem and will be improved in the final release. |l|p7cm|Option |Meaning M2ADDTYPES | add SHORT and LONG types M2BASE16 | use 16-bits basic types in Modula-2 M2CMPSYM | compare symbol files in Modula-2 M2EXTENSIONS | enables Modula-2 extensions M2UNPACKTYPES | forces to use unpack form of some types O2EXTENSIONS | enables Oberon-2 extensions O2ISOPRAGMA | enables ISO Modula-2 pragmas in Oberon O2NUMEXT | enables Oberon-2 scientific extensions STORAGE | enables the default memory management in Modula-2 Source language control options [htbp] The appearance of the following table is a known problem and will be improved in the final release. |l|p7cm| Option |Meaning __GEN_C__ | ANSI C code generation __GEN_X86__ | code generation for 386/486/Pentium ALIGNMENT | align data DEFLIBS | put the default library names into object files GENDEBUG | generate code in the debug mode GENFRAME | always generate a procedure frame GENHISTORY | enables postmorten history GENPTRINIT | generate a local pointer initialization LINENO | generate line numbers in object files NOPTRALIAS | ignore the pointer aliasing PROCINLINE | enables in-line procedure's expansion SPACE | code size is more important then speed VERSIONKEY | append version key to the module initialization Code generator control options [htbp] The appearance of the following table is a known problem and will be improved in the final release. |l|l| Option | Meaning BSCLOSURE | browse control option BSREDEFINE | browse control option DEF | permission to change a symbol file GCAUTO | enables implicit call of a garbage collector LONGNAME | use long names in batch files M2 | forces the Modula-2 compiler MAIN | marks the Oberon-2 main module MAKEDEF | generate definition MAKEFILE | generate makefile OVERWRITE | create a file, always overwrites the old one O2 | forces the Oberon-2 compiler SHOWALIEN | produce warnings on absent modules VERBOSE | produce verbose messages WOFF | suppresse warning messages XCOMMENTS | preserve exported comments Miscellaneous options ═══ 7.1.1. Description of options ═══ The section lists all options in an alphabetical order. Those options that may be arbitrarily placed in the source code are marked as inline options (See also Source code directives). Some options can be place in the source code, but only before the module header - such options are marked as header. If an option is not marked as a header or inline, then the result of setting it in the source text is undefined. The operation modes to which an option is significant are listed in square brackets ([]) after the option name; the character '*' stands for all operation modes. For example, [browse] means that the option is used by the compiler in the BROWSE operation mode only. Note: in the MAKE and PROJECT mode the compiler switches to the COMPILE mode to compile a module. Run-time check options are ON by default. If it is not explicitly specified, other options are OFF (FALSE) by default. __GEN_X86__ [compile] The compiler sets this option ON, if the code generation for 386/486/Pentium is in operation. The option can be used for compiling different text fragments for different targets. See also Conditional compilation. __GEN_C__ [compile] The compiler sets this option ON, if the C code generation is in operation. The option can be used for compiling different text fragments for different targets. See also Conditional compilation. ALIGNMENT [compile] If the option is set OFF, the compiler does not consider any alignment of data, otherwise it does. See Record types for further details. Note: the option cannot be used inline in the source text. ASSERT [compile] (inline) If the option is OFF, the compiler ignores all calls of the standard ASSERT procedure. The option is ON by default. BSCLOSURE [browse] Include all visible methods. If the option is set ON, the browser includes all defined and inherited type-bound procedure declarations with all record declarations when creating a pseudo-definition module. See also Creating a definition. BSREDEFINE [browse] Include all redefined methods. If the option is set ON, the browser includes original definitions of any overwritten type-bound procedures with record declarations. See also Creating a definition. CHECKDINDEX [compile] (inline) A check of dynamic array bounds. If the option is set ON, the compiler generates index check of dynamic arrays (POINTER TO ARRAY OF T). The option is ON by default. CHECKDIV [compile] (inline) If the option is set ON, the compiler generates a check if a divisor is positive of DIV and MOD operators. The option is ON by default. CHECKINDEX [compile] (inline) A check of static array bounds. If the option is set ON, the compiler generates index check of all arrays except dynamic ones (See the CHECKDINDEX option). The option is ON by default. CHECKNIL [compile] (inline) If the option is set ON, the compiler generates NIL check of all pointer dereferencies. The option is ON by default. CHECKPROC [compile] (inline) If the option is set ON, the compiler generates NIL check when calling a procedure variable. The option is ON by default. CHECKRANGE [compile] (inline) If the option is set ON, the compiler generates range checks of range types and enumerations. The option is ON by default. CHECKSET [compile] (inline) If the option is set ON, the compiler generates range checks of set operations (INCL, EXCL, set aggregates). The option is ON by default. CHECKTYPE [compile, Oberon-2 only] (inline) If the option is set ON, the compiler generates dynamic type guards. The option is ON by default. COVERFLOW [compile] (inline) If the option is set ON, the compiler generates overflow checks of all cardinal (unsigned) arithmetic operators. The option is ON by default. DEF [compile] (header) Permission to change a module interface (a symbol file). The Oberon-2 compiler creates a temporary symbol file every time an Oberon-2 module is compiled, compares this symbol file with the existing one and overwrites it with the new one if necessary. When the option is OFF (by default), the compiler reports an error if the symbol file (and the module interface respectively) had been changed and does not replace the old symbol file. Note: if the M2CMPSYM option is set, the same is valid for the compilation of the Modula-2 definition module, i.e., the DEF option should be set if the module interface has been changed. DEFLIBS [compile] If the option is set ON, the compiler writes the default library names to the generated object files. The option is ON by default. GCAUTO [compile,top-level module only] (header) Enables the implicit call of a garbage collector in the generated program. The option is ignored for all modules except the top-level module of the program. We recommend to set the option in the project or configuration file. GENDEBUG [compile] (header) If the option is set ON, the compiler puts additional debug information (in OMF format) into an object file. In the current release (v2.10) this information includes all global variables and its types. In some rare cases, switching the option ON may decrease the code quality. GENFRAME [compile] (header) If the option is set ON, the compiler always generates a stack frame. It may be necessary to simplify the debugging. GENHISTORY [compile] (header) If the option is set ON, the run-time system prints a stack of procedure calls (a file name and a line number) on abnormal termination of your program. It should be set when compiling a main module of the program. In this case the required part of the run-time system will be added to the program. The option LINENO should be set for all modules in the program. In some cases the printed list contains wrong lines, i.e. it contains procedures that were not called in the given context (See History). See also Debugging a program for an example. GENPTRINIT [compile, Oberon-2 only] (header) If the option is set ON, the compiler generates code for initialization all local pointers, including variables, record fields and array elements. Values of all non-pointer record fields and array elements are undefined. The option is ON by default. IOVERFLOW [compile] (inline) If the option is set ON, the compiler generates the overflow checks of all integer (signed) arithmetic operators. The option is ON by default. LINENO [compile] (header) If the option is set ON, the compiler insert line numbers information into the object files. This option should be set to get the postmorten history (See the GENHISTORY option) and for debugging. LONGNAME [make,project] Use long names. If the option is set ON, the compiler uses the full path as the prefix to all module names in the generated batch files. See also BATCH submode. M2 [compile] Force the Modula-2 compiler. If the option is set ON, XDS invokes the Modula-2 compiler regardless of file extension. The option is ignored in MAKE and PROJECT modes. M2ADDTYPES [compile,Modula-2 only] (header) Add short and long modifications of whole types. If the option is set ON, the compiler recognizes the types SHORTINT, LONGINT, SHORTCARD and LONGCARD as pervasive identifiers . Warning: A use of additional types may cause problems with the software portability to other compilers. M2BASE16 [compile,Modula-2 only] (header) If the option is set ON, the basic types INTEGER, CARDINAL and BITSET are 16 bits wide in Modula-2 (by default, 32 bits wide). M2CMPSYM [compile,Modula-2 only] If the option is set ON, the compiler compares the symbol file generated for a definition module with the old version exactly as the Oberon-2 compiler does. If the symbol files are equal, the old one is preserved, otherwise the compiler overwrites symbol file, but only if the DEF option is set ON. M2EXTENSIONS [compile,Modula-2 only] (header) Enable Modula-2 extensions. If the option is set ON, the compiler allows the Modula-2 language extensions to be used, such as line comment "-", read-only parameters, and so on. Warning: A use of extensions may cause problems with the software portability to other compilers. M2UNPACKTYPES [compile,Modula-2 only] (header) If the option is set ON, the compiler uses 32-bit representation for Modula-2 enumeration, set and BOOLEAN types. See ??? for further details. MAIN [compile, Oberon-2 only] (header) Mark the Oberon-2 main module. If the option is set ON, the compiler generates a program entry point (`main' function) for the Oberon-2 module (See Program structure). Inline usage is recommended. MAKEDEF [compile,Oberon-2 only] The MAKEDEF option forces the Oberon compiler to generate a (pseudo-) definition module after a successful compilation of an Oberon module. The compiler preserves the so-called exported comments (i.e. comments started with `(**') if the XCOMMENTS option is set. See Creating a definition. MAKEFILE [project] The MAKEFILE option forces XDS to generate a makefile after successful compilation of a project. See also GEN mode. NOPTRALIAS [compile] (header) If the option is set ON, the compiler assumes that there is no pointer aliasing, i.e. there are no pointers bounded to non-structure variables. The only way to get a pointer to a variable is to use the low-level facilities from the module SYSTEM. We recommend to turn this option ON for all modules except low-level ones. Note: the code quality is better if the option is ON. O2 [compile] Force Oberon-2 compiler. If the option is set ON, XDS invokes the Oberon-2 compiler regardless of the file extension. The option is ignored in MAKE and PROJECT modes. O2EXTENSIONS [compile,Oberon-2 only] (header) Enable Oberon-2 extensions. If the option is set ON, the compiler allows Oberon-2 language extensions to be used (See Language extensions). Warning: A use of extensions may cause problems with the software portability to other compilers. O2ISOPRAGMA [compile,Oberon-2 only] If the option is set ON, the compiler allows the ISO M2 style pragmas <* *> to be used in Oberon-2. See Source code directives and Source code directives. Warning: A use of ISO M2 pragmas may cause problems with the software portability to other compilers. O2NUMEXT [compile,Oberon-2 only] (header) Enable Oberon-2 scientific extensions. If the option is set ON, the compiler allows the Oberon-2 scientific language extensions to be used (See Language extensions), including COMPLEX and LONGCOMPLEX types and the in-line exponentiation operator. Warning: A use of additional types may cause problems with the software portability to other compilers. OVERWRITE [*] The option changes the directory where the compiler creates new files. If the option is OFF, the compiler creates a file in the directory which appears first in the search path list appropriate to the filename pattern. Otherwise, the compiler overwrites the old file. See also Redirection file. PROCINLINE [compile] If the option is ON, the compiler tries to expand procedures in-line. In-line expansion of a procedure saves the overhead usually associated with procedure calls, parameter passing, register saving, etc. Sometimes, further optimizations become possible because the actual parameters used in the call become visible. A procedure is not expanded in-line under the following circumstances:  the procedure is deemed too complex or too large by the compiler.  there are too many calls of the procedure.  the procedure is recursive. SHOWALIEN [make,project,gen] If the option is set ON, the compiler reports a list of all Modula-2 or Oberon-2 modules which are not found within the current project. SPACE [compile] If the option is set ON, the compiler performs optimizations to produce smaller code, otherwise (by default) to produce faster code. STORAGE [compile, Modula-2 only] (header) If the option is set ON, the compiler uses the default memory allocation and deallocation procedures for standard procedures NEW and DISPOSE. Warning: A use of this option may cause problems with the software portability to other compilers. VERBOSE [make,project] If the option is set ON, the compiler will report the reason of a module recompilation (See Smart recompilation). VERSIONKEY [compile] The option is introduced to perform version checks at a link time. If the option is set ON, the compiler generates the name of module body as composition of  a module name  a string "_BEGIN_"  a time stamp  value of the ALIGNMENT option. If the definition (or Oberon) module imported by different compilation units has the same version, the same name is generated for each call of the module body. In all other cases unresolved references will occur at link time. If the option is OFF, the compiler generates the name in the form: _BEGIN. Note: the option should be set when compiling definition or Oberon module. WOFF# [*] (inline) When WOFF# (e.g. WOFF301) is ON, the compiler does not report a warning # (301 in the above example). See xc.msg file for warning texts and numbers. WOFF without parameter sets all warnings OFF. XCOMMENTS [compile,Oberon-2 only] If the option is set ON, the browser includes the so-called exported comments (i.e. comments which start with `(**') into the generated pseudo definition module. See also Creating a definition. ═══ 7.2. Equations ═══ An equation is a pair (name,value). Equations are used for a change of default file extensions (table ???, page table:equ:ext), code generation settings (table ???, page table:equ:code) and miscellaneous settings (table Description of equations, page table:equ:misc) by the statements of the form: -NAME=value There should be no spaces in an equation statement on the command line. In the configuration or project file, spaces can be placed anywhere, but only one equation per line is allowed (see Options). Equations may be set in the configuration file (See Configuration file), in the project file (See Project files) and on the command line, but not in the source text. At any point of operation, the most recent value of an equation is in effect. All equations are listed in the section Equations. In some cases, the value part of an equation statement may be empty. Examples -mod = .mod -DEF = .def -mkfext = ═══ 7.2.1. Description of equations ═══ The operation modes to which an equation is significant are enclosed in square brackets ([]) after the option name; the character '*' stands for all operation modes. For example [browse] means that the equation is used by the compiler in the BROWSE operation mode only. Note: the compiler switches from the MAKE and PROJECT mode to the COMPILE mode to compile a module. [htbp] The appearance of the following table is a known problem and will be improved in the final release. |l|c|l| Name | Default | Meaning BATEXT | .bat | recompilation batch file BSDEF | .odf | pseudo-definition file created by browser CODE | .obj| object file DEF | .def | Modula-2 definition module MKFEXT | .mkf | makefile MOD | .mod | Modula-2 implementation or main module OBERON | .ob2 | Oberon-2 module OBJEXT | .obj | object file PRJEXT | .prj | project file SYM | .sym | symbol file File extensions [htbp] The appearance of the following table is a known problem and will be improved in the final release. |l|c|p6.0cm| Name | Default | Meaning CC | WATCOM | C compiler compatibility GCTHRESHOLD | 0 | garbage collector threshold HEAPLIMIT | 0 | heap limit of a generated program STACKLIMIT | 0 | stack limit of a generated program Code generator equations [htbp] The appearance of the following table is a known problem and will be improved in the final release. |l|c|p5.0cm| Name | Default | Meaning ATTENTION | ! | attention character in a template file BATNAME | out | batch file name BATWIDTH | 128 | maximum line width in a batch file BSTYLE | DEF | browse style (See Creating a definition) COMPILERHEAP | | heap limit of the compiler COMPILERTHRES | | GC threshold of the compiler DECOR | hcrt | control of compiler messages ERRFMT | See Error message format specification | error message format ERRLIM | 16 | maximum number of errors LINK | | linker command line LOOKUP | | lookup directive MKFNAME | | makefile name PRJ | | project file name PROJECT | | project name TABSTOP | 8 | tabulation alignment TEMPLATE | | template name (for makefile) Miscellaneous equations ATTENTION [project,gen] The equation defines an attention character which is used in the template file ("!" by default). See Template files. BATEXT [batch] Sets the file extension for recompilation batch files (by default .bat). See BATCH submode. BATNAME [batch] Sets the batch file name. The name of the Project file will be used if no batch file name is explicitly specified. See BATCH submode. BATWIDTH [batch] Sets the maximum width of a line in a generated batch file (by default 128). See BATCH submode. BSDEF [browse] Sets the file extension for pseudo-definition modules created by the browser (by default .odf). See BROWSE mode. BSTYLE [browse] Sets the style of generated pseudo-definition modules. See Creating a definition. CC [compile] Sets the compatibility mode with a C compiler. Currently the following values are defined: "WATCOM", "MSVC" and "GCC". If the value of equation is undefined, "WATCOM" is assumed. The compiler will allow one to use C libraries from the C compiler specified. See Chapter Configuring XDS for a C Compiler for more details. CODE [*] Sets the file extension for code files generated by the compiler (by default .obj). COMPILERHEAP [*] Sets the maximum amount of a heap memory (in bytes), that can be used by the compiler. For the systems with virtual memory, we recommend to use the value which is less than the amount of the available memory. COMPILERTHRES [*] Sets the garbage collector threshold (in bytes) for the compiler. The garbage collector will be implicitly called if the amount of a busy memory exceeds the value of the equation. For the systems with virtual memory, we recommend to use the value which is less than COMPILERHEAP. DECOR [*] The equation specifies the output of the xc utility. The value of equation is a string that contains any combination of letters "h", "t" "c", "r", "p" (capital letters are also allowed). Each character turn on an output of h header line, which contains a copyright message and an utility version t the summary of compilation of multiple files c the name and version of the compiler's front-end and back-end p progress messages r compiler report: number of errors, lines, etc. By default, the equation value is "hrtc". DEF [*] Sets the file extension for Modula-2 definition modules (by default .def). ERRFMT [*] Sets the error message format. See Error message format specification for details. ERRLIM [*] Sets the maximum number of errors allowed for one compilation unit (by default 16). GCTHRESHOLD [compile,top-level module only] Sets the garbage collector threshold (in bytes). The garbage collector will be implicitly called if the amount of a busy memory exceeds the value of the equation. The valid values of the equation are in the range 0..HEAPLIMIT. We recommend to set the equation for the systems with virtual memory. HEAPLIMIT [compile,top-level module only] Sets the maximum amount of a heap memory, that can be used in the generated program. The value is set in bytes. The equation should be set when the top-level module of the program is compiled. We recommend to set the option in the project or configuration file. LINK [project] Defines a command line, which will be executed after a successful completion of a project. As a rule, the equation is used for calling a linker or make utility. See Running a program for examples. LOOKUP [*] Sets the lookup directive: -LOOKUP = pattern = directory {";" directory } The equation can be used for redefining the search paths that are set in the redirection file. The configuration or project file or command line can contain several LOOKUP equations. See also Configuration file and Project files. MKFEXT [gen] Sets the file extension for a generated makefile (by default .mkf). See GEN mode. MKFNAME [gen] Sets the name for a generated makefile. See GEN mode. MOD [*] Sets the file extension for the Modula-2 implementation and program modules (by default .mod). OBERON [*] Sets the file extension for Oberon-2 modules (by default .ob2). OBJEXT [*] Sets the file extension for object files (by default .obj). PRJ [compile,make,project] In the COMPILE and MAKE operation modes, the equation defines a project file to read settings from. In the PROJECT mode, the compiler sets this equation to a project file name from the command line. See PROJECT mode. PRJEXT [compile,make,project] Sets the file extension for a project file (by default .prj). See PROJECT mode. PROJECT [compile,make,project] If a project file name is defined, the compiler sets the equation to a project name without a file path and extension. For example, if the project file name is defined as prj/Work.prj, the value of the equation is set to Work. The equation may be used in a template file to set the name of executable file, etc. STACKLIMIT [compile,top-level module only] Sets the maximum size of program stack memory. The value is set in bytes. The equation should be set when the top-level module of the program is compiled. We recommend to set the option in the project or configuration file. Note: for some linkers the stack size should be set as a linker option. SYM [*] Sets the file extension for symbol files (by default .sym). See Files generated during compilation. TABSTOP [gen] When reading text files, XDS replaces TAB character by the number of spaces required to align a text (by default TABSTOP is equal to 8). The wrong value may cause misplaced comments in the generated pseudo-definition module, a wrong location of an error in the error message, etc. We recommend to set this equation to the number used in your text editor. TEMPLATE [gen] Sets the name of a template file. See Template files. ═══ 7.2.2. Error message format specification ═══ The format in which XDS reports the errors is user configurable by using the ERRFMT equation. Its syntax is as follows: { string "," [ argument ] ";" }. Any format specification allowed in the C procedure "printf" can be used in string. The appearance of the following table is a known problem and will be improved in the final release. |l|c|l| Argument |Type |Meaning line | integer | position in a source text column | integer | position in a source text file | string | name of a source file module | string | module name errmsg | string | message text errno | integer | error code language | string | Oberon-2 or Modula-2 mode | string | ERROR or WARNING or FAULT utility | string | name of an utility Argument names are not case sensitive. By default the error format includes the following clauses: The appearance of the following table is a known problem and will be improved in the final release. lcl "(%s",file; |-| a file name "%d",line; |-| a line number ",%d",column; |-| a column number ") [%.1s] ",mode; |-| the first letter of an error mode "%s\n",errmsg; |-| an error message If the warning is reported in the file test.mod on line 5, column 6, the generated error message will look like this: (test.mod 5,6) [W] variable declared but never used ═══ 8. XDS Modula-2 ═══ This chapter includes the details of Modula-2 language which are specific to this implementation. In the standard modeWhen options M2EXTENSIONS and M2ADDTYPES are OFF XDS Modula-2 complies with International Standard (See the statement of compliance and further details in ISO Standard compliance). The compatibility rules are described in Compatibility. The differences between ISO Modula-2 and the language described in the 4th edition of Wirth's ``Programming in Modula-2'' PIM are listed in New language's features. Language extensions are described in Language extensions. ═══ 8.1. ISO Standard compliance ═══ XDS Modula-2 partially complies with the requirements of ISO 10514. The details of non-conformities are as follows:  Not all libraries are available in the current release.  The current release may impose some restrictions on using new language features. See Chapter Implementation limitations and restrictions for further details. ═══ 8.1.1. Ordering of declarations ═══ XDS Modula-2 is a so-called `single-pass' implementation. It means that all identifiers must be declared before use. According to the International Standard this declare-before-use approach is perfectly valid. The alternative approach (declare-before-use-in-declarations) can be used in the so-called `multi-pass' implementations. A forward declaration must be used to allow forward references to a procedure whose actual declaration appears later in the text. Example PROCEDURE a(x: INTEGER); FORWARD; (* FORWARD declaration *) PROCEDURE b(x: INTEGER); BEGIN a(x-1); END b; PROCEDURE a(n: INTEGER); (* proper procedure declaration *) BEGIN b(n-1); END a; To provide a source compatibility between `single-pass' and `multi-pass' implementation, the standard demands that all conforming `multi-pass' implementations accept and correctly process the FORWARD directive. ═══ 8.2. New language's features ═══ The language described in the International Standard varies in many details from that one described in Wirth's ``Programming in Modula-2'' PIM. The most important innovations are  complex numbers  module finalization  exception handling  array and record constructors  four new system modules  standard library Note: The system modules (except the module SYSTEM) are not embedded in the compiler and are implemented as separate modules. ═══ 8.2.1. Lexis ═══ The ISO Modula-2 appends new keywords (table ???, page table:m2:ISO:keywords) and pervasive identifiers (table ???, page table:m2:ISO:pervasive), and provides alternatives for some symbols (table Lexis, page table:m2:ISO:alt). It also introduces a syntax for source code directives (or pragmas): pragma = "<*" pragma_body "*>" The standard does not specify a syntax of pragma_body. In XDS, source code directives are used for the in-line option control and for the source control (conditional compilation). See Source code directives for further details. [bht] The appearance of the following table is a known problem and will be improved in the final release. p3.4cmp3.4cmp3.4cm AND | ARRAY | BEGIN BY | CASE | CONST DEFINITION | DIV | DO ELSE | ELSIF | END EXIT | EXCEPT (Exceptions) | EXPORT FINALLY (Finalization) | FOR | FORWARD (Ordering of declarations) FROM | IF | IMPLEMENTATION IMPORT | IN | LOOP MOD | MODULE | NOT OF | OR | PACKEDSET (Sets and packedsets) PROCEDURE | QUALIFIED | RECORD REM (Whole number division) | RETRY (Exceptions) | REPEAT RETURN | SET | THEN TO | TYPE | UNTIL VAR | WHILE | WITH Modula-2 keywords [hbtp] The appearance of the following table is a known problem and will be improved in the final release. p5.0cmp5.0cm ABS | BITSET BOOLEAN | CARDINAL CAP | CHR CHAR | COMPLEX (Complex types) CMPLX (Complex types) | DEC DISPOSE | EXCL FALSE | FLOAT HALT | HIGH IM (Complex types) | INC INCL | INT (Type conversions) INTERRUPTIBLE (Protection) | INTEGER LENGTH (Strings) | LFLOAT (Type conversions) LONGCOMPLEX (Complex types) | LONGREAL MAX | MIN NEW | NIL ODD | ORD PROC | PROTECTION (Protection) RE (Complex types) | REAL SIZE | TRUE TRUNC | UNINTERRUPTIBLE (Protection) VAL Modula-2 pervasive identifiers [htbp] The appearance of the following table is a known problem and will be improved in the final release. |c|l|c| Symbol | Meaning | Alternative [ | left bracket | (! ] | right bracket | !) { | left brace | (: } | right brace | :) | | case separator | ! ^ | dereference | @ Modula-2 alternative symbols ═══ 8.2.2. Complex types ═══ Types COMPLEX and LONGCOMPLEX can be used to represent complex numbers. These types differ in a the range and precision. The COMPLEX type is defined as a (REAL,REAL) pair, while LONGCOMPLEX consists of a pair of LONGREAL values. There is no notation for a complex literal. A complex value can be obtained by applying the standard function CMPLX to two reals. If both CMPLX arguments are real constants the result is the complex constant. CONST i = CMPLX(0.0,1.0); If both expressions are of the REAL type, or if one is of the REAL type and the other is a real constant, the function returns the COMPLEX value. If both expressions are of the LONGREAL type, or if one is of the LONGREAL type and the other is a real constant the function returns the LONGCOMPLEX value. The following table summarizes the permitted types and the result type: The appearance of the following table is a known problem and will be improved in the final release. |l|lll|| REAL | LONGREAL | real constant REAL | REAL | error| COMPLEX LONGREAL | error| LONGCOMPLEX | LONGCOMPLEX real constant | COMPLEX | LONGCOMPLEX | complex constant Standard functions RE and IM can be used to obtain a real or imaginary part of a value of a complex type. Both functions have one parameter. If the parameter is of the COMPLEX type, both functions return a REAL value; if the parameter is of the LONGCOMPLEX type, functions return a LONGREAL value; otherwise the parameter should be a complex constant and functions return a real constant. CONST one = IM(CMPLX(0.0,1.0)); There are four arithmetic operations for operands of a complex type: addition (+), subtraction (-), multiplication (*) and division (/). The following table indicates the result of an operation for the combinations permitted: The appearance of the following table is a known problem and will be improved in the final release. |l|lll|| COMPLEX | LONGCOMPLEX | complex constant COMPLEX | COMPLEX | error| COMPLEX LONGCOMPLEX | error| LONGCOMPLEX | LONGCOMPLEX complex constant | COMPLEX | LONGCOMPLEX | complex constant There are two arithmetic monadic operations that can be applied to the values of a complex type: identity (+) and negation (-). The result of an operation is of the operand's type. Two complex comparison operators are provided for operands of complex type: equality and inequality. Example PROCEDURE abs(z: COMPLEX): REAL; BEGIN RETURN RealMath.sqrt(RE(z)*RE(z)+IM(z)*IM(z)) END abs; ═══ 8.2.3. Sets and packedsets ═══ A set and packedset Packedset types are innovated in the standard. type defines a new type, whose set of values is the power set of an associated ordinal type called the base type of a set type. SetType = SET OF Type; PackedsetType = PACKEDSET OF Type; The International Standard does not require a specific representation for set types. Packedset types have a representation that is mapped to the individual bits of a particular underlying architecture. The standard type BITSET is a pre-defined packedset type. The current XDS implementation does not distinguish between the set and packedset types. A set of at least 256 elements can be defined. Example TYPE CharSet = SET OF CHAR; LongSet = PACKEDSET OF [-127..128]; All set operations, such as union (+), difference (-), intersection (*), and symmetrical difference (/) can be applied to the values of both set and packedset types. Example VAR letters, digits, alphanum: CharSet; ... letters := CharSet{'a'..'z','A'..'Z'}; digits := CharSet{'0'..'9'}; alphanum := letters + digits; ═══ 8.2.4. Strings ═══ For operands of the string literal type the string concatenation operation is defined, denoted by the symbol "+". Note: a character number literal (e.g. 15C) denotes a value of a literal string type of length 1. The empty string is compatible with the type CHAR and has a value equal to the string terminator (0C). Example CONST CR = 15C; LF = 12C; LineEnd = CR + LF; Greeting = "hello " + "world" + LineEnd; The new standard function LENGTH can be used to obtain the length of a string value. PROCEDURE LENGTH(s: ARRAY OF CHAR): CARDINAL; ═══ 8.2.5. Value constructors ═══ A value constructor is an expression denoting a value of an array type, a record type, or a set type. In case of array constructors and record constructors a list of values, known as structure components, is specified to define the values of components of an array value or the fields of a record value. In case of a set constructor, a list of members is specified, whose elements define the elements of the set values. ValueConstructor = ArrayValue | RecordValue | SetValue. ArrayValue = TypeIdentifier "{" ArrayComponent { "," ArrayComponent } "}". ArrayComponent = Component [ BY RepeatCount ]. Component = Expression. RepeatCount = ConstExpression. RecordValue = TypeIdentifier "{" Component { "," Component } "}". Set constructors are described in PIM. The total number of components of an array constructor must be exactly the same as the number of array's elements (taking into account any repetition of components). Each component must be an assignment compatible with the array base type. The number of components of a record constructor must be exactly the same as the number of fields. Each component must be an assignment compatible with the type of the field. A special case is a record constructor for a record with variant parts. If the n-th field is the tag field the n-th component must be a constant expression. If there is no ELSE variant part associated with the tag field, then a variant associated with the value of expression should exist. If no variant is associated with the value, then the fields of the ELSE variant part should be included in the sequence of components. The constructor's components may themselves contain lists of elements, and such nested constructs need not specify a type identifier. This relaxation is necessary for multi-dimensional arrays, where the types of the inner components may be anonymous. Examples TYPE String = ARRAY [0..15] OF CHAR; Person = RECORD name: String; age : CARDINAL; END; Vector = ARRAY [0..2] OF INTEGER; Matrix = ARRAY [0..2] OF Vector; VAR string: String; person: Person; vector: Vector; matrix: Matrix; .... string:=String{" " BY 16}; person:=Person{"Alex",32}; vector:=Vector{1,2,3}; matrix:=Matrix{vector,{4,5,6},Vector{7,8,9}}; matrix:=Matrix{vector BY 3}; ═══ 8.2.6. Multi-dimensional open arrays ═══ According to the International Standard, the parameters of a multi-dimensional open array are allowed. Example PROCEDURE Foo(VAR matrix: ARRAY OF ARRAY OF REAL); VAR i,j: CARDINAL; BEGIN FOR i:=0 TO HIGH(matrix) FOR j:=0 TO HIGH(matrix[i]) ... matrix[i,j] ... END; END; END Foo; VAR a: ARRAY [0..2],[0..2] OF REAL; BEGIN Foo(a); END ... ═══ 8.2.7. Procedure type declarations ═══ A procedure type identifier may be used in declaring of the type itself. This feature is used in the Standard Library. See, for example, modules ConvTypes and WholeConv. Example TYPE Scan = PROCEDURE (CHAR; VAR Scan); Func = PROCEDURE (INTEGER): Func; ═══ 8.2.8. Procedure constants ═══ A constant expression may contain the values of procedure types, or structured values whose components are the values of procedure types. Procedure constants may be used as a mechanism for procedure renaming. In a definition module it is possible to export a renamed version of imported procedure. Examples TYPE ProcTable = ARRAY [0..3] OF PROC; CONST WS = STextIO.WriteString; Table = ProcTable{Up,Down,Left,Right}; ═══ 8.2.9. Whole number division ═══ Along with DIV and MOD the International Standard includes two additional operators for whole number division: `/' and REM. Operators DIV and MOD are defined for positive divisors only, while `/' and REM can be used for both negative and positive divisors. The language exception wholeDivException (See Exceptions) is raised if:  second operand is zero (for all four operators)  second operand of DIV or MOD is negative. For the given lval and rval quotient := lval / rval; remainder := lval REM rval; the following is true (for all non-zero values of rval):  lval = rval * quotient + remainder  a value of remainder is either zero, or an integer of the same sign as lval and of a smaller absolute value than rval. For the given lval and rval quotient := lval DIV rval; modulus := lval MOD rval; the following is true (for all positive values of rval):  lval = rval * quotient + modules  a value of modulus is a non-negative integer less than rval. Operations are exemplified in the following table: The appearance of the following table is a known problem and will be improved in the final release. |c|c|c|c|c| op | 31 op 10 | 31 op (-10) | (-31) op 10 | (-31) op (-10) / | 3 | -3 | -3 | 3 REM | 1 | 1 | -1 | -1 DIV | 3 | exception | -4 | exception MOD | 1 | exception | 9 | exception ═══ 8.2.10. Type conversions ═══ The language includes the following type conversion functions: CHR, FLOAT, INT, LFLOAT, ORD, TRUNC and VAL. The functions INT and LFLOAT are not described in PIM. All the type conversion functions (except VAL) have a single parameter and can be expressed in terms of the VAL function. The appearance of the following table is a known problem and will be improved in the final release. |l|l|l|l| Name | Parameter | Result | Equal to CHR | whole | CHAR | VAL(CHAR,x) FLOAT | real or whole | REAL | VAL(REAL,x) INT | real or ordinal | INTEGER | VAL(INTEGER,x) LFLOAT | real or whole | LONGREAL | VAL(LONGREAL,x) ORD | ordinal | CARDINAL | VAL(CARDINAL,x) TRUNC | real | CARDINAL | VAL(CARDINAL,x) The function VAL can be used to obtain a value of a specified scalar type from an expression of a scalar type. The function has two parameters. The first parameter should be a type parameter that denotes a scalar type. If the type is a subrange type, the call of VAL returns the host type of the subrange type, otherwise it returns the type denoted by the type parameter. The second parameter should be an expression of a scalar type and at least one of the restriction shall hold:  the result type and the type of the expression are identical  both the result type and the type of the expression are whole or real  the result type or the type of the expression is a whole type In the following table, √ denotes a valid combination of types and · denotes an invalid combination: The appearance of the following table is a known problem and will be improved in the final release. |lccccc| the type of | 5cthe type denoted by the type parameter expression | whole | real | CHAR |BOOLEAN | enumeration whole type | √ | √ | √ | √ | √ real type | √ | √ | · | · | · CHAR | √ | · | √ | · | · BOOLEAN | √ | · | · | √ | · enumeration | √ | · | · | · | √ An exception is raised if the value x lies outside the range of type T in the call VAL(T,x). If x is of a real type, the calls VAL(INTEGER,x) and VAL(CARDINAL,x) both truncates the value of x. ═══ 8.2.11. NEW and DISPOSE ═══ The standard NEW and DISPOSE procedures are back in the language. Calls of NEW and DISPOSE are substituted by calls of ALLOCATE and DEALLOCATE which should be visible in the current scope. The compiler checks the compliance of these substitution procedures with the expected formal type: PROCEDURE ALLOCATE(VAR a: ADDRESS; size: CARDINAL); PROCEDURE DEALLOCATE(VAR a: ADDRESS; size: CARDINAL); As a rule, the procedures ALLOCATE and DEALLOCATE declared in the module Storage are used. These procedures are made visible by including the import list: FROM Storage IMPORT ALLOCATE, DEALLOCATE; When the language extensions are enabled the procedures NEW and DISPOSE can be applied to dynamic arrays. See NEW and DISPOSE for dynamic arrays for further details. ═══ 8.2.12. Finalization ═══ A special mechanism called finalization is provided to perform certain operation during program termination. A module declaration contains an optional finalization body, which is executed during program termination for static modules (See Termination) or dynamic module finalization. ModuleBody = [ BEGIN BlockBody [ FINALLY BlockBody ] ] END BlockBody = NormalPart [ EXCEPT ExceptionalPart ]. NormalPart = StatementSequence. ExceptionalPart = StatementSequence. Note: a RETURN statement can be used in a BlockBody. Example MODULE Test; .... VAR cid: StreamFile.ChanId; BEGIN StreamFile.Open(cid,"tmp",flags,res); Process(cid); FINALLY StreamFile.Close(cid); END Test If the Test module is declared in a procedure block, then the initialization body will be executed on a call of the procedure, while the finalization body is executed after the procedure body. If the Test module is a static module, its finalization will be executed during the program termination. In any case the finalization bodies are executed in inverse order to their initializations. In the following example, a finalization of a local module is used to provide a correct context restoration: VAR state: State; PROCEDURE Foo; MODULE AutoSave; IMPORT state, State; VAR save: State; BEGIN save:=state; (* save state *) state:=fooState; FINALLY state:=save; (* restore state *) END AutoSave; BEGIN ... process ... END Foo; The initialization part of the AutoSave module will be executed before any statement in the Foo body and finalization part will be executed directly before ending of the call of Foo. ═══ 8.2.13. Exceptions ═══ An exception handling mechanism is now included in the language. Both user-defined exceptions and language exceptions can be handled. There is no special exception type, exceptions are identified by the pair: exception source value and cardinal value. Two keywords (EXCEPT and RETRY) are added to the language. The essential part of exception handling is provided in two system modules: EXCEPTIONS and M2EXCEPTION. The EXCEPTIONS module provides facilities for raising and identifying the user-defined exceptions, for reporting their occurrence, and for making enquiries concerning the execution state of the current coroutine. The M2EXCEPTION module provides facilities for identifying language exceptions that have been raised. A procedure body, the initialization or finalization part of module body may contain an exceptional part. BlockBody = NormalPart [ EXCEPT ExceptionalPart ]. NormalPart = StatementSequence. ExceptionalPart = StatementSequence. Example PROCEDURE Div(a,b: INTEGER): INTEGER; BEGIN RETURN a DIV b (* try to divide *) EXCEPT RETURN MAX(INTEGER) (* if exception *) END Fly; When an exception is raised (explicitly or implicitly) the `nearest' (in terms of procedure calls) exceptional part in the current coroutine gets the control. Each coroutine is executed initially in a normal state. If an exception is raised, the coroutine state switches to an exceptional state. If there is no exceptional part, the raising of exception is a termination event (See Termination). A procedure with an exceptional part is executed in the normal state. The state is restored after the block execution. A procedure without an exceptional part is executed in the state of the caller. If an exception is raised in the state of exceptional execution it is re-raised in the calling context. In this case finalization of local modules and restoring protection (See Protection) will not take place. An additional statement (RETRY) can be used in the exceptional part. Execution of the RETRY statement causes the normal part to be re-executed in the normal state. Execution of the RETURN statement in the exceptional part causes switch to the normal state. If neither RETURN nor RETRY was executed in the exceptional part, the exceptional completion will occur. In this case after finalization of local modules (if any) and restoring protection state (if necessary), the exception will be re-raised. Example PROCEDURE Foo; BEGIN TryFoo(...); EXCEPT IF CanBeRepaired() THEN Repair; RETRY; (* re-execute the normal part *) ELSIF CanBeProcessed() THEN Process; RETURN; (* exception is handled *) ELSE (* exception will be automatically re-raised *) END; END Foo; ═══ 8.2.14. The system module EXCEPTIONS ═══ The module EXCEPTIONS provides facilities for raising user's exceptions and for making enquiries concerning the current execution state. User-defined exceptions are identified uniquely by a pair (exception source, number). When the source of a used-defined exception is a separate module, it prevents the defined exceptions of the module from being raised directly by other sources. See e.g. the module Storage. TYPE ExceptionSource; Values of the opaque type ExceptionSource are used to identify the source of exceptions raised; they should be allocated before usage. TYPE ExceptionNumber = CARDINAL; Values of the type ExceptionNumber are used to distinguish between different exceptions of one source. PROCEDURE AllocateSource(VAR newSource: ExceptionSource); The procedure allocates an unique value of the type ExceptionSource. The procedure is normally called during initialization of the module, and the resulting value is then used in all calls of RAISE. If an unique value cannot be allocated the language exception exException is raised (See The system module M2EXCEPTION). PROCEDURE RAISE(source: ExceptionSource; number: ExceptionNumber; message: ARRAY OF CHAR); The procedure call associates the given values of a source, number and message with the current context and raises an exception. The function CurrentNumber can be used to obtain the exception number for the current exception. PROCEDURE CurrentNumber (source: ExceptionSource): ExceptionNumber; If the calling coroutine is in the exceptional execution state because of raising an exception from source, the procedure returns the corresponding number, and otherwise raises an exception. The procedure GetMessage can be used to obtain the message passed when an exception is raised. This may give further information about the nature of the exception. PROCEDURE GetMessage(VAR text: ARRAY OF CHAR); If the calling coroutine is in the exceptional execution state, the procedure returns the possibly truncated string associated with the current context. Otherwise, in a normal execution state, it returns the empty string. PROCEDURE IsCurrentSource (source: ExceptionSource): BOOLEAN; If the current coroutine is in the exceptional execution state because of raising an exception from source, the procedure returns TRUE, and FALSE otherwise. PROCEDURE IsExceptionalExecution (): BOOLEAN; If the current coroutine is in the exceptional execution state because of raising an exception, the procedure returns TRUE, and FALSE otherwise. The following example illustrates the recommended form of a library module and the usage of procedures from EXCEPTIONS. DEFINITION MODULE FooLib; PROCEDURE Foo; (* Raises Foo exception if necessary *) PROCEDURE IsFooException(): BOOLEAN; (* Returns TRUE, if the calling coroutine is in exceptional state because of the raising of an exception from Foo, and otherwise returns FALSE. *) END FooLib. IMPLEMENTATION MODULE FooLib; IMPORT EXCEPTIONS; VAR source: EXCEPTIONS.ExceptionSource; PROCEDURE Foo; BEGIN TryFoo(...); IF NOT done THEN EXCEPTIONS.RAISE(source,0,"Foo exception"); END; END Foo; PROCEDURE IsFooException(): BOOLEAN; BEGIN RETURN EXCEPTIONS.IsCurrentSource(source) END IsLibException; BEGIN EXCEPTIONS.AllocateSource(source) END FooLib. If we want to distinguish the exceptions raised in the FooLib we will append an enumeration type and an additional enquiry procedure in the FooLib definition: TYPE FooExceptions = (fault, problem); PROCEDURE FooException(): FooExceptions; The FooException procedure can be implemented as follows: PROCEDURE FooException(): FooExceptions; BEGIN RETURN VAL(FooExceptions, EXCEPTIONS.CurrentNumber(source)) END FooException; The Client module illustrates the usage of a library module: MODULE Client; IMPORT FooLib, EXCEPTIONS, STextIO; PROCEDURE ReportException; VAR s: ARRAY [0..63] OF CHAR; BEGIN EXCEPTIONS.GetMessage(s); STextIO.WriteString(s); STextIO.WriteLn; END ReportException; PROCEDURE TryFoo; BEGIN FooLib.Foo; EXCEPT IF FooLib.IsFooException() THEN ReportException; RETURN; (* exception is handled *) ELSE (* Exception will be re-raised *) END END TryFoo; END Client. ═══ 8.2.15. The system module M2EXCEPTION ═══ The system module M2EXCEPTION provides facilities for identifying language exceptions. The language (which includes the system modules) is regarded as one source of exceptions. The module provides the enumeration type in terms of which the language exceptions are raised and two enquiry functions. TYPE M2Exceptions = (indexException, rangeException, caseSelectException, invalidLocation, functionException, wholeValueException, wholeDivException, realValueException, realDivException, complexValueException, complexDivException, protException, sysException, coException, exException ); PROCEDURE IsM2Exception(): BOOLEAN; If the current coroutine is in the exceptional execution state because of raising a language exception, the procedure returns TRUE, and FALSE otherwise. PROCEDURE M2Exception(): M2Exceptions; If the current coroutine is in the exceptional execution state because of raising a language exception, the procedure returns the corresponding enumeration value, and otherwise raises an exception. The following description lists all language exceptions (in an alphabetical order) along with the circumstances in which exceptions are detected. Note: Compiler options can be used to control the detection of some exceptions (See Chapter Compiler Options and Equations). Detection of some exceptions is not required, however such exceptions can be detected on some platforms (See Chapter Implementation limitations and restrictions). caseSelectException Case selector is out of range and the ELSE clause does not exist. coException The system module COROUTINES exceptions:  RETURN from coroutine other than the main coroutine  size of the supplied workspace smaller than the minimum required (See NEWCOROUTINE)  the caller is not attached to the source of interrupts (See HANDLER)  coroutine workspace overflow complexDivException Divide by zero in a COMPLEX expression. complexValueException Overflow in evaluation of a COMPLEX expression. exException The system modules EXCEPTIONS and M2EXCEPTION exception:  exception identity is enquiry in normal execution (See CurrentNumber)  exception identity enquiry to a wrong source (SeeCurrentNumber)  no further exception source values can be allocated (See AllocateSource) functionException No RETURN statement before the end of a function. indexException Array indexed out of range. See options CHECKINDEX and CHECKDINDEX. invalidLocation Attempt to dereference NIL or uninitialized pointer. See the option CHECKNIL. protException Given protection is less restrictive than the current protection. rangeException Range exception (See the CHECKRANGE option):  assignment value is out of range of a target  structure component value is out of range  expression cannot be converted to a new type  value to be included/excluded is not of the base type of a set (See also the CHECKSET option)  return value is out of range  set value is out of range (See also the CHECKSET option)  tag value is out of range (in a variant record). realDivException Divide by zero in a REAL expression. realValueException Overflow in evaluation of a REAL expression. sysException The system module SYSTEM exceptions. Note: All these exceptions are non-mandatory.  invalid use of ADDADR, SUBADR or DIFADR  the result of MAKEADR is out of the address range  alignment problem with CAST  the result of CAST is not a valid representation for the target type wholeDivException Whole division exception:  divided by zero in evaluation of a whole number expression  the second operand of DIV or MOD is negative (See the CHECKDIV option) wholeValueException Overflow in evaluation of a whole number expression. Example of handling a language exception PROCEDURE Div(a,b: INTEGER): INTEGER; BEGIN RETURN a DIV b EXCEPT IF IsM2Exception() THEN IF M2Exception() = wholeDivException THEN IF a < 0 THEN RETURN MIN(INTEGER) ELSE RETURN MAX(INTEGER) END; END; END; END Div; ═══ 8.2.16. Termination ═══ During the program termination, finalization of those static modules that have started initialization are executed in inverse order their initialization (See also Finalization). The static modules are the program module, the implementation modules, and any local modules declared in the module blocks of these modules. Program termination starts from the first occurrence of the following event: 1. the end of the program module body is reached 2. a RETURN statement is executed in the program module body 3. the standard procedure HALT is called 4. an exception is raised and this exception is not handled. The system module TERMINATION provides facilities for enquiries concerning the occurrence of termination events. PROCEDURE IsTerminating(): BOOLEAN; Returns TRUE if any coroutine has started the program termination and FALSE otherwise. PROCEDURE HasHalted(): BOOLEAN; Returns TRUE if a call of HALT has been made and FALSE otherwise. ═══ 8.2.17. Coroutines ═══ The system module COROUTINES provides facilities for creating coroutines, for the explicit transfer of control between coroutines, and for handling of the interrupts. Note: Some features can be unavailable in the current release. See Chapter Implementation limitations and restrictions for details. Values of the type COROUTINE are created dynamically by a call of NEWCOROUTINE and identify the coroutine in subsequent operations. A particular coroutine is identified by the same value of the coroutine type throughout the lifetime of that coroutine. TYPE COROUTINE; The correspondent type was called PROCESS in PIM. From the third edition of PIM, the ADDRESS type was used to identify a coroutine. PROCEDURE NEWCOROUTINE( procBody: PROC; workspace: SYSTEM.ADDRESS; size: CARDINAL; VAR cr: COROUTINE [; initProtection: PROTECTION]); Creates a new coroutine whose body is given by procBody, and returns the identity of the coroutine in cr. workspace is a pointer to the work space allocated to the coroutine; size specifies the size of this workspace in terms of SYSTEM.LOC. initProtection is an optional parameter that specifies the initial protection level of the coroutine. An exception is raised (See coException) if the value of size is less than the minimum workspace size. If the optional parameter is omitted, the initial protection of the coroutine is given by the current protection of the caller. The created coroutine is initialized in such a way that when control is first transferred to that coroutine, the procedure given by procBody is called in a normal state. The exception (coException) is raised when the procBody procedure attempts to return to its caller. Since the caller has no exception handler, raising this exception is a termination event. The procedure TRANSFER can be used to transfer control from one coroutine to another. PROCEDURE TRANSFER (VAR from: COROUTINE; to: COROUTINE); Returns the identity of the calling coroutine in from and transfers control to the coroutine specified by to. PROCEDURE CURRENT (): COROUTINE; Returns the identity of the calling coroutine. Interrupt handling The INTERRUPTSOURCE type is used to identify interrupts. TYPE INTERRUPTSOURCE = INTEGER; Programs that use the interrupt handling facilities may be non-portable since the type is implementation-defined. PROCEDURE ATTACH(source: INTERRUPTSOURCE); Associates the specified source of interrupts with the calling coroutine. More than one source of interrupts may be associated with a single coroutine. PROCEDURE DETACH(source: INTERRUPTSOURCE); Dissociates the specified source of interrupts from the calling coroutine. The call has no effect if the coroutine is not associated with source. PROCEDURE IsATTACHED(source: INTERRUPTSOURCE): BOOLEAN; Returns TRUE if and only if the specified source of interrupts is currently associated with a coroutine; otherwise returns FALSE. PROCEDURE HANDLER(source: INTERRUPTSOURCE): COROUTINE; Returns the coroutine, if any, that is associated with the source of interrupts. The result is undefined if there is no coroutine associated with the source. PROCEDURE IOTRANSFER(VAR from: COROUTINE; to: COROUTINE); Returns the identity of the calling coroutine in from and transfers control to the coroutine specified by to. On occurrence of an interrupt, associated with the caller, control is transferred back to the caller, and from returns the identity of the interrupted coroutine. An exception is raised if the calling coroutine is not associated with a source of interrupts. Protection See section Protection for information about PROTECTION type. PROCEDURE LISTEN(prot: PROTECTION); Momentarily changes protection of the calling coroutine to prot, usually lowering it so as to allow an interrupt request to be granted. PROCEDURE PROT(): PROTECTION; Returns protection of the calling coroutine. ═══ 8.2.18. Protection ═══ A program module, implementation module or local module may specify, by including protection in its heading, that execution of the enclosed statement sequence is protected. ModuleHeading = MODULE ident [ Protection ] ";". Protection = [ ConstExpression ]. A module with protection in its heading is called a directly protected module. A directly protected procedure is an exported procedure declared in the protected module. Protection of a module is provided by surrounding the externally accessible procedures and module body by calls of access control procedures. The value of the protection expression is passed to the call of access control procedures as an actual parameter. The protection expression should be of the PROTECTION type. The PROTECTION type is an elementary type with at least two values: INTERRUPTIBLE and UNINTERRUPTIBLE. Operators <, >, <= and >= can be used to compare the values of the PROTECTION type. If x is a value of PROTECTION type, then x satisfies the conditions: UNINTERRUPTIBLE ?x? INTERRUPTIBLE ═══ 8.3. Standard procedures ═══ [hbt] The appearance of the following table is a known problem and will be improved in the final release. |cl|p6.5cm| |Procedure |Meaning √ | ASSERT(x[,n]) | Terminates program if x?TRUE (See ASSERT) √ | COPY(x,v) | Copy string: v := x (See Proper procedures) |DEC(v[,n]) | v := v - n, default n=1 |DISPOSE(v) | Deallocates v'136(See NEW and DISPOSE) |EXCL(v,n) | v := v - {n} |HALT | Terminates program execution (See HALT) |INC(v[,n]) | v := v + n, default n=1 |INCL(v,n) | v := v + {n} |NEW(v) | Allocates v'136(See NEW and DISPOSE) √ | NEW(v,x0xn) | Allocates v'136of length x0xn (See NEW and DISPOSE for dynamic arrays and Pointer types) Modula-2 proper procedures [htb] The appearance of the following table is a known problem and will be improved in the final release. |cl|p7.5cm| |Function |Meaning |ABS(x) | Absolute value of x √|ASH(x,n) | Arithmetic shift |CAP(x) | Corresponding capital letter |CHR(x) | Character with an ordinal number x |CMPLX(x,y) | Complex number with real part x and imaginary part y √|ENTIER(x) | Largest integer not greater than x |FLOAT(x) |VAL(REAL,x) |HIGH(v) | High bound of the index of v |IM(x) | Imaginary part of a complex x |INT(x) |VAL(INTEGER,x) √|LEN(v[,n]) | Length of an array in the dimension n (default=0) |LENGTH(x) | String length |LFLOAT(x) |VAL(LONGREAL,x) |MAX(T) | Maximum value of type T |MIN(T) | Minimum value of type T |ODD(x) | x MOD 2 = 1 |ORD(x) |VAL(CARDINAL,x) |RE(x) | Real part of a complex x |SIZE(T) | The number of storage units, required by a variable of type T |TRUNC(x) | Truncation to the integral part |VAL(T,x) | Type conversion Modula-2 function procedures This section is intended for a brief description of the set of standard procedures and functions. Some of the procedures and functions are not described in the International Standard and are available only if the option M2EXTENSIONS is set. The procedure HALT may have an additional parameter, if the extensions are enabled (See HALT). In the tables (??? and Standard procedures) of predefined procedures, v means a designator, x, y and n mean an expression, T means a type. Non-standard procedures are marked with √. The procedure COPY and the functions ASH, ENTIER and LEN are described in the Oberon report (See Predeclared procedures). ═══ 8.4. Compatibility ═══ This section describes compatibility between entities of different types. There are three forms of compatibility:  expression compatibility (specifying the types that may be combined in expressions);  assignment compatibility (specifying the type of a value that may be assigned to a variable);  parameter compatibility (specifying the type of an actual parameter that may be passed to a formal parameter). The rules for parameter compatibility are relaxed in the case where a formal parameter is of a system storage type. This variation is known as the system parameter compatibility. In most cases the compatibility rules are the same as described in PIM. However, we suppose to explicitly list all the rules. ═══ 8.4.1. Expression compatibility ═══ Two expressions a and b of types Ta and Tb are expression compatible if any of the following statement is true: 1. The types Ta and Tb are identical. Note: If a type is a subrange type, then only its host type matters, therefore values of subranges of the same host type are expression compatible with each other and with the host type. 2. A type of one expression is a complex type, and the other expression is a complex constant. 3. A type of one expression is a real type, and the other expression is a real constant. 4. A type of one expression is a whole type, and the other expression is a whole constant. 5. A type of one expression is character, and the other expression is a string literal of length 0 or 1. See also Strings. VAR char: CHAR; ... WHILE (char # '') & (char # ".") DO ... ═══ 8.4.2. Assignment compatibility ═══ An expression e of type Te is assignment compatible with the variable v of type Tv if one of the following conditions holds For an expression of a subrange type only host type matters. : 1. Tv is identical to the type Te, and the type is not an open array type. 2. Tv is a subrange of the type Te. 3. Tv is the CARDINAL type or a subrange of the CARDINAL type and Te is the INTEGER type or e is a whole constant. 4. Tv is the INTEGER type or a subrange of the INTEGER type and Te is the CARDINAL type or e is a whole constant. 5. Tv is a real type and e is a real constant. 6. Tv is a complex type and e is a complex constant. 7. Tv is a pointer type and e is NIL. 8. Tv is a procedure type and e is the designator of a procedure which has the same structure as the procedure type Tv and which has been declared at level 0. 9. Tv is the character type or a subrange of the character type and e is a string literal of length 0 or 1. 10. Tv is an array type having the character type as its component type, and e is a string literal of length less then or equal to the number of components in arrays of type TvA string literal is not assignment compatible with an array whose component's type is a subrange of the character type.. 11. Tv is the address type and Te is a pointer type or Te is the address type and Tv is a pointer type. ═══ 8.4.3. Value parameter compatibility ═══ A formal type is value parameter compatible with an actual expression if any of the following statements is true: 1. The formal type is constructed from a system storage type and is system parameter compatible with the expression. 2. The formal parameter is an open array, the actual parameter is an array type and the component type of the formal type is value parameter compatible with the component type of the actual type A formal array parameter with the component's type T is not parameter compatible with the actual parameter of type T.. 3. The formal type is assignment compatible with the actual parameter. ═══ 8.4.4. Variable parameter compatibility ═══ A formal type is variable parameter compatible with an actual variable if any of the following statements is true: 1. The formal type is constructed from a system storage type and is system parameter compatible with the expression. 2. The formal parameter is an open array, the actual parameter is an array type and the component's type of the formal type is variable parameter compatible with the component's type of the actual parameter type. 3. The formal type is identical to the actual parameter type. ═══ 8.4.5. System parameter compatibility ═══ A formal type is system parameter compatible with an actual parameter if any of the following statements is true: 1. The formal parameter is of the SYSTEM.LOC type and the actual parameter is of any type T such that SIZE(T) is equal to 1. 2. The formal parameter is of the type ARRAY [0..n-1] OF SYSTEM.LOC and the actual parameter is of any type T such that SIZE(T) is equal to n. 3. The formal parameter is of the open array type ARRAY OF SYSTEM.LOC and the actual parameter is of any type but not numeric literal. 4. The formal parameter is of the multi-dimensional open array type ARRAY OF ARRAY [0..n-1] OF SYSTEM.LOC and the actual parameter is of any type T such that SIZE(T) is a multiple of n. ═══ 8.5. The module SYSTEM ═══ The module SYSTEM provides the low-level facilities for gaining an access to the address and underlying storage of variables, performing address arithmetic operations and manipulating the representation of values. Program that use these facilities may be non-portable. This module does not exist in the same sense as other libraries but is hard-coded into the compiler itself. To use the facilities provided, however, identifiers must be imported in a usual way. Some of the SYSTEM module procedures are generic procedures that cannot be explicitly declared, i.e. they apply to classes of operand types or have several possible forms of a parameter list . The SYSTEM module is the only module specified in the International Standard that can be extended in the implementation. The XDS SYSTEM module provides additional types and procedures. DEFINITION MODULE SYSTEM; CONST BITSPERLOC = 8; LOCSPERWORD = 4; LOCSPERBYTE = 1; TYPE LOC; ADDRESS = POINTER TO LOC; WORD = ARRAY [0 .. LOCSPERWORD-1] OF LOC; BYTE = LOC; PROCEDURE ADDADR(addr: ADDRESS; offset: CARDINAL): ADDRESS; PROCEDURE SUBADR(addr: ADDRESS; offset: CARDINAL): ADDRESS; PROCEDURE DIFADR(addr1, addr2: ADDRESS): INTEGER; PROCEDURE MAKEADR(val: ): ADDRESS; PROCEDURE ADR(VAR v: ): ADDRESS; PROCEDURE ROTATE(val: ; num: INTEGER): ; PROCEDURE SHIFT(val: ; num: INTEGER): ; PROCEDURE CAST(; val: ): ; PROCEDURE TSIZE (; ... ): CARDINAL; (*------------------------------------------------------- *) (* -------------- non-standard features ----------------- *) TYPE INT8 = ; INT16 = ; INT32 = ; CARD8 = ; CARD16 = ; CARD32 = ; BOOL8 = ; BOOL32 = ; INDEX = DIFADR_TYPE = TYPE (* for use in Oberon *) INT = ; CARD = ; TYPE (* for interfacing to C *) int = ; unsigned = ; size_t = ; void = ; PROCEDURE MOVE(src,dest: ADDRESS; size: CARDINAL); PROCEDURE GET(adr: ADDRESS; VAR var: SimpleType); PROCEDURE PUT(adr: ADDRESS; var: SimpleType); PROCEDURE CC(n: CARDINAL): BOOLEAN; END SYSTEM. ═══ 8.5.1. System types ═══ LOC Values of the LOC type are the uninterpreted contents of the smallest addressable unit of a storage in implementation. The value of the call TSIZE(LOC) is therefore equal to one. The type LOC was introduced as a mechanism to resolve the problems with BYTE and WORD types. Its introduction allows a consistent handling of both these types, and enables also WORD-like types to be further introduced, eg: TYPE WORD16 = ARRAY [0..1] OF SYSTEM.LOC; The only operation directly defined for the LOC type is an assignment. There are special rules affecting parameter compatibility for system storage types. See System parameter compatibility for further details. BYTE BYTE is defined as LOC and has all the properties of the type LOC. WORD The type WORD is defined as CONST LOCSPERWORD = 4; TYPE WORD = ARRAY [0..LOCSPERWORD-1] OF LOC; and the value of the call TSIZE(WORD) is equal to LOCSPERWORD. The only operation directly defined for the WORD type is an assignment. There are special rules affecting parameter compatibility for system storage types. See System parameter compatibility for further details. ADDRESS The type ADDRESS is defined as TYPE ADDRESS = POINTER TO LOC; The ADDRESS type is an assignment compatible with all pointer types and vice versa (See Assignment compatibility). A formal variable parameter of the ADDRESS type is a parameter compatible with an actual parameter of any pointer type. Variables of type ADDRESS are no longer expression compatible with CARDINAL (as was in PIM) and they cannot directly occur in expressions that include arithmetic operators. Functions ADDADR, SUBADR and DIFADR were introduced for address arithmetic. Whole system types Types INT8, CARD8, INT16, CARD16, INT32, CARD32 are guaranteed to contain 8, 16, or 32 bits respectively. These types are introduced to simplify constructing the interfaces for foreign libraries (See Chapter Multilanguage programming). Types SHORTINT, LONGINT, SHORTCARD, LONGCARD are synonyms of INT8, INT32, CARD8, CARD32, respectively (See also the M2ADDTYPES option). Types INTEGER and CARDINAL are synonyms of INT16/INT32, CARD16/CARD32, depending on the platform. These types are not described in the International Standard. Boolean system types Types BOOL8 and BOOL32 are guaranteed to contain 8 and 32 bits respectively. By default the compiler uses BOOL8 type for BOOLEAN. In some cases (e.g. in interface to OS/2 or Windows API) BOOL32 should be used instead (See also the option M2UNPACKTYPES). Modula-2 whole types Types INT and CARD are equal to Modula-2 INTEGER and CARDINAL types, respectively. These types can be used in Oberon-2 in order to use Modula-2 procedures in portable way. See Modula-2 and Oberon-2 for further details. Interface to C Types int, unsigned, size_t and void are introduced to simplify constructing the interfaces to C libraries. See Interfacing to C for further details. ═══ 8.5.2. System functions ═══ PROCEDURE ADDADR(addr: ADDRESS; offs: CARDINAL): ADDRESS; Returns an address given by (addr + offs). The subsequent use of the calculated address may raise an exception. PROCEDURE SUBADR(addr: ADDRESS; offs: CARDINAL): ADDRESS; Returns an address given by (addr - offs). The subsequent use of the calculated address may raise an exception. PROCEDURE DIFADR(addr1,addr2: ADDRESS): INTEGER; Returns the difference between addresses (addr1 - addr2). PROCEDURE MAKEADR(val: ): ADDRESS; The function is used to construct a value of the ADDRESS type from the value of a whole typeThe International Standard does not define the number and types of the parameters. Programs that use this procedure may be non-portable.. PROCEDURE ADR(VAR v: ): ADDRESS; Returns the address of the variable v. PROCEDURE CAST(; x: ): ; The function CAST can be used (as a type transfer function) to interpret a value of any type other than a numeric literal value as a value of another type The International Standard forbids the use of the PIM style type transfer, like CARDINAL(x).. The value of the call CAST(Type,val) is an unchecked conversion of val to the type Type. If SIZE(val) = TSIZE(Type), the bit pattern representation of the result is the same as the bit pattern representation of val; otherwise the result and the value of val have the same bit pattern representation for a size equal to the smaller of the numbers of storage units. The given implementation may forbid some combinations of parameter types. PROCEDURE TSIZE(Type; ... ): CARDINAL; Returns the number of storage units (LOCS) used to store the value of the specified type. The extra parameters, if present, are used to distinguish variants in a variant record and must be constant expressionsThose constant expressions are ignored in the current release.. Example TYPE R = RECORD CASE i: INTEGER OF |1: r: REAL; |2: b: BOOLEAN; END; END; ... TSIZE(R,1) ... The value of TSIZE(T) is equal to SIZE(T). Packedset functions Values of PACKEDSET types are represented as sequences of bits The current implementation does not distinguish between set and packedset types.. The bit number 0 is the least significant bit for a given platform. The following is true, where v is a variable of the type CARDINAL: CAST(CARDINAL,BITSET{0}) = VAL(CARDINAL,1) SHIFT(CAST(BITSET,v),1) = v * 2 SHIFT(CAST(BITSET,v),-1) = v DIV 2 The functions ROTATE and SHIFT can be applied to a set with size less than or equal to the size of BITSET. PROCEDURE ROTATE(x: T; n: integer): T; Returns the value of x rotated n bits to the left (for positive n) or to the right (for negative n). PROCEDURE SHIFT(x: T; n: integer): T; Returns the value of x logically shifted n bits to the left (for positive n) or to the right (for negative n). Non-standard functions PROCEDURE CC(n: whole constant): BOOLEAN; Returns TRUE if the corresponding condition flag is set. The function is not implemented in the current release. PROCEDURE BIT(adr: T; bit: INTEGER): BOOLEAN; Returns bit n of Mem[adr]. T is either ADDRESS or whole type. ═══ 8.5.3. System procedures ═══ Note: all these procedures are non-standard. PROCEDURE MOVE (src, dst: ADDRESS; size: CARDINAL); Copies size bytes from a memory location specified by src to a memory location specified by dst. PROCEDURE GET (adr: ADDRESS; VAR v: SimpleType); PROCEDURE PUT (adr: ADDRESS; x: SimpleType); Gets/puts a value from/to address specified by adr. The second parameter cannot be of a record or array type. VAR i: INTEGER; GET (128, i); (* get system cell value *) i := i+20; (* change it *) PUT (128, i); (* and put back *) PROCEDURE CODE(...); The procedure is intended to embed a sequence of machine instructions directly into the generated code. The procedure is not implemented in the current release. ═══ 8.6. Language extensions ═══ The appearance of the following table is a known problem and will be improved in the final release. |lp7.5cm|WARNING: | Using extensions may cause problems with the software portability to other compilers In the standard mode the XDS Modula-2 compiler is ISO compatible (See ISO Standard compliance). A set of language extensions may be enabled using the M2EXTENSIONS and M2ADDTYPES options. The main purposes of supporting the language extensions are  to improve interfacing with other languages (See Chapter Multilanguage programming)  to simplify migration from Modula-2 to Oberon-2  to implement important features not found in Modula-2  to provide backward compatibility with previous releases ═══ 8.6.1. Lexical extensions ═══ Comments The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option M2EXTENSIONS is set. As well as (**), there is another valid format for comments in the source texts. The portion of a line from ``-'' to the end is considered as a comment. Example VAR i: INTEGER; -- this is a comment --(* i:=0; (* this line will be compiled *) --*) Numeric constants The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option M2EXTENSIONS is set. Both Modula-2 and Oberon-2 syntax rules for the numeric and character representations may be used. number =["+"|"-"]integer|real. integer = digit{digit} |octalDigit{octalDigit}"B" |digit{hexDigit}"X". real =digit{digit}"."{digit}[ScaleFactor]. ScaleFactor = ("E"|"D")["+"|"-"]digit{digit}. character ='"'char'"'|"'"char"'" |digit{hexDigit}"H" |octalDigit{octalDigit}"C". Examples 1991 1991 (decimal) 0DH 13 (decimal) 15B 13 (decimal) 41X "A" 101C "A" Note: the identifier D in the scalefactor refers to a LONGREAL value. ═══ 8.6.2. Additional numeric types ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option M2ADDTYPES is set. The compiler option M2ADDTYPES allows us to use the following additional numeric types: The appearance of the following table is a known problem and will be improved in the final release. lll 1. |SHORTINT | the integers between -128 and 127 2. |LONGINT | the integers between -231 and 231-1 3. |SHORTCARD | unsigned integers between 0 and 255 4. |LONGCARD | unsigned integers between 0 and 232-1 The following terms for groups of types will be used: Real types for (REAL, LONGREAL) Integer types for (SHORTINT, INTEGER, LONGINT) Cardinal types for (SHORTCARD, CARDINAL, LONGCARD) Whole types for integer and cardinal types Numeric types for whole and real types All the integer types are implemented as subranges of an internal compiler integer types. Therefore, according to the compatibility rules (See Compatibility), the values of different integer types can be mixed in the expressions. The same holds for cardinal types. A mixture of integer and cardinal types in expressions is not allowed. As in Oberon-2, the numeric types form a hierarchy, and larger types include (i.e. can accept the values of) smaller types: LONGREAL ?REAL ?whole types Type compatibility in expressions is extended according to the following rules (See Expression compatibility):  The type of the result of an arithmetic or relation operation is the smallest type which includes the types of both operands.  Before the operation, the values of both operands are converted to the result's type. If the following variables are defined : s: SHORTCARD; c: CARDINAL; i: INTEGER; l: LONGINT; r: REAL; lr: LONGREAL; then The appearance of the following table is a known problem and will be improved in the final release. |l|l|l| Expression |Meaning |Result type s + c |VAL(CARDINAL,s) + c | CARDINAL l * i |l * VAL(LONGINT,i) | LONGINT r + 1 |r + VAL(REAL,1) | REAL r = s |r = VAL(REAL,s) | BOOLEAN r + lr |VAL(LONGREAL,r) + lr | LONGREAL c + i |not allowed | The assignment compatibility rules are also extended (See Assignment compatibility), so an expression e of type Te is assignment compatible with a variable v of type Tv if Te and Tv are of numeric types and Tv includes Te. Cardinal types and integer types are assignment compatible. The compiler generates the range checks whenever necessary. Examples (see declarations above): The appearance of the following table is a known problem and will be improved in the final release. |l|p7cm| Statement |Comment i:=c; | INTEGER and CARDINAL are assignment compatible i:=s; | INTEGER and SHORTCARD are assignment compatible l:=i; | LONGINT and INTEGER are subranges of the same host type r:=i; | REAL? INTEGER r:=c; | REAL? CARDINAL lr:=r; | LONGREAL? REAL ═══ 8.6.3. Assignment compatibility with BYTE ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option M2EXTENSIONS is set. Expressions of types CHAR, BOOLEAN, SHORTINT, and SYSTEM.CARD8 can be assigned to the variables of type BYTE or passed as actual parameters to a formal parameter of type BYTE. ═══ 8.6.4. Dynamic arrays ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option M2EXTENSIONS is set. XDS allows us to use the Oberon-2 style dynamic arrays according to the Oberon-2 rules. An open array is an array type with no lower and upper bound specified, i.e. ARRAY OF SomeType. Open arrays may be used only in procedure parameter lists or as a pointer base type. TYPE String = POINTER TO ARRAY OF CHAR; Neither variables nor record fields may be of open array type. If the designator type is formally an open array, then the only operations allowed with it are indexing and passing it to a procedure. The extended versions of standard procedures NEW and DISPOSE can be used to create and delete the dynamic arrays (See NEW and DISPOSE for dynamic arrays). Example TYPE VECTOR = ARRAY OF REAL; (* 1-dim open array *) Vector = POINTER TO VECTOR; (* pointer to open array *) MATRIX = ARRAY OF VECTOR; (* 2-dim open array *) Matrix = POINTER TO MATRIX; (* pointer to this *) VAR v: Vector; m: Matrix; PROCEDURE ClearVector(VAR v: VECTOR); VAR i: CARDINAL; BEGIN FOR i := 0 TO HIGH (v) DO v[i] := 0 END; END ClearVector; PROCEDURE ClearMatrix(VAR m: Matrix); VAR i: CARDINAL; BEGIN FOR i := 0 TO HIGH (m) DO ClearVector(m[i]) END; END ClearMatrix; PROCEDURE Test; BEGIN NEW(v, 10); NEW(m, 10, 20); ClearVector(v^); ClearMatrix(m^); v^[0] := 1; m^[1][1] := 2; m^[2,2] := 1000; DISPOSE(v); DISPOSE(m); END Test; ═══ 8.6.5. Constant array constructors ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option M2EXTENSIONS is set. XDS allows the declaration of constant arrays in the form ARRAY OF QualIdent "{" ExprList "}". QualIdent should refer to a basic type, range or enumeration type, and all expressions within ExprList should be of this type. Note: structured types and non-constant expressions are not allowed. The actual type of such a constant is ARRAY [0..n] OF QualIdent, where n+1 is the number of expressions in ExprList. Example CONST table = ARRAY OF INTEGER {1, 2+3, 3}; Constant arrays are subject to the same rules as all other constants, and may be read as a normal array. In some cases constructors of this form are more convenient than ISO standard value constructors (See Value constructors), because you do not need to declare a type and to calculate manually the number of expressions. However, to make your programs more portable, we recommend to use the standard features. ═══ 8.6.6. Set complement ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option M2EXTENSIONS is set. As in Oberon-2 a unary minus applied to a set denotes the complement of that set, i.e. -x is the set of all values which are not the elements of x. TYPE SmallSet = SET OF [0..5]; VAR x, y: SmallSet; BEGIN x := SmallSet{1,3,5}; y := -x; (* y = {0, 2, 4} *) y := SmallSet{0..5} - x; (* y = {0, 2, 4} *) END; ═══ 8.6.7. Read-only parameters ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option M2EXTENSIONS is set. In a formal parameter's section, the symbol "-" may be placed after the name of a value parameter. Such a parameter is called read-only; its value can not be changed in the procedure body. Read-only parameters need not be copied before procedure activation; this enables generation of procedure calls with structured parameters to be made more effective. For ARRAY and RECORD read-only parameters, the array elements and record fields are protected. Read-only parameters cannot be used in the definition modules. We recommend to use read-only parameters with care. The compiler does not check that the read-only parameter is not modified via the another parameter or global variable. Example PROCEDURE Foo(VAR dest: ARRAY OF CHAR; source-: ARRAY OF CHAR); BEGIN dest[0]:='a'; dest[1]:=source[0]; END Foo; The call Foo(x,x) produces the wrong result, because the first statement changes the value of source[0] (source is not copied and points to the same location as dest). ═══ 8.6.8. Variable number of parameters ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option M2EXTENSIONS is set. The last formal parameter in a procedure may be declared as a ``sequence of bytes'' (SEQ-parameter). In a procedure call, any (possibly empty) sequence of actual parameters may be substituted for such a parameter. Actual parameters corresponding to a sequence parameter may be of any type. Only the declaration SEQ name: SYSTEM.BYTE is allowed. Procedures may have only one SEQ parameter, and this must be the last element of the list of its formal parameters. Within the procedure, sequence parameters are very similar to ARRAY parameters. This means that :  HIGH function can be applied to the parameter;  a SEQ actual parameter may be subsequently passed to a further procedure  i-th byte of the sequence s can be accessed by s[i], like an array element. An array of bytes, which is passed to a procedure as a formal SEQ-parameter, is formed as follows:  values of all actual parameters forming the sequence are represented as described below and concatenated in an array in their textual order  integer values are converted to LONGINT  BOOLEAN, CHAR, cardinal and enumeration values are converted to LONGCARD  values of the range types are converted according to their base type  real values are converted to LONGREAL  values of pointer, opaque and procedure types are converted to ADDRESS  a structured value (record or array) is interpreted as an array of bytes and passed as a sequence of: - address of a structure - zero word (reserved for future extensions) - size of a structure (in LOCs) minus one See Sequence parameters for further information. ═══ 8.6.9. Read-only export ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option M2EXTENSIONS is set. The Oberon-2 read-only export symbol "-", after a variable or field identifier in a definition module will define the identifier as read-only for any client. Only the module in which a read-only identifier is declared may change its value. The compiler will not allow the value of read-only exported object to be changed explicitly (by an assignment) or implicitly (passing as a VAR parameter). For read-only variables of an array or record type, both array elements and record fields are also read-only. Example (extract from definition module): TYPE Rec = RECORD n-: INTEGER; m : INTEGER; END; VAR in-: FILE; x-: Rec; ═══ 8.6.10. Renaming of imported modules ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option M2EXTENSIONS is set. An imported module can be renamed inside an importing module. The real name of the module becomes invisible. Import = IMPORT [ Ident ":=" ] Ident { "," [ Ident ":=" ] ident } ";". Example MODULE test; IMPORT vw := VirtualWorkstation; VAR ws: vw.Station; BEGIN ws := vw.open(); END test. ═══ 8.6.11. NEW and DISPOSE for dynamic arrays ═══ Standard procedures NEW and DISPOSE can be applied to the variables of a dynamic array type (See Dynamic arrays). In this case procedures DYNALLOCATE and DYNDEALLOCATE should be visible in the calling context. PROCEDURE DYNALLOCATE(VAR a: ADDRESS; size: CARDINAL; len: ARRAY OF CARDINAL); The procedure must allocate a dynamic array, where size is the size of array base type (the size of an element) and len[i] is the length of the array in i-th dimension. PROCEDURE DYNDEALLOCATE(VAR a: ADDRESS; size,dim: CARDINAL); The procedure must deallocate a dynamic array, where size is the size of an element and dim is the number of dimensions. Dynamic arrays are represented as a pointer to the so-called array descriptor (See Array types). ═══ 8.6.12. HALT ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option M2EXTENSIONS is set. An optional integer parameter is allowed for the HALT procedure. PROCEDURE HALT ([code: INTEGER]); HALT terminates the program execution with an optional return code. Consult your operating system/environment documentation for more details. ═══ 8.6.13. ASSERT ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option M2EXTENSIONS is set. The procedure ASSERT checks its boolean parameter and terminates the program if it is not TRUE. The second optional parameter denotes task termination code. If it is omitted, a standard value is assumed. PROCEDURE ASSERT(cond: BOOLEAN [; code: INTEGER]); The call ASSERT(expr,code) is equal to IF NOT expr THEN HALT(code) END; ═══ 8.7. Source code directives ═══ Source code directives (or pragmas) are used for setting of compilation options in the source text and for selection of the specific source text to be compiled (conditional compilation). The ISO Modula-2 standard does not describe the syntax of pragmas. XDS allows one to use source code directives in both Modula-2 and Oberon-2. The syntax described in The Oakwood Guidelines for the Oberon-2 Compiler Developers is used. ═══ 8.7.1. Inline options ═══ In some cases it is more preferable to set the compiler options within the source text. Some compiler options such as MAIN are more meaningful there. XDS allows options to be changed in source text by using standard ISO pseudo comments <* ... *>The old style of pragmas (*\$..*) is supported to provide backward compatibility, but the compiler reports ``obsolete syntax'' warning. Some options can only be placed in source text before the module header (i.e. before keywords IMPLEMENTATION, DEFINITION or MODULE). These options will be ignored if found elsewhere in the source text. See Description of options for more details. The format of an inline option setting is described by the following syntax: The appearance of the following table is a known problem and will be improved in the final release. l@= l PragmaBody | <* Switch *> Switch | NewStyle | OldStyle | PragmaStyle NewStyle | [ NEW ] name [ '+' | '-' ] OldStyle | ('+' | '-') name PragmaStyle | '$' Modifier Modifier | name '+' | name '-' | > | < | ! The NewStyle and PragmaStyle PragmaStyle is an extention of Oakwood standard - option names are used instead of pragma letters. are proposed as the Oakwood standard for Oberon-2, OldStyle is the style used in the previous XDS release. All option names are case-independent. In the OldStyle and PragmaStyle there should be no space between <* and +, - or $. In all cases the symbol + sets the corresponding option ON, and the symbol - sets it OFF. In the pragmaStyle special modifiers have the following meaning: < saves the current option's state > restores the last saved option's state ! restore option's state set prior to the compilation of the given module Example PROCEDURE Length(VAR a: ARRAY OF CHAR): CARDINAL; VAR i: CARDINAL; BEGIN <*$<*> (* save state *) <* CHECKINDEX - *> (* turn CHECKINDEX off *) i := 0; WHILE (i<=HIGH(a)) & (a[i]#0C) DO INC(i) END; <*$>*> (* restore option's state *) RETURN i; END Length; ═══ 8.7.2. Conditional compilation ═══ It is possible to use the conditional compilation with Modula-2 and Oberon-2only if the O2ISOPRAGMA option is set ON compilers via the standard ISO pragma notation <* *>. Conditional compilation statements can be placed anywhere in the source code. The syntax of the conditional compilation IF statement: IfStatement = <* IF Expression THEN *> text { <* ELSIF Expression THEN *> text } [ <* ELSE *> text ] <* END *> Expression = SimpleExpression [ ("=" | "#") SimpleExpression]. SimpleExpression = Term { "OR" Term}. Term = Factor { "&" Factor}. Factor = ident | string | "DEFINED" "(" ident ")" | "(" Expression ")" | "~" Factor | "NOT" Factor. ident = option | equation. An operand in an expressions is either a name of option or equation or a string literal. An option has the value TRUE, if it was set to TRUE in the configuration or project file, on the command line, or within the source text. An option has the value FALSE, if it was set to FALSE explicitly or was not defined at all. The compiler will report a warning if an undeclared option or equation is used as a conditional compilation identifier. Examples IMPORT lib := <* IF __GEN_X86__ THEN *> MyX86Lib; <* ELSIF __GEN_C__ THEN *> MyCLib; <* ELSE *> *** Unknown *** <* END *> CONST Win = <* IF Windows THEN *> TRUE <* ELSE *> FALSE <* END *>; <* IF DEFINED(Debug) & (DebugLevel = "2") THEN *> PrintDebugInformation; <* END *>; <* IF platform = "OS2" THEN *> Strings.Capitalize(filename); <* IF NOT HPFS THEN *> TruncateFileName(filename); <* END *> <* END *> ═══ 9. XDS Oberon-2 ═══ This chapter includes the details of the Oberon-2 language which are specific for this implementation. In the standard mode When the options O2EXTENSIONS and O2NUMEXT are OFF. XDS Oberon-2 is fully compatible with ETH compilers (See Appendix The Oberon-2 Report for the language report). The last changes of the language are described in Last changes to the language. To provide a smooth path from Modula-2 to Oberon-2 XDS allows all Modula-2 data types to be used in Oberon-2 modules (See Using Modula-2 features). Several language extensions are implemented in the language according to The Oakwood Guidelines for the Oberon-2 Compiler Developers These guidelines have been produced by a group of Oberon-2 compiler developers, including ETH developers, after a meeting at the Oakwood Hotel in Croydon, UK in June 1993. (See Oakwood numeric extensions). Other language extensions are described in Language extensions. As XDS is a truly multi-lingual system, special features were introduced to provide for foreign language interfaces (See Chapter Multilanguage programming). ═══ 9.1. The Oberon environment ═══ The Oberon-2 language was originally designed for use in an environment that provides the command activation, garbage collection, and dynamic loading of the modules. Although not being a part of the language, these features contribute to the power of Oberon-2. The garbage collector and command activation are implemented in the Oberon Run-Time Support and can be used in any program. The dynamic loader is not provided in the current release. See The oberonRTS module for further information. ═══ 9.1.1. Program structure ═══ In the Oberon-2 environment, any declared parameterless procedure can be considered as a main procedure and can be called by its name (a qualified identifier of the form ModuleName.ProcName). Due to the nature of XDS, and its freedom from the Oberon system, a different approach is to be taken to declare the `top level' or program modules. The module which contains the top level of your program must be declared as such by translating it with the MAIN option set. This will generate an entry point to your program. Only one module per program shall be compiled with the option set. ═══ 9.1.2. Creating a definition ═══ XDS provides two different ways to create a definition for Oberon-2 module:  the BROWSE operation mode creates a definition module from a symbol file (See BROWSE mode);  the MAKEDEF option forces the Oberon compiler to generate a (pseudo) definition module after a successful compilation of an Oberon module. The MAKEDEF option provides additional services: the compiler will preserve the so-called exported comments (i.e. comments which start with `(**') if the XCOMMENTS option is set. The generated pseudo-definition module contains all exported declarations in the order of appearance in the source text. All exported comments are placed in the appropriate positions. The definition module can be generated in three styles.The BSTYLE equation can be used to choose one of the styles: DEF (default), DOC or MOD. The DEF style This produces an ETH-style definition module. All type-bound procedures (methods) and relative comments are shown as a part of the corresponding record type. This is the only style for which the BSREDEFINE and BSCLOSURE options are applicable. The DOC style This produces a pseudo-definition module in which methods are shown as a part of an appropriate record type (ignoring comments) and in the same positions as they occur in the source text. The MOD style This attempts to produce a file which can be compiled as an Oberon module after a slight modification (i.e.the file will contain "END procname" etc.) ═══ 9.2. Last changes to the language ═══ ═══ 9.2.1. ASSERT ═══ The procedure ASSERT checks its boolean parameter and terminates the program if it is not TRUE. The second optional parameter denotes task termination code. If omitted, a standard value is assumed. PROCEDURE ASSERT(cond: BOOLEAN [; code: INTEGER]); The call ASSERT(expr,code) is equal to IF NOT expr THEN HALT(code) END; ═══ 9.2.2. Underscores in identifiers ═══ According to the Oakwood Guidelines an underscore "_" may be used in identifiers (as a letter). ident = ( letter | "_" ) { letter | digit | "_" }. We recommend to use underscores with care, as it may cause problems with the software portability to other compilers. This feature may be important when interfacing with foreign languages (See Chapter Multilanguage programming). ═══ 9.2.3. Source code directives ═══ Source code directives (or pragmas) are used for setting of compilation options in the source text and for selection of the specific source text to be compiled (conditional compilation). According to the Oakwood Guidelines all directives are contained in ISO Modula-2 style pseudo comments using angled brackets <* ... *>. The additional language constructs should not be considered to be part of the Oberon-2 languages. Rather they defined a separate compiler control language that coexist with Oberon-2. The option O2ISOPRAGMA allows one to use pragmas. The syntax of the directives is the same for Modula-2 and Oberon-2. See Source code directives for further details. ═══ 9.3. Oakwood numeric extensions ═══ XDS Oberon-2 supports two extensions which are of importance for scientific programming, namely  complex numbers  in-line exponentiation operator The O2NUMEXT option should be set to use these extensions. ═══ 9.3.1. Complex numbers ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option O2NUMEXT is set. Two additional types are included in the type hierarchy if the O2NUMEXT option is set: The appearance of the following table is a known problem and will be improved in the final release. lcl COMPLEX | defined as | (REAL,REAL) LONGCOMPLEX | defined as | (LONGREAL,LONGREAL) All numeric types form a (partial) hierarchy LONGCOMPLEX ? l COMPLEX LONGREAL ? REAL ?whole types A common mathematical notation is used for complex number literals: number = integer | real | complex. complex = real "i". A literal of the form 5.0i denotes a complex number with real part equal to zero and an imaginary part equal to 5.0. Complex constants with a non-zero real part can be described using arithmetic operators. CONST i = 1.i; x = 1. + 1.i; For the declarations VAR c: COMPLEX; l: LONGCOMPLEX; r: REAL; x: INTEGER; the following statements are valid: c:=i+r; l:=c; l:=c*r; l:=l*c; New conversion functions RE and IM can be used to obtain a real or imaginary part of a value of a complex type. Both functions have one parameter. If the parameter is of the COMPLEX type, both functions return the REAL value; if the parameter is of the LONGCOMPLEX type, functions return the LONGREAL value; otherwise the parameter should be a complex constant and functions return real constant. A complex value can be formed by applying the standard function CMPLX to two reals. If both CMPLX arguments are real constants, the result is a complex constant. CONST i = CMPLX(0.0,1.0); If both expressions are of the REAL type, the function returns the COMPLEX value, otherwise it returns the LONGCOMPLEX value. ═══ 9.3.2. In-line exponentiation ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option O2NUMEXT is set. The exponentiation operator ** provides a convenient notation for arithmetic expressions, rather than using function calls. It is an arithmetic operator which has a higher precedence than multiplication operators. Term = Exponent { MulOp Exponent }. Exponent = Factor { "**" Factor }. Note: the operator is right-associated: a**b**c is evaluated as abc The left operand of the exponentiation (a**b) should be any numeric value (including complex), while the right operand should be of a real or integer type. The result type does not depend of the type of right operand and is defined by the table: The appearance of the following table is a known problem and will be improved in the final release. |l|l| Type of left operand | Result type integer type | REAL REAL | REAL LONGREAL | LONGREAL COMPLEX | COMPLEX LONGCOMPLEX | LONGCOMPLEX ═══ 9.4. Using Modula-2 features ═══ All Modula-2 types and corresponding operations can be used in Oberon-2, including enumeration types, range types, records with variant parts, sets, etc. Important Notes:  It is not allowed to declare the Modula-2 types in an Oberon-2 module.  A module using Modula-2 features may be non-portable to other compilers. Example (*MODULA-2*) DEFINITION MODULE UsefulTypes; TYPE TranslationTable = ARRAY CHAR OF CHAR; Color = (red,green,blue); Colors = SET OF Color; END UsefulTypes. (*OBERON-2*) MODULE UsingM2; IMPORT UsefulTypes; TYPE TranslationTable* = UsefulTypes.TranslationTable; VAR colors*: UsefulTypes.Color; BEGIN colors:=UsefulTypes.Colors{UsefulTypes.red}; END UsingM2. ═══ 9.5. Language extensions ═══ The appearance of the following table is a known problem and will be improved in the final release. |lp7.5cm|WARNING: | Using extensions may cause problems with the software portability to other compilers. In the standard mode the XDS Oberon-2 compiler is fully compatible with ETH compilers (See also Last changes to the language). The O2EXTENSIONS option enables some language extensions. The main purposes of language extensions are  to improve interfacing with other languages (See Chapter Multilanguage programming).  to provide backward compatibility with the previous versions of XDS. See also  Source language directives (Source code directives)  Oakwood numeric extensions (Oakwood numeric extensions). ═══ 9.5.1. Comments ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option O2EXTENSIONS is set. As well as "(**)", there is another valid format for comments in source texts. The portion of a line from "-" to the end is considered as a comment. Example VAR j: INTEGER; -- this is a comment ═══ 9.5.2. String concatenation ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option O2EXTENSIONS is set. The symbol "+" can be used for constant string and characters concatenation. See Strings for more details. ═══ 9.5.3. VAL function ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option O2EXTENSIONS is set. The function VAL can be used to obtain a value of a specified scalar type from an expression of a scalar type. See Type conversions for more details. PROCEDURE VAL(Type; expr: ScalarType): Type; The function can be applied to any scalar types, including the system fixed size types (See Whole system types). ═══ 9.5.4. Read-only parameters ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option O2EXTENSIONS is set. In a formal parameter's section, the symbol "-" may stand after the name of a value parameter. Such a parameter is called read-only; its value can not be changed in the procedure's body. Read-only parameters need not be copied before the procedure activation; this enables the generation of procedure calls with structured parameters to be made more effective. Read-only parameters can not be used in a procedure type declaration. We recommend to use read-only parameters with care. The compiler does not check that the read-only parameter is not modified via the another parameter or global variable. Example PROCEDURE Foo(VAR dest: ARRAY OF CHAR; source-: ARRAY OF CHAR); BEGIN dest[0]:='a'; dest[1]:=source[0]; END Foo; The call Foo(x,x) produces the wrong result, because the first statement changes the value of source[0] (source is not copied and points to the same location as dest). ═══ 9.5.5. Variable number of parameters ═══ The appearance of the following table is a known problem and will be improved in the final release. |l|NOTE: Only valid when option O2EXTENSIONS is set. Everything contained in section Variable number of parameters is applicable to Oberon-2. ═══ 9.6. The module SYSTEM ═══ Low level facilities are provided by the module SYSTEM. This module does not exist in the same sense as other libraries but is hard-coded into the compiler itself. However, to use the facilities provided, identifiers must be imported in the usual way. Some procedures in the module SYSTEM are generic procedures that cannot be explicitly declared, i.e. they apply to classes of operand types. XDS Oberon-2 compiler implements all system features described in Appendix The Oberon-2 Report (except GETREG, PUTREG and CC) and allows one to access to all features, described in the International Standard of Modula-2 (See The module SYSTEM). In this section we describe only features specific for this implementation. ═══ 9.6.1. Compatibility with BYTE ═══ Expressions of types CHAR, BOOLEAN, SHORTINT and SYSTEM.CARD8 can be assigned to the variables of type BYTE or passed as actual parameters to formal parameters of type BYTE. If a formal variable parameter is of type ARRAY OF BYTE then the corresponding actual parameter may be of any type, except numeric literals. ═══ 9.6.2. Whole system types ═══ Module SYSTEM contains the signed types INT8, INT16, INT32 and unsigned types CARD8, CARD16, CARD32, which are guaranteed to contain exactly 8, 16, or 32 bits respectively. These types were introduced to simplify consstructing the interfaces to foreign libraries (See Chapter Multilanguage programming). The basic types SHORTINT, INTEGER, LONGINT are synonyms of INT8, INT16 and INT32 respectively. The unsigned types form a hierarchy whereby larger types include (the values of) smaller types. SYSTEM.CARD32 ?SYSTEM.CARD16 ?SYSTEM.CARD8 The whole hierarchy of numeric types (See also Complex numbers): LONGREAL ?REAL ?{ l signed types unsigned types . ═══ 9.6.3. NEW and DISPOSE ═══ The procedure SYSTEM.NEW can be used to allocate the system memory, i.e. memory which is not the subject of garbage collection. SYSTEM.NEW is a generic procedure, which is applied to pointer types and can be used in several ways, depending on pointer's base type. PROCEDURE NEW(VAR p: AnyPointer [; x0,..xn: integer]); Let P be defined as POINTER TO T and p is of type T. The appearance of the following table is a known problem and will be improved in the final release. lp7.5cm NEW(p) | T is a record or fixed length array type. The procedure allocates a storage block of SIZE(p'136) bytes and assigns its address to p. NEW(p,n) | T is a record or fixed length array type. The procedure allocates a storage block of n bytes and assigns its address to p. NEW(p,x0,..xn-1) | T is n-dimensional open array. The procedure allocates an open array of lengths given by the expressions x0,..xn-1 Procedure SYSTEM.DISPOSE can be used to free the block allocated by the call of SYSTEM.NEW. It does not immediately deallocate the block, but marks it as a free block. Such a block will be deallocated by the next call of the garbage collector. PROCEDURE DISPOSE(VAR p: AnyPointer; [size: integer]); The appearance of the following table is a known problem and will be improved in the final release. lp7.5cm DISPOSE(p) | T is a record or array type. The procedure deallocates storage block for pointer p. DISPOSE(p,n) | T is record or fixed length array type. The procedure deallocates storage block of n bytes. ═══ 10. Multilanguage programming ═══ XDS allows the programmer to mix Modula-2, Oberon-2, C and Assembler modules, libraries, and object files in one project. ═══ 10.1. Modula-2 and Oberon-2 ═══ It is not necessary to notify the compiler of using Modula-2 objects in Oberon-2 module and vice versa. The compiler will detect the language automatically when processing symbol files on IMPORT clause. ═══ 10.1.1. Basic types ═══ In Oberon-2 the basic types have the same length on all platforms. In Modula-2 the size of types INTEGER, CARDINAL and BITSET may be different and depends on the value of the M2BASE16 option. The following table summarizes the correspondence between the basic types. The appearance of the following table is a known problem and will be improved in the final release. |l|c|c|c|c| Type | Size | Oberon-2 | 2c|Modula-2 | | | M2BASE16+ | M2BASE16- integer | 8 | SHORTINT | - | - integer | 16 | INTEGER | INTEGER | - integer | 32 | LONGINT | - | INTEGER cardinal | 8 | - | - | - cardinal | 16 | - | CARDINAL | - cardinal | 32 | - | - | CARDINAL bitset | 16 | - | BITSET | - bitset | 32 | SET | - | BITSET The system types INT and CARD correspond to Modula-2 INTEGER and CARDINAL types respectively. We recommend to use INT and CARD in Oberon-2 when importing Modula-2 modules. For example, if the procedure Foo is defined in the Modula-2 definition module as DEFINITION MODULE M; PROCEDURE Foo(VAR x: INTEGER); END M. the portable usage of this procedure in Oberon-2 is as follows: VAR x: SYSTEM.INT; ... M.Foo(x); ═══ 10.1.2. Data structures ═══ XDS allows any Modula-2 data structures to be used in Oberon-2 modules, even those that can not be defined in Oberon-2 (that is variant records, range types, set types, enumerations, etc). However, the use of Modula-2 types in Oberon-2 and vice versa is restricted. Whenever possible XDS tries to produce the correct code. If a correct translation is impossible, an error is reported:  a Modula-2 record field type cannot be of an Oberon-2 pointer, record or array type;  Modula-2 pointer to Oberon-2 record cannot be used in specific Oberon-2 constructs (type-bound procedures, type guards, etc);  an opaque type can not be defined as an Oberon pointer. Standard procedures NEW and DISPOSE are always applied according to the language of a parameter's type. For example, for the following declarations in the Oberon-2 module: TYPE Rec = RECORD END; MP = POINTER ["Modula"] TO Rec; (* Modula pointer *) OP = POINTER TO Rec; (* Oberon pointer *) VAR m: MP; o: OP; The call of NEW(m) will be treated as a call of the Modula-2 default ALLOCATE, while NEW(o) will be treated as a call of the standard Oberon run-time routine. See also Direct language specification. An implicit memory deallocation (garbage collection) is applied to Oberon-2 objects only. If a variable of the Modula-2 pointer type is declared in the Oberon-2 module, it shall be deallocated explicitly. Example: Using the Modula data type in Oberon (* Modula-2*) DEFINITION MODULE m2; TYPE Rec = RECORD (* a record with variant parts *) CASE tag: BOOLEAN OF |TRUE: i: INTEGER; |FALSE: r: REAL; END; END; Ptr = POINTER TO Rec; VAR r: Rec; p: Ptr; PROCEDURE Foo(VAR r: Rec); END m2. (* Oberon-2 *) MODULE o2; IMPORT m2; (* import of a Modula-2 module *) VAR r: m2.Rec; (* using the Modula-2 record type *) p: m2.Ptr; (* using the Modula-2 pointer type *) x: POINTER TO m2.Rec; BEGIN NEW(p); (* Modula-2 default ALLOCATE *) NEW(x); (* Oberon-2 NEW *) m2.Foo(r); m2.Foo(p^); m2.Foo(x^); END o2. ═══ 10.1.3. Garbage collection ═══ It is important to remember that Modula-2 and Oberon-2 have different approaches to memory utilization. When a program contains both Modula-2 and Oberon-2 modules, garbage collection is used. See also Memory management. ═══ 10.2. Direct language specification ═══ The compiler must know the language of implementation of any module to take into account different semantics of different languages and to generate a correct code. In some cases it is necessary for a procedure or data type to be implemented according to the rules of a language other than that of the whole module. XDS allows the language of a type or object to be specified explicitly. The direct language specification is allowed either if language extensions are allowed or if the module SYSTEM is imported. In a record, pointer, procedure type or procedure declaration, immediately following the keywords RECORD, POINTER or PROCEDURE, one may indicate the desired language (or the way in which this declaration is treated by the compiler) by [n], where n is a string or an integer constant expressionWe recommed to use strings, integer values are preserved for the backward compatibility.: The appearance of the following table is a known problem and will be improved in the final release. |l|c|c| Convention | String | Value Oberon-2 | "Oberon" | 0 Modula-2 | "Modula" | 1 C | "C" | 2 Pascal | "Pascal" | 5 Win32 | "StdCall" | 7 Example TYPE UntracedPtr = POINTER ["Modula"] TO Rec; Here UntracedPtr is defined as the Modula-2 pointer, hence all variables of that type will not be traced by garbage collector. The direct language specification placed after the name of a field, constant, type or variable points out that the name of the object will be treated according to the rules of the specified language. TYPE Rec ["C"] = RECORD name ["C"]: INTEGER; END; CONST pi ["C"] = 3.14159; VAR buffer[]["C"]: POINTER TO INTEGER; As in ISO Modula-2, an address may be specified for a variable, the empty brackets are required to set the language. A procedure name is treated according to the language of procedure decralation, i.e. in the following declaration: PROCEDURE ["C"] Foo; both the procedure type and procedure name will be treated according to the C language rules. Note: if you are using C++ compiler, the Foo function should be declared with C name mangling style. See your C++ manuals for further information. ═══ 10.3. Relaxation of compatibility rules ═══ The compiler performs all semantic checks for an object or type according to its language specifications. Any object declared as that of Modula-2 or Oberon-2 will satisfy Modula-2 or Oberon-2 compatibility rules respectively. The compiler uses relaxed compatibility rules for objects and types declared as "C", "Pascal" and "StdCall". ═══ 10.3.1. Parameter compatibility ═══ If a formal parameter is of the type POINTER TO T, the actual parameter may be of any array type of T. In this case address of the first array element will be passed, as it is done in C. Example TYPE Str = POINTER TO CHAR; PROCEDURE ["C"] Foo(s: Str); ... END Foo; VAR s: Str; a: ARRAY [0..5] OF CHAR; p: POINTER TO ARRAY OF CHAR; Foo(s); (* allowed - the same type *) Foo(a); (* allowed for the "C" procedure *) Foo(p^); (* allowed for the "C" procedure *) ═══ 10.3.2. Ignoring function result ═══ It is a standard practice in C programming to ignore a result of a function call. Some standard library functions are designed taking this practice into account. E.g. the string copy function gets a destination string as a variable parameter (in terms of Modula-2) and returns a pointer to it: extern char *strcpy(char *, const char *); In many cases, the result of the strcpy function call is ignored. The XDS compilers allow one to ignore the results of functions, defined as "C", "Pascal" or "StdCall". Thus, the function strcpy defined in the string.def foreign definition module as PROCEDURE ["C"] strcpy(VAR d: ARRAY OF CHAR; s: ARRAY OF CHAR): ADDRESS; can be used as a proper procedure or as function procedure: strcpy(d,s); ptr:=strcpy(d,s); ═══ 10.4. Interfacing to C ═══ A special attention was taken in XDS to provide a proper interface to other languages, primarily to the C language. The main goal is to provide an access to the existing software. Foreign definition modules provide definitions which allow the standard libraries to be directly accessed. ═══ 10.4.1. Foreign definition module ═══ The direct language specification [n] can appear immediately after keywords DEFINITION MODULE (See Direct language specification). This has an effect that all objects defined in the module will be translated according to the correspondent language rules. Therefore, a direct language specification for each object is not necessary. Several options are often used in foreign definition modules. Note: options CSTDLIB and NOHEADER are meaningful for the translators to C only. Example <*+ M2EXTENSIONS *> <*+ CSTDLIB *> (* C standard library *) <*+ NOHEADER *> (* we already have header file *) DEFINITION MODULE ["C"] string; IMPORT SYSTEM; PROCEDURE strlen(s: ARRAY OF CHAR): SYSTEM.size_t; PROCEDURE strcmp(s1: ARRAY OF CHAR; s2: ARRAY OF CHAR): SYSTEM.int; END string. If you are going to design your own foreign definition module, it will be usefull to take the following considerations into accout:  If you are developing an interface to the existing header file, use NOHEADER option to disable the generation of header file. This option is meaningful for the translators only.  If the header file is a standard header file, use the CSTDLIB option. This option is meaningful for the translators only.  Use the special SYSTEM types int, unsigned, size_t and void for corresponding C types.  A parameter of type T * should be replaced to the ARRAY OF T parameter, if only arrays are passed to this parameter, otherwise use POINTER TO T. Definition modules for the ANSI C libraries (stdio.def, string.def, etc) can be used as tutorial examples. ═══ 10.4.2. External procedures specification ═══ In some cases it may be desirable not to write a foreign definition module but to use some C functions directly. The XDS compilers allow a function to be declared as external. The declaration of an external procedure consists of a procedure header only. The procedure name in the header is prefixed by the symbol "/". PROCEDURE ["C"] / putchar(ch: SYSTEM.int): SYSTEM.int; ═══ 11. Libraries ═══ The following sets of libraries are provided with the compilers:  ISO standard Modula-2 libraries (ISO Standard Modula-2 libraries)  A subset of libraries defined in PIM (PIM standard libraries)  Utility libraries (Utility libraries)  A subset of Oberon-2 Oakwood standard libraries (Oberon-2 Oakwood libraries)  Interface to Oberon-2 run-time support (The oberonRTS module) All these libraries can be used with both Modula-2 and Oberon-2 compilers. Some libraries are written in Oberon-2, others in Modula-2. If a program consists of a mix of Oberon-2 and Modula-2 modules, two memory managers are in use. In rare cases it can lead to the problem with memory allocation or deallocation. A special care was taken to eliminate this problem (See Memory management). ═══ 11.1. ISO Standard Modula-2 libraries ═══ It is our aim to provide the full set of ISO Modula-2 libraries. However, not all libraries may be included in the current release (See Chapter Implementation limitations and restrictions). System libraries are described in Chapter XDS Modula-2. This section does not aim at full description of ISO libraries. All libraries are divided into groups. A brief description and few samples are provided for each group. For further information refer to the corresponding definition modules. ═══ 11.1.1. Input/output library ═══ The IO library allows one to read and write the data streams over one or more channels. Channels are connected to the source of input data, or to destination of output data, known as devices. A set of devices can be extended. A group of modules is provided to operate on the default input and output channel (Reading and writing via default channels). Another group of modules provide facilities to operate on channels specified explicitly by a parameter (Reading and writing data). The device modules provide facilities to get a channel connected to a source (Device modules). The primitive device-independent operations are provided by the module IOChan; the module IOLink allows specialized device module to be implemented (See Low-level IO modules). ═══ 11.1.2. Reading and writing via default channels ═══ The following modules provide procedures that operate via default input and output channels and do not take a parameter which identifies a channel: The appearance of the following table is a known problem and will be improved in the final release. lp7cm IOConsts | Types and constants for IO modules SLongIO | LONGREAL numbers IO operations SRawIO | Raw IO operations (no conversion or interpretation) SRealIO | REAL numbers IO operations SResultIO | Read results for the default input channel STextIO | Character and string types IO operations SWholeIO | Whole numbers IO operations The module STextIO resembles the well-known InOut library. The Hello, World program is implemented in the following example: MODULE Hello; IMPORT STextIO; BEGIN STextIO.WriteString('Hello, World!'); STextIO.WriteLn; END Hello. The modules SWholeIO, SRealIO, SLongIO provides facilities for the input and output of whole and real numbers in a decimal form using text operations on a channel. PROCEDURE Print(stage: CARDINAL; val: REAL); BEGIN STextIO.WriteString("On stage"); SWholeIO.WriteCard(stage,0); STextIO.WriteString(" the value is equal to "); SRealIO.WriteReal(val,15); STextIO.WriteLn; END Print; The module SIOResult allows one to determine whether the last input operation from a default input channel succeed. Text operations produce or consume data streams as a sequence of characters and line marks. The text input procedures (such as ReadString never remove a line mark from the input stream. The procedure SkipLine should be used to pass a line mark. The Copy procedure reads strings from the input channel and copies them to the output channel. PROCEDURE Copy; VAR s: ARRAY [0..63] OF CHAR; BEGIN LOOP STextIO.ReadString(s); CASE SIOResult.ReadResult() OF |SIOResult.allRight: STextIO.WriteString(s); |SIOResult.endOfLine: STextIO.SkipLine; STextIO.WriteLn; |SIOResult.endOfInput: EXIT END; END; END Copy; No procedure is provided to get the result of a `write' operation. Device errors are reported by raising an exception (See module IOChan). ═══ 11.1.3. Reading and writing data ═══ For all modules in this group a channel is specified by an actual parameter of the type IOChan.ChanId. The appearance of the following table is a known problem and will be improved in the final release. lp7cm IOResult | Read results for specified channels LongIO | LONGREAL numbers IO operations RawIO | Raw IO operations (no conversion or interpretation) RealIO | REAL numbers IO operations TextIO | Character and string types IO operations WholeIO | Whole numbers IO operations The following procedure copies an input channel to an output channel byte by byte: PROCEDURE CopyChars(in,out: IOChan.ChanId); VAR ch: CHAR; BEGIN LOOP TextIO.ReadChar(in,ch); CASE IOResult.ReadResult(in) OF |IOResult.allRight: TextIO.WriteChar(out,ch); |IOResult.endOfLine: TextIO.SkipLine(in); TextIO.WriteLn(out); |IOResult.endOfInput: EXIT END; END; END CopyChars; ═══ 11.1.4. Device modules ═══ The device modules allows to get a channel connected to a stream, a file, program arguments and to default channels. The appearance of the following table is a known problem and will be improved in the final release. lp7cm ChanConsts | Common types and values for channel open requests and results ProgramArgs | Access to program arguments RndFile | Random access files SeqFile | Rewindable sequential files StdChans | Access to standard and default channels StreamFile | Independent sequential data streams In the following example a channel connected to a rewindable file is opened: MODULE Example; IMPORT SeqFile, STextIO, TextIO; CONST flags = SeqFile.text + SeqFile.old; VAR cid: SeqFile.ChanId; res: SeqFile.OpenResults; i : CARDINAL; BEGIN SeqFile.OpenWrite(cid,"example.dat",flags,res); IF res = SeqFile.opened THEN FOR i:=0 TO 9 DO TextIO.WriteString(cid,"Hello"); TextIO.WriteLn(cid); END; SeqFile.Close(cid); ELSE STextIO.WriteString("Open error"); STextIO.WriteLn; END; END Example. The module StdChans allows one to get channels already open to sources and destinations of standard input, standard output and standard error output. Default channels initially corresponds to the standard channels, but their values may be changed to redirect input or output. PROCEDURE RedirectOutput(cid: StdChans.ChanId); BEGIN (* writing to the current default channel: *) STextIO.WriteString("Redirecting output..."); STextIO.WriteLn; (* redirecting output: *) StdChans.SetOutChan(cid); END RedirectOutput; After the call of RedirectOutput(cid) all subsequent output via modules STextIO, SWholeIO, etc will be written to the channel cid. To restore output call StdChans.SetOutChan(StdChans.StdOutChan()); The module ProgramArgs provides a channel to access program's arguments. The following program prints all its arguments. MODULE Args; IMPORT ProgramArgs, TextIO, STextIO; VAR str: ARRAY [0..255] OF CHAR; cid: ProgramArgs.ChanId; BEGIN cid:=ProgramArgs.ArgChan(); WHILE ProgramArgs.IsArgPresent() DO TextIO.ReadToken(cid,str); (* Note: read result test is omitted *) STextIO.WriteString(str); STextIO.WriteLn; END; END Args. ═══ 11.1.5. Low-level IO modules ═══ Two low-level modules are described in this section. The module IOChan defines the type ChanId that is used to identify channels and provides a set of procedures forming the channel's interface in a device-independent manner. The module IOLink provides facilities that allow one to define new device modules. Let us implement an encryption channel, i.e. a channel that encrypts all information that is written to it. To make the encryption device-independent we need a channel for input/output operations. In the following example a sketch of the encryption device module is shown. DEFINITION MODULE EncryptChan; IMPORT IOChan, ChanConsts; TYPE ChanId = IOChan.ChanId; OpenResults = ChanConsts.OpenResults; PROCEDURE Connect(VAR cid: ChanId; io: ChanId; VAR res: OpenResults); (* Attempts to open an encryption channel. All I/O operations will be made through "io" channel. *) PROCEDURE Close(VAR cid: ChanId); (* Closes the channel. *) END EncryptChan. Values of the type DeviceId are used to identify device modules. By calling the procedure DeviceTablePtrValue, a device module can obtain a pointer to a device table for the channel. Each channel has it own copy of a device table. A device table contains a field in which the device module can store private data. In our example, the io channel will be stored in this field. The device table also serves as a method table (or virtual function table) in object-oriented languages. It contains the procedure variables for each device procedure. All fields are initialized by the call of MakeChan procedure. A device module has to assign its own device procedures to the fields of a device table. See the Connect procedure below. IMPLEMENTATION MODULE EncryptChan; IMPORT IOChan, IOLink, ChanConsts, SYSTEM; (* "did" is used to identify the channel's kind: *) VAR did: IOLink.DeviceId; PROCEDURE EncryptChar(from: SYSTEM.ADDRESS; i: CARDINAL; VAR ch: CHAR); BEGIN ch:='a'; (* very simple encryption :-) *) END EncryptChar; PROCEDURE TextWrite(x: IOLink.DeviceTablePtr; from: SYSTEM.ADDRESS; len: CARDINAL); VAR i: CARDINAL; ch: CHAR; cid: IOChan.ChanId; BEGIN (* get the channel id *) cid:=SYSTEM.CAST(IOChan.ChanId,x^.cd); FOR i:=0 TO len-1 DO (* encrypt i-th character *) EncryptChar(from,i,ch); (* write an encrypted character *) IOChan.TextWrite(cid,SYSTEM.ADR(ch),1); END; END TextWrite; PROCEDURE Connect(VAR cid: ChanId; io: ChanId; VAR res: OpenResults); VAR x: IOLink.DeviceTablePtr; BEGIN IOLink.MakeChan(did,cid); IF cid = IOChan.InvalidChan() THEN res:=ChanConsts.outOfChans ELSE (* get a pointer to the device table *) x:=IOLink.DeviceTablePtrValue(cid,did, IOChan.notAvailable,""); (* store the channel id *) x^.cd:=SYSTEM.CAST(SYSTEM.ADDRESS,io); x^.doTextWrite:=TextWrite; (* ... *) END; END Connect; PROCEDURE Close(VAR cid: ChanId); BEGIN IOLink.UnMakeChan(did,cid); END Close; BEGIN IOLink.AllocateDeviceId(did); END EncryptChan. The module EncryptChan can be used as any standard device module. ═══ 11.1.6. String conversions ═══ The string conversion library admits the conversion of the values of numeric data types to and from the character string representation. It contains the following modules: The appearance of the following table is a known problem and will be improved in the final release. lp8cm ConvTypes | Common types used in the string conversion modules LongConv | Low-level LONGREAL/string conversions LongStr | LONGREAL/string conversions RealConv | Low-level REAL/string conversions RealStr | REAL/string conversions WholeConv | Low-level whole number/string conversions WholeStr | Whole number/string conversions The module ConvTypes defines the enumeration type ConvResults. It also defines the types ScanClass and ScanState to use in the low-level conversion modules. The low-level conversion modules allow to control lexical scanning of character sequences. For example, the WholeConv module implements procedures ScanInt and ScanCard representing the start state for a finite state scanner for signed and unsigned whole numbers. In the following example the procedure ScanInt is used to locate a position of the first character in a string which is not a part of an integer. PROCEDURE SkipInt(str: ARRAY OF CHAR; VAR pos: CARDINAL); VAR len: CARDINAL; state: ConvTypes.ConvState; class: ConvTypes.ConvClass; BEGIN pos:=0; len:=LENGTH(str); state:=WholeConv.ScanInt; WHILE pos < len DO state(str[pos],class,state); IF (class = WholeConv.invalid) OR (class = WholeConv.terminator) THEN RETURN END; INC(pos); END; END SkipInt; ═══ 11.1.7. Mathematical libraries ═══ The following modules constitute a mathematical library: The appearance of the following table is a known problem and will be improved in the final release. lp6.5cm ComplexMath | Mathematical functions for the type COMPLEX LongComplexMath | Mathematical functions for the type LONGCOMPLEX LongMath | Mathematical functions for the type LONGREAL RealMath | Mathematical functions for the type REAL ═══ 11.1.8. Processes and Semaphores ═══ The following modules concurrent algorithms to be expressed using processes: The appearance of the following table is a known problem and will be improved in the final release. ll Processes | Provides process creation and manipulation facilities. Semaphores | Provides mutual exclusion facilities for use by processes. Example MODULE Test; IMPORT Processes, Semaphores, STextIO; VAR sig : Semaphores.SEMAPHORE; prs : Processes.ProcessId; main: Processes.ProcessId; PROCEDURE Proc; BEGIN STextIO.WriteString('Proc 1'); STextIO.WriteLn; Semaphores.Claim(sig); (* suspend until released *) STextIO.WriteString('Proc 2'); STextIO.WriteLn; Processes.StopMe; END Proc; BEGIN STextIO.WriteString('Main 1'); STextIO.WriteLn; Semaphores.Create(sig,0); main:=Processes.Me(); Processes.Start(Proc,40000,Processes.UrgencyOf(main)+1,NIL,prs); STextIO.WriteString('Main 2'); STextIO.WriteLn; Semaphores.Release(sig); STextIO.WriteString('Main 3'); STextIO.WriteLn; Processes.StopMe; END Test. ═══ 11.1.9. Other libraries ═══ In this section we list the ISO modules that do not belong to any of the above groups: The appearance of the following table is a known problem and will be improved in the final release. ll CharClass | provides predicates to test a value of a character type Storage | Facilities for allocating and deallocating storage Strings | Facilities for string manipulation SysClock | Access to a system clock ═══ 11.2. PIM standard libraries ═══ The following libraries defined in PIM are provided: The appearance of the following table is a known problem and will be improved in the final release. lp8cm InOut | general-purpose IO operations LongInOut | LONGREAL numbers IO operations MathLib0 | mathematical functions RealInOut | REAL numbers IO operations Terminal | computer's terminal IO operations The library LongInOut (similar to RealInOut is not described in PIM. All PIM libraries are implemented on a basis of the ISO library. Since PIM Storage library is not compatible with the corresponding ISO library, it is omitted. ═══ 11.3. Utility libraries ═══ Starting from XDS v2.0 those modules that are portable between all versions of XDS on all platforms are included in the utility library. If you use ISO library, your program may be portable to any ISO compatible Modula-2 compiler. However, there are some essential omissions in the ISO library. The utility library is aimed at taking away some of those omissions. Some library modules are written in Oberon-2, others in Modula-2. In general any library can be used from both languages. However, do not forget that Oberon modules use implicit memory deallocation scheme and require garbage collection (See also Modula-2 and Oberon-2). The following modules of an utility library are provided (implementation language is pointed out in parentheses): The appearance of the following table is a known problem and will be improved in the final release. lll FileName | (M2) | Creating and parsing file names FileSys | (M2) | Common file operations FormOut | (M2) | Generic module for formatting output FormStr | (M2) | Formatting output to strings ProgEnv | (M2) | Access to program environment DStrings | (O2) | Dynamic strings FilePath | (O2) | File search operations RegComp | (O2) | Regular expressions ═══ 11.3.1. The FileName module ═══ The module provides operations for parsing and constructing file names. A file name consists of three parts: the directory, name and extension. All the procedures that construct a string value (Get, GetDir, GetName, GetExt, Convert, Create), have the property that if the length of the constructed string value exceeds the capacity of the variable parameter, a truncated value is assigned. If the length of the constructed string value is less than the capacity of the variable parameter, a string terminator is appended. Parsing file names The GetFormat procedure returns the position and length of file name parts. ═══ 11.3.2. Format - File name format record ═══ TYPE Format = RECORD ok: BOOLEAN; (* directory position and length: *) dirPos, dirLen : CARDINAL; (* name position and length: *) namePos,nameLen: CARDINAL; (* extension position and length: *) extPos, extLen : CARDINAL; END; ═══ 11.3.3. GetFormat - Get file name format ═══ PROCEDURE GetFormat(str: ARRAY OF CHAR; VAR f: Format); Returns the format of the string. The values of all fields are undefined if f.ok = FALSE. The following procedures return file name part(s). The appearance of the following table is a known problem and will be improved in the final release. |l@r| GetDir | Get directory GetName | Get name of file GetExt | Get extension PROCEDURE GetDir (fname: ARRAY OF CHAR; VAR dir: ARRAY OF CHAR); PROCEDURE GetName(fname: ARRAY OF CHAR; VAR name: ARRAY OF CHAR); PROCEDURE GetExt (fname: ARRAY OF CHAR; VAR ext: ARRAY OF CHAR); ═══ 11.3.4. Get - Get file name parts ═══ PROCEDURE Get(fname: ARRAY OF CHAR; VAR dir,name,ext: ARRAY OF CHAR); File name construction ═══ 11.3.5. Convert - Convert String to File Name ═══ PROCEDURE Convert(str: ARRAY OF CHAR; VAR fname: ARRAY OF CHAR); Converts a string to a file name according to the conventions of the underlying file system. ═══ 11.3.6. ConvertExt - Convert File Name Extension ═══ PROCEDURE ConvertExt(VAR ext: ARRAY OF CHAR); Converts an extension according to the conventions of the underlying file system. ═══ 11.3.7. Length - Calculate File Name Length ═══ PROCEDURE Length(dir,name,ext: CARDINAL): CARDINAL; Using the lengths of the directory, name and extension returns an estimated file name length which is greater than or equal to the length of the name generated by the Create procedure call. ═══ 11.3.8. Create - Create File Name ═══ PROCEDURE Create(dir,name,ext: ARRAY OF CHAR; VAR fname: ARRAY OF CHAR); Creates a file name from the parts. Example The following procedure can be used to change file name extension: PROCEDURE ChangeExt(VAR fname: ARRAY OF CHAR; newext: ARRAY OF CHAR); CONST Len = 64; VAR dir,name: ARRAY [0..Len-1] OF CHAR; f: FileName.Format; len: CARDINAL; BEGIN FileName.GetFormat(fname,f); IF NOT f.ok THEN Error("wrong format") ELSIF (f.dirLen > Len) OR (f.nameLen > Len) THEN Error("too long part"); ELSE len:=FileName.Length(f.dirLen,f.nameLen,LENGTH(newext)); IF len-1 > HIGH(fname) THEN Error("cannot create file name") ELSE FileName.Create(dir,name,newext,fname); END; END; END ChangeExt; When programming in Oberon-2 dynamic strings can be used to create strings of a required length: PROCEDURE ChangeExt(VAR fname: ARRAY OF CHAR; newext: ARRAY OF CHAR); VAR dir,name: DStrings.String; f: FileName.Format; BEGIN FileName.GetFormat(fname,f); IF NOT f.ok THEN Error("wrong format") ELSE NEW(dir,f.dirLen+1); NEW(name,f.nameLen+1); ... END; END; END ChangeExt; ═══ 11.3.9. The FileSys module ═══ The module provides file common operations. ═══ 11.3.10. Exists - Is File Exist ═══ PROCEDURE Exists(fname: ARRAY OF CHAR): BOOLEAN; Returns TRUE, if file fname exists. ═══ 11.3.11. ModifyTime - Return Modify Time ═══ PROCEDURE ModifyTime(fname: ARRAY OF CHAR; VAR time: LONGCARD; VAR exists: BOOLEAN); Returns a file modification time; time is valid only if exists=TRUE. ═══ 11.3.12. Rename - Rename File ═══ PROCEDURE Rename(fname,newname: ARRAY OF CHAR; VAR done: BOOLEAN); Renames the file fname to newname. ═══ 11.3.13. Remove - Remove File ═══ PROCEDURE Remove(fname: ARRAY OF CHAR; VAR done: BOOLEAN); Removes a file. ═══ 11.3.14. The FormOut module ═══ The module implements a formatting output procedure which outputs its arguments according to the format parameter. The syntax of the format string is compatible with the corresponding parameter of the C procedure printf. Some useful format extensions are provided. ═══ 11.3.15. writeProc - Write Procedure Type ═══ TYPE writeProc = PROCEDURE( (*handle:*) SYSTEM.ADDRESS, (*string:*) ARRAY OF CHAR, (*length:*) INTEGER ); ═══ 11.3.16. format - Formating Procedure ═══ PROCEDURE format(handle : SYSTEM.ADDRESS; write : writeProc; fmt : ARRAY OF CHAR; linesep: CHAR; args : SYSTEM.ADDRESS; size : CARDINAL); The procedure forms a string and outputs it via the write procedure parameter. The parameter handle is passed to the procedure write and provides a useful method to pass any information between the caller and the write procedure (e.g. output channel or something like it). The (args, size) pair denotes the address and size of the parameter block. The linesep parameter determines the line separator character sequence corresponding to "\n". Several standard values of the parameter are defined in the definition module: The appearance of the following table is a known problem and will be improved in the final release. ll default | default line separator for binary files text | default line separator for text files crlf | CR LF character sequence If the linesep is not equal to any of the values above, its value will be used as a line separator. The format string has the following syntax: Format = { character | Specifier }. Specifier = "%" Modifier Width [ "." Precision [ "." Start ] ] Base. Modifier = "-" | "+" | "|" | "0" | "$" | "#". Width = [ unsigned number | "*" ]. Precision = [ unsigned number | "*" ]. Start = [ unsigned number | "*" ]. Base = "d" | "i" | "x" | "X" | "o" | "{}" | "f" | "g" | "e". The appearance of the following table is a known problem and will be improved in the final release. c|p8cm Modifier |Meaning "-" | justifies a value to the left "+" | prints a sign for numbers (even of a non-negative) "|" | center value "0" | fills a place for numbers with "0" characters (default - filling with spaces) "$" | the same as "0" "#" | prints a base character ("H" or "B") according to the base; for "o", "x" and "X" bases only. The appearance of the following table is a known problem and will be improved in the final release. c|p8.5cm Base |Meaning "c" | prints a character "d" | prints a value in a decimal form "e" | prints a value in a floating-point form "f" | prints a value in a fixed-point form "g" | prints a value in a fixed-point or in floating-point form depending on the value "i" | the same as "d" "o" | prints a value in an octal form "s" | prints a string "x" | prints a value in a hexadecimal form, use letters "A".."F" "X" | prints a value in a hexadecimal form, use letters "a".."f" "{}" | prints a value as a bitset, e.g. {1,3..5} The procedure format converts the following pairs of symbols starting from the backslash (no convertion is done for strings printed using "%s" specifier): The appearance of the following table is a known problem and will be improved in the final release. c|p8cm Pair |Meaning \n | prints the line separator according to the value of the linesep parameter \r | prints CR (15C) \f | prints FF (14C) \t | prints TAB (11C) \\ | prints the backslash Important notes:  neither the compiler nor the library checks the correspondence between actual arguments and format specifications. However, unlike printf, all specifiers whose arguments are not passed will be ignored.  the ISO conversion library (See String conversions) is used to output numbers.  "*" in the width or precision position means that the corresponding number is passed as current actual parameter.  for the "c" base the second number (precision) is a repetition factor.  for the "s" base the second number (precision) is the maximum number of characters in a string to process.  for the "s" base the third number (start) specifies the start position in a string to process. ═══ 11.3.17. LineSeparator - Set Line Separator ═══ PROCEDURE LineSeparator(nl: ARRAY OF CHAR); Sets the default line separator for binary files. The correct value for the given platform is set in the module initializaion. ═══ 11.3.18. TextSeparator - Set Line Separator ═══ PROCEDURE TextSeparator(nl: ARRAY OF CHAR); Sets the default line separator for text files. The correct value for the given platform is set in the module initializaion. Examples The following example shows the implementation of a procedure which produces a format output to an ISO channel. PROCEDURE ChanWrite(handle: SYSTEM.ADDRESS; str: ARRAY OF CHAR; len: INTEGER); VAR chan: IOChan.ChanId; pos: INTEGER; BEGIN chan:=SYSTEM.CAST(IOChan.ChanId,handle); pos:=0; WHILE len > 0 DO IF str[pos] = ASCII.LF THEN IOChan.WriteLn ELSE IOChan.TextWrite(chan,SYSTEM.ADR(str[pos]),1) END; INC(pos); DEC(len); END; END ChanWrite; PROCEDURE Print(chan: IOChan.ChanId; format: ARRAY OF CHAR; SEQ args: SYSTEM.BYTE); BEGIN FormOut.format(chan,ChanWrite,format,FormOut.text, SYSTEM.ADR(args),SIZE(args)); END Print; The procedure printf prints to the standard output channel: PROCEDURE printf(f: ARRAY OF CHAR; SEQ x: SYSTEM.BYTE); BEGIN Print(StdChans.StdOutChan(),f,x); END printf; The procedure printf can be used in the conventional for C programmers way, e.g. the call printf("%d! = %d\n",5,Factorial(5)); will produce the line Provided that the implementation of the procedure Factorial corresponds to its name. 5! = 120 Examples: The appearance of the following table is a known problem and will be improved in the final release. l|l Call | Output printf("%5.3s","abcdef") | abc printf("%-5.3s","abcdef") | abc printf("%|5.3s","abcdef") | abc printf("%..3s","abcdef") | def printf("pos=%3d",13) | pos= 13 printf("%$3o",13) | 015 printf("%04X",33C) | 001B printf("%{}",13) | {0,2..3} ═══ 11.3.19. The FormStr module ═══ The appearance of the following table is a known problem and will be improved in the final release. |lp8cm|WARNING: | Language extensions are used in the interface of this module. All your modules importing this one may be non-portable to other compilers. A string is an array of characters of an arbitrary length. The procedures print, append and image guarantee the presence of the string terminator (0C) in the resulting string. See the module FormOut for information about the format syntax. ═══ 11.3.20. print - Print to string ═══ PROCEDURE print(VAR str: ARRAY OF CHAR; format: ARRAY OF CHAR; SEQ args: SYSTEM.BYTE); Constructs a string specified by the pair (format,args) and places it into str. ═══ 11.3.21. append - Append to the end of string ═══ PROCEDURE append( VAR str: ARRAY OF CHAR; format: ARRAY OF CHAR; SEQ args: SYSTEM.BYTE); Appends a string specified by the pair (format,args) to the end of the string str. ═══ 11.3.22. image - Print from the given position ═══ PROCEDURE image( VAR str: ARRAY OF CHAR; VAR pos: LONGINT; format: ARRAY OF CHAR; SEQ args: SYSTEM.BYTE); Places a string specified by the pair (format,args) in the string str starting from the position pos. After the procedure call, pos points to the 0C or to the position next to the end of the string. ═══ 11.3.23. iscan - Read integer in Modula-2 format ═══ PROCEDURE iscan( VAR num: INTEGER; str: ARRAY OF CHAR; VAR pos: CARDINAL; VAR done: BOOLEAN); Reads the integer value from the string str starting from the position pos. After the procedure call we have: The appearance of the following table is a known problem and will be improved in the final release. done becomes TRUE, if the attempt was successful; pos is the index of the first character following the number; num is the read value when done=TRUE. The number may be represented in any form permissible in Modula-2. In case of an integer overflow done=FALSE. ═══ 11.3.24. The ProgEnv module ═══ The module provides an access to the program environment. ═══ 11.3.25. ArgNumber - Return the number of arguments ═══ PROCEDURE ArgNumber(): CARDINAL; Returns the number of arguments (0 if there is no arguments). ═══ 11.3.26. GetArg - Get argument ═══ PROCEDURE GetArg(n: CARDINAL; VAR arg: ARRAY OF CHAR); Copies n-th argument to arg, or empty string if n >= ArgNumber(). ═══ 11.3.27. ArgLength - Return length of argument ═══ PROCEDURE ArgLength(n: CARDINAL): CARDINAL; Returns the length of the n-th argument (0 if n>=ArgNumber(). ═══ 11.3.28. ProgramName - Get program name ═══ PROCEDURE ProgramName(VAR name: ARRAY OF CHAR); Copies a program name to name. ═══ 11.3.29. ProgramNameLength - Length of program name ═══ PROCEDURE ProgramNameLength(): CARDINAL; Returns the length of the program name. ═══ 11.3.30. String - Get environment string ═══ PROCEDURE String(name: ARRAY OF CHAR; VAR str: ARRAY OF CHAR); Copies a value of the environment variable name to str (empty string if the variable is undefined). ═══ 11.3.31. StringLength - Return environment string length ═══ PROCEDURE StringLength(name: ARRAY OF CHAR): CARDINAL; Returns the length of the environment variable name (0 if the variable is undefined). Example The following procedure (in Oberon-2) prints all its arguments: PROCEDURE ShowArgs; VAR str: POINTER TO ARRAY OF CHAR; i,args: LONGINT; BEGIN i:=0; args:=ProgEnv.ArgNumber(); FOR i:=0 TO args-1 DO NEW(str,ProgEnv.ArgLength(i)+1); ProgEnv.GetArg(i,str^); STextIO.WriteString(str^); STextIO.WriteLn; END; END ShowArgs; ═══ 11.3.32. The DStrings module ═══ The module DStrings (written in Oberon-2) defines a dynamic string type and provides some conventional operations. ═══ 11.3.33. String - Dynamic String Type ═══ TYPE String* = POINTER TO ARRAY OF CHAR; ═══ 11.3.34. Assign - Create and Initialize String ═══ PROCEDURE Assign*(s: ARRAY OF CHAR; VAR d: String); Allocates a new string and copies from s. The resulting string always contains the string terminator (0X). ═══ 11.3.35. Append - Append to Dynamic String ═══ PROCEDURE Append*(s: ARRAY OF CHAR; VAR d: String); Appends the string s, extending d if necessary. The resulting string always contains the string terminator (0X). ═══ 11.3.36. The FilePath module ═══ The module (written in Oberon-2) provides file search facilities. In the following procedures path is a list of directories separated by semicolons, e.g. .\SYM;C:\LIB\SYM;C:\XDS\LIB\SYM;. (MS-DOS) ./sym;~/lib/sym;/usr/bin/xds/sym;. (Unix) ═══ 11.3.37. IsSimpleName - Is just a File Name ═══ PROCEDURE IsSimpleName*(name: ARRAY OF CHAR): BOOLEAN; Returns TRUE, if the name contains the file name only (does not contain directories). ═══ 11.3.38. Lookup - Look up File ═══ PROCEDURE Lookup*(path,name: ARRAY OF CHAR; VAR fname: DStrings.String; VAR n: INTEGER); Builds a filename using the search path. Returns: The appearance of the following table is a known problem and will be improved in the final release. ll n = -1 | name is not simple (fname := name) n = 0 | file is not found (the first directory is used) n > 0 | file is found in the n-th directory ═══ 11.3.39. UseFirst - Use First Directory ═══ PROCEDURE UseFirst*(path,name: ARRAY OF CHAR; VAR fname: DStrings.String); Builds a filename using the first directory from the search path. ═══ 11.3.40. The RegComp module ═══ This module (written in Oberon-2) implements a comparison of a string with regular expression. Regular expressions A regular expression is a string which contains certain special symbols. These are * denotes an arbitrary sequence of any character, possibly empty (equivalent to {\000-\377} expression) ? denotes an arbitrary single character; (equivalent to the [\000-\377] expression) [characters] denotes one of the named characters {characters} denotes an arbitrary sequence of the named characters; \char '134nnn denotes an ASCII character with an octal code nnn where n is [0-7]. & denotes the logical operation AND; | denotes the logical operation OR; '136 denotes the logical operation NOT; (..) sets the priority of operations; $digit may be attached to *, ?, [], {} and (). In the later expressions, it will represent the string of symbols comparable with the subexpression preceding it. A sequence of the form a-b used within either [] or {} brackets represents all ASCII characters from a to b. If you need to use any special symbols as an ordinary symbol, you should precede it by the symbol \ which suppresses its interpretation. Examples of regular expressions {0-9A-F} defines the set of hexadecimal numbers [a-zA-z_] defines a single small or capital letter or an underscore character. (({0-9A-Fa-f})$1|({a-zA-Z_})$2))$3 corresponds to both hexadecimal numbers and Modula-2 identifiers. The program could address a hexadecimal number by the $1 reference, identifier by the $2 reference and both of them by the $3 reference. \\\$\{\}\[\]\*\? is equal to the string \${}[]*?. Module description ═══ 11.3.41. Expr - Regular expression ═══ TYPE Expr* = POINTER TO ExprDesc; ExprDesc = RECORD END; ═══ 11.3.42. Compile - Compile regular expression ═══ PROCEDURE Compile*(expr: ARRAY OF CHAR; VAR reg: Expr; VAR res: LONGINT); Compiles a regular expression to an internal form. The appearance of the following table is a known problem and will be improved in the final release. l|l Value of res | Meaning res?0 | error in position ABS(res) res > 0 | done ═══ 11.3.43. Const - Is constant expression ═══ PROCEDURE Const*(re: Expr): BOOLEAN; Returns TRUE, if an expression does not contain wildcards. ═══ 11.3.44. Match - Compare string with expression ═══ PROCEDURE Match*(re: Expr; s: ARRAY OF CHAR; pos: LONGINT): BOOLEAN; Returns TRUE, if the expression matches the string s starting from the position pos. ═══ 11.3.45. Len - Length of substring ═══ PROCEDURE Len*(re: Expr; n: INTEGER): LONGINT; Returns the length of the substring which corresponds to $n in the last call of the Match procedure with the parameter re. ═══ 11.3.46. Pos - Position of substring ═══ PROCEDURE Pos*(re: Expr; n: INTEGER): LONGINT; Returns the position of the substring which corresponds to $n in the last call of the Match procedure with the parameter re. ═══ 11.3.47. Substitute - Substitute substrings ═══ PROCEDURE Substitute*(re: Expr; s,m: ARRAY OF CHAR; VAR d: ARRAY OF CHAR); The substrings of s which match re are substituted instead of $digit into m and the result string is copied into d. Example After the following sequence of calls Compile("{a-z}$1{0-9}$2",re,res); Substitute(re,"abcdef153","tail: $2 head: $1",dest); the dest string will contain "tail: 153 head: abcdef" ═══ 11.4. Oberon-2 Oakwood libraries ═══ The Oakwood Guidelines (See Chapter XDS Oberon-2) specifies a set of libraries that should be provided with all Oberon implementations. The current XDS release does not contain all libraries. The following libraries are currently available: The appearance of the following table is a known problem and will be improved in the final release. lp8cm In | input from a standard stream MathR | mathematical functions for REAL MathL | mathematical functions for LONGREAL MathC | mathematical functions for COMPLEX MathLC | mathematical functions for LONGCOMPLEX Out | output to a standard stream O2Strings | simple manipulations for strings The Math library is renamed to MathR, because it coincides with the ANSI C math library interface (See ???). The complex types and, hence, MathC and MathLC modules may be not available for other Oberon implementations (See also Oakwood numeric extensions). The Strings library is renamed to O2Strings, since it is not compatible with the correspondent ISO library. ═══ 12. Run-time Support ═══ Some language features are implemented in a run-time support library, including  exceptions and finalization  coroutines  memory management  garbage collection  postmorten history XDS provides an integrated Modula-2 and Oberon-2 run-time library, taking into account the possibility that modules written in both languages are used in one project. As a rule, if you do not use any feature the part of RTS that implements this feature will not be added to your executable program. For example, if you program is written in Modula-2 only, the Oberon part of RTS (garbage collector, meta-language facilities) will not included. The integrated memory manager is described in Memory management. The section The oberonRTS module describes an interface to the Oberon-2 run-time support. ═══ 12.1. Memory management ═══ The XDS integrated memory manager implements  default memory allocation and deallocation procedures for Modula-2 (See the option STORAGE);  the memory allocation procedure for Oberon-2;  the system memory allocation procedure for Oberon-2 (See NEW and DISPOSE);  the garbage collector. The XDS provides the GCAUTO option and equations (GCTHRESHOLD and HEAPLIMIT) to control the memory management. The option and equations should be set when the top-level module of the program is compiled We recommend to set them into the configuration or project file.. The XDS uses their values when generating the call of RTS initialization. The option GCAUTO allows the garbage collector to be called implicitly. If the option is not set the garbage collector must be called explicitly (See The oberonRTS module). The garbage collector is called implicitly by the memory allocation procedure in the following cases:  a memory block of a required length cannot be allocated;  the amount of a busy memory exceeds the limit specified by the HEAPLIMIT equation;  the amount of a busy memory exceeds the threshold specified by the GCTHRESHOLD equation. If the memory cannot be allocated after the call of the garbage collector, an exception is raised, for a call from Oberon-2. ═══ 12.2. History ═══ If the option GENHISTORYis set ON when compiling your program, the run-time system prints a stack of procedure calls on abnormal termination of your program, including  a file name  a line number  a program counter value  a procedure name (sometimes) Note: all modules constituting your program should be compiled with the option LINENO set ON. To print the history, RTS scans the procedure stack of the coroutine that caused an exception and tries to find procedure calls. This is not trivial because of the highly optimized code generated by the compiler. For example, not all procedures have a stack frame. For each pointer to the code segment on the stack RTS checks the previous command. If this command is a call command, it decides that this is a procedure call. It is unlikely that RTS misses a procedure call, but it can be cheated by something that looks like a procedure call. As a rule, it is caused by uninitialized variables. The first line of the history is always correct. For each line, except the first, we recommend to check that the procedure shown in the previous line is called from the given line. The following example shows a sketch of the program and the procedure stack: PROCEDURE P1; (* uninitialized variable: *) VAR x: ARRAY [0..50] OF INTEGER; BEGIN i:=i DIV j; (* line 50 *) END P1; PROCEDURE P2; BEGIN i:=i DIV j; (* line 100 *) END P2; PROCEDURE P3; BEGIN P1; (* line 150 *) END P2; #RTS: No exception handler #6: zero or negative divisor ------------------------------------------------------------ Source file LINE OFFSET PROCEDURE ------------------------------------------------------------ "test.mod" 50 000000DE "test.mod" 100 0000024C "test.mod" 150 0000051D It is obvious from the source text that the procedure P1 cannot be called from P2. The second line is superfluous. ═══ 12.3. The oberonRTS module ═══ The run time support (RTS) is an integral part of the Oberon-2 language implementation. It includes the command activation, memory allocation, garbage collection and meta-language facilities. The module oberonRTS provides an interface to these features. ═══ 12.3.1. Types and variables ═══ The appearance of the following table is a known problem and will be improved in the final release. |l@r| Module | run-time data structure for a module Type | run-time data structure for a data type Command | parameterless procedure TYPE Module; Type; Command = PROC; CARDINAL = SYSTEM.CARD32; The appearance of the following table is a known problem and will be improved in the final release. |l@r| nullModule | NIL of Module Type nullType | NIL of Type Type VAR nullModule: Module; nullType: Type; ═══ 12.3.2. Garbage collection ═══ ═══ 12.3.3. Collect - Garbage Collector ═══ PROCEDURE Collect; Invokes the garbage collector. ═══ 12.3.4. GetInfo - Get Memory Information ═══ PROCEDURE GetInfo(VAR objects, busymem: CARDINAL); Returns the number of allocated objects and the total size of the allocated memory. ═══ 12.3.5. Object finalization ═══ A system with garbage collection has some specific features. Its main difference from other systems is that deallocation of any system resource must be postponed until garbage collection. For example, let some data structure contain descriptors of open files. To close a file (i.e. to destroy its descriptor), one needs to know that there are no references to this file. This information becomes known only in the course of garbage collection. The same argument also holds for other kinds of resources. One immediate implication is that there must be some finalization mechanism: the ability to perform certain operations with an object when there are no more references to it. The XDS allows one to attach a finalization procedure to any object. ═══ 12.3.6. Finalizer - Type of a finalization procedure ═══ TYPE Finalizer = PROCEDURE (SYSTEM.ADDRESS); ═══ 12.3.7. InstallFinalizer - Set a finalizer to an object ═══ PROCEDURE InstallFinalizer(f: Finalizer; obj: SYSTEM.ADDRESS); The procedure sets a finalization procedure "f" for object "obj". This procedure is called when the object becomes unreachable. Note: a finalizer is called on GC stack (stack size is limited); Example TYPE Obj = POINTER TO ObjDesc; ObjDesc = RECORD file: File; (* file handler *) END; PROCEDURE Final(x: SYSTEM.ADDRESS); VAR o: Obj; BEGIN o:=SYSTEM.CAST(Obj,x); IF o.file # NIL THEN Close(file) END; END Final; PROCEDURE Create(): Obj; VAR o: Obj; BEGIN NEW(o); o.file:=NIL; oberonRTS.InstallFinalizer(Final,o); TryOpen(o.file); END Create; ═══ 12.3.8. Meta-language facilities ═══ The meta-programming operations allow one to retrieve the type of an object, to create an object of a given type, to get the name of a type and a type by its name, etc. ═══ 12.3.9. Search - Search a Module by its Name ═══ PROCEDURE Search(name: ARRAY OF CHAR): Module; Returns a module by its name or nullModule. ═══ 12.3.10. NameOfModule - Name of Module ═══ PROCEDURE NameOfModule(m: Module; VAR name: ARRAY OF CHAR); Returns the name of the module. ═══ 12.3.11. ThisCommand - Get Command by its Name ═══ PROCEDURE ThisCommand(m: Module; name: ARRAY OF CHAR; ): Command; Returns a command (parameterless procedure) named name in the module m or NIL, if such a command does not exist. ═══ 12.3.12. ThisType - Get Type by its Name ═══ PROCEDURE ThisType(m: Module; name: ARRAY OF CHAR): Type; Returns a type declared in the module m or nullType, if such type does not exist. ═══ 12.3.13. SizeOf - Size of Type ═══ PROCEDURE SizeOf(t: Type): INTEGER; Returns the size (in bytes) of an object of the type t. ═══ 12.3.14. BaseOf - Base of Type ═══ PROCEDURE BaseOf(t: Type; level: INTEGER): Type; Returns the level-th base type of t. ═══ 12.3.15. LevelOf - Level of Type Extension ═══ PROCEDURE LevelOf(t: Type): INTEGER; Returns a level of the type extension. ═══ 12.3.16. ModuleOf - Module of Type ═══ PROCEDURE ModuleOf(t: Type): Module; Returns the module in which the type t was declared. ═══ 12.3.17. NameOfType - Name of Type ═══ PROCEDURE NameOfType(t: Type; VAR name: ARRAY OF CHAR); Returns the name of the record type. ═══ 12.3.18. TypeOf - Type of Object ═══ PROCEDURE TypeOf(obj: SYSTEM.ADDRESS): Type; Returns the type of the object. ═══ 12.3.19. New Object - Create Object ═══ PROCEDURE NewObj(type: Type): SYSTEM.ADDRESS; Creates a new object of the type t. ═══ 12.3.20. Module iterators ═══ The module provides iterators which allow one to iterate all loaded modules, all commands and all object types (i.e., exported record types). ═══ 12.3.21. NameIterator - Iterator Type ═══ TYPE NameIterator = PROCEDURE ( (*context:*) SYSTEM.ADDRESS, (*name:*) ARRAY OF CHAR ): BOOLEAN; The NameIterator is called by an iterator on each iterated item. An iterator passes the name of the item along with the so-called context word. This allows some context information to be passed to the user-defined procedure (e.g., the file handler). If the iterated procedure returns FALSE, the iteration is broken off. ═══ 12.3.22. IterModules - Iterate all Modules ═══ PROCEDURE IterModules(context: SYSTEM.ADDRESS; iter: NameIterator); The procedure iterates all Oberon-2 modules. ═══ 12.3.23. IterCommands - Iterate Commands ═══ PROCEDURE IterCommands(mod: Module; context: SYSTEM.ADDRESS; iter: NameIterator); Iterates all commands implemented in the module mod. ═══ 12.3.24. IterTypes - Iterate Record Types ═══ PROCEDURE IterTypes(mod: Module; context: SYSTEM.WORD; iter: NameIterator); Iterates all record types declared in the module mod. ═══ 13. Configuring XDS for a C Compiler ═══ XDS allows C functions and libraries to be used in your projects. Different C compilers have different naming and calling conventions. You have to specify your C compiler in XDS environment using the CC equation. The equation forces XDS to call all C functions in a way compatible with the specified C compiler. Also the compiler sets the default values of additional configuration options according to the value of the equation. See Additional configuration options. For Windows NT and Windows 95 XDS supports the MSVC++ and Watcom compilers. The corresponding values of the CC equations are MSVC and WATCOM, written in any case. If the equation does not set, the compiler will assume WATCOM by default. To configure XDS append the line -cc=Watcom or -cc=MSVC to your configuration file. See samples.txt from XDS on-line documentation for more information. ═══ 13.1. Possible problems ═══ To use a C function or a data type from Modula-2 or Oberon-2 one have to express its type in one of these languages. Usually it is done in a foreign definition module (See Interfacing to C). The current version of XDS does not support all calling conventions, thats why using of some functions is impossible, namely:  functions with parameter of a structured type, passed by value, e.g.: void foo(struct MyStruct s)  functions that return structured types, e.g.: struct MyStruct foo(void)  C functions with Pascal calling convention that return the real type.  functions that are compiled with non-stack calling conventions. Note: stack calling conventions shall be set for Watcom using "-3s", "-4s" or "-5s" option. XDS does not support the use of data structures with non-standard alignment. If the ALIGNMENT option is off, use the option "-zp1" for Watcom C and "-Zp1" for MSVC. Otherwise, use "-zp4" (Watcom) and "-Zp4" (MSVC). Both Modula-2 and C/C++ have exception handling and finalization facilities. Unpredictable results may occur if you try to use facilities from both languages in one program. ═══ 13.2. Using unsupported compiler ═══ XDS does not support all available C compilers. You can use additional configuration options (See Additional configuration options) to adapt XDS to your compiler. The DEFLIBS option should be switched off in that case. It may be necessary to make some changes in the run-time support for the particular extender. It can be done in terms of our support program. ═══ 13.3. Additional configuration options ═══ Additional configuration options can be used to adapt XDS to unsupported C compiler. We recommend to use these options with care. GENCPREF If the option is ON, the compiler appends underscore as a prefix for all names in object files. ONECODESEG If the option is ON, the compiler produces only one code segment which contains all code of a module, otherwise it generates separate code segment for each procedure. The table below shows the default values of these options for the supported C compilers: The appearance of the following table is a known problem and will be improved in the final release. |l|c|c|c|c| Option | WATCOM | MSVC comment DS_NEQ_SS | ? | ? GENCPREF | OFF | ON ONECODESEG | OFF | ON ═══ 14. Low-level Programming ═══ ═══ 14.1. Data representation ═══ The internal representation of values of Modula-2 and Oberon-2 basic types is described in the tables ??? and ???. In the table Data representation a representation of system types is described. The appearance of the following table is a known problem and will be improved in the final release. |l|c|l| Modula-2 type | Bits |Representation SHORTINT | 8 | signed INTEGER | 16/32 | signed (See Modula-2 INTEGER and CARDINAL types) LONGINT | 32 | signed SHORTCARD | 8 | unsigned CARDINAL | 16/32 | unsigned (See Modula-2 INTEGER and CARDINAL types) LONGCARD | 32 | unsigned CHAR | 8 | unsigned BOOLEAN | 8/32 | unsigned (See Modula-2 BOOLEAN type) | | 0 for FALSE, 1 for TRUE subranges | | according to the base type REAL | 32 | 80387 single-precision data format LONGREAL | 64 | 80387 double-precision data format Representation of Modula-2 basic types The appearance of the following table is a known problem and will be improved in the final release. |l|c|l| Oberon-2 type | Bits |Representation SHORTINT | 8 | signed INTEGER | 16 | signed LONGINT | 32 | signed CHAR | 8 | unsigned BOOLEAN | 8 | unsigned byte | | 0 for FALSE, 1 for TRUE REAL | 32 | 80387 single-precision data format LONGREAL | 64 | 80387 double-precision data format SET | 32 | unsigned Representation of Oberon-2 basic types The appearance of the following table is a known problem and will be improved in the final release. |l|c|l| System type | Bits |Representation ADDRESS | 32 | unsigned BOOL8 | 8 | unsigned BOOL32 | 32 | unsigned BYTE | 8 | unsigned CARD8 | 8 | unsigned CARD16 | 16 | unsigned CARD32 | 32 | unsigned INT8 | 8 | signed INT16 | 16 | signed INT32 | 32 | signed LOC | 8 | unsigned WORD | 32 | ARRAY [0..3] OF LOC Representation of SYSTEM types ═══ 14.1.1. Modula-2 INTEGER and CARDINAL types ═══ If the option M2BASE16 is OFF, INTEGER and CARDINAL types are represented as 32-bit values, otherwise as 16-bit values. ═══ 14.1.2. Modula-2 BOOLEAN type ═══ If the option M2UNPACKTYPES is OFF, boolean type is represented as unsigned 1-byte value, otherwise as unsigned 32-bit value. ═══ 14.1.3. Modula-2 enumeration types ═══ If the option M2UNPACKTYPES is OFF, Modula-2 enumeration types of no more than 256 and 65536 elements are represented as unsigned 1 or 2-byte values respectively. Otherwise, enumerations are represented as 4-byte values. ═══ 14.1.4. Modula-2 set types ═══ If the option M2UNPACKTYPES is OFF, Modula-2 sets of no more than 8, 16 and 32 elements are represented as unsigned 1, 2 or 4-byte values respectively. Otherwise, sets of no more than 32 elements are represented as 4-byte values. If the option M2BASE16 is OFF, BITSET type is represented as unsigned 32-bit values, otherwise as 16-bit values. ═══ 14.1.5. Pointer, address and opaque types ═══ Address types are represented as 32-bit (4-byte) unsigned values containing the byte offset in the task data segment. The address arithmetic is implemented as 32-bit unsigned arithmetic without overflow checks. ═══ 14.1.6. Procedure types ═══ Procedure types are represented by the 32-bit (4-byte) address of the procedure's entry point in the task code segment. ═══ 14.1.7. Record types ═══ Records are represented by a continuous memory segment containing all record components (fields) in a representation corresponding to their type. If the option ALIGNMENT is ON, the compiler aligns each field according to its size. It aligns a 2-byte value to an even offset and a value of 4 or more bytes to an offset divisible by 4. A record itself is aligned according to its field with largest alignment, e.g. if a record contains a field of the REAL type, its size will be multiple of 4 (SIZE(REAL) = 4). ═══ 14.1.8. Array types ═══ An array is represented by a continuous memory segment containing all array elements in a representation corresponding to their type. Note that elements within an array could be aligned so, that in general for TYPE A = ARRAY [0..N-1] OF T; SIZE(A) is not equal to SIZE(T) * N. Open arrays, as well as procedure formal parameters of type ARRAY OF ... ARRAY OF T, are represented by an open array descriptor. For an N-dimensional open array, the descriptor is an array of 3N 32-bit elements, which are:  the first element the address of the array proper;  for each of N-1 higher dimensions, the descriptor contains three consecutive signed integers, which are: - the lowest array index of this dimension (always 0); - the highest array index (the length of this dimension minus one); - the size of the array element in bytes;  for the last dimension, the array descriptor contains its lowest (0) and highest indices. Example: let A be an open 3-dimensional array of INTEGER (SIZE(INTEGER)=2 in Oberon-2) created as NEW(A,4,3,6) then its descriptor is a 9-element array containing: #0: Address of array itself #1: 0 #2: 3 (4-1) #3: 36 (12*3) #4: 0 #5: 2 (3-1) #6: 12 (6*2) #7: 0 #8: 5 (6-1) ═══ 14.2. Sequence parameters ═══ The array of bytes which is passed to a procedure in place of a formal SEQ-parameter is formed as follows:  values of all actual parameters forming the sequence are represented as described below and concatenated in the array in their textual order  integer values are converted to LONGINT  BOOLEAN, CHAR, cardinal and enumeration values are converted to LONGCARD  range type values are converted according to their base type  real values are converted to LONGREAL  pointer, address, opaque and procedure type values are converted to ADDRESS  structured value (record or array) is interpreted as one-dimensional array of bytes and then is represented by an array descriptor (See ???). Example PROCEDURE write(SEQ args: SYSTEM.BYTE); BEGIN END write; VAR i: INTEGER; c: SYSTEM.CARD8; r: LONGREAL; S: RECORD a: LONGINT; c: CHAR END; p: POINTER TO ARRAY OF CHAR; ... write(i,c,S,r,p^); For this call the actual byte array passed to write will contain:  4 bytes of the sign-extended value of i  4 bytes of the zero-extended value of c  12 bytes of the array descriptor - 4 bytes containing the address of S - 4 bytes containing 0 - 4 bytes containing 4 (SIZE(S)-1)  8 bytes value of r in the double-precision 80387 format  12 bytes of the array descriptor - 4 bytes containing the address of array of CHAR itself - 4 bytes containing value 0 - 4 bytes containing SIZE(p^)-1 ═══ 15. Implementation limitations and restrictions ═══ There are some limitations and restrictions in both Modula-2 and Oberon-2 compilers. ═══ 15.1. Length of identifiers ═══ The length of an identifier is at most 127 characters. ═══ 15.2. Length of literal strings ═══ The length of a literal string is at most 256 characters. Longer strings may be constructed by use of the string concatenation operator (See Strings). ═══ 15.3. Record extension hierarchy ═══ The length of a record extension hierarchy is at most 15 extensions. ═══ 15.4. Unimplemented ISO libraries ═══ The following Modula-2 ISO standard library modules are not available in the current release: The appearance of the following table is a known problem and will be improved in the final release. lp7cm TermFile | Access to an interactive terminal LowLong | Access to underlying properties of the type LONGREAL LowReal | Access to underlying properties of the type REAL ═══ 15.5. Unimplemented Oakwood libraries ═══ The following Oberon-2 Oakwood library modules are not available in the current release: The appearance of the following table is a known problem and will be improved in the final release. lp7cm Input | Keyboard and pointer device access Files | File input/output, riders XYPlane | Elementary pixel plotting ═══ 15.6. Coroutines ═══ The current release provides a restricted implementation of the system module COROUTINES: the interrupt requests are not detected. ═══ 15.7. Dynamic loader ═══ The Oberon-2 dynamic loading facility is not provided in the current release, ReWi88 [MoWi91]MW91 H.Mossenbock, N.Wirth. The Programming Language Oberon-2. Structured Programming,1991, 12, 179-195. [PIM]PIM N.Wirth. Programming in Modula-2. 4th edition. Springer-Verlag, 1988. ISBN 0-387-50150-9. [Wirth88]M2O2 N.Wirth. From Modula-2 to Oberon. Software, Practice and Experience 18:7(1988), 661-670. [ReWi92]PIO M.Reiser, N.Wirth. Programming in Oberon - Steps Beyond Pascal and Modula. ACM Press, Addison Wessley, 1992. ISBN 0-201-56543-9 [Mo93]M93 H.Mossenbock. Object Oriented Programming in Oberon-2. Springer-Verlag, 1993. ISBN 3-540-56411-X ═══ 16. The Oberon-2 Report ═══ The Programming Language Oberon-2 H.Mossenbock, N. Wirth Institut fur Computersysteme, ETH Zurich October 1993 ═══ 16.1. Introduction ═══ Oberon-2 is a general-purpose language in the tradition of Oberon and Modula-2. Its most important features are block structure, modularity, separate compilation, static typing with strong type checking (also across module boundaries), and type extension with type-bound procedures. Type extension makes Oberon-2 an object-oriented language. An object is a variable of an abstract data type consisting of private data (its state) and procedures that operate on this data. Abstract data types are declared as extensible records. Oberon-2 covers most terms of object-oriented languages by the established vocabulary of imperative languages in order to minimize the number of notions for similar concepts. This report is not intended as a programmer's tutorial. It is intentionally kept concise. Its function is to serve as a reference for programmers, implementors, and manual writers. What remains unsaid is mostly left so intentionally, either because it can be derived from stated rules of the language, or because it would require to commit the definition when a general commitment appears as unwise. Section Definition of terms defines some terms that are used to express the type checking rules of Oberon-2. Where they appear in the text, they are written in italics to indicate their special meaning (e.g. the same type). ═══ 16.2. Syntax ═══ An extended Backus-Naur Formalism (EBNF) is used to describe the syntax of Oberon-2: Alternatives are separated by |. Brackets [ and ] denote optionality of the enclosed expression, and braces { and } denote its repetition (possibly 0 times). Non-terminal symbols start with an upper-case letter (e.g. Statement). Terminal symbols either start with a lower-case letter (e.g. ident), or are written all in upper-case letters (e.g. BEGIN), or are denoted by strings (e.g. ":="). ═══ 16.3. Vocabulary and Representation ═══ The representation of (terminal) symbols in terms of characters is defined using the ASCII set. Symbols are identifiers, numbers, strings, operators, and delimiters. The following lexical rules must be observed: Blanks and line breaks must not occur within symbols (except in comments, and blanks in strings). They are ignored unless they are essential to separate two consecutive symbols. Capital and lower-case letters are considered as distinct. 1. Identifiers are sequences of letters and digits. The first character must be a letter. ident = letter {letter | digit}. Examples: x Scan Oberon2 GetSymbol firstLetter 2. Numbers are (unsigned) integer or real constants. The type of an integer constant is the minimal type to which the constant value belongs (see Basic types). If the constant is specified with the suffix H, the representation is hexadecimal otherwise the representation is decimal. A real number always contains a decimal point. Optionally it may also contain a decimal scale factor. The letter E (or D) means "times ten to the power of". A real number is of type REAL, unless it has a scale factor containing the letter D. In this case it is of type LONGREAL. number = integer | real. integer = digit {digit} | digit{hexDigit}"H". real = digit{digit}"."{digit} [ScaleFactor]. ScaleFactor = ("E" | "D") ["+" | "-"] digit {digit}. hexDigit = digit |"A"|"B"|"C"|"D"|"E"|"F". digit = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9". Examples: 1991 INTEGER 1991 0DH SHORTINT 13 12.3 REAL 12.3 4.567E8 REAL 456700000 0.57712566D-6 LONGREAL 0.00000057712566 3. Character constants are denoted by the ordinal number of the character in hexadecimal notation followed by the letter X. character = digit {hexDigit} "X". 4. Strings are sequences of characters enclosed in single (') or double (") quote marks. The opening quote must be the same as the closing quote and must not occur within the string. The number of characters in a string is called its length. A string of length 1 can be used wherever a character constant is allowed and vice versa. string = '"' {char} '"' | "'" {char} "'". Examples: "Oberon-2" "Don't worry!" "x" 5. Operators and delimiters are the special characters, character pairs, or reserved words listed below. The reserved words consist exclusively of capital letters and cannot be used as identifiers. + := ARRAY IMPORT RETURN - ^ BEGIN IN THEN * = BY IS TO / # CASE LOOP TYPE ~ < CONST MOD UNTIL & > DIV MODULE VAR . <= DO NIL WHILE , >= ELSE OF WITH ; .. ELSIF OR | : END POINTER ( ) EXIT PROCEDURE [ ] FOR RECORD { } IF REPEAT 6. Comments may be inserted between any two symbols in a program. They are arbitrary character sequences opened by the bracket (* and closed by *). Comments may be nested. They do not affect the meaning of a program. ═══ 16.4. Declarations and scope rules ═══ Every identifier occurring in a program must be introduced by a declaration, unless it is a predeclared identifier. Declarations also specify certain permanent properties of an object, such as whether it is a constant, a type, a variable, or a procedure. The identifier is then used to refer to the associated object. The scope of an object x extends textually from the point of its declaration to the end of the block (module, procedure, or record) to which the declaration belongs and hence to which the object is local. It excludes the scopes of equally named objects which are declared in nested blocks. The scope rules are: 1. No identifier may denote more than one object within a given scope (i.e. no identifier may be declared twice in a block); 2. An object may only be referenced within its scope; 3. A type T of the form POINTER TO T1 (see Pointer types) can be declared before the scope of T1. The declaration of T1 must follow in the same block to which T is local; 4. Identifiers denoting record fields (see Record types) or type-bound procedures (see Type-bound procedures) are valid in record designators only. An identifier declared in a module block may be followed by an export mark ("*" or "-") in its declaration to indicate that it is exported. An identifier x exported by a module M may be used in other modules, if they import M (see section Modules). The identifier is then denoted as M.x in these modules and is called a qualified identifier. Identifiers marked with "-" in their declaration are read-only in importing modules. Qualident = [ident "."]ident. IdentDef = ident [" * " | " - "]. The following identifiers are predeclared; their meaning is defined in the indicated sections: The appearance of the following table is a known problem and will be improved in the final release. ll@2.0cmll ABS | (Predeclared procedures) | LEN | (Predeclared procedures) ASH | (Predeclared procedures) | LONG | (Predeclared procedures) BOOLEAN | (Basic types) | LONGINT | (Basic types) CAP | (Predeclared procedures) | LONGREAL | (Basic types) CHAR | (Basic types) | MAX | (Predeclared procedures) CHR | (Predeclared procedures) | MIN | (Predeclared procedures) COPY | (Predeclared procedures) | NEW | (Predeclared procedures) DEC | (Predeclared procedures) | ODD | (Predeclared procedures) ENTIER | (Predeclared procedures) | ORD | (Predeclared procedures) EXCL | (Predeclared procedures) | REAL | (Basic types) FALSE | (Basic types) | SET | (Basic types) HALT | (Predeclared procedures) | SHORT | (Predeclared procedures) INC | (Predeclared procedures) | SHORTINT | (Basic types) INCL | (Predeclared procedures) | SIZE | (Predeclared procedures) INTEGER | (Basic types) | TRUE | (Basic types) ═══ 16.5. Constant declarations ═══ A constant declaration associates an identifier with a constant value. ConstantDeclaration = IdentDef "=" ConstExpression. ConstExpression = Expression. A constant expression is an expression that can be evaluated by a mere textual scan without actually executing the program. Its operands are constants (section Expressions) or predeclared functions (section Predeclared procedures) that can be evaluated at compile time. Examples of constant declarations are: N = 100 limit = 2*N - 1 fullSet = {MIN(SET)..MAX(SET)} ═══ 16.6. Type declarations ═══ A data type determines the set of values which variables of that type may assume, and the operators that are applicable. A type declaration associates an identifier with a type. In the case of structured types (arrays and records) it also defines the structure of variables of this type. TypeDeclaration = IdentDef "=" Type. Type = Qualident | ArrayType | RecordType | PointerType | ProcedureType. Examples: Table = ARRAY N OF REAL Tree = POINTER TO Node Node = RECORD key: INTEGER; left, right: Tree END CenterTree = POINTER TO CenterNode CenterNode = RECORD (Node) width: INTEGER; subnode: Tree END Function = PROCEDURE(x: INTEGER): INTEGER ═══ 16.6.1. Basic types ═══ The basic types are denoted by predeclared identifiers. The associated operators are defined in Operators and the predeclared function procedures in Predeclared procedures The values of the given basic types are the following: The appearance of the following table is a known problem and will be improved in the final release. llp7cm 1. | BOOLEAN | the truth values TRUE and FALSE 2. | CHAR | the characters of the extended ASCII set (0X..0FFX) 3. | SHORTINT | the integers between MIN(SHORTINT) and MAX(SHORTINT) 4. | INTEGER | the integers between MIN(INTEGER) and MAX(INTEGER) 5. | LONGINT | the integers between MIN(LONGINT) and MAX(LONGINT) 6. | REAL | the real numbers between MIN(REAL) and MAX(REAL) 7. | LONGREAL | the real numbers between MIN(LONGREAL) and MAX(LONGREAL) 8. | SET | the sets of integers between 0 and MAX(SET) Types 3 to 5 are integer types, types 6 and 7 are real types, and together they are called numeric types. They form a hierarchy; the larger type includes (the values of) the smaller type: LONGREAL ?REAL ?LONGINT ?INTEGER ?SHORTINT ═══ 16.6.2. Array types ═══ An array is a structure consisting of a number of elements which are all of the same type, called the element type. The number of elements of an array is called its length. The elements of the array are designated by indices, which are integers between 0 and the length minus 1. ArrayType = ARRAY [ Length { "," Length}] OF Type. Length = ConstExpression. A type of the form ARRAY L0, L1, ..., Ln OF T is understood as an abbreviation of ARRAY L0 OF ARRAY L1 OF ... ARRAY Ln OF T Arrays declared without length are called open arrays. They are restricted to pointer base types (see Pointer types), element types of open array types, and formal parameter types (see Formal parameters). Examples: ARRAY 10, N OF INTEGER ARRAY OF CHAR ═══ 16.6.3. Record types ═══ A record type is a structure consisting of a fixed number of elements, called fields, with possibly different types. The record type declaration specifies the name and type of each field. The scope of the field identifiers extends from the point of their declaration to the end of the record type, but they are also visible within designators referring to elements of record variables (see Operands). If a record type is exported, field identifiers that are to be visible outside the declaring module must be marked. They are called public fields; unmarked elements are called private fields. RecordType = RECORD ["("BaseType")"] FieldList{";" FieldList} END. BaseType = Qualident. FieldList = [IdentList ":" Type ]. Record types are extensible, i.e. a record type can be declared as an extension of another record type. In the example T0 = RECORD x: INTEGER END T1 = RECORD (T0) y: REAL END T1 is a (direct) extension of T0 and T0 is the (direct) base type of T1 (see section Definition of terms). An extended type T1 consists of the fields of its base type and of the fields which are declared in T1 (see section Type declarations). All identifiers declared in the extended record must be different from the identifiers declared in its base type record(s). Examples of record type declarations: RECORD day, month, year: INTEGER END RECORD name, firstname: ARRAY 32 OF CHAR; age: INTEGER; salary: REAL END ═══ 16.6.4. Pointer types ═══ Variables of a pointer type P assume as values pointers to variables of some type T. T is called the pointer base type of P and must be a record or array type. Pointer types adopt the extension relation of their pointer base types: if a type T1 is an extension of T, and P1 is of type POINTER TO T1, then P1 is also an extension of P. PointerType = POINTER TO Type. If p is a variable of type P = POINTER TO T, a call of the predeclared procedure NEW(p) (see Predeclared procedures) allocates a variable of type T in free storage. If T is a record type or an array type with fixed length, the allocation has to be done with NEW(p); if T is an n-dimensional open array type the allocation has to be done with NEW(p,e0,,en-1) where T is allocated with lengths given by the expressions e0,,en-1. In either case a pointer to the allocated variable is assigned to p. p is of type P. The referenced variable p'136(pronounced as p-referenced) is of type T. Any pointer variable may assume the value NIL, which points to no variable at all. ═══ 16.6.5. Procedure types ═══ Variables of a procedure type T have a procedure (or NIL) as value. If a procedure P is assigned to a variable of type T, the formal parameter lists (see section Formal parameters) of P and T must match (see Definition of terms). P must not be a predeclared or type-bound procedure nor may it be local to another procedure. ProcedureType = PROCEDURE [FormalParameters]. ═══ 16.7. Variable declarations ═══ Variable declarations introduce variables by defining an identifier and a data type for them. VariableDeclaration = IdentList ":" Type. Record and pointer variables have both a static type (the type with which they are declared - simply called their type) and a dynamic type (the type they assume at run time). For pointers and variable parameters of record type the dynamic type may be an extension of their static type. The static type determines which fields of a record are accessible. The dynamic type is used to call type-bound procedures (see Type-bound procedures). Examples of variable declarations (refer to examples in Type declarations): i, j, k: INTEGER x, y: REAL p, q: BOOLEAN s: SET F: Function a: ARRAY 100 OF REAL w: ARRAY 16 OF RECORD name : ARRAY 32 OF CHAR; ccount: INTEGER END t, c: Tree ═══ 16.8. Expressions ═══ Expressions are constructs denoting rules of computation whereby constants and current values of variables are combined to compute other values by the application of operators and function procedures. Expressions consist of operands and operators. Parentheses may be used to express specific associations of operators and operands. ═══ 16.8.1. Operands ═══ With the exception of set constructors and literal constants (numbers, character constants, or strings), operands are denoted by designators. A designator consists of an identifier referring to a constant, variable, or procedure. This identifier may possibly be qualified by a module identifier (see sections Declarations and scope rules and Modules) and may be followed by selectors if the designated object is an element of a structure. Designator = Qualident { "." ident | "[" ExpressionList "]" | "^" | "(" Qualident ")" }. ExpressionList = Expression {"," Expression}. If a designates an array, then a[e] denotes that element of a whose index is the current value of the expression e. The type of e must be an integer type. A designator of the form a[e0,e1,,en] stands for a[e0][e1][en]. If r designates a record, then r.f denotes the field f of r or the procedure f bound to the dynamic type of r (section Type-bound procedures). If p designates a pointer, p'136 denotes the variable which is referenced by p. The designators p'136.f and p'136[e] may be abbreviated as p.f and p[e], i.e. record and array selectors imply dereferencing. If a or r are read-only, then also a[e] and r.f are read-only. A type guard v(T) asserts that the dynamic type of v is T (or an extension of T), i.e. program execution is aborted, if the dynamic type of v is not T (or an extension of T). Within the designator, v is then regarded as having the static type T. The guard is applicable, if 1. v is a variable parameter of record type or v is a pointer, and if 2. T is an extension of the static type of v If the designated object is a constant or a variable, then the designator refers to its current value. If it is a procedure, the designator refers to that procedure unless it is followed by a (possibly empty) parameter list in which case it implies an activation of that procedure and stands for the value resulting from its execution. The actual parameters must correspond to the formal parameters as in proper procedure calls (see Formal parameters). Examples of designators (refer to examples in Variable declarations): i (INTEGER) a[i] (REAL) w[3].name[i] (CHAR) t.left.right (Tree) t(CenterNode).subnode (Tree) ═══ 16.8.2. Operators ═══ Four classes of operators with different precedences (binding strengths) are syntactically distinguished in expressions. The operator ~ has the highest precedence, followed by multiplication operators, addition operators, and relations. Operators of the same precedence associate from left to right. For example, x-y-z stands for (x-y)-z. Expression = SimpleExpression [ Relation SimpleExpression]. SimpleExpression = ["+" | "-"] Term {AddOperator Term}. Term = Factor {MulOperator Factor}. Factor = Designator [ActualParameters] | number | character | string | NIL | Set | "(" Expression ")" | "~" Factor. Set = "{"[Element {","Element}]"}". Element = Expression [".."Expression]. ActualParameters = "(" [ExpressionList] ")". Relation = "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS. AddOperator = "+" | "-" | OR. MulOperator = "*" | "/" | DIV | MOD | "&". The available operators are listed in the following tables. Some operators are applicable to operands of various types, denoting different operations. In these cases, the actual operation is identified by the type of the operands. The operands must be expression compatible with respect to the operator (see Definition of terms). Logical operators The appearance of the following table is a known problem and will be improved in the final release. llll OR | logical disjunction | p OR q | "if p then TRUE, else q" & | logical conjunction | p & q | "if p then q, else FALSE" ~ | negation | ~p | "not p" These operators apply to BOOLEAN operands and yield a BOOLEAN result. Arithmetic operators The appearance of the following table is a known problem and will be improved in the final release. ll + | sum - | difference * | product / | real quotient DIV | integer quotient MOD | modulus The operators +, -, *, and / apply to operands of numeric types. The type of the result is the type of that operand which includes the type of the other operand, except for division (/), where the result is the smallest real type which includes both operand types. When used as monadic operators, - denotes sign inversion and + denotes the identity operation. The operators DIV and MOD apply to integer operands only. They are related by the following formulas defined for any x and positive divisors y: x = (x DIV y) * y + (x MOD y) 0 <= (x MOD y) < y Examples: The appearance of the following table is a known problem and will be improved in the final release. cccc x | y | x DIV y | x MOD y 5 | 3 | 1 | 2 -5 | 3 | -2 | 1 Set Operators The appearance of the following table is a known problem and will be improved in the final release. ll + | union - | difference (x - y = x * (-y)) * | intersection / | symmetric set difference (x / y = (x-y) + (y-x)) Set operators apply to operands of type SET and yield a result of type SET. The monadic minus sign denotes the complement of x, i.e. -x denotes the set of integers between 0 and MAX(SET) which are not elements of x. Set operators are not associative ((a+b)-c # a+(b-c)). A set constructor defines the value of a set by listing its elements between curly brackets. The elements must be integers in the range 0..MAX(SET). A range a..b denotes all integers in the interval [a, b]. Relations The appearance of the following table is a known problem and will be improved in the final release. ll = | equal # | unequal < | less <=| less or equal > | greater >=| greater or equal IN| set membership IS| type test Relations yield a BOOLEAN result. The relations =, #, <, <=, > and >= apply to the numeric types, CHAR, strings, and character arrays containing 0X as a terminator. The relations = and # also apply to BOOLEAN and SET, as well as to pointer and procedure types (including the value NIL). x IN s stands for "x is an element of s". x must be of an integer type, and s of type SET. v IS T stands for "the dynamic type of v is T (or an extension of T)" and is called a type test. It is applicable if 1. v is a variable parameter of record type or v is a pointer, and if 2. T is an extension of the static type of v Examples of expressions (refer to examples in Variable declarations): 1991 INTEGER i DIV 3 INTEGER ~p OR q BOOLEAN (i+j) * (i-j) INTEGER s - {8, 9, 13} SET i + x REAL a[i+j] * a[i-j] REAL (0<=i) & (i<100) BOOLEAN t.key = 0 BOOLEAN k IN {i..j-1} BOOLEAN w[i].name <= "John" BOOLEAN t IS CenterNode BOOLEAN ═══ 16.9. Statements ═══ Statements denote actions. There are elementary and structured statements. Elementary statements are not composed of any parts that are themselves statements. They are the assignment, the procedure call, the return, and the exit statement. Structured statements are composed of parts that are themselves statements. They are used to express sequencing and conditional, selective, and repetitive execution. A statement may also be empty, in which case it denotes no action. The empty statement is included in order to relax punctuation rules in statement sequences. Statement = [ Assignment | ProcedureCall | IfStatement | CaseStatement | WhileStatement | RepeatStatement | ForStatement | LoopStatement | WithStatement | EXIT | RETURN [Expression] ]. ═══ 16.9.1. Assignments ═══ Assignments replace the current value of a variable by a new value specified by an expression. The expression must be assignment compatible with the variable (see Definition of terms). The assignment operator is written as ":=" and pronounced as becomes. Assignment = Designator ":=" Expression. If an expression e of type Te is assigned to a variable v of type Tv, the following happens: 1. if Tv and Te are record types, only those fields of Te are assigned which also belong to Tv (projection); the dynamic type of v must be the same as the static type of v and is not changed by the assignment; 2. if Tv and Te are pointer types, the dynamic type of v becomes the dynamic type of e; 3. if Tv is ARRAY n OF CHAR and e is a string of length m < n, v[i] becomes ei for i=0..m-1 and v[m] becomes 0X. Examples of assignments (refer to examples in Variable declarations): The appearance of the following table is a known problem and will be improved in the final release. ll i := 0 p := i = j x := i + 1 k := log2(i+j) F := log2 | (* see Formal parameters *) s := {2, 3, 5, 7, 11, 13} a[i] := (x+y) * (x-y) t.key := i w[i+1].name := "John" t := c ═══ 16.9.2. Procedure calls ═══ A procedure call activates a procedure. It may contain a list of actual parameters which replace the corresponding formal parameters defined in the procedure declaration (see section Procedure types). The correspondence is established by the positions of the parameters in the actual and formal parameter lists. There are two kinds of parameters: variable and value parameters. If a formal parameter is a variable parameter, the corresponding actual parameter must be a designator denoting a variable. If it denotes an element of a structured variable, the component selectors are evaluated when the formal/actual parameter substitution takes place, i.e. before the execution of the procedure. If a formal parameter is a value parameter, the corresponding actual parameter must be an expression. This expression is evaluated before the procedure activation, and the resulting value is assigned to the formal parameter (see also Formal parameters). ProcedureCall = Designator [ActualParameters]. Examples: The appearance of the following table is a known problem and will be improved in the final release. ll WriteInt(i*2+1) | (* see Formal parameters *) INC(w[k].count) t.Insert("John") | (* see Modules *) ═══ 16.9.3. Statement sequences ═══ Statement sequences denote the sequence of actions specified by the component statements which are separated by semicolons. StatementSequence = Statement {";" Statement}. ═══ 16.9.4. If statements ═══ IfStatement = IF Expression THEN StatementSequence { ELSIF Expression THEN StatementSequence } [ ELSE StatementSequence ] END. If statements specify the conditional execution of guarded statement sequences. The Boolean expression preceding a statement sequence is called its guard. The guards are evaluated in sequence of occurrence, until one evaluates to TRUE, whereafter its associated statement sequence is executed. If no guard is satisfied, the statement sequence following the symbol ELSE is executed, if there is one. Example: IF (ch >= "A") & (ch <= "Z") THEN ReadIdentifier ELSIF (ch >= "0") & (ch <= "9") THEN ReadNumber ELSIF (ch = " ' ") OR (ch = '"') THEN ReadString ELSE SpecialCharacter END; ═══ 16.9.5. Case statements ═══ Case statements specify the selection and execution of a statement sequence according to the value of an expression. First the case expression is evaluated, then that statement sequence is executed whose case label list contains the obtained value. The case expression must either be of an integer type that includes the types of all case labels, or both the case expression and the case labels must be of type CHAR. Case labels are constants, and no value must occur more than once. If the value of the expression does not occur as a label of any case, the statement sequence following the symbol ELSE is selected, if there is one, otherwise the program is aborted. CaseStatement = CASE Expression OF Case {"|" Case} [ ELSE StatementSequence ] END. Case = [CaseLabelList ":" StatementSequence]. CaseLabelList = CaseLabels {"," CaseLabels}. CaseLabels = ConstExpression [ ".." ConstExpression]. Example: CASE ch OF "A" .. "Z": ReadIdentifier | "0" .. "9": ReadNumber | "'", '"' : ReadString ELSE SpecialCharacter END ═══ 16.9.6. While statements ═══ While statements specify the repeated execution of a statement sequence while the Boolean expression (its guard) yields TRUE. The guard is checked before every execution of the statement sequence. WhileStatement = WHILE Expression DO StatementSequence END. Examples: WHILE i > 0 DO i := i DIV 2; k := k + 1 END WHILE (t # NIL) & (t.key # i) DO t := t.left END ═══ 16.9.7. Repeat statements ═══ A repeat statement specifies the repeated execution of a statement sequence until a condition specified by a Boolean expression is satisfied. The statement sequence is executed at least once. RepeatStatement = REPEAT StatementSequence UNTIL Expression. ═══ 16.9.8. For statements ═══ A for statement specifies the repeated execution of a statement sequence for a fixed number of times while a progression of values is assigned to an integer variable called the control variable of the for statement. ForStatement = FOR ident":="Expression TO Expression [ BY ConstExpression ] DO StatementSequence END. The statement FOR v := beg TO end BY step DO statements END is equivalent to temp := end; v := beg; IF step > 0 THEN WHILE v <= temp DO statements; v := v + step END ELSE WHILE v >= temp DO statements; v := v + step END END; temp has the same type as v. step must be a non-zero constant expression. If step is not specified, it is assumed to be 1. Examples: FOR i := 0 TO 79 DO k := k + a[i] END FOR i := 79 TO 1 BY -1 DO a[i] := a[i-1] END ═══ 16.9.9. Loop statements ═══ A loop statement specifies the repeated execution of a statement sequence. It is terminated upon execution of an exit statement within that sequence (see Return and exit statements). LoopStatement = LOOP StatementSequence END. Example: LOOP ReadInt(i); IF i < 0 THEN EXIT END; WriteInt(i) END Loop statements are useful to express repetitions with several exit points or cases where the exit condition is in the middle of the repeated statement sequence. ═══ 16.9.10. Return and exit statements ═══ A return statement indicates the termination of a procedure. It is denoted by the symbol RETURN, followed by an expression if the procedure is a function procedure. The type of the expression must be assignment compatible (see Definition of terms) with the result type specified in the procedure heading (see section Procedure declarations). Function procedures require the presence of a return statement indicating the result value. In proper procedures, a return statement is implied by the end of the procedure body. Any explicit return statement therefore appears as an additional (probably exceptional) termination point. An exit statement is denoted by the symbol EXIT. It specifies termination of the enclosing loop statement and continuation with the statement following that loop statement. Exit statements are contextually, although not syntactically associated with the loop statement which contains them. ═══ 16.9.11. With statements ═══ With statements execute a statement sequence depending on the result of a type test and apply a type guard to every occurrence of the tested variable within this statement sequence. WithStatement = WITH Guard DO StatementSequence { "|" Guard DO StatementSequence } [ ELSE StatementSequence ] END. Guard = Qualident ":" Qualident. If v is a variable parameter of record type or a pointer variable, and if it is of a static type T0, the statement WITH v: T1 DO S1 |v: T2 DO S2 ELSE S3 END has the following meaning: if the dynamic type of v is T1, then the statement sequence S1 is executed where v is regarded as if it had the static type T1; else if the dynamic type of v is T2, then S2 is executed where v is regarded as if it had the static type T2; else S3 is executed. T1 and T2 must be extensions of T0. If no type test is satisfied and if an else clause is missing the program is aborted. Example: WITH t: CenterTree DO i := t.width; c := t.subnode END ═══ 16.10. Procedure declarations ═══ A procedure declaration consists of a procedure heading and a procedure body. The heading specifies the procedure identifier and the formal parameters. For type-bound procedures it also specifies the receiver parameter. The body contains declarations and statements. The procedure identifier is repeated at the end of the procedure declaration. There are two kinds of procedures: proper procedures and function procedures. The latter are activated by a function designator as a constituent of an expression and yield a result that is an operand of the expression. Proper procedures are activated by a procedure call. A procedure is a function procedure if its formal parameters specify a result type. The body of a function procedure must contain a return statement which defines its result. All constants, variables, types, and procedures declared within a procedure body are local to the procedure. Since procedures may be declared as local objects too, procedure declarations may be nested. The call of a procedure within its declaration implies recursive activation. Objects declared in the environment of the procedure are also visible in those parts of the procedure in which they are not concealed by a locally declared object with the same name. ProcedureDeclaration = ProcedureHeading ";" ProcedureBody ident ProcedureHeading = PROCEDURE [Receiver] IdentDef [FormalParameters]. ProcedureBody = DeclarationSequence [ BEGIN StatementSequence ] END. DeclarationSequence = { CONST { ConstantDeclaration";" } | TYPE { TypeDeclaration ";" } | VAR { VariableDeclaration ";" } } | { ProcedureDeclaration ";" | ForwardDeclaration ";" }. ForwardDeclaration = PROCEDURE "^"[Receiver] IdentDef [FormalParameters]. If a procedure declaration specifies a receiver parameter, the procedure is considered to be bound to a type (see Type-bound procedures). A forward declaration serves to allow forward references to a procedure whose actual declaration appears later in the text. The formal parameter lists of the forward declaration and the actual declaration must match (see Definition of terms). ═══ 16.10.1. Formal parameters ═══ Formal parameters are identifiers declared in the formal parameter list of a procedure. They correspond to actual parameters specified in the procedure call. The correspondence between formal and actual parameters is established when the procedure is called. There are two kinds of parameters, value and variable parameters, indicated in the formal parameter list by the absence or presence of the keyword VAR. Value parameters are local variables to which the value of the corresponding actual parameter is assigned as an initial value. Variable parameters correspond to actual parameters that are variables, and they stand for these variables. The scope of a formal parameter extends from its declaration to the end of the procedure block in which it is declared. A function procedure without parameters must have an empty parameter list. It must be called by a function designator whose actual parameter list is empty too. The result type of a procedure can be neither a record nor an array. FormalParameters = "(" [FPSection {";"FPSection}] ")" [ ":" Qualident ]. FPSection = [VAR] ident {"," ident} ":" Type. Let Tf be the type of a formal parameter f (not an open array) and Ta the type of the corresponding actual parameter a. For variable parameters, Ta must be the same as Tf, or Tf must be a record type and Ta an extension of Tf. For value parameters, a must be assignment compatible with f (see Definition of terms). If Tf is an open array, then a must be array compatible with f (see Definition of terms). The lengths of f are taken from a. Examples of procedure declarations: PROCEDURE ReadInt(VAR x: INTEGER); VAR i: INTEGER; ch: CHAR; BEGIN i := 0; Read(ch); WHILE ("0" <= ch) & (ch >= "9") DO i:= 10*i + (ORD(ch)-ORD("0")); Read(ch) END; x := i; END ReadInt PROCEDURE WriteInt(x: INTEGER); (* 0 <= x <= 100000 *) VAR i: INTEGER; buf: ARRAY 5 OF INTEGER; BEGIN i := 0; REPEAT buf[i] := x MOD 10; x := x DIV 10; INC(i) UNTIL x = 0; REPEAT DEC(i); Write(CHR(buf[i] + ORD("0"))) UNTIL i = 0; END WriteInt PROCEDURE WriteString(s: ARRAY OF CHAR); VAR i: INTEGER; BEGIN i := 0; WHILE (i < LEN(s)) & (s[i] # 0X) DO Write(s[i]); INC(i) END END WriteString PROCEDURE log2(x: INTEGER): INTEGER; VAR y: INTEGER; (* assume x>0 *) BEGIN y := 0; WHILE x > 1 DO x := x DIV 2; INC(y) END; RETURN y END log2 ═══ 16.10.2. Type-bound procedures ═══ Globally declared procedures may be associated with a record type declared in the same module. The procedures are said to be bound to the record type. The binding is expressed by the type of the receiver in the heading of a procedure declaration. The receiver may be either a variable parameter of record type T or a value parameter of type POINTER TO T (where T is a record type). The procedure is bound to the type T and is considered local to it. ProcedureHeading = PROCEDURE [Receiver] IdentDef [FormalParameters]. Receiver = "(" [VAR] ident ":" ident ")". If a procedure P is bound to a type T0, it is implicitly also bound to any type T1 which is an extension of T0. However, a procedure P' (with the same name as P) may be explicitly bound to T1 in which case it overrides the binding of P. P' is considered a redefinition of P for T1. The formal parameters of P and P' must match (see Definition of terms). If P and T1 are exported (see section Declarations and scope rules) P' must be exported too. If v is a designator and P is a type-bound procedure, then v.P denotes that procedure P which is bound to the dynamic type of v (dynamic binding). Note, that this may be a different procedure than the one bound to the static type of v. v is passed to P's receiver according to the parameter passing rules specified in section Formal parameters. If r is a receiver parameter declared with type T, r.P'136 denotes the (redefined) procedure P bound to the base type of T. In a forward declaration of a type-bound procedure the receiver parameter must be of the same type as in the actual procedure declaration. The formal parameter lists of both declarations must match (Definition of terms). Examples: PROCEDURE (t: Tree) Insert (node: Tree); VAR p, father: Tree; BEGIN p := t; REPEAT father := p; IF node.key = p.key THEN RETURN END; IF node.key < p.key THEN p := p.left ELSE p := p.right END UNTIL p = NIL; IF node.key < father.key THEN father.left := node ELSE father.right := node END; node.left := NIL; node.right := NIL END Insert; PROCEDURE (t: CenterTree) Insert (node: Tree); (*redefinition*) BEGIN WriteInt(node(CenterTree).width); t.Insert^(node) (* calls the Insert procedure bound to Tree *) END Insert; ═══ 16.10.3. Predeclared procedures ═══ The following table lists the predeclared procedures. Some are generic procedures, i.e. they apply to several types of operands. v stands for a variable, x and n for expressions, and T for a type. Function procedures ENTIER(x) LONGREAL =2.8cm =4.4cm The appearance of the following table is a known problem and will be improved in the final release. pppp Name | Argument type | Result type | Function | ABS(x) | numeric type | type of x | absolute value ASH(x,n) | x,n: integer type | LONGINT | arithmetic shift (x*2n) The appearance of the following table is a known problem and will be improved in the final release. pppp CAP(x) | CHAR | CHAR | x is letter: corresponding capital letter CHR(x) | integer type | CHAR | character with ordinal number x ENTIER(x)| real type | LONGINT | largest integer not greater than x LEN(v,n) | v: array; n: integer const. | LONGINT | length of v in dimension n (first dimension = 0) The appearance of the following table is a known problem and will be improved in the final release. pppp LEN(v) | v: array | LONGINT | the same as LEN(v,0) LONG(x) | SHORTINT | INTEGER | identity | INTEGER | LONGINT | REAL | LONGREAL The appearance of the following table is a known problem and will be improved in the final release. pppp MAX(T) | T = basic type | T | maximum value of type T | T = SET | INTEGER | maximum element of a set MIN(T) | T = basic type | T | minimum value of type T | T = SET | INTEGER | 0 ODD(x) | integer type | BOOLEAN | x MOD 2 = 1 ORD(x) | CHAR | INTEGER | ordinal number of x The appearance of the following table is a known problem and will be improved in the final release. pppp SHORT(x) | LONGINT | INTEGER | identity | INTEGER | SHORTINT | identity | LONGREAL | REAL | identity (truncation possible) SIZE(T) | any type | integer | number of bytes required by T Proper procedures NEW(v,x0,...,xn) =4.4cm =4.2cm The appearance of the following table is a known problem and will be improved in the final release. ppp Name | Argument types | Function | ASSERT(x) | x: Boolean expression | terminate program execution if not x ASSERT(x,n) | x: Boolean expression; n: integer constant | terminate program execution if not x The appearance of the following table is a known problem and will be improved in the final release. ppp COPY(x,v) | x: character array, string; v: character array | v := x DEC(v) | integer type | v := v - 1 DEC(v,n) | v, n: integer type | v := v - n The appearance of the following table is a known problem and will be improved in the final release. ppp EXCL(v,x) | v: SET; x: integer type | v := v - x HALT(n) | integer constant | terminate program execution The appearance of the following table is a known problem and will be improved in the final release. ppp INC(v) | integer type | v := v + 1 INC(v,n) | v, n: integer type | v := v + n INCL(v,x) | v: SET; x: integer type | v := v + x The appearance of the following table is a known problem and will be improved in the final release. ppp NEW(v) | pointer to record or fixed array | allocate v'136 NEW(v,x0,...,xn) | v: pointer to open array; xi: integer type | allocate v'136 with lengths x0.. xn COPY allows the assignment of a string or a character array containing a terminating 0X to another character array. If necessary, the assigned value is truncated to the target length minus one. The target will always contain 0X as a terminator. In ASSERT(x,n) and HALT(n), the interpretation of n is left to the underlying system implementation. ═══ 16.11. Modules ═══ A module is a collection of declarations of constants, types, variables, and procedures, together with a sequence of statements for the purpose of assigning initial values to the variables. A module constitutes a text that is compilable as a unit. Module = MODULE ident ";" [ImportList] DeclarationSequence [ BEGIN StatementSequence ] END ident ".". ImportList = IMPORT Import {"," Import} ";". Import = [ident ":="] ident. The import list specifies the names of the imported modules. If a module A is imported by a module M and A exports an identifier x, then x is referred to as A.x within M. If A is imported as B := A, the object x is referenced as B.x. This allows short alias names in qualified identifiers. A module must not import itself. Identifiers that are to be exported (i.e. that are to be visible in client modules) must be marked by an export mark in their declaration (see section Declarations and scope rules). The statement sequence following the symbol BEGIN is executed when the module is added to a system (loaded), which is done after the imported modules have been loaded. It follows that cyclic import of modules is illegal. Individual (parameterless and exported) procedures can be activated from the system, and these procedures serve as commands. MODULE Trees; (* exports: Tree, Node, Insert, Search, Write, NewTree *) (* exports read-only: Node.name *) IMPORT Texts, Oberon; TYPE Tree* = POINTER TO Node; Node* = RECORD name-: POINTER TO ARRAY OF CHAR; left, right: Tree END; VAR w: Texts.Writer; PROCEDURE (t: Tree) Insert* (name: ARRAY OF CHAR); VAR p, father: Tree; BEGIN p := t; REPEAT father := p; IF name = p.name^ THEN RETURN END; IF name < p.name^ THEN p := p.left ELSE p := p.right END UNTIL p = NIL; NEW(p); p.left := NIL; p.right := NIL; NEW(p.name,LEN(name)+1); COPY(name,p.name^); IF name < father.name^ THEN father.left := p ELSE father.right := p END; END Insert; PROCEDURE (t: Tree) Search* (name: ARRAY OF CHAR): Tree; VAR p: Tree; BEGIN p := t; WHILE (p # NIL) & (name # p.name^) DO IF name = p.name^ THEN p := p.left ELSE p := p.right END END; RETURN p END Search; PROCEDURE (t: Tree) Write*; BEGIN IF t.left # NIL THEN t.left.Write END; Texts.WriteString(w, t.name^); Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf); IF t.right # NIL THEN t.right.Write END END Write; PROCEDURE NewTree* (): Tree; VAR t: Tree; BEGIN NEW(t); NEW(t.name, 1); t.name[0] := 0X; t.left := NIL; t.right := NIL; RETURN t END NewTree; BEGIN Texts.OpenWriter(w) END Trees. ═══ 16.12. Definition of terms ═══ The appearance of the following table is a known problem and will be improved in the final release. Integer typeshahaha Integer types SHORTINT, INTEGER, LONGINT Real types REAL, LONGREAL Numeric types integer types, real types Same types Two variables a and b with types Ta and Tb are of the same type if 1. Ta and Tb are both denoted by the same type identifier, or 2. Ta is declared to equal Tb in a type declaration of the form Ta = Tb, or 3. a and b appear in the same identifier list in a variable, record field, or formal parameter declaration and are not open arrays. Equal types Two types Ta and Tb are equal if 1. Ta and Tb are the same type, or 2. Ta and Tb are open array types with equal element types, or 3. Ta and Tb are procedure types whose formal parameter lists match. Type inclusion Numeric types include (the values of) smaller numeric types according to the following hierarchy LONGREAL ?REAL ?LONGINT ?INTEGER ?SHORTINT Type extension (base type) Given a type declaration Tb = RECORD (Ta)... END, Tb is a direct extension of Ta, and Ta is a direct base type of Tb. A type Tb is an extension of a type Ta (Ta is a base type of Tb) if 1. Ta and Tb are the same types, or 2. Tb is a direct extension of an extension of Ta If Pa = POINTER TO Ta and Pb = POINTER TO Tb, Pb is an extension of Pa (Pa is a base type of Pb) if Tb is an extension of Ta. Assignment compatible An expression e of type Te is assignment compatible with a variable v of type Tv if one of the following conditions hold: 1. Te and Tv are the same type; 2. Te and Tv are numeric types and Tv includes Te; 3. Te and Tv are record types and Te is an extension of Tv and the dynamic type of v is Tv ; 4. Te and Tv are pointer types and Te is an extension of Tv; 5. Tv is a pointer or a procedure type and e is NIL; 6. Tv is ARRAY n OF CHAR, e is a string constant with m characters, and m < n; 7. Tv is a procedure type and e is the name of a procedure whose formal parameters match those of Tv. Array compatible An actual parameter a of type Ta is array compatible with a formal parameter f of type Tf if 1. Tf and Ta are the same type, or 2. Tf is an open array, Ta is any array, and their element types are array compatible, or 3. Tf is ARRAY OF CHAR and a is a string. Expression compatible For a given operator, the types of its operands are expression compatible if they conform to the following table (which shows also the result type of the expression). Character arrays to be compared must contain 0X as a terminator. Type T1 must be an extension of type T0: compact = = < <= > >= =2.7cm =3.7cm The appearance of the following table is a known problem and will be improved in the final release. pppp operator | first operand | second operand | result type + - * | numeric | numeric | smallest numeric type including both operands / | numeric | numeric | smallest real type including both operands + - * / | SET | SET | SET The appearance of the following table is a known problem and will be improved in the final release. pppp DIV MOD | integer | integer | smallest integer type including both operands OR & ~ | BOOLEAN | BOOLEAN | BOOLEAN The appearance of the following table is a known problem and will be improved in the final release. pppp = # < <= > >= | numeric | numeric | BOOLEAN | CHAR | CHAR | BOOLEAN | character array, string | character array, string | BOOLEAN = # | BOOLEAN | BOOLEAN | BOOLEAN | SET | SET | BOOLEAN | NIL, POINTER TO T0 or T1 | NIL, POINTER TO T0 or T1 | BOOLEAN | procedure type T, NIL | procedure type T, NIL | BOOLEAN IN | integer | SET | BOOLEAN IS | pointer | pointer | BOOLEAN | record | record | BOOLEAN ═══ 16.12.1. Matching formal parameter lists ═══ Two formal parameter lists match if 1. they have the same number of parameters, and 2. they have either the same function result type or none, and 3. parameters at corresponding positions have equal types, and 4. parameters at corresponding positions are both either value or variable parameters. ═══ 16.13. Syntax of Oberon-2 ═══ Module = MODULE ident ";" [ImportList] DeclSeq [BEGIN StatementSeq] END ident ".". ImportList = IMPORT [ident ":="] ident {"," [ident ":="] ident} ";". DeclSeq = { CONST {ConstDecl ";" } | TYPE {TypeDecl ";"} | VAR {VarDecl ";"}} {ProcDecl ";" | ForwardDecl ";"}. ConstDecl = IdentDef "=" ConstExpr. TypeDecl = IdentDef "=" Type. VarDecl = IdentList ":" Type. ProcDecl = PROCEDURE [Receiver] IdentDef [FormalPars] ";" DeclSeq [BEGIN StatementSeq] END ident. ForwardDecl = PROCEDURE "^" [Receiver] IdentDef [FormalPars]. FormalPars = "(" [FPSection {";" FPSection}] ")" [":" Qualident]. FPSection = [VAR] ident {"," ident} ":" Type. Receiver = "(" [VAR] ident ":" ident ")". Type = Qualident | ARRAY [ConstExpr {"," ConstExpr}] OF Type | RECORD ["("Qualident")"] FieldList {";" FieldList} END | POINTER TO Type | PROCEDURE [FormalPars] FieldList = [IdentList ":" Type]. StatementSeq= Statement {";" Statement}. Statement = [Designator ":=" Expr | Designator ["(" [ExprList] ")"] | IF Expr THEN StatementSeq {ELSIF Expr THEN StatementSeq} [ELSE StatementSeq] END | CASE Expr OF Case {"|" Case} [ELSE StatementSeq] END | WHILE Expr DO StatementSeq END | REPEAT StatementSeq UNTIL Expr | FOR ident ":=" Expr TO Expr [BY ConstExpr] DO StatementSeq END | LOOP StatementSeq END | WITH Guard DO StatementSeq {"|" Guard DO StatementSeq} [ELSE StatementSeq] END | EXIT | RETURN [Expr]]. Case = [CaseLabels {"," CaseLabels} ":" StatementSeq]. CaseLabels = ConstExpr [".." ConstExpr]. Guard = Qualident ":" Qualident. ConstExpr = Expr. Expr = SimpleExpr [Relation SimpleExpr]. SimpleExpr = ["+" | "-"] Term {AddOp Term}. Term = Factor {MulOp Factor}. Factor = Designator ["(" [ExprList] ")"] | number | character | string | NIL | Set | "(" Expr ")" | " ~ " Factor. Set = "{" [Element {"," Element}] "}". Element = Expr [".." Expr]. Relation = "=" | "#" | "<" | "<=" | ">" | ">=" | IN | IS. AddOp = "+" | "-" | OR. MulOp = " * " | "/" | DIV | MOD | "&". Designator = Qualident {"." ident | "[" ExprList "]" | "^" | "(" Qualident ")"}. ExprList = Expr {"," Expr}. IdentList = IdentDef {"," IdentDef}. Qualident = [ident "."] ident. IdentDef = ident [" * " | "-"]. ═══ 16.14. The module SYSTEM ═══ The module SYSTEM contains certain types and procedures that are necessary to implement low-level operations particular to a given computer and/or implementation. These include for example facilities for accessing devices that are controlled by the computer, and facilities to break the type compatibility rules otherwise imposed by the language definition. It is strongly recommended to restrict their use to specific modules (called low-level modules). Such modules are inherently non-portable, but easily recognized due to the identifier SYSTEM appearing in their import list. The following specifications hold for the implementation of Oberon-2 on the Ceres computer. Module SYSTEM exports a type BYTE with the following characteristics: Variables of type CHAR or SHORTINT can be assigned to variables of type BYTE. If a formal variable parameter is of type ARRAY OF BYTE then the corresponding actual parameter may be of any type. Another type exported by module SYSTEM is the type PTR. Variables of any pointer type may be assigned to variables of type PTR. If a formal variable parameter is of type PTR, the actual parameter may be of any pointer type. The procedures contained in module SYSTEM are listed in the following tables. Most of them correspond to single instructions compiled as in-line code. For details, the reader is referred to the processor manual. v stands for a variable, x, y, a, and n for expressions, and T for a type. Function procedures ROT(w,w) BOOLEAN =2.8cm =4.4cm The appearance of the following table is a known problem and will be improved in the final release. pppp Name | Argument types | Result type | Function | ADR(v) | any | LONGINT | address of variable v BIT(a,n) | a: LONGINT n: integer | BOOLEAN | bit n of Mem[a] CC(n) | integer constant | BOOLEAN | condition n (0?n?15) The appearance of the following table is a known problem and will be improved in the final release. pppp LSH(x,n) | x: integer, CHAR, BYTE; n: integer | type of x | logical shift ROT(x,n) | x: integer, CHAR, BYTE; n: integer | type of x | rotation VAL(T,x) | T, x: any type | T | x interpreted as of type T Proper procedures MOVE(a0,a1,n) =4.4cm =4.2cm The appearance of the following table is a known problem and will be improved in the final release. ppp Name | Argument types | Function | GET(a,v) | a: LONGINT; v: any basic type, pointer, procedure type | v := Mem[a] PUT(a,x) | a: LONGINT; x: any basic type, pointer, procedure type | Mem[a] :=x The appearance of the following table is a known problem and will be improved in the final release. ppp GETREG(n,v) | n: integer constant; v: any basic type, pointer, procedure type | v := Register n PUTREG(n,x) | n: integer constant; x: any basic type, pointer, procedure type | Register n := x The appearance of the following table is a known problem and will be improved in the final release. ppp MOVE(a0,a1,n) | a0, a1: LONGINT; n: integer | Mem[a1..a1+n-1] := Mem[a0..a0+n-1] NEW(v,n) | v: any pointer; n: integer | allocate storage block of n bytes assign its address to v