Welcome to the Bartels Group of Companies make this page your Homepage... add this page to your Favorites... send this page to a friend... display printer friendly page... display sitemap... display sitemap with all page contents...
Sprachbeschreibung - Deutsche Version Language Description - 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.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
Bartels User Language - Programmer's Guide

Chapter 2
Language Description

Bartels AutoEngineer® Dokumentation

This chapter describes in detail the definition of the Bartels User Language and explains how to write User Language application programs. The Bartels User Language elements are explained in detail, and their usage is illustrated by examples wherever necessary. Additionally, hints are given on how to use the User Language programming environment and how to interface to the Bartels AutoEngineer.

 

Contents

2.1Introducing User Language Programming
2.1.1The first User Language Program
2.1.2Variables, Arithmetic and Functions
2.1.3Arrays and Control Structures
2.2Lexical Conventions
2.2.1Spacing
2.2.2Identifiers
2.2.3Constants and Constant Expressions
2.2.4Terminal Symbols
2.3Data Types and Definitions
2.3.1Data Types
2.3.2Variables
2.3.3Functions
2.3.4Scope Rules
2.4Expressions
2.4.1Primary Expressions
2.4.2Unary Expressions
2.4.3Binary Expressions
2.4.4Expression List
2.4.5Precedence and Order of Evaluation
2.5Control Structures
2.5.1Concatenations
2.5.2Alternations
2.5.3Repetitions
2.5.4Program Flow Control
2.6Preprocessor Statements
2.6.1File Inclusion
2.6.2Constant Definition
2.6.3Conditional Compilation
2.6.4BNF Precompiler
2.6.5Program Caller Type and Undo Mechanism
2.7Syntax Definition
Tables
2-1Character Escape Sequences
2-2Reserved Words
2-3Operators
2-4Operator Precedence and Order of Evaluation

 

2.1 Introducing User Language Programming

This section provides small programming examples in order to introduce the most important elements of the Bartels User Language. The purpose thereby is to demonstrate - without entering into formal details or describing exceptions - the basic methods of developing User Language programs.

 

2.1.1 The first User Language Program

The only way to learn how to use the User Language is by doing it, i.e., write User Language programs, and compile and execute them. The first User Language program to be implemented should print a message, and then wait for an interactive keyboard input to abort the program (this is a frequently required programming feature). As already mentioned in the introduction, an User Language program must at least contain a main function. What we need in this main function are instructions for printing the desired message and for activating the requested user query. Both of these instructions can be realized by calling corresponding User Language system functions (printf and askstr). These system functions are known to the User Language Compiler, and they are bound to the Bartels User Language Interpreter. The programmer just has to know, how these functions are to be called, and what they do (this information can be taken from appendix C of this manual). You should now use your editor for preparing a file named ulprog.ulc with the following User Language source code (the .ulc file name extension is used by the User Language Compiler for User Language source code file recognition):

main()
{
      printf("User Language Program");
      askstr("Press ENTER to continue ",1);
}

The above listed User Language program does just contain the definition of the function main. Parentheses are required after the function name. Usually, formal function parameters are listed inside these parentheses. To distinguish function names from variable names, the parentheses are required even if (as in the example above) no function parameter exists at all. Within the braces the function block is defined. The function block is composed of the statements to be executed by the function. Each statement must be delimited by a semicolon (;). The first statement of the main function is the call to the printf function (to be recognized by the opening parenthesis after the function name). The parameter which is passed to the printf function is a constant string (enclosed by double quotes). This string is the message, which the program will print to the screen when executed after faultless compilation. The second statement is a call to the function askstr. This function issues the prompt string, which is specified with the first function parameter, and waits for an interactive string input. The user interaction takes place in the status line of the Bartels AutoEngineer, and the second askstr parameter indicates the maximum permissible input string length. The askstr call is the last statement to be processed by the program, i.e., the program finishes after executing the askstr call. Once the program source code is edited and stored to ulprog.ulc, it can be translated with the following User Language Compiler call:

ulc ulprog

The User Language Compiler issues the following message, if no compilation error occurred:

==============================
BARTELS USER LANGUAGE COMPILER
==============================

Compiling source code file "ulprog.ulc"...
Program 'ulprog' successfully created.
Source code file "ulprog.ulc" successfully compiled.

No errors, no warnings.
User Language Compilation successfully done.

Once the User Language Compiler program source code has been translated, and the corresponding machine program named ulprog has been stored to the ulcprog.vdb file of the Bartels AutoEngineer programs directory, the program can be executed by the Bartels User Language Interpreter. This can be applied e.g., by starting the Bartels AutoEngineer Schematic Editor and by activating the Run User Script function from the File menu. The program name (ulprog) must be specified to the corresponding query:

FileLeft Mouse Button (LMB)
Run User ScriptLeft Mouse Button (LMB)
Program Name ?ulprog Return/Enter Key (CR)

After starting the program, the BAE graphic workspace is switched to text output mode, and the User Language Program message is printed to the screen. Subsequently, the Press ENTER to continue prompt is displayed in the BAE input window. Return key input terminates the User Language program and restores the graphic workspace.

 

2.1.2 Variables, Arithmetic and Functions

The next example illustrates a series of further specific User Language characteristics. The following User Language program examines some circles (specified by center point and radius) to check whether they overlap (drill data test?!), and issues corresponding messages:

// Circle Test Program

double tol=0.254*5;           // Tolerance

struct pos {                  // Position descriptor
      double x;               // X coordinate
      double y;               // Y coordinate
      };

struct circle {               // Circle descriptor
      double rad;             // Circle radius
      struct pos c;           // Circle position
      };

// Main program
main()
{
      // Define three circles
      struct circle c1 = {  4.5, { 19.4, 28.3} };
      struct circle c2 = { 17.0, { 37.6, 9.71} };
      struct circle c3 = { 1.5E01, { 25, 0.2e2} };
      // Perform circle test
      printf("Circle 1 - 2 overlap : %d\n",circletest(c1,c2));
      printf("Circle 1 - 3 overlap : %d\n",circletest(c1,c3));
      printf("Circle 2 - 3 overlap : %d\n",circletest(c2,c3));
      // Prompt for continue
      askstr("Press ENTER to continue ",1);
}

int circletest(c1,c2)
// Circle test function
// Returns: nonzero if overlapping or zero else
struct circle c1,c2           /* Test circles 1 and 2 */;
{
      double d                /* Distance value */;
      // Get circle center point distances
      d=distance(c1.c,c2.c);
      // Error tolerant check distance against radius sum
      return(d<=(c1.rad+c2.rad+tol));
}

double distance(p1,p2)
// Get distance between two points
// Returns: distance length value
struct pos p1                 /* Point 1 */;
struct pos p2                 /* Point 2 */;
{
      double xd=p2.x-p1.x     /* X distance */;
      double yd=p2.y-p1.y     /* Y distance */;
      // Calculate and return distance
      return(sqrt(xd*xd+yd*yd));
}

The above listed program source code contains a series of comments enclosed by /* and */; such comments can extend over several lines, but they must not nest. Another type of comment starts with // and extents to the end of line. Since comments can keep the program source code well understandable, it is recommended to use such inline documentation to simplify User Language software maintenance.

The program above also contains a series of variable definitions. All variables must be declared before use. A variable declaration determines the name and the data type of the variable. Bartels User Language distinguishes between global variables, local variables and function parameters. Global variables are valid throughout the entire program text. Local variables are valid in the function where they are defined. Function parameters are used for passing values to functions. In the example above, tol is the only global variable (with data type double). Local variables are, e.g., xd and yd (data type double) in the distance function. Function parameters are, e.g., c1 and c2 in the circletest function; these two parameters are of the specially defined combined struct circle data type. Variable declarations can contain variable value initializations (see the global variable tol or the local variables xd and yd in the distance function). Combined data type variables can be initialized (see the local struct variables c1, c2 and c3 in the main function). A list of variable names can be specified at the declaration of variables (see the declaration of the parameters c1 and c2 in the circletest function).

