|
|
The
Bartels User Language Compiler contains a preprocessor capable of processing special preprocessor statements. Preprocessor statements must start with a hash
( 2.6.1 File Inclusion
Bartels User Language provides the
#include "filename" EOLN where
When including source code files one should consider, that not all of the therein contained definitions are really needed by a program; thus, it is recommended to run the User Language Compiler with the optimizer to eliminate redundant parts of the program. The following example shows the include file
baecall.ulh, which contains the function
// baecall.ulh -- BAE menu call facilities void call(int menuitem) // Call a BAE menu function { // Perform the BAE menu call if (bae_callmenu(menuitem)) { // Error; print error message and exit from program perror("BAE menu call fault!"); exit(-1); } } The following example shows the source code of the
zoomall program for executing the
AutoEngineer
command; the program utilizes the
// ZOOMALL -- Call the BAE Zoom All command #include "baecall.ulh" // BAE menu call include main() { // Call Zoom All call(101); } 2.6.2 Constant DefinitionA preprocessor statement of the form #define IDENT constexpr EOLN causes the Compiler to replace subsequent instances of the
IDENT identifier with the value of the given constant expression (constexpr). This statement is terminated by end-of-line. The features introduced by the
A constant definition introduced by
#undef IDENT EOLN A special
#define IDENT EOLN where just a name is defined. The existence or no-existence of such a name definition can be checked with the
The
#define DDBCLASSLAYOUT 100 #define mmtoinch 25.4 #define inchtomm 1.0/mmtoinch #define REPABORT "Operation aborted." #define ERRCLASS "Operation not allowed for this element!" #define GERMAN 1 #define ENGLISH 0 #define LANGUAGE GERMAN #define DEBUG 2.6.3 Conditional CompilationA preprocessor statement of the form #if constexpr EOLN causes the Compiler to check, whether the specified constant expression is nonzero. A preprocessor statement of the form #ifdef IDENT EOLN causes the Compiler to check, whether the name specified with the identifier is defined (through a
#ifndef IDENT EOLN causes the Compiler to check, whether the name specified with the identifier is undefined. The source code lines following to
An
#else EOLN A preprocessor statement of the form #endif EOLN terminates the if construct. If the checked condition is true then the source code lines between
The following example illustrates the features of conditional compilation: #define ENGLISH 0 #define GERMAN 1 #define LANGUAGE ENGLISH #if LANGUAGE == GERMAN #define MSGSTART "Programm gestartet." #endif #if LANGUAGE == ENGLISH #define MSGSTART "Program started." #endif #define DEBUG main() { #ifdef DEBUG perror(MSGSTART); #endif : } 2.6.4 BNF PrecompilerA BNF precompiler is integrated to the Bartels User Language. This BNF precompiler with its corresponding scanner and parser functions can be utilized to implement programs for processing almost any foreign ASCII file data format. Each User Language program text can contain up to one preprocessor statement of the form #bnf { ... } which can cover an arbitrary number of source code lines. The
The language's vocabulary is determined by the terminal symbols specified with the BNF definition. The keywords
Keywords can be specified as in "SECTION" 'ENDSEC' 'Inch' 'begin' "end" 'include' Double character operators can be specified as in '<=' '++' "&&" "+=" '::' ':=' Single character operators can be specified as in '+' '-' "*" "/" "=" "[" "}" '#' '.' Spacings (blanks, tabulator, newline) are not significant for defining a grammar; they serve just for separating adjacent symbols. Comments belong to the spacing token class as well. For comment type definitions, it is necessary to define comment delimiter operators. On default, the BNF Precompiler assigns the operators
COMMENT ("/*", "*/") ; Omitting the second parameter from the
COMMENT ('//'); configures comments which extend to the end of the line. Please note that the
COMMENT ('/*', '*/'); COMMENT ('//'); A special feature of the
Bartels User Language BNF Precompiler is the possibility of defining an action for each grammar term and/or input symbol of a formulation. An action is specified by appending the (parentheses-enclosed) name of a user function to the symbol. The parser automatically calls the referenced user function upon recognition of the corresponding input symbol. These user functions must be properly defined with data type
See the Bartels User Language syntax description in chapter 2.7 for a detailed description of the BNF precompiler syntax definition. The BNF Precompiler compiles the BNF definition to User Language machine code, which represents a state machine for processing text of the defined language. This state machine can be activated with either of the Bartels User Language system functions synparsefile and/or synparsestring. synparsefile activates a parser for processing a file name specified input file, whilst synparsestring can be used to process strings rather than file contents; the referenced parser action functions are automatically called as required. The synscanline and synscanstring system functions can be utilized in these parser action functions for querying the current input scan line number and the input scan string. The current scan string can be subjected to semantic tests. The synparsefile and/or synparsestring functions are terminated if the end of the input file and/or the input string terminator is reached or if a syntax error (or a semantic error encountered by a parser action function) has occurred. For completeness reasons the system functions
synscnaeoln,
synscanigncase and
synparseincfile are still to be mentioned. The
synscaneoln scan function is used to enable and/or disable the BNF parser's end-of-line recognition, which is disabled on default. The EOLN terminal symbol of a BNF definition can be recognized only if the EOLN recognition is activated with the
synscaneoln function. The
synscanigncase scan function is used to enable and/or disable the BNF parser's case-sensitivity when scanning keyords. The
synparseincfile function can be utilized for processing include files. The parser starts reading at the beginning of the name-specified include file when calling the
synparseincfile function. An
See appendix C for a detailed description of the Bartels User Language BNF scanner and parser system functions. The usage of the BNF Precompiler is illustrated by the following User Language example program. The purpose of this program is to read part placement data from an ASCII file and to perform the corresponding placement on the layout currently loaded to the Bartels AutoEngineer Layout Editor. The input placement data is supposed to be organized according to the following example: // This is a comment @ LAYOUT # This is a comment extending to the end of line UNITS { LENGTH = ( 1.0 INCH ) ; ANGLE = ( 1.0 DEGREE ) ; } PLACEMENT { 'ic1' : 'dil16' { POSITION = (0.000,0.000) ; ROTATION = 0.000 ; MIRROR = 0 ; } 'ic2' : 'dil16' { POSITION = (2.250,0.100) ; } 'ic3' : 'dil16' { POSITION = (1.000,0.394) ; ROTATION = 23.500 ; } 'ic4' : 'so16' { POSITION = (0.000,0.700) ; ROTATION = 0.000 ; MIRROR = 1 ; } } END The following listing shows a program which utilizes the BNF Precompiler and the corresponding scanner/parser functions in order to load placement data from external files according to the example above: // READLPLC -- Read Layout Placement from ASCII File //_________________________________________________________ // BNF input syntax definition #bnf { COMMENT ("//", "@") ; COMMENT ("#") ; placefile : "LAYOUT" placeunits placedata "END" ; placeunits : "UNITS" "{" "LENGTH" "=" "(" floatnum placelengthunit ")" ";" "ANGLE" "=" "(" floatnum placeangleunit ")" ";" "}" ; placelengthunit : "INCH" (p_unitl(1)) | "MM" (p_unitl(2)) | "MIL" (p_unitl(3)) ; placeangleunit : "DEGREE" (p_unita(1)) | "RAD" (p_unita(2)) ; placedata : "PLACEMENT" "{" placecommands "}" ; placecommands : placecommands placecommand | ; placecommand : identifier (p_pname) ":" identifier (p_plname) "{" placepos placerot placemirror "}" (p_storepart) ; placepos : "POSITION" "=" "(" floatnum (p_px) "," floatnum (p_py) ")" ";" ; placerot : "ROTATION" "=" floatnum (p_pa) ";" | ; placemirror : "MIRROR" "=" NUMBER (p_pm) ";" | ; identifier : SQSTR (p_ident) ; floatnum : NUMBER (p_fltnum(0)) | "-" NUMBER (p_fltnum(1)) ; } //______________________________________________________________ // Globals double plannx=bae_planwsnx(); // Element origin X coordinate double planny=bae_planwsny(); // Element origin Y coordinate double lenconv; // Length conversion factor double angconv; // Angle conversion factor string curpn; // Current part name string curpln; // Current physical library name double curx,cury; // Current coordinates double cura = 0.0; // Current angle (default: 0.0) int curm = 0; // Current mirror flag (default: 0) string curid; // Current identifier double curflt; // Current float value struct partdes { // Part descriptor string pn; // Part name string pln; // Physical library name double x,y; // Coordinates double a; // Angle int m; // Mirror flag } pl[]; // Part list int pn=0; // Part count //______________________________________________________________ // Main program main() { string fname; // Input file name // Test if layout loaded if (bae_planddbclass()!=100) errormsg("Command not allowed for this element!",""); // Get and test the placement file name if ((fname=askstr("Placement File : ",40))=="") errormsg("Operation aborted.",""); // Parse the placement file perror("Reading placement data..."); parseerr(synparsefile(fname),fname); // Perform the placement placement(); // Done perror("Operation completed without errors."); } //______________________________________________________________ // Part list management and placement void gcpart() // Get or create some part list entry { index L_CPART cpart; // Part index index L_NREF nref; // Named reference index int slb=0; // Search lower boundary int sub=pn-1; // Search upper boundary int idx; // Search index int compres; // Compare result // Loop until search area empty while (slb<=sub) { // Get the search index idx=(slb+sub)>>1; // Get and test the compare result if ((compres=strcmp(curpn,pl[idx].pn))==0) errormsg("Multiple defined part '%s'!",curpn); // Update the search area if (compres<0) sub=idx-1; else slb=idx+1; } // Check if part is placed already forall (nref where curpn==nref.NAME) // Part already placed; abort return; // Check net list consistence forall (cpart where curpn==cpart.NAME) { // Check the plname if (curpln!=cpart.PLNAME) // Netlist definition mismatch errormsg("Wrong part macro name '%s'!",curpln); // Done break; } // Insert the new entry to the part list pn++; for (idx=pn-2;idx>=slb;idx--) pl[idx+1]=pl[idx]; pl[slb].pn=curpn; pl[slb].pln=curpln; pl[slb].x=curx; pl[slb].y=cury; pl[slb].a=cura; pl[slb].m=curm; } void placement() // Perform the placement { int i; // Loop control variable // Iterate part list for (i=0;i<pn;i++) { // Place the part if (ged_storepart(pl[i].pn,pl[i].pln, pl[i].x,pl[i].y,pl[i].a,pl[i].m)) errormsg("Error placing part '%s'!",pl[i].pn); } } //______________________________________________________________ // Error handling void parseerr(status,fn) // Handle a syntax/parser error int status; // Scan status string fn; // File name { string msg; // Error message // Evaluate the scan status switch (status) { case 0 : // No error return; case 1 : msg="No BNF definition available!"; break; case 2 : msg="Parser already active!"; break; case 3 : sprintf(msg," Error opening file '%s'!",fn); break; case 4 : msg="Too many open files!"; break; case 5 : sprintf(msg,"[%s/%d] Fatal read/write error!", fn,synscanline()); break; case 6 : sprintf(msg,"[%s/%d] Scan item '%s' too long!", fn,synscanline(),synscanstring()); break; case 7 : sprintf(msg,"[%s/%d] Syntax error at '%s'!", fn,synscanline(),synscanstring()); break; case 8 : sprintf(msg,"[%s/%d] Unexpected end of file!", fn,synscanline()); break; case 9 : sprintf(msg,"[%s/%d] Stack overflow (BNF too complex)!", fn,synscanline()); break; case 10 : sprintf(msg,"[%s/%d] Stack underflow (BNF erroneous)!", fn,synscanline()); break; case 11 : sprintf(msg,"[%s/%d] Error from parse action function!", fn,synscanline()); break; default : sprintf(msg,"Unknown parser error code %d!",status); break; } // Print the error message errormsg(msg,""); } void errormsg(string errfmt,string erritem) // Print an error message with error item and exit from program { string errmsg; // Error message string // Build and print the error message string sprintf(errmsg,errfmt,erritem); perror(errmsg); // Exit from program exit(-1); } //______________________________________________________________ // Parser action routines int p_unitl(code) // Handle the length units definition request // Returns : zero if done or (-1) on error { // Set the length conversion factor switch (code) { case 1 : lenconv=cvtlength(curflt,1,0); break; // Inch case 2 : lenconv=cvtlength(curflt,2,0); break; // mm case 3 : lenconv=cvtlength(curflt,3,0); break; // mil default : return(-1); // Error } // Return without errors return(0); } int p_unita(code) // Handle the angle units definition request // Returns : zero if done or (-1) on error { // Set the angle conversion factor switch (code) { case 1 : angconv=cvtangle(curflt,1,0); break; // Deg case 2 : angconv=cvtangle(curflt,2,0); break; // Rad default : return(-1); // Error } // Return without errors return(0); } int p_storepart() // Handle the store part request // Returns : zero if done or (-1) on error { // Get or create the part list entry gcpart(); // Re-init the current angle and mirror mode cura=0.0; curm=0; // Return without errors return(0); } int p_pname() // Receive a part name // Returns : zero if done or (-1) on error { // Store the current part name strlower(curpn=curid); // Return without errors return(0); } int p_plname() // Receive a physical library name // Returns : zero if done or (-1) on error { // Store the current physical library name strlower(curpln=curid); // Return without errors return(0); } int p_px() // Receive a part X coordinate // Returns : zero if done or (-1) on error { // Store the current part X coordinate curx=curflt*lenconv+plannx; // Return without errors return(0); } int p_py() // Receive a part Y coordinate // Returns : zero if done or (-1) on error { // Store the current part Y coordinate cury=curflt*lenconv+planny; // Return without errors return(0); } int p_pa() // Receive a part angle // Returns : zero if done or (-1) on error { // Store the current part angle cura=curflt*angconv; // Return without errors return(0); } int p_pm() // Receive a part mirror flag // Returns : zero if done or (-1) on error { // Get and store the current part mirror flag curm=atoi(synscanstring())==0?0:1; // Return without errors return(0); } int p_ident() // Receive an identifier // Returns : zero if done or (-1) on error { // Store the current string curid=synscanstring(); // Return without errors return(0); } int p_fltnum(negflag) // Receive a float value // Returns : zero if done or (-1) on error int negflag; // Negative number flag { // Get the current float value curflt=atof(synscanstring()); // Set negative on request if (negflag) curflt*=(-1); // Return without errors return(0); } //______________________________________________________________ // User Language program end 2.6.5 Program Caller Type and Undo MechanismProgram Caller Type SettingThe
The #pragma ULCALLERSTD preprocessor statement forces the compiled User Language program caller type setting to standard (STD). The User Language Compiler error is suppressed. User Language programs compiled with the above statement can be called in any Bartels User Language Interpreter environment, even if the program code contains system functions or index variable types which are not compatible to that Interpreter environment. This allows for the implementation of programs with conditionally executed environment-specific program code. It is up to the program design to prevent from calling incompatible system functions or accessing invalid index variable types; otherwise the Bartels User Language Interpreter quits the program with a runtime error message. Configuring Undo MechanismOn default, the execution of a User Language program adds an undo step in the BAE system. The #pragma ULCALLERNOUNDO can be used to prevent the system from adding an undo step for the execution of the compiled program. I.e., by declaring
Preprocessor Statements |
|