Bartels :: Bartels AutoEngineer :: BAE Documentation :: User Language Programmer's Guide :: Language Description :: Preprocessor Statements |
Bartels User Language - Programmer's Guide2.6 Preprocessor Statements |
![]() |
The
Bartels User Language Compiler contains a preprocessor capable of processing special preprocessor statements. Preprocessor statements must start with a hash
(#
) and they are delimited by the end of the corresponding source code line. Preprocessor statements can appear anywhere and have effect which lasts (independent of scope) until the end of the source code program file.
Bartels User Language provides the
#include
statement as known from C. The formal syntax of the
#include
statement is
#include "filename" EOLN
where
filename
must be the name of a
User Language source code file. This statement is terminated by end-of-line. An
#include
statement causes the replacement of that statement by the entire contents of the specified
User Language source code file. Such inclusion files can contain general definitions frequently used for different programs. It is a good idea to use include files in order to reduce the expenditure of software maintenance.
#include
statements can be nested, unless they do not refer identical file names (data recursion).
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
call
for activating
AutoEngineer menu functions:
// 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
call
function from the included
baecall.ulh source file:
// ZOOMALL -- Call the BAE Zoom All command #include "baecall.ulh" // BAE menu call include main() { // Call Zoom All call(101); }
A 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
#define
statement are most valuable for definition of substantial constants.
A constant definition introduced by
#define
is valid to the end of the program text unless deleted by a preprocessor statement of the form
#undef IDENT EOLN
A special
#define
statement form is given by
#define IDENT EOLN
where just a name is defined. The existence or no-existence of such a name definition can be checked with the
#ifdef
and
#ifndef
preprocessor statements (see also
chapter 2.6.3).
The
#define
statement can be applied as in the following examples:
#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
A 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
#define
statement). A preprocessor statement such as
#ifndef IDENT EOLN
causes the Compiler to check, whether the name specified with the identifier is undefined.
The source code lines following to
#if
,
#ifdef
or
#ifndef
are only compiled if the checked condition is true. Otherwise, the the corresponding source code lines are merely checked for correct syntax.
An
#if
preprocessor statement can optionally be followed by a preprocessor statement of the form
#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
#else
and
#endif
are not compiled. If the checked condition is false then the source code lines between if-statement and
#else
(or
#endif
on lacking
#else
) are not be compiled. Such
#if
preprocessor constructs can nest.
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 : }
A 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
#bnf
statement activates the BNF Precompiler of the
Bartels User Language. BNF (Backus Naur Form) is a formalism for describing the syntax of a language. The BNF definition (enclosed with the braces of the
#bnf
statement) of a language consists of a sequence of rule defining the language grammar, i.e., the valid word and/or character sequences for building sentences in this language. A rule of the BNF notation consists of a grammar term (non-terminal symbol) and a sequence of one or more alternative formulations, which are assigned to the grammar term by the operator
:
(to be read as "consists of"); alternative formulations are separated by the
|
operator. A formulation consists of a sequence of grammar terms and input symbols (terminal symbols), whith empty formulations being also allowed. The grammar term of the first rule of a BNF definition is called start symbol. Grammar terms can be referenced recursively, thus allowing for the specification an infinite number of valid sentences of infinite length. Each rule definition must be terminated by the colon operator
(;
).
The language's vocabulary is determined by the terminal symbols specified with the BNF definition. The keywords
IDENT
(identifier),
NUMBER
(numeric constants),
SQSTR
(single quoted string),
DQSTR
(double quoted string),
EOLN
(end of line,
\n
),
EOF
(end of file and/or end of string scanning strings),
EOFINC
(end of include file) and
UNKNOWN
(special character sequence not explicitly defined) stand for generalized terminal symbols from in the corresponding word class. Special user-specific terminal symbols must be quoted. The BNF Precompiler applies automatic classification to assign these terminal symbols to either of the word classes keyword (reserved words consisting of three or more characters), single character operator (consisting of one special character) or double character operator (consisting of two special characters).
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 start recognition) and
*/
(comment end recognition) for comments which can span multiple lines. This assignment can be changed at the beginning of the BNF definition. The command for the default setting is:
COMMENT ("/*", "*/") ;
Omitting the second parameter from the
COMMENT
statement as in
COMMENT ('//');
configures comments which extend to the end of the line. Please note that the
COMMENT
default setting is reset if a
COMMENT
statement is added to the BNF definition. I.e., the following statements must both be added at the beginning of the BNF definition to configure
/*
and
*/
for multi-line comments and
//
for comments to end-of-line:
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
int
; their return value must be (-1) on error or zero otherwise. Up to one action function parameter of type
int
is allowed; this parameter must be specified as parentheses-enclosed integer constant after the function name in the BNF definition.
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
EOFINC
terminal symbol is returned if the end of an include file is reached, and reading resumes where it was interrupted in the previously processed input file. The
EOFINC
terminal symbol is obsolete if the
synparseincfile function is not used.
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
The
#pragma
preprocessor statement can be used to set the caller type of the compiled
User Language program. This feature can be used to relax or restrict the compatibility of the
User Language program at compile time, no matter whether module-specific system functions and/or index variable types are refered or not. The following table lists the possible caller type specifications (see also
Appendix A.1.2).
Caller Type | Valid Interpreter Environment(s) |
---|---|
ULCALLERSTD | all BAE program modules |
ULCALLERCAP | all Schematic Capture program modules |
ULCALLERSCM | Schematic Editor |
ULCALLERLAY | all Layout program modules |
ULCALLERGED | Layout Editor |
ULCALLERAR | Autorouter |
ULCALLERCAM | CAM Processor |
ULCALLERCV | CAM View |
ULCALLERICD | all IC Design program modules |
ULCALLERCED | Chip Editor |
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.
On 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
ULCALLERNOUNDO
for programs which are not performing any operations relevant to the system's undo mechanism, it is possible to avoid redundant undo steps.
Bartels :: Bartels AutoEngineer :: BAE Documentation :: User Language Programmer's Guide :: Language Description :: Preprocessor Statements |
Preprocessor Statements
© 1985-2025 Oliver Bartels F+E