Values are calculated within expressions. The equals sign (=) can be used for assigning the resulting expression value to a variable.

A data type must be specified at the definition of functions. In the example above, the distance function is of type double, and the circletest function is of type int. The function data type is set to int if the function data type specification is omitted (as with the main function in the example above). A special function data type is void. Each function - except for the void functions - returns a value compatible to the corresponding function data type. The function return value is passed back to the caller of the function with the return statement, which is coincidentally the last instruction to be executed by the function.

 

2.1.3 Arrays and Control Structures

The following example shows how to use arrays and control structures. A list of integer values are transformed into strings, and a report of the transformations is printed:

// Integer list
int intary[]={ 0,17,-12013,629,0770,0xFF,-16*4+12 };

// Main program
main()
{
      int i                   /* Loop control variable */;
      // Set last integer value
      intary[10]=(-1);
      // Loop through integer list
      for (i=0;i<=10;i++)
              // Print integer and integer string
              printf("%8d : \"%s\"\n",intary[i],inttostr(intary[i]));
      // Prompt for continue
      askstr("Press ENTER to continue ",1);
}

string inttostr(int intval)
// Convert integer value to a string
// Returns: resulting string
{
      string resstr=""        /* Result string */;
      int n=intval,i=0        /* Integer value, loop counter */;
      char sign               /* Sign character */;
      // Test for negative integer value
      if (n==0)
              // Return zero integer string
              return("0");
      else if (n>0)
              // Set sign to plus character
              sign='+';
      else {
              // Make integer value positive
              n=-n;
              // Set sign to minus character
              sign='-';
              }
      // Build result string
      do {    // Get and append next character
              resstr[i++]=n%10+'0';
              } while ((n/=10)!=0);
      // Append zeros
      while (i++<15)
              resstr+='0';
      // Append sign character
      resstr+=sign;
      // Reverse string
      strreverse(resstr);
      // Return string result
      return(resstr);
}

In the example above, an integer array (global int variable intary) is declared and (partially) initialized. The bracket pair after the variable name intary defines an one-dimensional int vector. Multiple vector dimensions can be specified by appending further bracket pairs to the declaration (intary[][]...[]). Since the User Language provides powerful in-build features for dynamically managing arrays, it is not necessary, to define array length limits; i.e., both the User Language Compiler and the Bartels User Language Interpreter require just the information about the dimension of an array and/or vector. Nevertheless some checks are applied in order to prevent from accessing non-existent array elements (which would cause memory protection faults); the Compiler is able to check for constant negative (i.e., invalid) array indices, and the Interpreter is able to check whether an array index refers to an array element outside the currently engaged array field range. The array index value 0 always refers to the first array element.

The string data type corresponds to an one-dimensional array of type char. User Language provides in-build features for the direct assignment of arrays and/or vectors with corresponding data type and equal dimension. These features have been utilized at the initialization of resstr (local string variable of the inttostr function) as well as with the assignment of the return value of the inttostr function. The add operator can also be applied to string values, with the result of the add operation corresponding to a string catenation.

The example above contains some control structures. A for loop for processing the elements of the intary array variable is applied in the main function. The inttostr function uses a while loop and a do-while loop for manipulating the resstr string variable. The inttostr function utilizes an if control structure to process dependent program blocks according to the current value of local variable n.

 

2.2 Lexical Conventions

Bartels User Language defines spacing, identifier, constant, reserved word and operator token classes.

 

2.2.1 Spacing

The spacing token class includes blanks, tabulators, newlines and comments. Comments start with the token /* and end with */; they do not nest. Another type of comment starts with the token // and extends to the end of line. Spacings are ignored by the Compiler except as they serve to separate adjacent identifiers, reserved words and constants.

 

2.2.2 Identifiers

An identifier is the name of a variable, a function or a symbolic constant. Each Identifier consists of a sequence of letters and digits. The first identifier character must be a letter. The underscore (_) is treated as a letter. The User Language Compiler distinguishes between upper case letters and lower case letters (case-sensitivity). Identifiers must differ from reserved words (see below).

Examples:

X_coord   value   P4   File_Name   _euklid
 

2.2.3 Constants and Constant Expressions

This section describes the Bartels User Language constant types.

Integer Constants

Numeric integer constants are associated with the data type int. They consist of a sequence of digits, and they are usually interpreted as decimal numbers. A constant integer specification is interpreted as octal number (in base 8), if it starts with 0 (digit zero), in which case the digits 8 and 9 are not allowed. A constant integer specification is interpreted as hexadecimal number (in base 16), if it starts with 0x or 0X (digit zero followed by letter x) in which case the letters a to A through f to F correspond to the hexadecimal digit values 10 through 15. Negative integer constants are not provided (the minus sign rather works as operator in order to form negative constant integer expressions; see below).

Examples:

1432   073   0xF4A5   9

Floating Point Constants

Floating point constants are associated with the data type double. They consist of an integer part, a decimal point (.), a fraction part, an e or E character (letter e), and an optionally signed integer exponent. Either the integer part or fraction part (not both) can be missing; or otherwise either the decimal point or the e letter and the exponent (not both) can be missing.

Examples:

2.54   .78   4.   4.1508E-3   0.81037e6   17228E5

Character Constants

Character constants are associated with the data type char. They consist of a single character enclosed by single quotes (apostrophes). The value of a character constant accords to the corresponding numeric value of the character in the machine's character set.

The escape character \ (backslash) can be used for specifying special characters. Table 2-1 contains a list of characters represented by escape sequences.

Table 2-1: Character Escape Sequences

Backspace BS \b
Horizontal Tabulator HT \t
Line Feed LF \n
Form Feed FF \f
Carriage Return CR \r
Escape Character \ \\
Apostrophe ' \'
Null Character NUL\0

Arbitrary bit patterns consisting of an escape symbol followed by up to three octal digits can be specified to represent the value of the desired character; the null character (NUL, \0) is a special case of this construction.

String Constants

String constants are associated with the data type string. They consist of a sequence of characters enclosed by double quotes (quotation marks). The Bartels User Language Compiler automatically appends a null character (NUL, \0) to the end of string constants; this convention is utilized by the Bartels User Language Interpreter to match the end of constant strings. Quotation marks included with a constant string must be preceded by the escape character (\); in addition the same escape character sequences as for character constants (see above) are permitted.

Examples:

"IC1"   "4.8 kOhm"   "This is a string with Newline\n"

Constant Expressions

A constant expression is an expression, which is composed of constant values and operators. Constant expressions are evaluated at compile time already (CEE, Constant Expression Evaluation), i.e., they do not have to be calculated by the Interpreter at runtime. This means, that wherever constants are required, corresponding constant expressions can be used without disadvantages regarding to the program memory or runtime requirements.

Examples:

int i=19-(010+0x10);                  CEE: int i=-5;
double d=-(4.7+2*16.3);               CEE: double d=-37.3;
string s="Part"+' '+"IC1";            CEE: string s="Part IC1";
 

2.2.4 Terminal Symbols

Reserved Words

Table 2-2 contains the list of Bartels User Language identifiers reserved for use as keywords. These identifiers can only be used in their predefined meaning.

Table 2-2: Reserved Words

#bnf #define #else #endif #if #ifdef #ifndef #include
#undef break case char continue default do double
else for forall if index int of return
static string struct switch typedef void where while

Operators

Table 2-3 lists the Bartels User Language operators. These operators activate special operations regarding to the current program context.

Table 2-3: Operators

! != % %= & && &= ( ) * *=
+ ++ += , - -- -= . / /= :
; < << <<= <= = == > >= >> >>=
? [ ] ^ ^= { | |= || } ~
 

2.3 Data Types and Definitions

 

2.3.1 Data Types

Bartels User Language provides the following basic data types:

