| Bartels :: Bartels AutoEngineer :: BAE Documentation :: User Language Programmer's Guide :: Language Description |
Bartels User Language - Programmer's GuideChapter 2
|
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.
| 2.1 | Introducing User Language Programming |
| 2.1.1 | The first User Language Program |
| 2.1.2 | Variables, Arithmetic and Functions |
| 2.1.3 | Arrays and Control Structures |
| 2.2 | Lexical Conventions |
| 2.2.1 | Spacing |
| 2.2.2 | Identifiers |
| 2.2.3 | Constants and Constant Expressions |
| 2.2.4 | Terminal Symbols |
| 2.3 | Data Types and Definitions |
| 2.3.1 | Data Types |
| 2.3.2 | Variables |
| 2.3.3 | Functions |
| 2.3.4 | Scope Rules |
| 2.4 | Expressions |
| 2.4.1 | Primary Expressions |
| 2.4.2 | Unary Expressions |
| 2.4.3 | Binary Expressions |
| 2.4.4 | Expression List |
| 2.4.5 | Precedence and Order of Evaluation |
| 2.5 | Control Structures |
| 2.5.1 | Concatenations |
| 2.5.2 | Alternations |
| 2.5.3 | Repetitions |
| 2.5.4 | Program Flow Control |
| 2.6 | Preprocessor Statements |
| 2.6.1 | File Inclusion |
| 2.6.2 | Constant Definition |
| 2.6.3 | Conditional Compilation |
| 2.6.4 | BNF Precompiler |
| 2.6.5 | Program Caller Type and Undo Mechanism |
| 2.7 | Syntax Definition |
| Tables |
| 2-1 | Character Escape Sequences |
| 2-2 | Reserved Words |
| 2-3 | Operators |
| 2-4 | Operator Precedence and Order of Evaluation |
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.
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
function from the
menu. The program name
(ulprog) must be specified to the corresponding query:
| File |
| Run User Script |
| Program Name ? | ulprog |
After starting the program, the BAE graphic workspace is switched to text output mode, and the message is printed to the screen. Subsequently, the prompt is displayed in the BAE input window. Return key input terminates the User Language program and restores the graphic workspace.
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.
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.
Bartels User Language defines spacing, identifier, constant, reserved word and operator token classes.
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.
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
This section describes the Bartels User Language constant types.
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 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 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 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"
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";
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 |
Table 2-3 lists the Bartels User Language operators. These operators activate special operations regarding to the current program context.
Table 2-3: Operators
! |
!= |
% |
%= |
& |
&& |
&= |
( |
) |
* |
*= |
+ |
++ |
+= |
, |
- |
-- |
-= |
. |
/ |
/= |
: |
; |
< |
<< |
<<= |
<= |
= |
== |
> |
>= |
>> |
>>= |
? |
[ |
] |
^ |
^= |
{ |
| |
|= |
|| |
} |
~ |
Bartels User Language provides the following basic data types:
char | Character taken from the machine's character set |
int | Numeric integer value |
double | Numeric double precision floating point value |
string | Character array |
index | Index to predefined BAE DDB structure |
Bartels User Language provides the following combined data types:
| array | Collection of elements with same data type |
struct | Collection of elements with different data types |
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.
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.
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.
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.
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[];
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.
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.
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);
}
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
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.
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.
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.
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.
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.
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).
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.
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();
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.
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;
}
Unary expressions include all operators evaluating a single operand. These operations are right-associative, i.e., they are evaluated from right to left.
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).
The resulting value of the unary operator
- is the arithmetic negation of its operand, i.e., the operand's value multiplied by (-1).
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.
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.
Binary expressions include all operators evaluating two operands. These operations are left-associative, i.e., they are evaluated from left to right.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 ;
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.
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.
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
| Operation | Operator(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 |
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).
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.
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.
Alternations make decisions according to a special expression value in order to branch to the execution of corresponding dependent (compound) statements.
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.
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.
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.
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.
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).
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.
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.
Besides the previously described control flow statements Bartels User Language provides some additional structures for controlling the program flow.
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.
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.
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.
The
Bartels User Language Compiler contains a preprocessor capable of processing special preprocessor statements. Preprocessor statements must start with a hash
(#) and they are delimited by the end of the corresponding source code line. Preprocessor statements can appear anywhere and have effect which lasts (independent of scope) until the end of the source code program file.
Bartels User Language provides the
#include statement as known from C. The formal syntax of the
#include statement is
#include "filename" EOLN
where
filename must be the name of a
User Language source code file. This statement is terminated by end-of-line. An
#include statement causes the replacement of that statement by the entire contents of the specified
User Language source code file. Such inclusion files can contain general definitions frequently used for different programs. It is a good idea to use include files in order to reduce the expenditure of software maintenance.
#include statements can be nested, unless they do not refer identical file names (data recursion).
When including source code files one should consider, that not all of the therein contained definitions are really needed by a program; thus, it is recommended to run the User Language Compiler with the optimizer to eliminate redundant parts of the program.
The following example shows the include file
baecall.ulh, which contains the function
call for activating
AutoEngineer menu functions:
// baecall.ulh -- BAE menu call facilities
void call(int menuitem) // Call a BAE menu function
{
// Perform the BAE menu call
if (bae_callmenu(menuitem))
{
// Error; print error message and exit from program
perror("BAE menu call fault!");
exit(-1);
}
}
The following example shows the source code of the
zoomall program for executing the
AutoEngineer
command; the program utilizes the
call function from the included
baecall.ulh source file:
// ZOOMALL -- Call the BAE Zoom All command
#include "baecall.ulh" // BAE menu call include
main()
{
// Call Zoom All
call(101);
}
A preprocessor statement of the form
#define IDENT constexpr EOLN
causes the Compiler to replace subsequent instances of the
IDENT identifier with the value of the given constant expression (constexpr). This statement is terminated by end-of-line. The features introduced by the
#define statement are most valuable for definition of substantial constants.
A constant definition introduced by
#define is valid to the end of the program text unless deleted by a preprocessor statement of the form
#undef IDENT EOLN
A special
#define statement form is given by
#define IDENT EOLN
where just a name is defined. The existence or no-existence of such a name definition can be checked with the
#ifdef and
#ifndef preprocessor statements (see also
chapter 2.6.3).
The
#define statement can be applied as in the following examples:
#define DDBCLASSLAYOUT 100 #define mmtoinch 25.4 #define inchtomm 1.0/mmtoinch #define REPABORT "Operation aborted." #define ERRCLASS "Operation not allowed for this element!" #define GERMAN 1 #define ENGLISH 0 #define LANGUAGE GERMAN #define DEBUG
A preprocessor statement of the form
#if constexpr EOLN
causes the Compiler to check, whether the specified constant expression is nonzero. A preprocessor statement of the form
#ifdef IDENT EOLN
causes the Compiler to check, whether the name specified with the identifier is defined (through a
#define statement). A preprocessor statement such as
#ifndef IDENT EOLN
causes the Compiler to check, whether the name specified with the identifier is undefined.
The source code lines following to
#if,
#ifdef or
#ifndef are only compiled if the checked condition is true. Otherwise, the the corresponding source code lines are merely checked for correct syntax.
An
#if preprocessor statement can optionally be followed by a preprocessor statement of the form
#else EOLN
A preprocessor statement of the form
#endif EOLN
terminates the if construct. If the checked condition is true then the source code lines between
#else and
#endif are not compiled. If the checked condition is false then the source code lines between if-statement and
#else (or
#endif on lacking
#else) are not be compiled. Such
#if preprocessor constructs can nest.
The following example illustrates the features of conditional compilation:
#define ENGLISH 0
#define GERMAN 1
#define LANGUAGE ENGLISH
#if LANGUAGE == GERMAN
#define MSGSTART "Programm gestartet."
#endif
#if LANGUAGE == ENGLISH
#define MSGSTART "Program started."
#endif
#define DEBUG
main()
{
#ifdef DEBUG
perror(MSGSTART);
#endif
:
}
A BNF precompiler is integrated to the Bartels User Language. This BNF precompiler with its corresponding scanner and parser functions can be utilized to implement programs for processing almost any foreign ASCII file data format.
Each User Language program text can contain up to one preprocessor statement of the form
#bnf { ... }
which can cover an arbitrary number of source code lines. The
#bnf statement activates the BNF Precompiler of the
Bartels User Language. BNF (Backus Naur Form) is a formalism for describing the syntax of a language. The BNF definition (enclosed with the braces of the
#bnf statement) of a language consists of a sequence of rule defining the language grammar, i.e., the valid word and/or character sequences for building sentences in this language. A rule of the BNF notation consists of a grammar term (non-terminal symbol) and a sequence of one or more alternative formulations, which are assigned to the grammar term by the operator
: (to be read as "consists of"); alternative formulations are separated by the
| operator. A formulation consists of a sequence of grammar terms and input symbols (terminal symbols), whith empty formulations being also allowed. The grammar term of the first rule of a BNF definition is called start symbol. Grammar terms can be referenced recursively, thus allowing for the specification an infinite number of valid sentences of infinite length. Each rule definition must be terminated by the colon operator
(;).
The language's vocabulary is determined by the terminal symbols specified with the BNF definition. The keywords
IDENT (identifier),
NUMBER (numeric constants),
SQSTR (single quoted string),
DQSTR (double quoted string),
EOLN (end of line,
\n),
EOF (end of file and/or end of string scanning strings),
EOFINC (end of include file) and
UNKNOWN (special character sequence not explicitly defined) stand for generalized terminal symbols from in the corresponding word class. Special user-specific terminal symbols must be quoted. The BNF Precompiler applies automatic classification to assign these terminal symbols to either of the word classes keyword (reserved words consisting of three or more characters), single character operator (consisting of one special character) or double character operator (consisting of two special characters).
Keywords can be specified as in
"SECTION" 'ENDSEC' 'Inch' 'begin' "end" 'include'
Double character operators can be specified as in
'<=' '++' "&&" "+=" '::' ':='
Single character operators can be specified as in
'+' '-' "*" "/" "=" "[" "}" '#' '.'
Spacings (blanks, tabulator, newline) are not significant for defining a grammar; they serve just for separating adjacent symbols. Comments belong to the spacing token class as well. For comment type definitions, it is necessary to define comment delimiter operators. On default, the BNF Precompiler assigns the operators
/* (comment start recognition) and
*/ (comment end recognition) for comments which can span multiple lines. This assignment can be changed at the beginning of the BNF definition. The command for the default setting is:
COMMENT ("/*", "*/") ;
Omitting the second parameter from the
COMMENT statement as in
COMMENT ('//');
configures comments which extend to the end of the line. Please note that the
COMMENT default setting is reset if a
COMMENT statement is added to the BNF definition. I.e., the following statements must both be added at the beginning of the BNF definition to configure
/* and
*/ for multi-line comments and
// for comments to end-of-line:
COMMENT ('/*', '*/');
COMMENT ('//');
A special feature of the
Bartels User Language BNF Precompiler is the possibility of defining an action for each grammar term and/or input symbol of a formulation. An action is specified by appending the (parentheses-enclosed) name of a user function to the symbol. The parser automatically calls the referenced user function upon recognition of the corresponding input symbol. These user functions must be properly defined with data type
int; their return value must be (-1) on error or zero otherwise. Up to one action function parameter of type
int is allowed; this parameter must be specified as parentheses-enclosed integer constant after the function name in the BNF definition.
See the Bartels User Language syntax description in chapter 2.7 for a detailed description of the BNF precompiler syntax definition.
The BNF Precompiler compiles the BNF definition to User Language machine code, which represents a state machine for processing text of the defined language. This state machine can be activated with either of the Bartels User Language system functions synparsefile and/or synparsestring. synparsefile activates a parser for processing a file name specified input file, whilst synparsestring can be used to process strings rather than file contents; the referenced parser action functions are automatically called as required. The synscanline and synscanstring system functions can be utilized in these parser action functions for querying the current input scan line number and the input scan string. The current scan string can be subjected to semantic tests. The synparsefile and/or synparsestring functions are terminated if the end of the input file and/or the input string terminator is reached or if a syntax error (or a semantic error encountered by a parser action function) has occurred.
For completeness reasons the system functions
synscnaeoln,
synscanigncase and
synparseincfile are still to be mentioned. The
synscaneoln scan function is used to enable and/or disable the BNF parser's end-of-line recognition, which is disabled on default. The EOLN terminal symbol of a BNF definition can be recognized only if the EOLN recognition is activated with the
synscaneoln function. The
synscanigncase scan function is used to enable and/or disable the BNF parser's case-sensitivity when scanning keyords. The
synparseincfile function can be utilized for processing include files. The parser starts reading at the beginning of the name-specified include file when calling the
synparseincfile function. An
EOFINC terminal symbol is returned if the end of an include file is reached, and reading resumes where it was interrupted in the previously processed input file. The
EOFINC terminal symbol is obsolete if the
synparseincfile function is not used.
See appendix C for a detailed description of the Bartels User Language BNF scanner and parser system functions.
The usage of the BNF Precompiler is illustrated by the following User Language example program. The purpose of this program is to read part placement data from an ASCII file and to perform the corresponding placement on the layout currently loaded to the Bartels AutoEngineer Layout Editor. The input placement data is supposed to be organized according to the following example:
// This is a comment @
LAYOUT # This is a comment extending to the end of line
UNITS {
LENGTH = ( 1.0 INCH ) ;
ANGLE = ( 1.0 DEGREE ) ;
}
PLACEMENT {
'ic1' : 'dil16' {
POSITION = (0.000,0.000) ;
ROTATION = 0.000 ;
MIRROR = 0 ;
}
'ic2' : 'dil16' {
POSITION = (2.250,0.100) ;
}
'ic3' : 'dil16' {
POSITION = (1.000,0.394) ;
ROTATION = 23.500 ;
}
'ic4' : 'so16' {
POSITION = (0.000,0.700) ;
ROTATION = 0.000 ;
MIRROR = 1 ;
}
}
END
The following listing shows a program which utilizes the BNF Precompiler and the corresponding scanner/parser functions in order to load placement data from external files according to the example above:
// READLPLC -- Read Layout Placement from ASCII File
//_________________________________________________________
// BNF input syntax definition
#bnf {
COMMENT ("//", "@") ;
COMMENT ("#") ;
placefile
: "LAYOUT" placeunits placedata "END"
;
placeunits
: "UNITS" "{"
"LENGTH" "=" "(" floatnum placelengthunit ")" ";"
"ANGLE" "=" "(" floatnum placeangleunit ")" ";"
"}"
;
placelengthunit
: "INCH" (p_unitl(1))
| "MM" (p_unitl(2))
| "MIL" (p_unitl(3))
;
placeangleunit
: "DEGREE" (p_unita(1))
| "RAD" (p_unita(2))
;
placedata
: "PLACEMENT" "{" placecommands "}"
;
placecommands
: placecommands placecommand
|
;
placecommand
: identifier (p_pname) ":" identifier (p_plname)
"{" placepos placerot placemirror "}" (p_storepart)
;
placepos
: "POSITION" "="
"(" floatnum (p_px) "," floatnum (p_py) ")" ";"
;
placerot
: "ROTATION" "=" floatnum (p_pa) ";"
|
;
placemirror
: "MIRROR" "=" NUMBER (p_pm) ";"
|
;
identifier
: SQSTR (p_ident)
;
floatnum
: NUMBER (p_fltnum(0))
| "-" NUMBER (p_fltnum(1))
;
}
//______________________________________________________________
// Globals
double plannx=bae_planwsnx(); // Element origin X coordinate
double planny=bae_planwsny(); // Element origin Y coordinate
double lenconv; // Length conversion factor
double angconv; // Angle conversion factor
string curpn; // Current part name
string curpln; // Current physical library name
double curx,cury; // Current coordinates
double cura = 0.0; // Current angle (default: 0.0)
int curm = 0; // Current mirror flag (default: 0)
string curid; // Current identifier
double curflt; // Current float value
struct partdes { // Part descriptor
string pn; // Part name
string pln; // Physical library name
double x,y; // Coordinates
double a; // Angle
int m; // Mirror flag
} pl[]; // Part list
int pn=0; // Part count
//______________________________________________________________
// Main program
main()
{
string fname; // Input file name
// Test if layout loaded
if (bae_planddbclass()!=100)
errormsg("Command not allowed for this element!","");
// Get and test the placement file name
if ((fname=askstr("Placement File : ",40))=="")
errormsg("Operation aborted.","");
// Parse the placement file
perror("Reading placement data...");
parseerr(synparsefile(fname),fname);
// Perform the placement
placement();
// Done
perror("Operation completed without errors.");
}
//______________________________________________________________
// Part list management and placement
void gcpart()
// Get or create some part list entry
{
index L_CPART cpart; // Part index
index L_NREF nref; // Named reference index
int slb=0; // Search lower boundary
int sub=pn-1; // Search upper boundary
int idx; // Search index
int compres; // Compare result
// Loop until search area empty
while (slb<=sub) {
// Get the search index
idx=(slb+sub)>>1;
// Get and test the compare result
if ((compres=strcmp(curpn,pl[idx].pn))==0)
errormsg("Multiple defined part '%s'!",curpn);
// Update the search area
if (compres<0)
sub=idx-1;
else
slb=idx+1;
}
// Check if part is placed already
forall (nref where curpn==nref.NAME)
// Part already placed; abort
return;
// Check net list consistence
forall (cpart where curpn==cpart.NAME) {
// Check the plname
if (curpln!=cpart.PLNAME)
// Netlist definition mismatch
errormsg("Wrong part macro name '%s'!",curpln);
// Done
break;
}
// Insert the new entry to the part list
pn++;
for (idx=pn-2;idx>=slb;idx--)
pl[idx+1]=pl[idx];
pl[slb].pn=curpn;
pl[slb].pln=curpln;
pl[slb].x=curx;
pl[slb].y=cury;
pl[slb].a=cura;
pl[slb].m=curm;
}
void placement()
// Perform the placement
{
int i; // Loop control variable
// Iterate part list
for (i=0;i<pn;i++) {
// Place the part
if (ged_storepart(pl[i].pn,pl[i].pln,
pl[i].x,pl[i].y,pl[i].a,pl[i].m))
errormsg("Error placing part '%s'!",pl[i].pn);
}
}
//______________________________________________________________
// Error handling
void parseerr(status,fn)
// Handle a syntax/parser error
int status; // Scan status
string fn; // File name
{
string msg; // Error message
// Evaluate the scan status
switch (status) {
case 0 : // No error
return;
case 1 :
msg="No BNF definition available!";
break;
case 2 :
msg="Parser already active!";
break;
case 3 :
sprintf(msg," Error opening file '%s'!",fn);
break;
case 4 :
msg="Too many open files!";
break;
case 5 :
sprintf(msg,"[%s/%d] Fatal read/write error!",
fn,synscanline());
break;
case 6 :
sprintf(msg,"[%s/%d] Scan item '%s' too long!",
fn,synscanline(),synscanstring());
break;
case 7 :
sprintf(msg,"[%s/%d] Syntax error at '%s'!",
fn,synscanline(),synscanstring());
break;
case 8 :
sprintf(msg,"[%s/%d] Unexpected end of file!",
fn,synscanline());
break;
case 9 :
sprintf(msg,"[%s/%d] Stack overflow (BNF too complex)!",
fn,synscanline());
break;
case 10 :
sprintf(msg,"[%s/%d] Stack underflow (BNF erroneous)!",
fn,synscanline());
break;
case 11 :
sprintf(msg,"[%s/%d] Error from parse action function!",
fn,synscanline());
break;
default :
sprintf(msg,"Unknown parser error code %d!",status);
break;
}
// Print the error message
errormsg(msg,"");
}
void errormsg(string errfmt,string erritem)
// Print an error message with error item and exit from program
{
string errmsg; // Error message string
// Build and print the error message string
sprintf(errmsg,errfmt,erritem);
perror(errmsg);
// Exit from program
exit(-1);
}
//______________________________________________________________
// Parser action routines
int p_unitl(code)
// Handle the length units definition request
// Returns : zero if done or (-1) on error
{
// Set the length conversion factor
switch (code) {
case 1 : lenconv=cvtlength(curflt,1,0); break; // Inch
case 2 : lenconv=cvtlength(curflt,2,0); break; // mm
case 3 : lenconv=cvtlength(curflt,3,0); break; // mil
default : return(-1); // Error
}
// Return without errors
return(0);
}
int p_unita(code)
// Handle the angle units definition request
// Returns : zero if done or (-1) on error
{
// Set the angle conversion factor
switch (code) {
case 1 : angconv=cvtangle(curflt,1,0); break; // Deg
case 2 : angconv=cvtangle(curflt,2,0); break; // Rad
default : return(-1); // Error
}
// Return without errors
return(0);
}
int p_storepart()
// Handle the store part request
// Returns : zero if done or (-1) on error
{
// Get or create the part list entry
gcpart();
// Re-init the current angle and mirror mode
cura=0.0;
curm=0;
// Return without errors
return(0);
}
int p_pname()
// Receive a part name
// Returns : zero if done or (-1) on error
{
// Store the current part name
strlower(curpn=curid);
// Return without errors
return(0);
}
int p_plname()
// Receive a physical library name
// Returns : zero if done or (-1) on error
{
// Store the current physical library name
strlower(curpln=curid);
// Return without errors
return(0);
}
int p_px()
// Receive a part X coordinate
// Returns : zero if done or (-1) on error
{
// Store the current part X coordinate
curx=curflt*lenconv+plannx;
// Return without errors
return(0);
}
int p_py()
// Receive a part Y coordinate
// Returns : zero if done or (-1) on error
{
// Store the current part Y coordinate
cury=curflt*lenconv+planny;
// Return without errors
return(0);
}
int p_pa()
// Receive a part angle
// Returns : zero if done or (-1) on error
{
// Store the current part angle
cura=curflt*angconv;
// Return without errors
return(0);
}
int p_pm()
// Receive a part mirror flag
// Returns : zero if done or (-1) on error
{
// Get and store the current part mirror flag
curm=atoi(synscanstring())==0?0:1;
// Return without errors
return(0);
}
int p_ident()
// Receive an identifier
// Returns : zero if done or (-1) on error
{
// Store the current string
curid=synscanstring();
// Return without errors
return(0);
}
int p_fltnum(negflag)
// Receive a float value
// Returns : zero if done or (-1) on error
int negflag; // Negative number flag
{
// Get the current float value
curflt=atof(synscanstring());
// Set negative on request
if (negflag)
curflt*=(-1);
// Return without errors
return(0);
}
//______________________________________________________________
// User Language program end
The
#pragma preprocessor statement can be used to set the caller type of the compiled
User Language program. This feature can be used to relax or restrict the compatibility of the
User Language program at compile time, no matter whether module-specific system functions and/or index variable types are refered or not. The following table lists the possible caller type specifications (see also
Appendix A.1.2).
| Caller Type | Valid Interpreter Environment(s) |
|---|---|
ULCALLERSTD | all BAE program modules |
ULCALLERCAP | all Schematic Capture program modules |
ULCALLERSCM | Schematic Editor |
ULCALLERLAY | all Layout program modules |
ULCALLERGED | Layout Editor |
ULCALLERAR | Autorouter |
ULCALLERCAM | CAM Processor |
ULCALLERCV | CAM View |
ULCALLERICD | all IC Design program modules |
ULCALLERCED | Chip Editor |
The
#pragma ULCALLERSTD
preprocessor statement forces the compiled User Language program caller type setting to standard (STD). The User Language Compiler error is suppressed. User Language programs compiled with the above statement can be called in any Bartels User Language Interpreter environment, even if the program code contains system functions or index variable types which are not compatible to that Interpreter environment. This allows for the implementation of programs with conditionally executed environment-specific program code. It is up to the program design to prevent from calling incompatible system functions or accessing invalid index variable types; otherwise the Bartels User Language Interpreter quits the program with a runtime error message.
On default, the execution of a User Language program adds an undo step in the BAE system. The
#pragma ULCALLERNOUNDO
can be used to prevent the system from adding an undo step for the execution of the compiled program. I.e., by declaring
ULCALLERNOUNDO for programs which are not performing any operations relevant to the system's undo mechanism, it is possible to avoid redundant undo steps.
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