Bartels :: Bartels AutoEngineer :: BAE Dokumentation :: User Language Programmierhandbuch :: Sprachbeschreibung :: Kontrollstrukturen |
Bartels User Language - Programmierhandbuch2.5 Kontrollstrukturen |
In diesem Abschnitt werden die in der User Language verfügbaren Kontrollstrukturen vorgestellt. Kontrollstrukturen definieren die Reihenfolge, in der die Aktionen eines Programms ausgeführt werden. Grundsätzlich wird in der Bartels User Language - entsprechend den Grundlagen der strukturierten Programmierung - unterschieden zwischen sequentiellen Programm-Elementen, Alternativen und Repetitionen (englisch: CAR - Concatenation, Alternation, Repetition).
Eine Anweisung besteht aus einem Ausdruck (siehe
Kapitel 2.4) gefolgt von einem Semikolon
(;
). Somit sind z.B.
tabulator = '\t' ; distance = sqrt(a*a+b*b) ; filename += extension = ".ddb" ; ++ary[i] ; printf("Part %s ;\n",partname) ;
gültige Anweisungen. Fehlt der Ausdruck vor dem Semikolon, hat die Anweisung also die Form
;
dann liegt liegt der Sonderfall der leeren Anweisung vor. Die leere Anweisung kann in Ausnahmefällen dazu dienen, abhängige Anweisungen (z.B. innerhalb von Schleifen) zu definieren. Grundsätzlich sind sonst aber nur solche Anweisungen sinnvoll, die auch einen Seiteneffekt haben, d.h. irgendeine Variable durch eine Zuweisung verändern oder eine Funktion aktivieren. In der User Language sind allerdings auch Anweisungen zulässig, die keine Seiteneffekte haben wie z.B.
27+16.3; ++11;
Der User Language Compiler erkennt Ausdrücke ohne Seiteneffekte und gibt ggf. entsprechende Warnungen aus.
Anweisungen, die in einer Kontext-Abhängigkeit zu einer Alternative bzw. Repetition (siehe unten) stehen, werden im Folgenden als abhängige Anweisungen bezeichnet.
Ein Block besteht aus einer Folge von Vereinbarungen (siehe
Kapitel 2.3.2) und Anweisungen und ist in geschweifte Klammern
({
und
}
) einzuschließen. Ein Block ist syntaktisch äquivalent zur einfachen Anweisung, kann also auch immer anstelle einer einfachen Anweisung angegeben werden.
Alternativen sind Verzweigungen, die die Ausführung bestimmter abhängiger Anweisungen vom Wert eines Ausdrucks abhängig machen.
Die
if
-Anweisung hat die allgemeine Form
if (expression) statement
Die abhängige Anweisung der
if
-Anweisung wird nur ausgeführt, wenn der
if
-Ausdruck einen vom Nullwert (0 bzw. Leerstring für
string
-Ausdruck) verschiedenen Wert besitzt; andernfalls wird sie übersprungen.
Die
if-else
-Anweisung besitzt die allgemeine Form
if (expression) statement else statement
Zunächst wird der Ausdruck der
if-else
-Anweisung bewertet. Ist dabei das Resultat vom Null-Wert (0 bzw. Leerstring beim
string
-Ausdruck) verschieden, wird die erste abhängige Anweisung ausgeführt. Die zweite abhängige Anweisung wird genau dann ausgeführt, wenn das Resultat des Ausdrucks dem Null-Wert entspricht. Die abhängigen Anweisungen der
if-else
-Anweisung können selbstverständlich wiederum
if-else
-Anweisungen sein. Damit können auch Verschachtelungen und Ketten von
if-else
-Anweisungen der Form
if (expression) statement else if (expression) { if (expression) statement } else if (expression) statement : else statement
gebildet werden. Bei verschachtelten
if-else
-Anweisungen gehört die
else
-Anweisung immer zum nächstfrüheren
if
, für das noch kein
else
-Teil existiert. In folgendem Beispiel wird die Klasse des aktuell geladenen SCM-Elements abgefragt, und der Wert der Variablen
classname
wird entsprechend gesetzt:
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"); }
Die
switch
-Anweisung sorgt dafür, dass in Abhängigkeit vom Wert eines auf Gleichheit prüfenden Äquivalenz-Ausdrucks bestimmte Anweisungen ausgeführt werden und andere nicht. Die allgemeine Form der
switch
-Anweisung ist gegeben durch
switch (expression) statement
Jeder Anweisung innerhalb der abhängigen
switch
-Anweisung kann eine beliebige Anzahl von
case
-Marken der Form
case expression :
oder
default :
vorausgehen. Die zwischen
case
-Marken befindlichen Anweisungen stehen in Abhängigkeit zu den unmittelbar vorhergehenden
case
-Marken. Die abhängigen Anweisungen einer
case
-Marke werden nur dann ausgeführt, wenn der Wert des
switch
-Ausdrucks mit dem Wert des
case
-Ausdrucks übereinstimmt; die
default
-Marke spezifiziert dabei einen beliebigen Wert, d.h. die einer
default
-Marke folgenden abhängigen Anweisungen kommen immer zur Ausführung.
case
-Marken selbst haben keinen Einfluss auf die sequentielle Ausführung einer Reihe von Anweisungen; die Ausführung wird fortgesetzt, als ob keine
case
-Marken vorhanden sind. Um eine
switch
-Anweisung zu verlassen wird üblicherweise in einem
case
-Abschnitt die
break
-Anweisung (siehe auch
Kapitel 2.5.4) benutzt. In folgendem Beispiel wird die Klasse des aktuell geladenen SCM-Elements abgefragt, und der Wert der Variablen
classname
wird entsprechend gesetzt:
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"); }
Repetitionen sind Anweisungen, mit deren Hilfe Schleifen zur wiederholten, also repetitiven Abarbeitung bestimmter Teile des Programms definiert werden können. Jede repetitive Anweisung verfügt auch über einen Mechanismus zur Überprüfung einer Ende-Bedingung, bei der die Schleifen-Bearbeitung beendet wird. Wird diese Ende-Bedingung nie erreicht, dann spricht man von einer Endlos-Schleife. Läuft ein Programm während der Abarbeitung in eine derartige Endlos-Schleife, dann kann es den Kontrollfluss nicht mehr von sich aus an den Aufrufer zurückgeben. In solchen Fällen liegt in der Regel ein schwerer Programmierfehler vor. Erkennt der User Language Compiler eine Endlos-Schleife, wozu er in bestimmten Fällen in der Lage ist, dann gibt er eine entsprechende Fehlermeldung aus und erzeugt keinen Programm-Code.
Die
while
-Anweisung hat die allgemeine Form
while (expression) statement
Die abhängige Anweisung wird solange wiederholt, wie der Wert des
while
-Ausdrucks ungleich dem Nullwert (0 oder Leerstring für
string
-Ausdruck) ist. Das folgende Programm dient dazu, die Inhalte von ASCII-Dateien auf dem Bildschirm anzuzeigen:
// 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; } }
Die
continue
-Anweisung (siehe auch Kapitel 2.5.4) wird in obigem Beispiel verwendet, um wieder an den Beginn der
while
-Anweisung zu springen; die
break
-Anweisungen (siehe auch
Kapitel 2.5.4) dienen dazu, im Fehlerfall die
while
-Anweisung zu verlassen.
Die
do-while
-Anweisung hat die allgemeine Form
do statement while (expression);
Die abhängige Anweisung wird solange wiederholt, bis der Wert des
do-while
-Ausdrucks gleich dem Nullwert (0 oder Leerstring für
string
-Ausdruck) ist. Die abhängige Anweisung wird also im Unterschied zur
while
-Anweisung mindestens einmal ausgeführt.
Die
for
-Anweisung hat die allgemeine Form
for (expression1; expression2; expression3) statement
und ist äquivalent zu
experession1; while (expression2) { statement; expression3; }
Zunächst wird der erste Ausdruck bewertet. Anschließend werden, solange der zweite Ausdruck nicht gleich dem Nullwert (0 bzw. Leerstring für
string
-Ausdruck) ist, die abhängige Anweisung ausgeführt und der dritte Ausdruck bewertet. Der erste
for
-Ausdruck dient also der Initialisierung, der zweite der Schleifenende-Prüfung, und der dritte typischerweise einer Inkrementierung. Jeder einzelne der drei
for
-Ausdrücke darf auch fehlen. Die folgende Funktion
strwords
zerlegt einen als Parameter übergebenen
string
-Wert in seine einzelnen (durch Blank oder Tabulator) getrennten Worte, speichert diese in einer Liste ab und gibt sie anschließend in umgekehrter Reihenfolge aus:
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]); }
Die
forall
-Anweisung hat die allgemeine Form
forall (identifier1 of identifier2 where expression) statement
Die
forall
-Anweisung dient der automatischen sequentiellen Abarbeitung der aktuell verfügbaren Elemente eines
index
-Datentyps. Der erste Identifier muss eine
index
-Variable referenzieren, über die der Typ des abzuarbeitenden
forall
-Index spezifiziert wird. Die
forall
-Anweisung sorgt selbsttätig für die Initialisierung und Inkrementierung dieser Variablen. Ist keine Inkrementierung mehr möglich, weil das letzte Element der
index
-Liste erreicht ist, dann ist automatisch auch das Ende-Kriterium erreicht und die Programmausführung wird mit der Anweisung, die der
forall
-Anweisung folgt, fortgesetzt. Die
of
-Anweisung innerhalb der
forall
-Anweisung dient der Spezifikation eines dem
forall
-Index hierarchisch übergeordneten
index
-Wertes, d.h. der zweite Identifier muss eine
index
-Variable referenzieren, innerhalb der die Abarbeitung des
forall
-Index per Definition zulässig ist. Der
where
-Ausdruck schließlich dient dazu, zu entscheiden, ob die abhängige Anweisung für den aktuellen
forall
-Index ausgeführt werden soll (Wert des Ausdrucks ungleich dem Nullwert) oder nicht (Wert des Ausdrucks gleich dem Nullwert). Sowohl die
of
-Anweisung als auch die
where
-Anweisung können wahlweise weggelassen werden, so dass die kürzeste Form der
forall
-Anweisung gegeben ist durch
forall (identifier1) statement
Die
index
-Variablen-Typen sind in
Anhang B beschrieben. Folgendes Beispiel gibt für alle platzierten Bauteile der aktuell geladenen physikalischen Netzliste eine Liste der Pins aus, die mit dem Netz
vcc
verbunden sind:
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);
Neben den bisher vorgestellten Kontrollstrukturen verfügt die User Language über einige spezielle Kontrollstrukturen, mit denen der Kontrollfluss zusätzlich gesteuert werden kann.
Die
break
-Anweisung hat die allgemeine Form
break;
Die
break
-Anweisung muss sich in der Abhängigkeit einer repetitiven
(while
,
do-while
,
for
oder
forall
) oder einer
switch
-Anweisung befinden (andernfalls Fehlermeldung durch den Compiler). Sie sorgt für den Abbruch der nächstgelegenen repetitiven bzw.
switch
-Anweisung, d.h. die Ausführung des Programms wird mit der Anweisung fortgesetzt, die der abgebrochenen Anweisung normalerweise folgt.
Die
continue
-Anweisung hat die allgemeine Form
continue;
Die
continue
-Anweisung muss sich in der Abhängigkeit einer repetitiven
(while
,
do-while
,
for
oder
forall
) Anweisung befinden (andernfalls Fehlermeldung durch den Compiler). Sie sorgt dafür, dass die Ausführung des Programms an der Stelle fortgesetzt wird, an der über die Wiederholung der nächstgelegenen repetitiven Anweisung entschieden wird.
Sowohl der Funktionsaufruf als auch die
return
-Anweisung wurden bereits vorgestellt. Sie seien an dieser Stelle jedoch nochmals aufgeführt, um zu verdeutlichen, dass auch sie den Kontrollfluss entscheidend beeinflussen.
Der Funktionsaufruf ist ein Ausdruck, der einen Sprung zur ersten Anweisung der entsprechenden Funktionsdefinition bewirkt. Die
return
-Anweisung darf nur innerhalb von Funktionen verwendet werden. Sie sorgt dafür, dass die Ausführung des Programms unmittelbar nach dem Funktionsaufruf der den Sprung in die Funktion veranlasst hat, fortgesetzt wird, d.h. es erfolgt ein Abbruch der Funktionsausführung, und der Kontrollfluss wird wieder an den Aufrufer der Funktion zurückgegeben. Die allgemeine Form der
return
-Anweisung ist gegeben durch
return;
bzw.
return expression;
Enthält die
return
-Anweisung keinen Ausdruck, dann ist der Rückgabewert des Funktionsaufrufes undefiniert. Im anderen Fall ergibt sich der Rückgabewert der Funktion aus dem
return
-Ausdruck, der typ-kompatibel zu dem für die Funktion definierten Datentyp sein muss (ansonsten Fehlermeldung durch den Compiler). Trifft der
User Language Compiler auf das Ende eines Funktionsblocks, dann erzeugt er, sofern die letzte Anweisung des Blocks nicht eindeutig als
return
-Anweisung erkannt werden kann, an dieser Stelle Programm-Code, der einer
return
-Anweisung (ggf. mit zum Funktionsdatentyp kompatiblem Null-Wert) entspricht. Die Erzeugung eines Nullwertes ist hierbei allerdings nicht für zusammengesetzte Datentypen möglich. D.h., Funktionsdefinitionen der Form
struct structname functionname() { }
provozieren eine Compiler-Fehlermeldung, da der User Language Interpreter beim lesenden Zugriff auf ein Element des Rückgabewertes einer solchen Funktion in jedem Fall eine Speicherzugriffs-Verletzung (Memory Protection Fault) feststellen würde.
Bartels :: Bartels AutoEngineer :: BAE Dokumentation :: User Language Programmierhandbuch :: Sprachbeschreibung :: Kontrollstrukturen |
Kontrollstrukturen
© 1985-2024 Oliver Bartels F+E