char Character taken from the machine's character set
int Numeric integer value
doubleNumeric double precision floating point value
stringCharacter array
index Index to predefined BAE DDB structure

Bartels User Language provides the following combined data types:

arrayCollection of elements with same data type
structCollection of elements with different data types

Data Type Conversion

Some operators can cause implicit data type conversions. A series of arithmetic operations require operand(s) with special data types. Likewise a corresponding data type compatibility is required with the assignment of values to variables and/or the passing of function parameters. The User Language Compiler checks the compatibility of the operands. Operands of different data types are automatically casted to a common or valid data type if possible. These type casts are applied according to the following rules: valid type conversions without loss of information are char to int, char to string and int to double; permissible type casts with a loss of information are int to char and double to int. The User Language Compiler issues error messages if the required type compatibility can not be achieved by applying the type cast rules.

 

2.3.2 Variables

All global and local variables must be declared before use. A variable declaration defines the name and the data type of the variable. Such declarations determine, how user-introduced names are to be interpreted by the User Language. Each declaration consists of a data type specification and a list of declarators. Each declarator is composed of the corresponding variable name and an optional initialization.

Basic Data Types

The declaration of char variables is applied as in

char c;
char TAB = '\t', NEWLINE = '\n';

where the char variables c (not initialized), TAB (initialized with the tabulator control character) and NEWLINE (initialized with the newline control character) are declared.

The declaration of int variables is applied as in

int i, MAXLINELEN = 80;
int pincount = 0;

where the int variables i (not initialized), MAXLINELEN (initialized with the value 80) and pincount (initialized with 0) are declared.

The declaration of double variables is applied as in

double x_coord, y_coord;
double MMTOINCH = 1.0/25.4;
double starttime = clock();

where the double variables x_coord and y_coord (not initialized), MMTOINCH (initialized with a numeric expression) and starttime are declared; the variable starttime is initialized with the return value of the system function clock (i.e., the elapsed CPU time).

The declaration of string variables is applied as in

string s1;
string ProgName = "TESTPROGRAM", ProgVer = "V1.0";
string ProgHeader = ProgName+"\t"+ProgVer;

where the string variables s1 (not initialized), ProgName (initialized with TESTPROGRAM), ProgVer (initialized with V1.0) and ProgHeader are declared; ProgHeader is initialized with an expression which emerges from a catenation of the string variable ProgName, the tabulator control character and the string variable ProgVer.

The declaration of index type variables is applied as in

index L_MACRO macro;
index L_CNET net1, net2;

where the index variables macro (index variable type L_MACRO), net1 and net2 (index variable type L_CNET) are declared. The declaration of index variable types consists of the keyword index followed by the name of the index variable type (e.g., L_MACRO and/or L_CNET) and the desired variable name(s). The identifiers of the index variable types are predefined (see also appendix B of this manual). Only index variable types compatible to each other can be used in the same program. The reason for this restriction is, that with index data types the access to corresponding entries of the Bartels AutoEngineer design data base (DDB) is defined; the availability of these DDB entries differs according to the interpreter environment (i.e., the Schematic Editor provides data type definitions which are not available in the layout system). The User Language Compiler issues an error message if incompatible index variable types are used in the same program. The User Language Interpreter behaves similarly; an error message is issued and the program is canceled when trying to run a User Language program with references to index variable types not compatible with the current interpreter environment. Please refer to appendix A and/or appendix B of this manual for information about index data type compatibilities.

Arrays

An array (or vector) is a complex data type composed of elements of the same data type. With the declaration of array variables the specification of the array dimension is required in addition to the data type and variable name definition. The dimension is specificied by appending bracket pairs to the variable name, with each bracket pair corresponding to one dimension. At the initialization of array variables, the corresponding values are to be separated by commas, and each array dimension is to be enclosed with braces.

The declaration of array variables is applied as in

int intary[], intfield[][][];
double valtab[][] = {
      { 1.0, 2.54, 3.14 },
      { 1.0/valtab[0][1], clock() }
      };
string TECHNOLOGIES[] = {
      "TTL", "AC", "ACT", "ALS", "AS", "F",
      "H", "HC", "HCT", "HCU", "L", "LS", "S"
      };

where the int arrays intary (1-dimensional) and intfield (3-dimensional), the 2-dimensional double array valtab and the 1-dimensional string array TECHNOLOGIES are declared. The declarations of valtab and TECHNOLOGIES contain initializations according to the following assignments:

valtab[0][0] = 1.0;
valtab[0][1] = 2.54;
valtab[0][2] = 3.14;
valtab[1][0] = 1.0/valtab[0][1];
valtab[1][1] = clock();
TECHNOLOGIES[0] = "TTL";
TECHNOLOGIES[1] = "AC";
TECHNOLOGIES[2] = "ACT";
:
TECHNOLOGIES[11] = "LS";
TECHNOLOGIES[12] = "S";

The basic User Language data type string is equivalent to a 1-dimensional char array, i.e., the declarations

string s;

and

char s[];

are synonymous.

Structures

A structure is a complex data type composed of elements with different data types, i.e., the elements of a structure are to be defined with different names. The purpose of structure definitions is to unite different variable types, which share special relations regarding on how they are to be processed. It has to be distinguished between structure definitions and structure declarations. A structure definition is composed of the keyword struct, the name of the structure definition and the list of structure element definitions (enclosed with braces). A structure declaration consists of the keyword struct, the name of a valid structure definition and the name of the variable to be associated with the structure definition. Structure definitions and structure declarations can be combined. The name of the structure definition can be omitted. Initializations in structure declarations are allowed in which case the syntax corresponds to the array declaration conventions (see above).

The declaration of structures is applied as in

// Structure declarations

struct coordpair {
      double x
      double y;
      };

struct coordpair elementsize = {
      bae_planwsux()-bae_planwslx(),
      bae_planwsuy()-bae_planwsly()
      };

struct elementdes {
      string fname, ename;
      int class;
      struct coordpair origin, size;
      } element = {
              bae_planfname(),
              bae_planename(),
              bae_planddbclass(),
              {
                      bae_planwsnx(),
                      bae_planwsny()
                      },
              elementsize
              };

struct {
      string id, version;
      struct {
              int day;
              string month;
              int year;
              } reldate;
      } program = {
              "UL PROGRAM",
              "Version 1.1",
              { 4, "July", 1992 }
              };

where the definition of the structure coordpair, the declaration of the variable elementsize (structure of type coordpair), the definition of the structure elementdes, the declaration of the variable element (structure of type elementdes) and the declaration of the struct variable program is accomplished. The declarations of elementsize, element and program contain initializations according to the following assignments:

elementsize.x=bae_planwsux()-bae_planwslx();
elementsize.y=bae_planwsuy()-bae_planwsly();
element.fname=bae_planfname();
element.ename=bae_planename();
element.class=bae_planddbclass();
element.origin.x=bae_planwsnx();
element.origin.y=bae_planwsny();
element.size=plansize;
program.id="UL PROG";
program.version="Version 1.1";
program.reldate.day=4;
program.reldate.month="July";
program.reldate.year=1992;

The following example illustrates how structure and array definitions and/or declarations can be combined:

struct drilldef {
      index L_DRILL drilltool;
      struct { double x, y; } drillcoords[];
      } drilltable[];

Data Type Renaming

Bartels User Language provides a mechanism for renaming data types. This feature allocates an additional name for an already known data type (but it does not create a new data type). Data type renaming is accomplished by the specification of the keyword typedef followed by a valid data type specification and the new name to be introduced for this data type. A data type specification introduced with typedef can subsequently be used as data type specifier when declaring variables, functions or function parameters.

Data type renaming is utilized as in

typedef index L_CNET NETLIST[];
typedef int IARY[];
typedef IARY MAT_2[];
typedef struct {
      int pointcount;
      struct {
              int t;
              double x,y;
              } pointlist[];
      } POLYLIST[];
