Psion 3a OPL Preprocessor OPP V1.4 User Guide Copyright Andy Clarkson 1995 1. INTRODUCTION OPP is an OPL preprocessor for the Psion 3a. If you are familiar with the operation of a C preprocessor then the function of OPP will be immediately clear. S3ATRAN provides similar facilities when translating OPL code on a PC. OPP also provides a number of extensions to the OPL language such as support for multi- dimensional arrays and C style structures. When using a preprocessor additional preprocessor commands are added in-line to the source file. These commands are read and executed by the preprocessor which strips the lines out prior to the actual compilation or translation of the source file. OPP looks for lines within the OPL source file which begin with a # character. These lines contain commands which are read and acted upon by the preprocessor. Any # lines are removed prior to dispatching the lines to the OPL translator. The standard OPL code is also scanned by the preprocessor which may substitute symbolic names for text defined using the # commands. The output from OPP is pure OPL source code. This resultant pre-processed OPL code is passed on to the OPL translator which does the actual job of creating an OPO or OPA file. The operation of the preprocessor can be seen with an example. In the Program editor the following code is written: #define ARRAY_SIZE 10 #define FN(x) (3*x**2+2*x+1) /* Here is the main procedure */ PROC main: local x%, y%(ARRAY_SIZE) #ifdef DEBUG /* print debug info if required */ print "In main" #endif while x%Install menu option on the main Psion System Screen. This will add the OPP and OPH Program editor icons to the system screen. These icons are similar to the standard OPL Program icon but have a trailing letter P and H instead of L on the icon. The OPP and OPH editors are aliases of the OPL Program editor. When writing OPP code which includes preprocessor directives use the OPP or OPH editor rather than the standard OPL editor. They operate in exactly the same way as the OPL Program application, with two main exceptions... Firstly the files which are displayed beneath the icon have a different extension. To distinguish between OPL source files containing preprocessor directives from plain OPL files the following directory and file extensions are recommended: Normal OPL files \OPL\ .OPL OPL containing OPP directives \OPP\ .OPP OPP include files \OPP\ .OPH OPP system include files \OPP\INCLUDE\ .OPH Files with an OPP extension in the OPP directory will appear beneath the OPP editor. Files with an OPH extension in the OPP directory will be listed beneath the OPH editor. The second main difference between the OPP and OPH editors and the OPL editor is that they run the OPL preprocessor rather than the OPL translator when translating, running and debugging OPP code. Once OPP has been installed, exactly the same OPL programming environment is available, i.e. OPP source code may be written, translated and run via an editor. When the translate menu option is selected from the OPP editor instead of activating the OPL translator directly this activates the OPL preprocessor. The preprocessor in turn starts up the OPL translator under its control. The preprocessor reads OPP code directly from the editor a line at a time, pre-processes each line and passes it to the OPL translator. Note that the OPL outliner is not available within the OPP and OPH editors due to the way Psion alias files work. It will probably not be possible to provide this facility since the required changes would need to be made to the software built into the ROM of the Psion. OPP requires a minimum of 52K of disk space for installation, more if the include files are also installed. When translating an OPP file additional run-time memory is required by the preprocessor. The amount of run-time memory used by the preprocessor is proportional to the number of macros defined. 3. USING OPP Once OPP has been installed the menu options "Translate", "S3 Translate" and "Run" may be used from the OPP program editor to translate and run OPP programs in exactly the same way as with the normal Program editor. When translating an OPP program the OPP screen will appear. This screen will show the progress of the translation along with any error and warning messages. (The #pragma directive discussed in a later section may be used to control the display of additional information during translation). If during translation the screen is switched to another Psion application then the shift-system key combination may be used to cycle through the active applications in order to get back to the OPP screen. The escape key may be used to abort the translation at any point when the OPP screen is visible. If any preprocessor errors are found during the preprocessing of the OPP code then control will return to the OPP editor. An error message will be displayed in the lower right corner and the cursor will be repositioned on the line where the error was detected. If an OPL translator error is discovered during translation this will be displayed on the OPP screen along with the associated pre- processed OPL line. A ^ character will indicate the position on the line where the error was detected. To continue press any key. The cursor in the OPP editor will then be repositioned on the line where the error was found. Note that with OPL errors the position of the cursor along the line may not necessarily indicate the exact character where the error was found. This is due to the fact that the cursor is placed on the relevant character in relation to the pre-processed line. If there are any macros before this point on the line the position may be incorrect. Running an application from the OPP editor will also activate the OPL preprocessor. This in turn will then run the requested program. If a run-time error is encountered then control will return to the OPP editor. At this point, if the program being run was translated from the source in the program editor, then OPP will be restarted. This is in order to determine exactly where the error occurred within the source file. During this "error decoding" phase the preprocessor will scan the source code for the line where the error was encountered. This line will be displayed on the OPP screen and the cursor repositioned in the OPP editor. The escape key may be used during the error decoding phase to abort the process. 4. SHAREWARE REGISTRATION OPP is shareware, which means that if you continue to use it you should register with the author. To encourage registration the preprocessor will pause for 30 seconds at the end of every translation until it is registered. To register OPP send a 10 UK pounds cheque to: Mr. A. Clarkson, 3 Ashmead Drive, Hardwick, Cambridgeshire. CB3 7XT. U.K. State the version of the program, which is 1.4, and where you obtained the copy. This will enable me to check that you are using the latest version. When you register you will receive instructions on how to remove the time delay at the end of a translation. If you have an email address quote this in order to receive the instructions via email more quickly. For users outside the UK the following methods of payment will be accepted: - 10 UK pounds money order or cheque. - 10 UK pounds in cash. - The equivalent of 12 UK pounds in local currency. (At the time of writing this is about 20 US dollars). - A non-UK pounds cheque to the equivalent of 14 UK pounds. (At the time of writing this is about 22 US dollars). The additional charge for the last two payment methods is to cover currency exchange charges. Members of Compuserve may register OPP electronically using the Compuserve SWREG facility. When logged on to Compuserve type GO SWREG and select the option to register shareware. When requested for the shareware ID use the value 6311 for OPP. The charge is $20 which will be added to your normal Compuserve account. When I receive notification from Compuserve of the registration I will email the registration details directly to you. OPP may be freely distributed and uploaded to BBS's provided all files in the package are included. For any further information I can be contacted via email: 100661.2440@compuserve.com For users with access to the WWW my home page contains the latest information about OPP, including an on-line version of this manual: http://ourworld.compuserve.com/homepages/andyc Note that this software is provided as is, without any warranty of any kind. The author shall not be liable for any loss of data or damage arising from the use of this software. 5. KEY FEATURES OPP has the following key features: - #define directive for simple macros, e.g. #define MAX_ARRAY 10 - #define directive for macro functions, e.g. #define FN(x) 3*x**2+2*x+1 - #undef to undefine macros - Various built-in macros such as: __LINE__, __FILE__, __DATE___, __TIME__, __PROC__, etc. - ANSI # string producer supported in macro functions (although the ! character is used rather than #). - ANSI ## token pasting supported in macro functions (although the !! characters are used rather than ##). - Conditional translation directives: #if, #ifdef, #ifndef, #else, #elif, #endif. - Include files, both system and standard, e.g. #include #include "myprocs.oph" - Supplied with lots of system include files (OS calls, etc.). - Line continuation character, e.g. menu "menu", \ "item1",%i \ "item2",%j - Multi-dimensional array support (standard OPL is limited to 1 dimensional arrays, OPP allows N-dimensional arrays). - C style structures and pointers to structures, e.g. STRUCT my_struct name$(10) value% next% ENDS PROC test local p% p%=alloc(SIZEOF(my_struct)) p%->name$ = "test" p%->value% = 1 p%->next% = 0 ENDP - A number of additional C style operators are supported, e.g. i%++ i%-- i%+=2 i%-=2 i%*=2 i%/=2 - C style comments, e.g. /* This is a comment */ global a% /* embedded comment */, b% /* A multi- line comment */ - Hooks for an OPL run-time debugger which is available separately (see #pragma debug). - Facility for creating library OPL source files containing useful procedures which are only included in the resultant OPO module if they are called. - Can check for calls to undefined procedures or procedures which are never explicitly called. - Integrates with the Psion Program editor application and the OPL translator. - Facility to abort translation at any point. 6. MACROS Macros are a convenient way of representing a sequence of commonly used characters with a symbolic name. Suppose for example there is a fixed size OPL array which has a size of 10. The size of the array may be referenced in a number of places throughout the OPL code and it is therefore useful to define a symbol or macro for the value 10. The symbolic name is then used throughout the code. This makes it more obvious what the value 10 represents in each particular case. Also, if ever the size of the array needs to be changed this only has to be done in one place rather than throughout the source code. 6.1 Defining simple macros The #define preprocessor directive is used to create a simple macro definition. For example the following line is added to the OPP source file: #define MAX_ARRAY 10 From this point onwards any occurrence of MAX_ARRAY in the OPP code will be replaced by 10 when the preprocessor is run. The # character must be the first none-space character on the line; it may be proceeded by any number of space or tab characters. In general there may be any number of space or tab characters throughout the line provided that there is at least one between the define and the macro name, and the macro name and its value. Macros may be defined from other macros, e.g. #define WIDTH 5 #define HEIGHT 10 #define SIZE (WIDTH*HEIGHT) In the above case when expanding the macro SIZE the value (5*10) would be substituted. The order is not important since macros are expanded when they are referenced rather than when they are defined, i.e. in theory the above could be written: #define SIZE (WIDTH*HEIGHT) #define WIDTH 5 #define HEIGHT 10 Note that macros which include operators are not automatically evaluated by OPP. Thus the macro SIZE is sent to the OPL translator as (5*10) not as 50. This can cause problems with the OPL translator. For example the following will not work: local array%(SIZE) The problem is that the OPL translator also does not evaluate any expressions when the code is being translated and hence expects an integer for the array size, not an expression. You can work around this problem using a built in OPP function macro (see next section). Following the ANSI standard, if a recursive macro is encountered it is not further expanded, e.g. #define ONE (TWO-1) #define TWO (ONE+1) This could lead to a recursive expansion, e.g. when expanding TWO it would lead to: TWO (ONE+1) ((TWO-1)+1) (((ONE+1)-1)+1) etc... The ANSI standard states that the expansion will terminate at the value ((TWO-1)+1). Although at first site the above recursive macro definition would appear to be pointless, it does have its uses, as demonstrated below: #define OPEN PRINT "DEBUG MESSAGE:opening file" :OPEN In the above case the following OPP code... OPEN "file.opd",a,a$,b$ ...would expand to... PRINT "DEBUG MESSAGE: opening file" :OPEN "file.opd",a,a$,b$ Note that the OPEN macro above could also be written as: #define OPEN \ PRINT "DEBUG MESSAGE:opening file" :\ OPEN A line continuation character is used in the above macro definition. If the "\" character is the very last character on a line OPP drops the "\" character and concatenates the line with the following line to produce one long line. Multiple spaces or tabs at the beginning of the line following the "\" are replaced with a single space or tab character. The total length of a line consisting of concatenated lines must not exceed 255 characters. The ":" character is required by the OPL translator in the above example to separate multiple statements on one line. The line continuation character may be used in long or multi- statement macro definitions (as above) or in normal OPL code, e.g. dchoice "Choice", "Value1,\ Value2,\ Value3" 6.2 Defining function macros Macro functions are an extension of the simple macros described in the previous section. They are analogous to OPL procedures in that they take arguments. For example: #define GT?(a,b) if (a<=b) then :\ print a,">",b,"failed" :\ endif With the above definition the following OPP code.... GT?(x%,0) ...would expand to... if (x%<=0) then :print x%,">",0,"failed" :endif To define a macro function make sure that the "(" follows immediately after the macro function name. This is followed by a comma separated list of function variables terminated by a ")". A macro function may have any number of arguments between 0 and 20 inclusive. Each occurrence of a function argument within the macro definition is replaced by the associated value when the macro is expanded. Like simple macros, a macro function definition may include other macros, for example: #define AX axreg% #define OSFLAGS osflags% #define OSFN(fn) OSFLAGS=os(fn,addr(AX)) #define OSSUB(fn,sub) AX=sub :OSFN(fn) Using some of the above macros the following code demonstrates how to access an OS call in the Psion ROM using a macro function (the full macro definitions appear in the supplied include files os_call.oph and os_gen.oph): #include #include PROC main: local OSREGS OSSUB(GenManager,GenGetRamSizeInParas) print -(AX/64);"K RAM" get ENDP There is a built in macro function called OPPEVAL() which takes one argument. When preprocessing a file the argument is evaluated by OPP, for example, going back to the example in the previous section: #define WIDTH 5 #define HEIGHT 10 #define SIZE (WIDTH*HEIGHT) local array%(OPPEVAL(SIZE)) The OPPEVAL() function is required in order to evaluate (5*10) since otherwise OPP would pass the line as follows to the OPL translator: local array%((5*10)) This would fail since the OPL translator can only handle simple numbers in an array dimension. The OPPEVAL() macro may also be used to evaluate an expression at translation time rather than at run time for performance and space reasons, for example: #define FLAGMASK OPPEVAL(FLAG1 OR FLAG2 OR FLAG3) If the OPPEVAL() function was not present in the above definition then for every occurrence of FLAGMASK within the OPL source a calculation would be required at run time. 6.3 Notes on macro expansion When pre-processing a line OPP looks for macro symbols delimited by certain characters. The complete set of characters is: ( , - < : SPACE ) = * > | TAB ; + / # ! START/END OF LINE The same delimiters are also used when OPP looks for argument variables within a macro function definition. When the preprocessor encounters any strings they are copied directly without pre-processing. Take the following macro definition... #define TEST 1 ...then the following examples demonstrate what will and will not be expanded: print (TEST+1) TEST expanded print (Test+1) Test not expanded - macros are case sensitive print (TEST%+1) TEST not expanded since % is not a delimiter print "TEST" TEST not expanded - any strings are not preprocessed 6.4 Undefining macros A macro definition may be removed using the #undef directive followed by the macro name, for example: #define DEBUG #define MOD(a,b) (a-(a/b)*b) #undef DEBUG #undef MOD 6.5 Built-in macros OPP includes a number of pre-defined built-in macros: Macro Purpose Example of value __FILE__ Name of file being "LOC::M:\OPP\TEST.OPP" translated __LINE__ Line number being 23 translated __DATE__ Date of translation "May 10 1995" __TIME__ Time of translation "23:53:06" __PROC__ Name of current procedure "main" OPP OPP version number $14F Psion Indicates translating for Psion XTran Set if translating for S3 on a S3a OsVersion Psion OS version number, $318F 3.18F= RomVersion Psion ROM version number, $320F 3.20F= PsuType Power supply type 0=old 3 MC, 1=MC, 2=Series 3, 3=Series 3a LcdType LCD type, 11=S3a 11 OPPEVAL() Evaluate expression OPPEVAL(SIZE+2) SIZEOF() See section on structures SIZEOF(my_struct) OFFSETOF() See section on structures OFFSETOF(my_struct,field) 6.6 ! special character The ANSI standard defines the # character to have a special meaning within a macro definition. Due to the fact that # is used within OPL, OPP looks for the character ! rather than #. Consider: #define ASSERT(expr) if not (expr) :\ print !expr,"failed" :\ endif The presence of the ! character before the macro function variable instructs OPP to quote the expression when expanded. Thus the following line... ASSERT(a%>0) ...would expand to... if not (a%>0) :print "a%>0","failed" :endif This mechanism is required since simply adding quotes around the variable would cause OPP to copy the string directly, i.e. if the definition was... #define ASSERT(expr) if not (expr) :\ print "expr","failed" :\ endif ...then the above line would expand to... if not (a%>0) :print "expr","failed" :endif 6.7 !! special characters ## is another ANSI standard token. Again, due to the use of # by OPL, the preprocessor uses the identifiers !! instead. Consider the following macro definition: #define M(name) module!!name!!%: The !! characters indicate a delimiter for a macro function argument. OPP notes the delimiter and then discards the !! characters from the expansion. Thus the following... M(a) ...would expand to... modulea%: Without the !! OPP would not detect the presence of the name argument since neither "e" nor % are normal delimiter characters.