System: BeOS, Haiku, Zeta

1. Einleitung

Yab basiert auf dem Yabasic Interpreter (http://www.yabasic.de). Auf der Yabasic Webseite finden Sie zusätzliche Quellen, wie man den Interpreter erweitert (Titel: "Guide into the Guts of Yabasic"). Dennoch soll dieses Dokument eine ausreichende Einleitung sein. Sie benötigen für das Hinzufügen neuer Befehle in yab grundlegendes Wissen über yab, C, C++ und der BeAPI. Wissen über Flex und Bison sind nicht zwingend notwendig.

2. Entwerfen eines Befehls

Ein typischer yab Befehl ist normalerweise entweder eine Prozedur (also eine Funktion die void zurückgibt), eine Funktion, die eine Zahl zurück gibt (double oder int) oder eine Funktion, die eine Zeichenkette (char*) zurückgibt.

Ein Befehl besteht aus

- dem Befehlswort (lexikalischer Einheit/token)
- der Befehlsregel (Grammatik)
- die gekapselte C-Funktion, die die C++ Funktion Methode aufruft
- die C++ Methode

Die Details hierzu werden ausführlich in den nächsten Abschnitten beschrieben.

2.1 Die Befehlswörter

Die benötigten Wörter für einen Befehl (tokens/Terminale) werden in der Datei yabasic.flex definiert. Kontrollieren Sie bitte vorher, ob es nicht schon ein Befehlswort gibt, was genau Ihren Befehl beschreibt. Somit werden unnötige doppelte Befehle vermieden.

Ein Befehlswort besteht aus dem Wort selbst (in Großbuchstaben) und aus dem Zeichen, das es zurückgeben soll. Das Zeichen ist häufig einfach das Wort selbst mit einem t davor.

Beispiel:

BUTTON return tBUTTON;

Neue Befehle (Tokens) müssen in der Datei yabasic.bison beschrieben werden. Dazu fügen Sie den Befehlsnamen am Anfang der Datei in der Befehlliste hinzu.

Es gibt bei den Befehlswörtern Ausnahmen, z.B. tGet stellt das Befehlswort GET$ dar, während tGETNUM GET darstellt.

2.2 Die Befehlsregeln (Grammatik)

Die aktuelle Befehlsregeln (Grammatik) muss auch in der Datei yabasic.bison hinzugefügt werden. Wenn Sie sich die Datei anschauen, bekommen Sie eine gute Übersicht, wie die Grammatik aussehen soll.

Die folgenden drei Abschnitte sind interessant:

  1. der Abschnitt für Prozeduren
  2. der Abschnitt für numerische Funktionen
  3. der Abschnitt für Funktionen, die Zeichenketten verarbeiten.

Wir beschreiben in den nächsten beiden Unterabschnitten die Unterschiede zwischen den Verfahren und den Funktionen.

Eine Befehlsregel wird am Anfang mit einem senkrechten Strich | (pipe) geschrieben, gefolgt von einem Zeichen und dem Befehlswort. Hinter dem Wort werden die Argumente geschrieben. Hinter dem letzten Argument folgt noch ein, in zwei geschweifte Klammern gefasster C Funktionsname z.B. {add_command(cBUTTON,NULL);} . Dieser Bezeichner wird mit einem Semikolon beendet.

Beispiel:

| tBUTTON coordinates to coordinates ',' string_expression ',' string_expression ',' string_expression {add_command(cBUTTON,NULL);}

Es zwei Arten von Argumenten:

Das eine Argument ist eine expression, das ist immer eine Zahl von Typ double. oder
das Argument ist eine string_expression, also eine Zeichenkette von Type char*.

Sie können durch Kommata getrennt werden ",". Klammern ("(" und ")") sind auch möglich, aber ich habe sie so gut wie nie benutzt.
In diesem Beispiel hat der Befehl Button 7 Argumente, 4 Zahlen für die Koordinaten und 3 Zeichenketten. Die Zeichenketten enthalten die Identifikation des Buttons, den Buttontext und den View (auf dem der Button nachher liegen soll).

2.3 Prozeduren

Die Prozedur in dem obigen Beispiel gibt keinen Wert zurück, sondern es ist eine void-Funktion in C. Die Prozedur enthält einen internen Bezeichner, in dem obigen Beispiel cBUTTON. Dieser Bezeichner muss in der Datei yabasic.h beschrieben werden. Um den genauen Einfügepunkt für den Bezeichner zu finden, durchsuchen Sie die Datei nach ähnlichen Bezeichner und fügen Sie dann dementsprechenden an der Stelle den Bezeichner ein.

Außerdem muss in der Datei yabasic.h der Name der C-Funktion, die in der Datei graphic.c hinzugefügt wird, beschrieben werden. Prozeduren erhalten immer einen struct command *, YabInterface * als Argument. Der struct command enthält Informationen über die yab Argumente, Zeilennummer usw.
In graphic.c leitet die C-Funktion diese Informationen einfach an die C++ Klasse weiter (YabInterface).

Vor dem Hinzufügen der Funktion in graphic.c, müssen Sie die Funktion auch in der Datei main.c hinzufügen. Fügen Sie die Funktion der switch Abfrage hinzu, die entsprechend ihrem Bezeichner die Funktion benennt. z.B.:

case cBUTTON
    createbutton(current, yab); DONE;

Das folgende Beispiel zeigt eine typische Void Funktion in der Datei graphic.c:

void createbutton(struct command *cmd, YabInterface* yab)
{
   double x1,y1,x2,y2;
   char *id, *title, *view;

   view = pop(stSTRING)->pointer;
   title = pop(stSTRING)->pointer;
   id = pop(stSTRING)->pointer;
   y2=pop(stNUMBER)->value;
   x2=pop(stNUMBER)->value;
   y1=pop(stNUMBER)->value;
   x1=pop(stNUMBER)->value;

   yi_SetCurrentLineNumber(cmd->line, (const char*)cmd->lib->s, yab);
   yi_CreateButton(x1,y1,x2,y2, id, title, view, yab);
}

In unserem Beispiel zuerst werden die 7 yab Argumente vom Befehl struct zurückgeholt.

Die Argumente werden auf einem Stack gelagert, also müssen Sie sie im umgekehrten Reihenfolge zurückholen! Hier wird z.B. y1 vor x1 zurückgeholt. Auch Zeichenketten und Zahlen haben unterschiedliche pop calls.

Zahlen können entweder von Typ double oder int sein, aber für Koordinaten sollte man immer double benutzten.

Die aktuelle Zeilennummer wird der YabInterface Klasse hinzugefügt, indem man yi_SetCurrentLineNumber aufruft. Diese Zeile ist für alle void Funktionen gleich.

Schließlich werden die Argumente an die YabInterface Klasse weitergeleitet, indem man yi_CreateButton aufruft.

2.4 Funktionen (Functions)

Funktionen, die entweder eine Zahl oder eine Zeichenkette zurückgeben, werden unterschiedlich eingefügt. Zunächst erhalten sie einen anderen Bezeichner. Dieser beginnt mit einem f z.B.. fLISTBOXGETNUM. Dieser Bezeichner muss in yabasic.h in den enum Funktionen beschrieben werden

Die Funktionen werden durch die Anzahl ihrer Argumente sortiert!

Wie schon zuvor, muss der Name der C-Funktion in die Datei graphic.c hinzugefügt und auch in yabasic.h beschrieben werden. Anders als bei den Prozeduren, bekommt die Funktion ihre Argument sofort, z.B.:

int listboxgetnum(const char*, YabInterface *yab, int line, const char* libname);

listboxgetnum gibt ein int bei einer Zeichenkette als Argument zurück. Die weiteren Argumente YabInterface *yab, int line, const char* libname müssen hinzugefügt werden, um die Informationen der YabInterface Klasse zur Verfügung zu stellen.int listboxgetnum(const char*, YabInterface *yab, int line, const char* libname);

Anders als bei den Prozeduren sind die Argumente und der Funktionsaufruf in der Datei function.c gespeichert. Diese Argumente werden vom Stack zurückgeholt und an die Funktion in graphic.c weitergegeben.

Beispiel:

case fLISTBOXGETNUM:
   str=a1->pointer;
   value = listboxgetnum(str, yab, linenum, current->lib->s);
   result = stNUMBER;
break;

Das Zeichenkettenargument wird durch den a1->pointer zurückgegeben. Numerische Argumente werden durch z.B. a3->value adressiert (nicht in diesem Beispiel). Die Argumente werden von a1 bis a6 nummeriert. Mehr Argumente werden z.Z. nicht unterstützt.

Das Resultat wird als Zahl gespeichert. Das Resultat für Zeichenketten wird im stString (pointer and result = stSTRING; ) gespeichert. Damit Sie eine Vorstellung bekommen, wie andere Befehle aussehen, schauen Sie sich die Datei an.

Schließlich muss die Wrapper-Funktion in graphic.c. eingefügt werden. Der folgende Code zeigt Ihnen wie es aussehen könnte:

int listboxgetnum(const char* id, YabInterface *yab, int line, const char* libname)
{
   yi_SetCurrentLineNumber(line, libname, yab);
   return yi_ListboxGetNum(id, yab);
}

Die Zeilennummer wird an die YabInterface Klasse weitergeleitet, indem man die Werte an die Funktion yi_SetCurrentLineNumber übergibt. Diese Zeile ist für alle Funktionen gleich. Die zurückgegebene Zahl wird dann einfach durch yi_ListboxgetNum weitergeleitet.

Anmerkung:Zeichenketten müssen mit my_strdup kopiert werden, z.B. return my_strdup ((char*) yi_CheckMessages (yab)); Bedenken Sie, dass die Zeichenketten noch im Speicher bestehen bleiben, wenn sie kopiert werden!

3. Die C++-Klasse YabInterface

3.1 Eine Methode hinzufügen

Nach den oben beschriebenen Punkten sind wir jetzt soweit eine neue Methode (Befehle) einzupflegen. Die Methode hat einen C++ Namen und eine Wrapper-Funktion mit einem externen Namen beginnend mit yi_. Beide müssen in YabInterface.h definiert und in YabInterface.cpp eingepflegt werden.

Die Wrapper-Funktion nimmt den Pointer zum YabInterface-Objekt und ruft die Hauptmethode auf:

void yi_CreateButton(double x1, double y1, double x2, double y2, const char* id, const char* title, const char* view, YabInterface* yab)
{
   yab->CreateButton(BRect(x1,y1,x2,y2), id, _L(title), view);
}
Das _L () Makro, das für alle Texte verwendet wird, übersetzt die Texte automatisch über das ZETA Local Kit. Es wird erst dann benutzt, wenn Sie den Befehl LOCALIZE verwenden. Bitte benutzen Sie dieses Makro so oft wie möglich, damit Sie eine einfache Lokalisierung der Programme ermöglichen.

Die Methode selbst ist ein Teil der YabInterface-Klasse, die von der BApplication-Klasse abgeleitet wird. Da der Interpreter in einem eigenen Thread läuft, sind alle BApplication-Methoden direkt von Ihrer Methode ansprechbar.

void YabInterface::CreateButton(BRect frame, const char* id, const char* title, const char* view)
{
   // code here
}

3.2 Zugriff auf yab-Datenstrukturen

Yab speichert verschiedene Informationen in Objektlisten. Die vermutlich wichtigste Liste ist die von den vorhandenen Views. Diese Liste ist in viewList gespeichert. Um ein neues Widget zu initialisieren, genügt es häufig das Elternteil der Views zu finden. Dieses erreichen Sie, indem Sie folgendes schreiben. YabList::GetView (const char*):

YabView *myView = cast_as((BView*)viewList->GetView(view), YabView);
if(myView)
{
   YabWindow *w = cast_as(myView->Window(), YabWindow);
   if(w)
   {
    w->Lock();
    // inizialize widget here
    w->unlock();
   }
   else   ErrorGen("Unalbe to lock window"); }
else
  Error(view, "VIEW");

Wenn Sie ein bestimmtes Widget auf einem Ihnen unbekannten View finden möchten, müssen Sie die Views durchsuchen. Solch eine Schleife sieht folgendermaßen aus (unter der Bedingung, dass MyWidget von BView abgeleitet wurde):

YabView *myView = NULL;
MyWidget *myWidget = NULL;
for(int i=0; iCountItems(); i++)
{
myView = cast_as((BView*)viewList->ItemAt(i), YabView);
  if(myView)
  {
   YabWindow *w = cast_as(myView->Window(), YabWindow);
   if(w)
   {
    w->Lock();
    myWidget = cast_as(myView->FindView(id), MyWidget);
    if(myWidget)
    {
     // do something with myWidget
     w->Unlock();
     return;
    }
   }
  }
 }
Error(id, "MyWidget");
return wird nachdem etwas mit dem Widget gemacht worden ist und nachdem man wieder das Fenster geunlockt hat, aufgerufen. Dieses erlaubt eine einfache Fehlerüberprüfung am Ende der Schleife.

 

3.3. Kurze Anmerkungen

Am Ende einige Anmerkungen über…

- BUILD-Makros: Einige Befehle haben BUILD-Makros, so dass man vollständige Teile yab beim Kompilieren abschalten kann. Das wird für die Buildfactory verwendet, um kleinere Codegröße zu produzieren. Nicht erforderliche oder unbenutzte Bibliotheken und/oder Codeteile werden einfach ausgelassen.
- Drawing: Drawing Befehle sind ein wenig trickreich. Sie erlauben das Zeichnen auf ein View über eine Bitmap und ein Canvas an. Besonders hat das View-Drawing ein eigenes Speichersystem. Deshalb schauen Sie sich die anderen Drawing Befehle an, um zu verstehen wie sie arbeiten.
- Eigene Klasse: Eigene Klassen hinzufügen ist schön, aber bedenken Sie auch, diese neuen Informationen in den Makefiles hinzuzufügen (R5 und ZETA!).



4.0 Zusammenfassung

Zum Schluß noch eine kleine Checkliste, damit Sie sehen können, ob Sie alle Punkte abgearbeitet haben.

· fügen Sie, wenn notwendig, das neue Befehlswort (token) in yabasic.flex hinzu
· fügen Sie die Befehlsregel (Grammatik) in yabasic.bison hinzu
· fügen Sie die Namen des Befehlidentifer und der C-Funktion in yabasic.h hinzu
· fügen Sie den Befehlsaufruf entweder in function.c oder in main.c hinzu
· fügen Sie Wrapper-Funktion in graphic.c hinzu
· fügen Sie die C und C++ Wrapper-Funktionen in YabInterface.h und in YabInterface.cpp hinzu
· fügen Sie die C++ Methode in YabInterface.h und in YabInterface.cpp hinzu

 

Übersetzung erstellt durch Author (lorglas) 05 2008
Bereitgestellt durch BeSly, der BeOS & Zeta Wissensbasis.