MAT_2 routmatrix;
NETLIST netlist;
POLYLIST polygonlist;

where the variables routmatrix (2-dimensional int array), netlist (1-dimensional index array of type L_CNET) and polygonlist (1-dimensional array of structures containing an int element and a struct array) are declared.

 

2.3.3 Functions

A function usually is defined for solving a certain sub-problem derived from larger problems. The use of functions can simplify the process of software maintenance considerably since complex operation sequences can be applied repeatedely without the having to code the corresponding instructions time and again. Bartels User Language distinguishes between the predefined system functions and the user-defined functions.

Function Definition

The Bartels User Language system functions are known to the User Language Compiler, and they are bound to the User Language Interpreter. See appendix C of this manual for the description of the Bartels User Language system functions. The programmer can make use of the system functions or write his own functions.

A function definition consists of the function header and the function block. The function header is composed of the function type specification, the function name, and the definition and declaration of the function parameters. The function type determines the data type of the value to be returned by the function. The void function type applies to functions without return value. The function data type defaults to int if the function type specification is omitted. The function type is followed by the function name, which must be unique throughout the program text. The function parameter definition consists of the list of the function parameter names and/or declarations (separated by commas); the function parameter list must be enclosed by parentheses. All function parameters - except for the int parameters and those already explicitly declared with the parameter list - must be declared at the end of the function header, with the declaration of the parameters corresponding to the declaration of normal variables (see above). The function header is followed by the function block. The function block must be enclosed by braces, and consists of the statements to be executed by the function.

Function definition examples:

double netroutwidth(index L_CNET net)
// Get the routing width of a given net
// Returns : width or 0.0 if two pins with different width
{
      index L_CPIN pin;       // Pin index
      int pincnt=0;           // Pin count
      double rw=0.0;          // Rout width
      // Loop through all pins
      forall (pin of net) {
              // Test if the pin introduces a new rout width
              if (pin.RWIDTH!=rw && pincnt++>0)
                      return(0.0);
              // Set the rout width
              rw=pin.RWIDTH;
              }
      // Return the rout width
      return(rw);
}

int allpartsplaced()
// Test if all net list parts are placed
// Returns : 1 if all parts are placed or zero otherwise
{
      index L_CPART cpart;    // Connection part index
      // Loop through the connection part list
      forall (cpart where !cpart.USED)
              // Unplaced part matched
              return(0);
      // All parts are placed
      return(1);
}

double getdistance(xs,ys,xe,ye)
// Get the distance between two points
// Returns : the distance length value
double xs, ys;                // Start point coordinate
double xe, ye;                // End point coordinate
{
      double xd=xe-xs;        // X distance
      double yd=ye-ys;        // Y distance
      // Calculate and return the distance (Pythagoras)
      return(sqrt(xd*xd+yd*yd));
}

double arclength(r,a1,a2)
// Get arc segment length by radius and start-/end-point angle
// Returns : the arc segment length value
double r;                     // Radius
double a1;                    // Start point angle (in radians)
double a2;                    // End point angle (in radians)
{
      // Arc; "absolute" angle between start and end point
      double arc = a1<a2 ? a2-a1 : 2*PI()+a2-a1;
      // Get and return the arc segment length
      return(arc*r);
}

double getangle(cx,cy,x,y)
// Get the angle of a circle arc point
// Returns : the angle (in radians; range [0,2*PI])
double cx, cy;                // Circle center coordinate
double x, y;                  // Circle arc point coordinate
{
      double res;             // Result value
      // Get arc tangent of angle defined by circle point
      res=atan2(y-cy,x-cx);
      // Test the result
      if (res<0.0)
              // Get the "absolute" angle value
              res=PI()-res;
      // Return the result value
      return(res);
}

double PI()
// Returns the value of PI in radians
{
      // Convert 180 degree and return the result
      return(cvtangle(180.0,1,2));
}

void cputimeuse(rn,st)
// Report CPU time usage (in seconds)
string rn;                    // Routine name
double st;                    // Start time
{
      // Print CPU time elapsed since start time
      printf("(%s) Elapsed CPU Time = %6.1f [Sec]\n",rn,clock()-st);
}

Function Call and Parameter Value Passing

Each function known in a User Language program and/or program module can be called in this program and/or program module. However, only system functions compatible to each other can be used in the same program. The reason for this restriction is, that a system function is implemented and/or available in a certain set of interpreter environments only (e.g., the system function for setting CAM Processor plot parameters obviously can not be called from the Schematic Editor). The User Language Compiler issues an error message if incompatible system functions are used in a program. The User Language Interpreter behaves similarly; an error message is issued and the program is canceled when trying to run a User Language program with references to system functions not compatible to the current interpreter environment. Please refer to appendix A and/or appendix C of this manual for information about system function compatibilities.

A function call consists of the function name and - enclosed with parentheses - the list of the parameters (arguments) to be passed to the function.

The contents of global program variables are available in each function of the same scope. I.e., global variables can be used at any time for passing values to functions. Besides that values can be passed with the function parameters. Since the usage of parameters provides easy maintenance, this method should be preferred. The list of parameters which is passed to the function must correspond with the formal parameter list introduced with the function definition (i.e., the parameter count as well as the data types must match). At the function call, the values of the current parameters are copied to the corresponding formal parameters. After successful execution of the function, each parameter value changed by the function is stored back to the current parameter (this applies only if the parameter refers to a variable). Finally, there is the possibility of passing values with the function return value, where the return statement is used for setting a function result value which is passed back to the caller of the function and can be evaluated in the expression containing the function call.

Examples for function calls and value passing:

// Date structure
struct date { int day, month, year; };
// Global program variables
string globalstr="Global string";
int fctcallcount=0;

// Main program
main()
{
      // Local variables of main
      string resultstr="function not yet called";
      struct date today = { 0, 0, 0 };
      double p=0.0, b=2.0, e=10.0;
      // Print the global variables
      printf("fctcallcount=%d, %s\n",fctcallcount,globalstr);
      // Print the local variables
      printf("resultstr=\"%s\"\n",resultstr);
      printf("today : %d,%d,%d",today.day,today.month,today.year);
      printf("\t\tb=%.1f, e=%.1f, p=%.1f\n",b,e,p);
      // Call function
      resultstr=function(today,b,e,p);
      // Print the global variables
      printf("fctcallcount=%d, %s\n",fctcallcount,globalstr);
      // Print the local variables
      printf("resultstr=\"%s\"\n",resultstr);
      printf("today : %d,%d,%d",today.day,today.month,today.year);
      printf("\t\tb=%.1f, e=%.1f, p=%.1f\n",b,e,p);
}

string function(curdate,base,exponent,power)
struct date curdate;          // Current date parameter
double base;                  // Base parameter
double exponent;              // Exponent parameter
double power;                 // Power parameter
{
      // Increment the function call count
      fctcallcount++;
      // Set the global string
      globalstr="Global string changed by function";
      // Get the current date
      get_date(curdate.day,curdate.month,curdate.year);
      // Calculate the power
      power=pow(base,exponent);
      // Return with a result string
      return("function result string");
}

The example above produces the following output:

fctcallcount=0, Global string
resultstr="function not yet called"
today : 0,0,0         b=2.0, e=10.0, p=0.0
fctcallcount=1, Global string changed by function
resultstr="function result string"
today : 4,6,92                b=2.0, e=10.0, p=1024.0

Control Flow and Program Structure

After calling a function, this function keeps control of the program flow until it meets with another function call or a return statement or else has reached its end after processing its last statement. At a function call the control is passed to the called function. When reaching a return statement or the end of the function, the control is passed back to the caller of the function. If a function with the name main is defined in a program, the User Language Compiler produces machine code for calling this function immediately after initializing the global program variables. I.e., the program control flow usually starts with the main function. Since each function passes the control back to the caller (unless the program contains endless recursions), the control will finally fall back to the main function. If the end of the main function is encountered or if a return statement is reached in the main function, then the end of the program is reached and the control is passed back to the User Language Interpreter, which then terminates the program flow.

