|
|
Der
Bartels User Language Compiler kann spezielle Preprozessor-Anweisungen zur Steuerung der Quelltextabarbeitung verarbeiten. Derartige Anweisungen beginnen mit dem Zeichen
2.6.1 DateieinbindungIm Sprachumfang der
User Language ist die aus Standard C bekannte
#include "filename" EOLN Die
// 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); } } Das folgende Beispiel zeigt den Source-Code für das Programm
zoomall zur Ausführung des
-Kommandos im
AutoEngineer; hierbei wird die über das Include-File
baecall.ulh eingebundene Routine
// ZOOMALL -- Call the BAE Zoom All command #include "baecall.ulh" // BAE menu call include main() { // Call Zoom All call(101); } 2.6.2 KonstantendefinitionEine Preprozessor-Anweisung der Form #define IDENT constexpr EOLN sorgt dafür, dass im nachfolgenden Programmtext der über IDENT spezifizierte Identifier jeweils durch den Wert des konstanten Ausdrucks (constexpr) ersetzt wird. Die Anweisung wird durch das Zeilenende abgeschlossen. Diese Anweisung eignet sich insbesondere zur Definition wesentlicher Konstanten. Die Gültigkeit der Konstantendefinition erstreckt sich bis zum Ende des Programmtextes, sofern die Definition nicht vorher durch eine Preprozessor-Anweisung der Form #undef IDENT EOLN gelöscht wird. Eine besondere Form der
#define IDENT EOLN gegeben. Hierbei wird lediglich ein Name definiert, dessen Existenz durch die Preprozessor-Anweisungen
Typische Beispiele für die Verwendung der
#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 Bedingte ÜbersetzungEine Preprozessor-Anweisung der Form #if constexpr EOLN überprüft, ob der angegebene konstante Ausdruck einen von Null verschiedenen Wert besitzt. Eine Preprozessor-Anweisung der Form #ifdef IDENT EOLN überprüft, ob der über den Identifier spezifizierte Name (durch eine
#ifndef IDENT EOLN überprüft, ob der über den Identifier spezifizierte Name unbekannt ist. Die auf
Auf eine If-Preprozessor-Anweisung kann optional eine Prepozessor-Anweisung der Form #else EOLN folgen. Durch eine Preprozessor-Anweisung der Form #endif EOLN wird die entsprechende If-Konstruktion abgeschlossen. Wenn die
If-Bedingung erfüllt ist, dann werden alle Zeilen zwischen
Das folgende Beispiel illustriert die Möglichkeiten der bedingten Übersetzung: #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-PrecompilerIn die Bartels User Language ist ein BNF-Precompiler zur Realisierung von Interface-Programmen für die Bearbeitung von Fremddatenformaten integriert. Unter Verwendung der zugehörigen Scanner- und Parserfunktionen lassen sich in einfacher und eleganter Weise Programme zur Verarbeitung praktisch beliebiger ASCII-Datenformate implementieren. Jeder User Language-Programmtext darf eine Preprozessor-Anweisung der Form #bnf { ... } enthalten. Das
Die in der BNF-Definition angegebenen Terminalsymbole definieren den Wortschatz, d.h. die Menge der zulässigen Worte der Sprache. Hierbei kennzeichnen die Schlüsselwörter
Beispiele für die Spezifikation von Schlüsselwörtern sind: "SECTION" 'ENDSEC' 'Inch' 'begin' "end" 'include' Beispiele für die Spezifikation von Double Character Operatoren sind: '<=' '++' "&&" "+=" '::' ':=' Beispiele für die Spezifikation von Single Character Operatoren sind: '+' '-' "*" "/" "=" "[" "}" '#' '.' Zwischenräume (Leerzeichen, Tabulatoren, Zeilentrenner) sind üblicherweise bei der Definition einer Grammatik nicht signifikant; sie dienen lediglich der Trennung von benachbarten Symbolen. Auch Kommentare stellen letztendlich nichts anderes als Zwischenräume dar. Von Bedeutung ist in in diesem Zusammenhang lediglich die Definition entsprechender Kommentarbegrenzungs-Operatoren.
Per Default trägt der BNF-Precompiler hierfür die Operatoren
COMMENT ('/*', '*/'); Durch die Angabe nur eines Kommentarbegrenzungsoperators wie z.B. in COMMENT ('//'); werden Kommentare konfiguriert, die sich bis zum Zeilenende erstrecken. Es ist zu beachten, dass die Kommentardefaulteinstellung verloren geht, sobald auch nur eine
COMMENT ('/*', '*/'); COMMENT ('//'); Eine Besonderheit des
Bartels User Language BNF-Precompilers besteht darin, dass zu jedem in einer Formulierung aufgeführten Grammatikbegriff bzw. Eingabesymbol optional eine Aktion definiert werden kann. Die Definition einer Aktion besteht aus der in runden Klammern
( Die exakte Definition der Syntax der BNF-Notation ist in der Syntaxdefinition der Bartels User Language (siehe Kapitel 2.7) enthalten. Der BNF-Precompiler übersetzt die BNF-Definition in User Language-Maschinencode, der eine State-Maschine zur Abarbeitung eines Textes in der definierten Sprache festlegt. Zur Aktivierung dieser State-Maschine stehen die User Language-Systemfunktionen synparsefile und synparsestring zur Verfügung. Die Funktion synparsefile aktiviert einen Parser zur Abarbeitung einer durch Dateinamen spezifizierten Eingabedatei während mit der Funktion synparsestring eine Zeichenkette anstelle eines Dateiinhaltes abgearbeitet werden kann. Beide Funktionen lösen nach Bedarf automatisch die definierten Aktionen bzw. Anwenderfunktionen aus. Innerhalb dieser Anwenderfunktionen kann mit der Systemfunktion synscanline die aktuelle Zeilennummer der Eingabedatei und mit synscanstring die aktuell eingelesene Zeichenkette ermittelt werden, wobei die aktuelle Zeichenkette nach Bedarf auch einer semantischen Prüfung unterzogen werden kann. Die Funktionen synparsefile bzw. synparsestring werden beendet, sobald das Ende der Eingabedatei bzw. des abzuarbeitenden Strings erreicht ist oder ein Syntaxfehler (bzw. ein durch eine Anwenderfunktion erkannter semantischer Fehler) aufgetreten ist. Der Vollständigkeit halber sei an dieser Stelle noch auf die Systemfunktionen
synscaneoln,
synscanigncase und
synparseincfile hingewiesen. Mit der Scannerfunktion
synscaneoln kann die Zeilenendeerkennung des BNF-Parsers aktiviert bzw. deaktiviert werden (default: Zeilenendeerkennung deaktiviert). Die Verwendung des Terminalsymbols
Die genauen Beschreibungen der Scanner- und Parserfunktionen finden sich im Anhang C. Die Funktionalität des BNF-Precompilers soll an dieser Stelle anhand eines Beispiels zur Übernahme von Bauteil-Platzierungsdaten in das aktuell im Layouteditor geladene Layout verdeutlicht werden. Die Eingabedaten sollen dabei nach folgendem Schema aufgebaut sein: // 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 Das Programm zum Einlesen von Platzierungsdaten aus externen Dateien gemäß obigem Beispiel kann unter Verwendung des BNF-Precompilers und der Scanner- und Parserfunktionen auf folgende Weise implementiert werden: // 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 Programmaufruftyp und Undo-MechanismusSetzen des ProgrammaufruftypsÜber die Preprozessoranweisung
Mit der Preprozessoranweisung #pragma ULCALLERSTD kann der Aufruftyp des generierten
User Language-Programms explizit auf Standard (STD) gesetzt werden. Ohne obige Preprozessoranweisung quittiert der
User Language Compiler die Verwendung von Funktionen und Indexvariablen zueinander inkompatibler Aufruftypen mit der Fehlermeldung
. Bei Verwendung obiger
Undo-Mechanismus konfigurierenDie Ausführung eines User Language-Programms generiert per Standard einen Undoschritt im BAE-System. Die Präprozessoranweisung #pragma ULCALLERNOUNDO deaktiviert den Undo-Mechanismus für das kompilierte Programm. Damit können redundante Undoschritte für Programme, die keine für das Undo relevanten Operationen ausführen, vermieden werden.
Preprozessor-Anweisungen |
|