Bartels :: Bartels AutoEngineer :: BAE Documentation :: User Language Programmer's Guide :: Language Description :: Data Types and Definitions |
Bartels User Language - Programmer's Guide2.3 Data Types and Definitions |
![]() |
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.
Bartels :: Bartels AutoEngineer :: BAE Documentation :: User Language Programmer's Guide :: Language Description :: Data Types and Definitions |
Data Types and Definitions
© 1985-2025 Oliver Bartels F+E