Recursive Functions

Functions can be used recursively, i.e., a function can call itself directly or indirectly. This however is meaningful only if with each recursive function call a condition changes in order to reach a clearly defined final state, which causes a recursion interrupt (otherwise the function is endless-recursive and the program runs "forever").

Recursive programming of functions can save program code and increase the legibility of the source code. However, program runtime and memory requirements increase with recursive programming and endless recursions might be implemented inadvertently. Hence careful considerations should be taken on the use of recursive functions. The Bartels User Language Interpreter eventually encounters an out of memory and/or stack overflow error when processing endless recursive functions since at least a return address must be stored for each function call.

 

2.3.4 Scope Rules

The User Language Compiler checks the validity of each object reference (name and/or identifier) of the program to be compiled. For that purpose a valid program scope is assigned to each identifier used in the program. This (lexical) identifier scope is the region of the program text where the identifier is defined. he corresponding object is known and can be referenced throughout this scope. There is a distinction between global and local scopes. The global scope extends to the entire program (i.e., separately compiled program modules and/or libraries to be linked later), whilst local scopes correspond with the function definitions.

The function names of a program are global, i.e., they are valid throughout the entire program. Variable and type names defined inside a function are local to this function; variable and type names defined outside any function are global. Function parameter names are treated like local variable names, i.e., they are local to the corresponding function. Structure definition names on principle are global throughout the currently compiled program text. Function and global variable scopes can be restricted to the currently compiled program text by assigning the static storage class. The static storage class is used to avoid name conflicts when binding and/or linking different program modules and/or libraries.

To avoid name conflicts, the elements of each object class must have different names inside their corresponding valid scopes. Local object references have higher priority than global object references.

 

2.4 Expressions

This section describes the expression operators provided by the Bartels User Language. The operators are introduced in a sequence according to decreasing operator precedence. The associativity, i.e., the order of evaluation is mentioned as well; each operator is evaluated either from the left to the right or vice versa (depending on the implicit parentheses). The precedence indicates the priority at which the corresponding expression is evaluated. The sequence of evaluation is undefined, unless the precedence takes effect. It is left up to the implementation, to evaluate partial expressions in the most efficient way, even if these expressions cause side effects. The evaluation sequence of the side effects thereby is undefined. Expressions with associative and/or commutative operators can be rearranged arbitrarily. Certain sequences of operator evaluation can be forced by assignments to (temporary) variables.

 

2.4.1 Primary Expressions

Primary expressions include constants, object references, parenthesis-enclosed expressions, function calls, array element accesses, structure element accesses or index element accesses. These operations are left-associative, i.e., they are evaluated from left to right.

Constants and Object References

Each suitable arranged identifier referring to an object is a valid primary expression. Integer constants, floating point constants, character constants and string constants are primary expressions as well (see chapter 2.2.3 for the representation of constants).

Parenthesis-Enclosed Expressions

An expression enclosed with parentheses is a primary expression. Parentheses can be set explicitly to redefine the sequence of evaluation. Since the multiplicative operators have higher precedence than the additive operators, the resulting value of the expression in

a + b * c

emerges from the sum of variable a plus the product of b*c, while the resulting value of the expression in

(a + b) * c

emerges from the product of the sum a+b multiplied by the variable c.

Function Call

A function call consists of the name of the function to be called, followed by a parenthsis-enclosed list of comma-separated expressions. The values of the expression list represent the current parameters for the function call. A function call can be used as primary in any other expression, if the corresponding function returns a non-void value. Examples for typical function calls are

init_states(pass=1);
printf("This is a message!\n");
printf("Element %s\n",bae_planename());
xpos=nref_xcoord(ask_partname())-bae_planwsnx();

Array Element Access

A primary expression followed by a bracket-enclosed expression again is a primary expression. This operation applies for the access to an array (or string) element. The expression left to the brackets refers to the array; the expression enclosed with the brackets is interpreted as int array index value indicating the required array element. The index value must not be negative, and the index value 0 (zero) refers to the first element of the array. When storing to an array, the User Language Interpreter automatically adjusts the array length limit and redefines the currently valid array range if necessary. Read access to an array is only permitted in the defined array range. In the following example the strisoctal function checks, whether the given string value contains octal digits only (0 to 7), and returns 1 if so or zero otherwise:

int strisoctal(string str)
{
      for (i=0;i<strlen(str);i++)
              if (!isdigit(str[i]) || str[i]=='8' || str[i]=='9')
                      return(0);
      return(1);
}

In the example above, the array range is checked with the strlen system function. The following example uses a special int variable ("filecount") for performing the array range check:

string curfilename="", filelist[];
int i, filecount=0;
while (scandirfnames(".",".ddb",curfilename)==1)
      filelist[filecount++]=curfilename;
for (i=0;i<filecount;i++)
      printf("File %s \n",filelist[i]);

Within the example above first a list of file names (to be found in the current directory and ending on .ddb) is build, and then this file name list is printed.

Structure and Index Element Access

A primary expression followed by a point and an identifier again is a primary expression. The expression left to the point refers to a structure or index type, and the identifier following to the point operator designates a defined element of the corresponding structure or index type. The write access to structure elements is always possible, but no storage is permitted on index type elements (this would cause the User Language Compiler to issue a corresponding error message). The read access to the elements of a currently valid index variable always is permissible, while only previously initialized struct variable elements can be read (otherwise the User Language Interpreter would encounter a memory access violation and issue a corresponding error message). The following program part defines a list of structures and produces for each macro of the currently loaded layout a list element containing the macro name and the macro class:

int macrocnt=0;
struct { string name; int class; } macrolist[];
index L_MACRO macro;
forall (macro) {
      macrolist[macrocnt].name=macro.NAME;
      macrolist[macrocnt++].class=macro.CLASS;
      }
 

2.4.2 Unary Expressions

Unary expressions include all operators evaluating a single operand. These operations are right-associative, i.e., they are evaluated from right to left.

Increment and Decrement

The unary increment operator ++ changes its operand by adding the value 1 to the value of the operand. The unary decrement operator -- changes its operand by subtracting the value 1 from the operand. The increment and decrement operators can be used either as prefix operator as in ++n or as postfix operator as in n++. Both the prefix and the postfix notation cause an increment and/or decrement of the operand. The difference however is that the prefix expression changes the operand before using its value, while the postfix expression changes the operand after using its value. I.e., the result of these expression can have different meanings according to the program context. If, e.g., the value of count is 12, then

n = --count ;

sets the value of n to 11, but

n = count-- ;

sets the value of n to 12 (in both cases the value of count becomes 11).

Arithmetic Negation

The resulting value of the unary operator - is the arithmetic negation of its operand, i.e., the operand's value multiplied by (-1).

Logical Negation

The resulting value of the unary operator ! is the logical negation of its operand. The value is set to either 1 for zero operand values (i.e., 0 or empty string for string operands), or 0 for nonzero operand values. The type of the result is int.

Bit Complement

The unary operator ~ yields the one's-complement of its operand. The operand must be of type int; each 1-bit of the operand is converted to a 0-bit and vice versa.

 

2.4.3 Binary Expressions

Binary expressions include all operators evaluating two operands. These operations are left-associative, i.e., they are evaluated from left to right.

Product

The operators for multiplication and division produce a product expression. The usual arithmetic type conversions are performed.

The binary * operator indicates multiplication. The resulting value of this operation is the product of its two operands. The multiplication operator is associative and commutative, and expressions with several multiplications at the same level can be rearranged arbitrarily.

The binary / operator indicates division. The resulting value emerges from dividing the first operand (dividend) by the second operand (divisor). Integer division truncates any fractional part. A zero value is not allowed for the divisor since division by zero is not permissible.

