Welcome to the Bartels Group of Companies
Preprozessor-Anweisungen - Deutsche Version Preprocessor Statements - English Version
Bartels

Bartels System GmbH
Bartels
Bartels AutoEngineer
BAE Product Info
BAE Price List
BAE Downloads
BAE Documentation
BAE Installation Guide
BAE User Manual
BAE Libraries
User Language Programmer's Guide
Preface
1 Introduction
2 Language Description
2.1 Introducing User Language Programming
2.2 Lexical Conventions
2.3 Data Types and Definitions
2.4 Expressions
2.5 Control Structures
2.6 Preprocessor Statements
2.6.1 File Inclusion
2.6.2 Constant Definition
2.6.3 Conditional Compilation
2.6.4 BNF Precompiler
2.6.5 Program Caller Type and Undo Mechanism
2.7 Syntax Definition
3 Programming System
4 BAE User Language Programs
A Conventions and Definitions
B Index Variable Types
C System Functions
BAE Update History
BAE Next Version Release Notes Preliminary
BAE V8.0 Release Notes
BAE V7.8 Release Notes
BAE V7.6 Release Notes
BAE V7.4 Release Notes
BAE V7.2 Release Notes
BAE V7.0 Release Notes
BAE V6.8 Release Notes
BAE V6.6 Release Notes
BAE V6.4 Release Notes
BAE V6.2 Release Notes
BAE V6.0 Release Notes
BAE V5.4 Release Notes
BAE V5.0 Release Notes
BAE V4.6 Release Notes
BAE V4.4 Release Notes
BAE V4.2 Release Notes
BAE V4.0 Release Notes
BAE V3.4 Release Notes
BAE Support
BAE Contributions
BAE Development and Service Companies
Electronics Development
Bartels Sport Service
Company Profile
Corporate Info
Bartels :: Bartels AutoEngineer :: BAE Documentation :: User Language Programmer's Guide :: Language Description :: Preprocessor Statements
Bartels User Language - Programmer's Guide

2.6 Preprocessor Statements

Bartels AutoEngineer® Dokumentation

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.

 

2.6.1 File Inclusion

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 Zoom All 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);
}
 

2.6.2 Constant Definition

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

 

2.6.3 Conditional Compilation

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
    :
}

 

2.6.4 BNF Precompiler

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
 

2.6.5 Program Caller Type and Undo Mechanism

Program Caller Type Setting

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)
ULCALLERSTDall BAE program modules
ULCALLERCAPall Schematic Capture program modules
ULCALLERSCMSchematic Editor
ULCALLERLAYall Layout program modules
ULCALLERGEDLayout Editor
ULCALLERAR Autorouter
ULCALLERCAMCAM Processor
ULCALLERCV CAM View
ULCALLERICDall IC Design program modules
ULCALLERCEDChip Editor

The

#pragma ULCALLERSTD

preprocessor statement forces the compiled User Language program caller type setting to standard (STD). The Incompatible index/function reference(s)! 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 UL(Line): System function not available in this environment! runtime error message.

Configuring Undo Mechanism

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-2017 Oliver Bartels F+E • Updated: 11 November 2009, 11:49 [UTC]

© 1985-2017 Oliver Bartels F+E Bartels Homepage Contact and Corporate Info

Web Development by Baumeister Mediasoft Engineering

Preprozessor-Anweisungen - Deutsche Version Preprocessor Statements - English Version