|
|
This chapter describes in detail the definition of the Bartels User Language and explains how to write User Language application programs. The Bartels User Language elements are explained in detail, and their usage is illustrated by examples wherever necessary. Additionally, hints are given on how to use the User Language programming environment and how to interface to the Bartels AutoEngineer. Contents
2.1 Introducing User Language ProgrammingThis section provides small programming examples in order to introduce the most important elements of the Bartels User Language. The purpose thereby is to demonstrate - without entering into formal details or describing exceptions - the basic methods of developing User Language programs. 2.1.1 The first User Language ProgramThe 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() { printf("User Language Program"); askstr("Press ENTER to continue ",1); } The above listed
User Language program does just contain the definition of the function
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
After starting the program, the BAE graphic workspace is switched to text output mode, and the User Language program and restores the graphic workspace. message is printed to the screen. Subsequently, the prompt is displayed in the BAE input window. Return key input terminates the2.1.2 Variables, Arithmetic and FunctionsThe 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
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,
Values are calculated within expressions. The equals sign
( A data type must be specified at the definition of functions. In the example above, the
2.1.3 Arrays and Control StructuresThe 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
The
The example above contains some control structures. A
2.2 Lexical ConventionsBartels User Language defines spacing, identifier, constant, reserved word and operator token classes. 2.2.1 SpacingThe spacing token class includes blanks, tabulators, newlines and comments. Comments start with the token
2.2.2 IdentifiersAn 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
( Examples: X_coord value P4 File_Name _euklid 2.2.3 Constants and Constant ExpressionsThis section describes the Bartels User Language constant types. Integer ConstantsNumeric integer constants are associated with the data type
Examples: 1432 073 0xF4A5 9 Floating Point ConstantsFloating point constants are associated with the data type
Examples: 2.54 .78 4. 4.1508E-3 0.81037e6 17228E5 Character ConstantsCharacter constants are associated with the data type
The escape character
Table 2-1: Character Escape Sequences
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,
String ConstantsString constants are associated with the data type
Examples: "IC1" "4.8 kOhm" "This is a string with Newline\n" Constant ExpressionsA constant expression is an expression, which is composed of constant values and operators. Constant expressions are evaluated at compile time already (CEE, Constant Expression Evaluation), i.e., they do not have to be calculated by the Interpreter at runtime. This means, that wherever constants are required, corresponding constant expressions can be used without disadvantages regarding to the program memory or runtime requirements. Examples: int i=19-(010+0x10); CEE: int i=-5; double d=-(4.7+2*16.3); CEE: double d=-37.3; string s="Part"+' '+"IC1"; CEE: string s="Part IC1"; 2.2.4 Terminal SymbolsReserved WordsTable 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
OperatorsTable 2-3 lists the Bartels User Language operators. These operators activate special operations regarding to the current program context. Table 2-3: Operators
2.3 Data Types and Definitions2.3.1 Data TypesBartels User Language provides the following basic data types:
Bartels User Language provides the following combined data types:
Data Type ConversionSome 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
2.3.2 VariablesAll global and local variables must be declared before use. A variable declaration defines the name and the data type of the variable. Such declarations determine, how user-introduced names are to be interpreted by the User Language. Each declaration consists of a data type specification and a list of declarators. Each declarator is composed of the corresponding variable name and an optional initialization. Basic Data TypesThe declaration of
char c; char TAB = '\t', NEWLINE = '\n'; where the
The declaration of
int i, MAXLINELEN = 80; int pincount = 0; where the
The declaration of
double x_coord, y_coord; double MMTOINCH = 1.0/25.4; double starttime = clock(); where the
The declaration of
string s1; string ProgName = "TESTPROGRAM", ProgVer = "V1.0"; string ProgHeader = ProgName+"\t"+ProgVer; where the
The declaration of
index L_MACRO macro; index L_CNET net1, net2; where the
ArraysAn 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
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 s; and char s[]; are synonymous. StructuresA 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
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
elementsize.x=bae_planwsux()-bae_planwslx(); elementsize.y=bae_planwsuy()-bae_planwsly(); element.fname=bae_planfname(); element.ename=bae_planename(); element.class=bae_planddbclass(); element.origin.x=bae_planwsnx(); element.origin.y=bae_planwsny(); element.size=plansize; program.id="UL PROG"; program.version="Version 1.1"; program.reldate.day=4; program.reldate.month="July"; program.reldate.year=1992; The following example illustrates how structure and array definitions and/or declarations can be combined: struct drilldef { index L_DRILL drilltool; struct { double x, y; } drillcoords[]; } drilltable[]; Data Type Renaming
Bartels User Language provides a mechanism for renaming data types. This feature allocates an additional name for an already known data type (but it does not create a new data type). Data type renaming is accomplished by the specification of the keyword
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
2.3.3 FunctionsA function usually is defined for solving a certain sub-problem derived from larger problems. The use of functions can simplify the process of software maintenance considerably since complex operation sequences can be applied repeatedely without the having to code the corresponding instructions time and again. Bartels User Language distinguishes between the predefined system functions and the user-defined functions. Function DefinitionThe 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
Function definition examples: double netroutwidth(index L_CNET net) // Get the routing width of a given net // Returns : width or 0.0 if two pins with different width { index L_CPIN pin; // Pin index int pincnt=0; // Pin count double rw=0.0; // Rout width // Loop through all pins forall (pin of net) { // Test if the pin introduces a new rout width if (pin.RWIDTH!=rw && pincnt++>0) return(0.0); // Set the rout width rw=pin.RWIDTH; } // Return the rout width return(rw); } int allpartsplaced() // Test if all net list parts are placed // Returns : 1 if all parts are placed or zero otherwise { index L_CPART cpart; // Connection part index // Loop through the connection part list forall (cpart where !cpart.USED) // Unplaced part matched return(0); // All parts are placed return(1); } double getdistance(xs,ys,xe,ye) // Get the distance between two points // Returns : the distance length value double xs, ys; // Start point coordinate double xe, ye; // End point coordinate { double xd=xe-xs; // X distance double yd=ye-ys; // Y distance // Calculate and return the distance (Pythagoras) return(sqrt(xd*xd+yd*yd)); } double arclength(r,a1,a2) // Get arc segment length by radius and start-/end-point angle // Returns : the arc segment length value double r; // Radius double a1; // Start point angle (in radians) double a2; // End point angle (in radians) { // Arc; "absolute" angle between start and end point double arc = a1<a2 ? a2-a1 : 2*PI()+a2-a1; // Get and return the arc segment length return(arc*r); } double getangle(cx,cy,x,y) // Get the angle of a circle arc point // Returns : the angle (in radians; range [0,2*PI]) double cx, cy; // Circle center coordinate double x, y; // Circle arc point coordinate { double res; // Result value // Get arc tangent of angle defined by circle point res=atan2(y-cy,x-cx); // Test the result if (res<0.0) // Get the "absolute" angle value res=PI()-res; // Return the result value return(res); } double PI() // Returns the value of PI in radians { // Convert 180 degree and return the result return(cvtangle(180.0,1,2)); } void cputimeuse(rn,st) // Report CPU time usage (in seconds) string rn; // Routine name double st; // Start time { // Print CPU time elapsed since start time printf("(%s) Elapsed CPU Time = %6.1f [Sec]\n",rn,clock()-st); } Function Call and Parameter Value PassingEach 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
Examples for function calls and value passing: // Date structure struct date { int day, month, year; }; // Global program variables string globalstr="Global string"; int fctcallcount=0; // Main program main() { // Local variables of main string resultstr="function not yet called"; struct date today = { 0, 0, 0 }; double p=0.0, b=2.0, e=10.0; // Print the global variables printf("fctcallcount=%d, %s\n",fctcallcount,globalstr); // Print the local variables printf("resultstr=\"%s\"\n",resultstr); printf("today : %d,%d,%d",today.day,today.month,today.year); printf("\t\tb=%.1f, e=%.1f, p=%.1f\n",b,e,p); // Call function resultstr=function(today,b,e,p); // Print the global variables printf("fctcallcount=%d, %s\n",fctcallcount,globalstr); // Print the local variables printf("resultstr=\"%s\"\n",resultstr); printf("today : %d,%d,%d",today.day,today.month,today.year); printf("\t\tb=%.1f, e=%.1f, p=%.1f\n",b,e,p); } string function(curdate,base,exponent,power) struct date curdate; // Current date parameter double base; // Base parameter double exponent; // Exponent parameter double power; // Power parameter { // Increment the function call count fctcallcount++; // Set the global string globalstr="Global string changed by function"; // Get the current date get_date(curdate.day,curdate.month,curdate.year); // Calculate the power power=pow(base,exponent); // Return with a result string return("function result string"); } The example above produces the following output: fctcallcount=0, Global string resultstr="function not yet called" today : 0,0,0 b=2.0, e=10.0, p=0.0 fctcallcount=1, Global string changed by function resultstr="function result string" today : 4,6,92 b=2.0, e=10.0, p=1024.0 Control Flow and Program StructureAfter calling a function, this function keeps control of the program flow until it meets with another function call or a
Recursive FunctionsFunctions can be used recursively, i.e., a function can call itself directly or indirectly. This however is meaningful only if with each recursive function call a condition changes in order to reach a clearly defined final state, which causes a recursion interrupt (otherwise the function is endless-recursive and the program runs "forever"). Recursive programming of functions can save program code and increase the legibility of the source code. However, program runtime and memory requirements increase with recursive programming and endless recursions might be implemented inadvertently. Hence careful considerations should be taken on the use of recursive functions. The Bartels User Language Interpreter eventually encounters an out of memory and/or stack overflow error when processing endless recursive functions since at least a return address must be stored for each function call. 2.3.4 Scope RulesThe 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
To avoid name conflicts, the elements of each object class must have different names inside their corresponding valid scopes. Local object references have higher priority than global object references. 2.4 ExpressionsThis section describes the expression operators provided by the Bartels User Language. The operators are introduced in a sequence according to decreasing operator precedence. The associativity, i.e., the order of evaluation is mentioned as well; each operator is evaluated either from the left to the right or vice versa (depending on the implicit parentheses). The precedence indicates the priority at which the corresponding expression is evaluated. The sequence of evaluation is undefined, unless the precedence takes effect. It is left up to the implementation, to evaluate partial expressions in the most efficient way, even if these expressions cause side effects. The evaluation sequence of the side effects thereby is undefined. Expressions with associative and/or commutative operators can be rearranged arbitrarily. Certain sequences of operator evaluation can be forced by assignments to (temporary) variables. 2.4.1 Primary ExpressionsPrimary expressions include constants, object references, parenthesis-enclosed expressions, function calls, array element accesses, structure element accesses or index element accesses. These operations are left-associative, i.e., they are evaluated from left to right. Constants and Object ReferencesEach suitable arranged identifier referring to an object is a valid primary expression. Integer constants, floating point constants, character constants and string constants are primary expressions as well (see chapter 2.2.3 for the representation of constants). Parenthesis-Enclosed ExpressionsAn expression enclosed with parentheses is a primary expression. Parentheses can be set explicitly to redefine the sequence of evaluation. Since the multiplicative operators have higher precedence than the additive operators, the resulting value of the expression in a + b * c emerges from the sum of variable a plus the product of b*c, while the resulting value of the expression in (a + b) * c emerges from the product of the sum a+b multiplied by the variable c. Function CallA function call consists of the name of the function to be called, followed by a parenthsis-enclosed list of comma-separated expressions. The values of the expression list represent the current parameters for the function call. A function call can be used as primary in any other expression, if the corresponding function returns a non-void value. Examples for typical function calls are init_states(pass=1); printf("This is a message!\n"); printf("Element %s\n",bae_planename()); xpos=nref_xcoord(ask_partname())-bae_planwsnx(); Array Element AccessA primary expression followed by a bracket-enclosed expression again is a primary expression. This operation applies for the access to an array (or
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
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
Structure and Index Element AccessA 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
int macrocnt=0; struct { string name; int class; } macrolist[]; index L_MACRO macro; forall (macro) { macrolist[macrocnt].name=macro.NAME; macrolist[macrocnt++].class=macro.CLASS; } 2.4.2 Unary ExpressionsUnary expressions include all operators evaluating a single operand. These operations are right-associative, i.e., they are evaluated from right to left. Increment and DecrementThe unary increment operator
n = --count ; sets the value of
n = count-- ; sets the value of
Arithmetic NegationThe resulting value of the unary operator
Logical NegationThe resulting value of the unary operator
Bit ComplementThe unary operator
2.4.3 Binary ExpressionsBinary expressions include all operators evaluating two operands. These operations are left-associative, i.e., they are evaluated from left to right. ProductThe operators for multiplication and division produce a product expression. The usual arithmetic type conversions are performed. The binary
The binary
The binary
febdays = (year%4==0 && year%100!=0 || year%400==0) ? 29 : 28 ; where the value of
SumThe operators for addition and subtraction produce a sum expression. The usual type conversions are performed. The binary
The binary
Shift OperationThe binary shift operators
ComparisonThe resulting value of the binary comparison operators
EquivalenceThe resulting value of the binary equivalence operators
Bitwise ANDThe binary bitwise AND operator
Bitwise Exclusive ORThe binary bitwise exclusive OR operator
Bitwise Inclusive ORThe binary bitwise inclusive OR operator
Logical ANDThe logical AND operator
x<100 && fct(x) the
Logical ORThe logical OR operator
test1() || test2() the
Conditional EvaluationThe ternary operator
result = logexpr ? trueexpr : falsexpr ; is equivalent to the following control structure: if (logexpr) result = trueexpr ; else result = falseexpr ; The following example utilizes the conditional expression operator to calculate the maximum of two values: maxval = (val1>=val2) ? val1 : val2 ; Assignments
User Language provides a series of assignment operators, all of which are right-associative. All assignment operators require an unary expression as their left operand; the right operand can be an assignment expression again. The type of the assignment expression corresponds to its left operand. The value of an assignment operation is the value stored in the left operand after the assignment has taken place. The binary
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
2.4.4 Expression ListEach 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
fct ( x, (y=8, y*25.4), z ) has three arguments, the second of which has the value 203.2. 2.4.5 Precedence and Order of EvaluationTable 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
2.5 Control StructuresThis section describes the control flow constructions provided by Bartels User Language. Control flow statements specify the order in which computations are processed. According to the principles of structured programming Bartels User Language distinguishes between concatenation (sequential program element), alternation and repetition (CAR - Concatenation, Alternation, Repetition). 2.5.1 ConcatenationsStatementsA statement consists of an expression (see
chapter 2.4) followed by a semicolon
( 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. BlocksA block consists of a sequence of declarations (see
chapter 2.3.2) and statements and is enclosed with braces
( 2.5.2 AlternationsAlternations make decisions according to a special expression value in order to branch to the execution of corresponding dependent (compound) statements. if- and if-else StatementThe formal syntax of the
if (expression) statement where the dependent statement of the
if (expression) statement1 else statement2 where the
if (expression) statement else if (expression) { if (expression) statement } else if (expression) statement : else statement Since the
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
switch StatementThe
switch (expression) statement Each dependent statement of the
case expression : or default : The statements between the
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
2.5.3 RepetitionsRepetitions are control structures forming a loop for the repetitive computing of certain parts of a program. Each repetitive statement provides a method for testing a certain condition in order to end the processing of the loop. If a program runs into a loop, where the loop-end condition is never reached, then the control flow cannot be passed back to the caller of the program (i.e., the program runs "forever"). This is a fatal programming error, which the User Language Compiler can recognize under certain conditions. while StatementThe formal syntax of the
while (expression) statement where the dependent statement is repeated until the value of the
// 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
do-while StatementThe formal syntax of the
do statement while (expression); where the dependent statement is repeated until the value of the
for StatementThe formal syntax of the
for (expression1; expression2; expression3) statement which is equivalent to experession1; while (expression2) { statement; expression3; } where
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
forall StatementThe
forall (identifier1 of identifier2 where expression) statement where identifier1 must refer to an
forall (identifier1) statement The
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
2.5.4 Program Flow ControlBesides the previously described control flow statements Bartels User Language provides some additional structures for controlling the program flow. break StatementThe formal syntax of the
break; The
continue StatementThe formal syntax of the
continue; The
Function Call and return StatementBoth the function call facilities and the
A function call is an expression which performs a jump to the first statement of the corresponding function definition. The
return; or return expression; If the
struct structname functionname() { } causes a Compiler error message since the Bartels User Language Interpreter would encounter a memory protection fault when trying to access any of the elements of this return value. 2.6 Preprocessor StatementsThe
Bartels User Language Compiler contains a preprocessor capable of processing special preprocessor statements. Preprocessor statements must start with a hash
( 2.6.1 File Inclusion
Bartels User Language provides the
#include "filename" EOLN where
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
// 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
// ZOOMALL -- Call the BAE Zoom All command #include "baecall.ulh" // BAE menu call include main() { // Call Zoom All call(101); } 2.6.2 Constant DefinitionA 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
A constant definition introduced by
#undef IDENT EOLN A special
#define IDENT EOLN where just a name is defined. The existence or no-existence of such a name definition can be checked with the
The
#define DDBCLASSLAYOUT 100 #define mmtoinch 25.4 #define inchtomm 1.0/mmtoinch #define REPABORT "Operation aborted." #define ERRCLASS "Operation not allowed for this element!" #define GERMAN 1 #define ENGLISH 0 #define LANGUAGE GERMAN #define DEBUG 2.6.3 Conditional CompilationA 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
#ifndef IDENT EOLN causes the Compiler to check, whether the name specified with the identifier is undefined. The source code lines following to
An
#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
The following example illustrates the features of conditional compilation: #define ENGLISH 0 #define GERMAN 1 #define LANGUAGE ENGLISH #if LANGUAGE == GERMAN #define MSGSTART "Programm gestartet." #endif #if LANGUAGE == ENGLISH #define MSGSTART "Program started." #endif #define DEBUG main() { #ifdef DEBUG perror(MSGSTART); #endif : } 2.6.4 BNF PrecompilerA 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
The language's vocabulary is determined by the terminal symbols specified with the BNF definition. The keywords
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 ("/*", "*/") ; Omitting the second parameter from the
COMMENT ('//'); configures comments which extend to the end of the line. Please note that the
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
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
See appendix C for a detailed description of the Bartels User Language BNF scanner and parser system functions. The usage of the BNF Precompiler is illustrated by the following User Language example program. The purpose of this program is to read part placement data from an ASCII file and to perform the corresponding placement on the layout currently loaded to the Bartels AutoEngineer Layout Editor. The input placement data is supposed to be organized according to the following example: // This is a comment @ LAYOUT # This is a comment extending to the end of line UNITS { LENGTH = ( 1.0 INCH ) ; ANGLE = ( 1.0 DEGREE ) ; } PLACEMENT { 'ic1' : 'dil16' { POSITION = (0.000,0.000) ; ROTATION = 0.000 ; MIRROR = 0 ; } 'ic2' : 'dil16' { POSITION = (2.250,0.100) ; } 'ic3' : 'dil16' { POSITION = (1.000,0.394) ; ROTATION = 23.500 ; } 'ic4' : 'so16' { POSITION = (0.000,0.700) ; ROTATION = 0.000 ; MIRROR = 1 ; } } END The following listing shows a program which utilizes the BNF Precompiler and the corresponding scanner/parser functions in order to load placement data from external files according to the example above: // READLPLC -- Read Layout Placement from ASCII File //_________________________________________________________ // BNF input syntax definition #bnf { COMMENT ("//", "@") ; COMMENT ("#") ; placefile : "LAYOUT" placeunits placedata "END" ; placeunits : "UNITS" "{" "LENGTH" "=" "(" floatnum placelengthunit ")" ";" "ANGLE" "=" "(" floatnum placeangleunit ")" ";" "}" ; placelengthunit : "INCH" (p_unitl(1)) | "MM" (p_unitl(2)) | "MIL" (p_unitl(3)) ; placeangleunit : "DEGREE" (p_unita(1)) | "RAD" (p_unita(2)) ; placedata : "PLACEMENT" "{" placecommands "}" ; placecommands : placecommands placecommand | ; placecommand : identifier (p_pname) ":" identifier (p_plname) "{" placepos placerot placemirror "}" (p_storepart) ; placepos : "POSITION" "=" "(" floatnum (p_px) "," floatnum (p_py) ")" ";" ; placerot : "ROTATION" "=" floatnum (p_pa) ";" | ; placemirror : "MIRROR" "=" NUMBER (p_pm) ";" | ; identifier : SQSTR (p_ident) ; floatnum : NUMBER (p_fltnum(0)) | "-" NUMBER (p_fltnum(1)) ; } //______________________________________________________________ // Globals double plannx=bae_planwsnx(); // Element origin X coordinate double planny=bae_planwsny(); // Element origin Y coordinate double lenconv; // Length conversion factor double angconv; // Angle conversion factor string curpn; // Current part name string curpln; // Current physical library name double curx,cury; // Current coordinates double cura = 0.0; // Current angle (default: 0.0) int curm = 0; // Current mirror flag (default: 0) string curid; // Current identifier double curflt; // Current float value struct partdes { // Part descriptor string pn; // Part name string pln; // Physical library name double x,y; // Coordinates double a; // Angle int m; // Mirror flag } pl[]; // Part list int pn=0; // Part count //______________________________________________________________ // Main program main() { string fname; // Input file name // Test if layout loaded if (bae_planddbclass()!=100) errormsg("Command not allowed for this element!",""); // Get and test the placement file name if ((fname=askstr("Placement File : ",40))=="") errormsg("Operation aborted.",""); // Parse the placement file perror("Reading placement data..."); parseerr(synparsefile(fname),fname); // Perform the placement placement(); // Done perror("Operation completed without errors."); } //______________________________________________________________ // Part list management and placement void gcpart() // Get or create some part list entry { index L_CPART cpart; // Part index index L_NREF nref; // Named reference index int slb=0; // Search lower boundary int sub=pn-1; // Search upper boundary int idx; // Search index int compres; // Compare result // Loop until search area empty while (slb<=sub) { // Get the search index idx=(slb+sub)>>1; // Get and test the compare result if ((compres=strcmp(curpn,pl[idx].pn))==0) errormsg("Multiple defined part '%s'!",curpn); // Update the search area if (compres<0) sub=idx-1; else slb=idx+1; } // Check if part is placed already forall (nref where curpn==nref.NAME) // Part already placed; abort return; // Check net list consistence forall (cpart where curpn==cpart.NAME) { // Check the plname if (curpln!=cpart.PLNAME) // Netlist definition mismatch errormsg("Wrong part macro name '%s'!",curpln); // Done break; } // Insert the new entry to the part list pn++; for (idx=pn-2;idx>=slb;idx--) pl[idx+1]=pl[idx]; pl[slb].pn=curpn; pl[slb].pln=curpln; pl[slb].x=curx; pl[slb].y=cury; pl[slb].a=cura; pl[slb].m=curm; } void placement() // Perform the placement { int i; // Loop control variable // Iterate part list for (i=0;i<pn;i++) { // Place the part if (ged_storepart(pl[i].pn,pl[i].pln, pl[i].x,pl[i].y,pl[i].a,pl[i].m)) errormsg("Error placing part '%s'!",pl[i].pn); } } //______________________________________________________________ // Error handling void parseerr(status,fn) // Handle a syntax/parser error int status; // Scan status string fn; // File name { string msg; // Error message // Evaluate the scan status switch (status) { case 0 : // No error return; case 1 : msg="No BNF definition available!"; break; case 2 : msg="Parser already active!"; break; case 3 : sprintf(msg," Error opening file '%s'!",fn); break; case 4 : msg="Too many open files!"; break; case 5 : sprintf(msg,"[%s/%d] Fatal read/write error!", fn,synscanline()); break; case 6 : sprintf(msg,"[%s/%d] Scan item '%s' too long!", fn,synscanline(),synscanstring()); break; case 7 : sprintf(msg,"[%s/%d] Syntax error at '%s'!", fn,synscanline(),synscanstring()); break; case 8 : sprintf(msg,"[%s/%d] Unexpected end of file!", fn,synscanline()); break; case 9 : sprintf(msg,"[%s/%d] Stack overflow (BNF too complex)!", fn,synscanline()); break; case 10 : sprintf(msg,"[%s/%d] Stack underflow (BNF erroneous)!", fn,synscanline()); break; case 11 : sprintf(msg,"[%s/%d] Error from parse action function!", fn,synscanline()); break; default : sprintf(msg,"Unknown parser error code %d!",status); break; } // Print the error message errormsg(msg,""); } void errormsg(string errfmt,string erritem) // Print an error message with error item and exit from program { string errmsg; // Error message string // Build and print the error message string sprintf(errmsg,errfmt,erritem); perror(errmsg); // Exit from program exit(-1); } //______________________________________________________________ // Parser action routines int p_unitl(code) // Handle the length units definition request // Returns : zero if done or (-1) on error { // Set the length conversion factor switch (code) { case 1 : lenconv=cvtlength(curflt,1,0); break; // Inch case 2 : lenconv=cvtlength(curflt,2,0); break; // mm case 3 : lenconv=cvtlength(curflt,3,0); break; // mil default : return(-1); // Error } // Return without errors return(0); } int p_unita(code) // Handle the angle units definition request // Returns : zero if done or (-1) on error { // Set the angle conversion factor switch (code) { case 1 : angconv=cvtangle(curflt,1,0); break; // Deg case 2 : angconv=cvtangle(curflt,2,0); break; // Rad default : return(-1); // Error } // Return without errors return(0); } int p_storepart() // Handle the store part request // Returns : zero if done or (-1) on error { // Get or create the part list entry gcpart(); // Re-init the current angle and mirror mode cura=0.0; curm=0; // Return without errors return(0); } int p_pname() // Receive a part name // Returns : zero if done or (-1) on error { // Store the current part name strlower(curpn=curid); // Return without errors return(0); } int p_plname() // Receive a physical library name // Returns : zero if done or (-1) on error { // Store the current physical library name strlower(curpln=curid); // Return without errors return(0); } int p_px() // Receive a part X coordinate // Returns : zero if done or (-1) on error { // Store the current part X coordinate curx=curflt*lenconv+plannx; // Return without errors return(0); } int p_py() // Receive a part Y coordinate // Returns : zero if done or (-1) on error { // Store the current part Y coordinate cury=curflt*lenconv+planny; // Return without errors return(0); } int p_pa() // Receive a part angle // Returns : zero if done or (-1) on error { // Store the current part angle cura=curflt*angconv; // Return without errors return(0); } int p_pm() // Receive a part mirror flag // Returns : zero if done or (-1) on error { // Get and store the current part mirror flag curm=atoi(synscanstring())==0?0:1; // Return without errors return(0); } int p_ident() // Receive an identifier // Returns : zero if done or (-1) on error { // Store the current string curid=synscanstring(); // Return without errors return(0); } int p_fltnum(negflag) // Receive a float value // Returns : zero if done or (-1) on error int negflag; // Negative number flag { // Get the current float value curflt=atof(synscanstring()); // Set negative on request if (negflag) curflt*=(-1); // Return without errors return(0); } //______________________________________________________________ // User Language program end 2.6.5 Program Caller Type and Undo MechanismProgram Caller Type SettingThe
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. Configuring Undo MechanismOn 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
2.7 Syntax DefinitionThe following listing contains a BNF (Backus Naur Form) description of the
User Language source code syntax. Comments are delimited by
/* User Language Source Code Syntax */ /* Program definition */ program : progdefs ; progdefs : progdefs progdef | ; progdef : preproccmd | typedef | storageclass vardef | storageclass fctdef ; /* Preprocessor command */ preproccmd :
Language Description |
|