The binary % (modulus) operator yields the remainder of dividing the first operand (dividend) by the second operand (divisor); floating point operand values are not allowed, and the divisor must not be zero. The % operator can be utilized as in

febdays = (year%4==0 && year%100!=0 || year%400==0) ? 29 : 28 ;

where the value of year is tested on leap-year match, and the value of febdays is set accordingly.

Sum

The operators for addition and subtraction produce a sum expression. The usual type conversions are performed.

The binary + (plus) operator indicates addition. If this operator is applied on numerical operands, then the result is the sum of its two operands, and the operation is commutative; if the add operator is applied on string operands, then the result is a string value generated by appending the second string to the first string, and the operation is not commutative.

The binary - (minus) operator indicates subtraction. The resulting value of this operation is the difference of its operands. The second operand is subtracted from the first.

Shift Operation

The binary shift operators << and >> can be applied to integer operands. They perform a left (<<) or right (>>) shift of their first operand by the number of bit positions specified with the second operand. Vacated significant bits of the first operand are filled with 0-bits. The result value of a bit-shift operation is undefined, if the second operand is negative. If the second operand is zero (i.e., 0 shift operations requested), then the first operand leaves unchanged. A right-shift by one bit corresponds with a (fast) integer division by 2; a left-shift by one bit corresponds to a (fast) multiplication by two; a left-shift by two bits corresponds with a multiplication by four, etc.

Comparison

The resulting value of the binary comparison operators < (less than), <= (less equal), > (greater than) and >= (greater equal) is the int value 1, if the specified comparison relation is true for the two operands; otherwise the result value is 0. The comparison operators can be applied on string operands directly.

Equivalence

The resulting value of the binary equivalence operators == (equal) and != (not equal) is the int value 1, if the specified equality relation is true for the two operands; otherwise the result value is 0. The equivalence operators can be applied on string operands directly; they correspond to the comparison operators, but they have less precedence.

Bitwise AND

The binary bitwise AND operator & applies to integer operands only; the usual arithmetic type conversions are performed. The result is the bitwise AND function of its operands. This operator is associative and commutative and expressions involving & can be rearranged.

Bitwise Exclusive OR

The binary bitwise exclusive OR operator ^ applies to integer operands only; the usual arithmetic type conversions are performed. The result is the bitwise exclusive OR (XOR) function of its operands. This operator is associative and commutative, and expressions involving ^ can be rearranged.

Bitwise Inclusive OR

The binary bitwise inclusive OR operator | applies to integer operands only; the usual arithmetic type conversions are performed. The result is the bitwise inclusive OR function of its operands. This operator is associative and commutative, and expressions involving | can be rearranged.

Logical AND

The logical AND operator && returns the int value 1 if both its operands are nonzero or 0 otherwise. This operator strictly guarantees left-to-right evaluation; i.e., the second operand is not evaluated if the value of the first operand is zero, such that in an expression like

x<100 && fct(x)

the fct function is only called if the value of x is less than 100.

Logical OR

The logical OR operator || returns the int value 1 if either of its its operands is nonzero or 0 otherwise. This operator strictly guarantees left-to-right evaluation; i.e. the second operand is not evaluated if the value of the first operand is nonzero, such that in an expression like

test1() || test2()

the test2 function is only called if the test1 function returns zero.

Conditional Evaluation

The ternary operator ?: is the conditional evaluation; this operation is right-associative, i.e., it is evaluated from right to left. The first expression is evaluated and if it is nonzero, the result is the value of the second expression, otherwise that of third expression. Usual type conversions are performed to bring the second and third expressions to a common type. Only one of the second and third expression is evaluated. An expression for assigning a conditional expression value to a result as in

result = logexpr ? trueexpr : falsexpr ;

is equivalent to the following control structure:

if (logexpr)
      result = trueexpr ;
else
      result = falseexpr ;

The following example utilizes the conditional expression operator to calculate the maximum of two values:

maxval = (val1>=val2) ? val1 : val2 ;

Assignments

User Language provides a series of assignment operators, all of which are right-associative. All assignment operators require an unary expression as their left operand; the right operand can be an assignment expression again. The type of the assignment expression corresponds to its left operand. The value of an assignment operation is the value stored in the left operand after the assignment has taken place. The binary = operator indicates the simple assignment; the binary operators *=, /=, %=, +=, -=, >>=, <<=, &=, ^= and |= indicate a compound assignment expression. A compound assignment of general form as in

expr1 <operator>= expr2 

is equivalent with the expression

expr1 = expr1 <operator> (expr2)

where, however, expr1 is evaluated only once (consider the parentheses round expression expr2). An assignment expression sequence as in

a = 5 ;
b = 3-a ;
c = b+a ;
c -= a *= b += 4+2*a ;

stores the values 60, 12 and -57 to the variables a, b and c, respectively.

 

2.4.4 Expression List

Each expression can consist of a list of comma-separated binary expressions. A pair of expressions separated by comma is evaluated left-to-right and the value of the left expression is discarded. The type and value of the result are the type and value of the right operand. The comma operator can be utilized as in

c -= (a=5, b=3-a, c=b+a, a*=b+=4+2*a) ;

where the values 60, 12 and -57 are stored to the variables a, b and c, respectively. In contexts where the comma is given a special meaning, e.g., in a list of actual function parameters and lists of initializers, the comma operator can only appear in parentheses; e.g., the function call

fct ( x, (y=8, y*25.4), z )

has three arguments, the second of which has the value 203.2.

 

2.4.5 Precedence and Order of Evaluation

Table 2-4 summarizes the rules for precedence and associativity of all User Language operators. Operators on the same line have the same precedence; rows are in order of decreasing precedence.

Table 2-4: Operator Precedence and Order of Evaluation

OperationOperator(s)Associativity
Primary () [] . left to right
Unary ! ~ ++ -- - right to left
Product * / % left to right
Sum + - left to right
Shift << >> left to right
Comparison < <= > >= left to right
Equality == != left to right
Bit And & left to right
Bit Xor ^ left to right
Bit Or | left to right
Logical And && left to right
Logical Or || left to right
Conditional ?: right to left
Assignment = += -= etc. right to left
Expression List, left to right
 

2.5 Control Structures

This section describes the control flow constructions provided by Bartels User Language. Control flow statements specify the order in which computations are processed. According to the principles of structured programming Bartels User Language distinguishes between concatenation (sequential program element), alternation and repetition (CAR - Concatenation, Alternation, Repetition).

 

2.5.1 Concatenations

Statements

A statement consists of an expression (see chapter 2.4) followed by a semicolon (;), as in

tabulator = '\t' ;
distance = sqrt(a*a+b*b) ;
filename += extension = ".ddb" ;
++ary[i] ;
printf("Part %s ;\n",partname) ;

The semicolon is a statement terminator. An empty statement is encountered by

;

where the expression at the left of the semicolon is omitted. Empty statements can be used to define dependent (dummy) statements (e.g., inside loops). A statement is indicated as dependent statement, if it is context-dependent to an alternation or a repetition (see below).

Bartels User Language allows the specification of statements without side-effect as in

27+16.3;
++11;

Statements without side-effect are worthless since they neither change any variable value by assignment nor do they activate any function. I.e., User Language Compiler issues a warning message if a statement without side-effects is encountered.

Blocks

A block consists of a sequence of declarations (see chapter 2.3.2) and statements and is enclosed with braces ({ and }). I.e., the braces apply for grouping declarations and statements together into a compound statement or block, which then is syntactically equivalent to a single statement. Compound statements are most commonly used at the definition of functions or for grouping multiple dependent statements of an alternation or repetition.

 

2.5.2 Alternations

Alternations make decisions according to a special expression value in order to branch to the execution of corresponding dependent (compound) statements.

if- and if-else Statement

The formal syntax of the if statement is

if (expression)
      statement

where the dependent statement of the if statement is only executed if the expression value is nonzero (i.e., a value different from 0 or the empty string on string expressions). The formal syntax of the if-else statement is

if (expression)
      statement1
else
      statement2

where the if expression is evaluated, and statement1 is executed if the expression value is nonzero or else statement2 is executed if the expression value is zero. Dependent statements of an if and/or if-else statement can be if or if-else statements again, i.e., if and if-else statements can nest as in

if (expression)
      statement
else if (expression) {
      if (expression)
              statement
      }
else if (expression)
      statement
:
else
      statement

Since the else part of an if-else statement is optional, there is an ambiguity when an else is omitted from a nested if sequence. This is resolved in a way that the else is associated with the closest previous else-less if. The if and/or if-else statement can be utilized as in

string classname="SCM ";
if (bae_planddbclass()==800)
      classname+="Sheet";
else if (bae_planddbclass()==801 || bae_planddbclass()==803)
      classname+="Symbol/Label";
else if (bae_planddbclass()==802)
      classname+="Marker";
else {
      classname="***INVALID***";
      printf("No valid element loaded!\n");
      }

where the class of the currently loaded SCM element is determined and the value of the variable classname is set accordingly.

switch Statement

The switch statement is a special multi-way decision maker that tests whether an expression matches one of a number of other expressions, and branches accordingly. The formal syntax of the switch statement is

switch (expression)
      statement

Each dependent statement of the switch statement can be preceded with an arbitrary number of case labels of the form

case expression :

or

default :

The statements between the case labels are strictly dependent to the closest previous case label. The dependent statements of a case label are only executed if the value of the case expression matches the value of the switch expression. The default label specifies an arbitrary value, i.e., the statements following to the default label is always executed. case labels do not have any effect on the sequence of computing (the execution continues as if there is no case label). The break statement (see also chapter 2.5.4) can be used in a case segment to leave the switch control structure. The switch statement can be utilized as in

string classname="SCM ";
switch (bae_planddbclass()) {
      case 800 :
      classname+="Sheet";
      break;
      case 801 :
      case 803 :
      classname+="Symbol/Label";
      break;
      case 802 :
      classname+="Marker";
      break;
      default :
      classname="***INVALID***";
      printf("No valid element loaded!\n");
      }

where the class of the currently loaded SCM element is determined and the value of the variable classname is set accordingly.

 

2.5.3 Repetitions

Repetitions are control structures forming a loop for the repetitive computing of certain parts of a program. Each repetitive statement provides a method for testing a certain condition in order to end the processing of the loop. If a program runs into a loop, where the loop-end condition is never reached, then the control flow cannot be passed back to the caller of the program (i.e., the program runs "forever"). This is a fatal programming error, which the User Language Compiler can recognize under certain conditions.

while Statement

The formal syntax of the while statement is

while (expression)
      statement

where the dependent statement is repeated until the value of the while expression is zero (0 or empty string for string expressions). The while statement can be utilized as in

// ASCII file view
main()
{
      string fname;           // File name
      int fh;                 // File handle
      string curstr="";       // Current input string
      // Set the file error handle mode
      fseterrmode(0);
      // Print the program banner
      printf("ASCII FILE VIEWER STARTED\n");
      // Repeatedly ask for the input file name
      while (fname=askstr("File Name (press RETURN to exit) : ",40)) {
              // Open the input file
              printf("\n");
              if ((fh=fopen(fname,0))==(-1)) {
                      printf("File open failure!\n");
                      continue;
                      }
              // Get the current input string
              while (fgets(curstr,128,fh)==0)
                      // Print the current input string
                      puts(curstr);
              // Test on read errors; close the file
              if (!feof(fh) || fclose(fh))
                      // Read or close error
                      break;
              }
}

where the contents of user-selectable files are listed to the terminal. The continue statement (see also chapter 2.5.4) causes the next iteration of the while loop to begin immediately. The break statement (see also chapter 2.5.4) provides an immediate exit from the while loop.

do-while Statement

The formal syntax of the do-while statement is

do
      statement
while (expression);

where the dependent statement is repeated until the value of the do-while expression is zero (0 or empty string for string expressions). The dependent statement always is executed at least once (contrary to while).

for Statement

The formal syntax of the for statement is

for (expression1; expression2; expression3)
      statement

which is equivalent to

experession1;
while (expression2) {
      statement;
      expression3;
      }

where expression1 is evaluated, and then the dependent statement is executed and expression3 is evaluated until expression2 is zero. I.e., expression1 typically is used for initialization, expression2 applies the loop-end test and expression3 performs something like an increment. Any of the three expressions can be omitted, although the semicolons must remain. The for statement can be utilized as in

void strwords(string s)
{
      string strlist[];
      int strcount,i,j;
      char c;
      for ( strcount=0,j=0,i=0 ; c=s[i++] ; ) {
              if (c==' ' || c=='\t') {
                      if (j>0) {
                              strcount++;
                              j=0;
                              }
                      continue;
                      }
              strlist[strcount][j++]=c;
              }
      for ( i=strcount ; i>=0 ; i-- )
              printf("%s\n",strlist[i]);
}

where the function strwords separates the given string parameter into words to be stored to a list and printed in reverse order afterwards.

forall Statement

The forall statement applies for automatic sequential processing of the currently available elements of an index data type. The formal syntax of the forall statement is

forall (identifier1 of identifier2 where expression)
      statement

where identifier1 must refer to an index variable type specifying the type of the forall index to be processed; the forall statement automatically initializes and "increments" this variable. The forall loop is terminated after the last element of the index list has been processed. The of statement of the forall statement restricts access to those elements of a currently valid index element of the next higher hierarchy level; i.e., the of index must refer to an index type which allows for the processing of the forall index type. The where expression determines whether the dependent statement should be processed for the current forall index (if where expression nonzero) or not (if where expression zero). Both the of statement and the where statement are optional; thus the shortest possible form of a forall statement is

forall (identifier1)
      statement

The forall statement can be utilized as in

index L_CPART part;
index L_CPIN pin;
forall (part where part.USED) {
      forall (pin of part where pin.NET.NAME=="vcc")
              printf("Part %s, Pin %s ;\n",part.NAME,pin.NAME);

where the list of part pins connected to the net vcc is printed to the terminal. See appendix B of this manual for a description of the index variable types.

 

2.5.4 Program Flow Control

Besides the previously described control flow statements Bartels User Language provides some additional structures for controlling the program flow.

break Statement

The formal syntax of the break statement is

break;

The break statement must be dependent to a repetitive statement (while, do-while, for or forall) or to a switch statement (otherwise the Compiler issues an error message). The break statement provides an early exit from a repetition, just as from switch; i.e., break statements cause the innermost enclosing loop (or switch) to be exited immediately.

continue Statement

The formal syntax of the continue statement is

continue;

The continue statement must be dependent to a repetitive statement (while, do-while, for or forall), or otherwise the Compiler issues an error message. The continue statement causes the innermost enclosing loop to restart immediately at the beginning of the loop. I.e., in while and do-while loops, continue causes the end-condition to be tested immediately; in for loops, continue passes control to the execution of the "increment" statement.

Function Call and return Statement

Both the function call facilities and the return statement have been introduced already. These features are so important for controlling the program flow that they are worth a brief separate discussion at this place.

A function call is an expression which performs a jump to the first statement of the corresponding function definition. The return statement can only be used in functions; it terminates the execution of the currently active function and jumps back to the instruction immediately following to the previous function call (i.e., it passes the control flow back to the caller of the function). The general formal syntax of the return statement is

return;

or

return expression;

If the return statement does not contain any expression, then the return value of the corresponding function is undefined. Otherwise the function return value is set to the return expression, which must be compatible to the function data type (otherwise, the Compiler issues an error message). If the end of a function block is encountered, and the last statement has not been a return statement, then the Compiler automatically produces code corresponding to a valid return statement with a function-compatible default (zero) return value. The generation of a default return value is only possible for basic function data types I.e., a function definition as in

struct structname functionname() { }

causes a Compiler error message since the Bartels User Language Interpreter would encounter a memory protection fault when trying to access any of the elements of this return value.

 

2.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.

 

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.

 

2.7 Syntax Definition

The following listing contains a BNF (Backus Naur Form) description of the User Language source code syntax. Comments are delimited by /* and */. The colon (:) corresponds with an assignment operator and is to be read as "is composed of". The vertical line (|) designates alternatives and is to be read as "or". Identifiers are denoted by the IDENT symbol, constants are marked by NUMBER (numeric), SQSTR (character) and DQSTR (string), respectively. The EOLN symbols defines the end-of-line control character. Boldfaced words or character sequences represent the terminal symbols (reserved words and/or operators).

/* User Language Source Code Syntax */

/* Program definition */

program
        : progdefs
        ;

progdefs
        : progdefs progdef
        |
        ;

progdef
        : preproccmd
        | typedef
        | storageclass vardef
        | storageclass fctdef
        ;

/* Preprocessor command */

preproccmd
        : #include DQSTR EOLN
        | #define IDENT optexpr EOLN
        | #undef IDENT EOLN
        | #if expression EOLN
        | #ifdef IDENT EOLN
        | #ifndef IDENT EOLN
        | #else EOLN
        | #endif EOLN
        | #bnf { syntaxdef } 
        | #pragma pragmaval
        ;

pragmaval
        : ULCALLERSTD
        | ULCALLERCAP
        | ULCALLERSCM
        | ULCALLERLAY
        | ULCALLERGED
        | ULCALLERAR
        | ULCALLERCAM
        | ULCALLERCV
        | ULCALLERICD
        | ULCALLERCED
        | ULCALLERNOUNDO
        ;

/* Type definition */

typedef
        : typedef declaration
        ;

/* Variable definition */

vardef
        : typespec initdecs ; 
        ;

/* Function definition */

fctdef
        : fcttype IDENT ( fctpars ) fctpardecs { cmditems }         ;

fcttype
        : typespec
        | void 
        |
        ;

fctpars
        : fctpardefs
        |
        ;

fctpardefs
        : fctpardefs , IDENT
        | IDENT
        ;

fctpardecs
        : fctpardecs vardef
        |
        ;

/* Storage class */

storageclass
        | static 
        | structdef
        ;

/* Type specification */

typespec
        : int 
        | double 
        | char 
        | string 
        | index IDENT
        | structdef
        | IDENT
        ;

/* Struct definition */

structdef
        : struct IDENT
        | struct IDENT { members } 
        | struct { members } 
        ;

members
        : members declaration
        | declaration
        ;

/* Declaration */

declaration
        : typespec decs ; 
        ;

decs
        : decs , declarator
        | declarator
        ;

/* Initialized declarator list */

initdecs
        : initdecs , initdec
        | initdec
        |
        ;

initdec
        : declarator
        | declarator = initializer
        ;

/* Declarator */

declarator
        : IDENT
        | declarator [ ] 
        ;

/* Initializer */

initializer
        : assignment
        | { initializers } 
        ;

initializers
        : initializers , initializer
        | initializer
        ;

/* Command block */

cmdblock
        : { cmditems } 
        | cmditem
        ;

/* Command list */

cmditems
        : cmditems cmditem
        |
        ;

/* Command item */

cmditem
        : preproccmd
        | typedef
        | vardef
        | ifcmd
        | switchcmd
        | forcmd
        | whilecmd
        | docmd
        | forallcmd
        | return optexpr ; 
        | break ; 
        | continue ; 
        | optexpr ; 
        ;

/* If control structure */

ifcmd
        : if ( expression ) cmdblock elsecmd
        ;

elsecmd
        : else cmdblock
        |
        ;

/* Switch control structure */

switchcmd
        : switch ( expression ) { caseblocks } 
        ;

caseblocks
        : caseblocks caseblock
        |
        ;

caseblock
        : cases cmditems
        ;

cases
        : cases case
        | case
        ;

case
        : case expression : 
        | default : 
        ;

/* For control structure */

forcmd
        : for ( optexpr ; optexpr ; optexpr ) cmdblock
        ;

/* While control structure */

whilecmd
        : while ( expression ) cmdblock
        ;

/* Do control structure */

docmd
        : do cmdblock while ( expression ) ; 
        ;

/* Forall control structure */

forallcmd
        : forall ( IDENT forallof forallwhere ) cmdblock
        ;

forallof
        : of IDENT
        |
        ;

forallwhere
        : where expression
        |
        ;

/* Expression */

optexpr
        : expression
        |
        ;

expression
        : expression , assignment
        | assignment
        ;

/* Assignment */

assignment
        : unary = assignment
        | unary |= assignment
        | unary ^= assignment
        | unary &= assignment
        | unary <<= assignment
        | unary >>= assignment
        | unary += assignment
        | unary -= assignment
        | unary *= assignment
        | unary /= assignment
        | unary %= assignment
        | conditional
        ;

/* Conditional evaluation */

conditional
        : log_or
        | log_or ? conditional : conditional
        ;

/* Logical OR */

log_or
        : log_and
        | log_and || log_or
        ;

/* Logical AND */

log_and
        : bit_or
        | bit_or && log_and
        ;

/* Bit OR */

bit_or
        : bit_xor
        | bit_or | bit_xor
        ;

/* Bit Exclusive OR */

bit_xor
        : bit_and
        | bit_xor ^ bit_and
        ;

/* Bit AND */

bit_and
        : equality
        | bit_and & equality
        ;

/* Equivalence comparison */

equality
        : comparison
        | equality == comparison
        | equality != comparison
        ;

/* Comparison */

comparison
        : shift
        | comparison < shift
        | comparison <= shift
        | comparison > shift
        | comparison >= shift
        ;

/* Shift operations */

shift
        : sum
        | shift << sum
        | shift >> sum
        ;

/* Addition and substraction */

sum
        : product
        | sum + product
        | sum - product
        ;

/* Multiplication and division */

product
        : unary
        | product * unary
        | product / unary
        | product % unary
        ;

/* Unary operators */

unary
        : primary
        | primary ++ 
        | primary -- 
        | - unary
        | ! unary
        | ~ unary
        | ++ unary
        | -- unary
        ;

/* Primary operators */

primary
        : IDENT
        | NUMBER
        | SQSTR
        | DQSTR
        | ( expression ) 
        | IDENT ( optexpr ) 
        | primary [ expression ] 
        | primary . IDENT
        ;

/* BNF Precompiler syntax definition */

syntaxdef
        : commentdef grammar
        ;

commentdef
        : COMMENT ( commentdel commentend ) ; 
        |
        ;

commentend
        : , commentdel
        |
        ;

commentdel
        : SQSTR
        | DQSTR
        ;

grammar
        : grammar rule
        | rule
        ;

rule
        : IDENT : forms ; 
        | IDENT : forms | ; 
        ;

forms
        : form | forms
        | form
        ;

form
        : form symbol action
        | symbol action
        ;

symbol
        : IDENT
        | SQSTR
        | DQSTR
        | IDENT 
        | NUMBER 
        | SQSTR 
        | DQSTR 
        | EOLN 
        | EOF 
        | EOFINC 
        | UNKNOWN 
        ;

action
        | ( IDENT ( NUMBER ) ) 
        | ( IDENT ) 
        |
        ;

/* BNF syntax description file end */
Bartels :: Bartels AutoEngineer :: BAE Documentation :: User Language Programmer's Guide :: Language Description

Language Description
© 1985-2025 Oliver Bartels F+E • Updated: 26 January 2007, 17:23 [UTC]

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

Web Development by Baumeister Mediasoft Engineering

make this page your Homepage... add this page to your Favorites... send this page to a friend... display printer friendly page... display sitemap... display sitemap with all page contents... Sprachbeschreibung - Deutsche Version Language Description - English Version