Exkurs: Ein Syntax-Highlighting-Programm


  • Was soll das Programm können?
  • Was ist HTML? Und wie funktioniert es?
  • Wie geht das Programm vor?
  • Einzelne Aufgaben lösen
  • Das ganze Programm
  • Im folgenden habe ich die neuen Sprachelemente einmal ein wenig in einem neuen Programm verwendet, dass das Syntax-Highlighting, die Formatierung der Programmtexte hier im Oma-Tutorial, durchführt. Die Betonung liegt auf "ein wenig". Das Programm ist weit davon entfernt, ein richtiges objektorientiertes Programm zu sein. Aber für so ein kleines Programm wäre das auch gar nicht zweckmässig. Immerhin lernen wir sogar eine Stelle kennen, an der "Vererbung" - ein Sprachelement, das Freebasic in Version 20 noch nicht besitzt, sinnvoll eingesetzt werden könnte.

    Was soll das Programm können?

    Ziel ist es, ein Programm zu schreiben, das einen HTML-Text durchgeht, die Basic-Programme selbstständig als solche identifiziert, alle Freebasic-Schlüsselwörter in einer Farbe einfärbt, alle Strings in einer anderen und alle Kommentare in einer dritten. Ausserdem sollte es dafür sorgen, dass alle Schlüsselwörter gross geschrieben sind. Das Programm soll auf der Kommandozeile mittels "fboutline " aufgerufen werden, so dass wir mittels for-Schleife auf der Kommandozeile auch gleich eine ganze Schar an Files auf einen Rutsch formatieren können.

    Was ist HTML? Und wie funktioniert es?

    HTML heisst "HyperText Markup Language" und ist sozusagen die Grundlage des World Wide Web. Es sind einfache Textseiten, die allerdings dazwischen immer wieder Schlüsselwörter haben, die zwischen zwei spitzen Klammern eingebettet sind. <b> wäre so ein Beispiel. Oder <font color=blue >. Es gibt immer zwei Tags, ein Öffnen-Tag und Schliess-Tag. <b> Fett </b> würde z.B. das Wort "Fett" fett machen. Den Textbereich, den wir blau machen wollten, müssten wir mit <font color=blue > und </font> einrahmen.

    Es wird nicht so kompliziert, wie man hier befürchten mag, da die BASIC-Texte selbst keine Tags enthalten, sondern nur von einem bestimmten Tag-Paar eingerahmt sind. Es heisst <pre> und <pre> und sorgt dafür, dass aller Text dazwischen in Blocksatz und mit Leerzeichen ausgedruckt wird. Das einzige Problem ist, dass dieses pre-Tag nicht nur für Programmtexte, sondern auch für Tabellen u.a. hergenommen wird.

    Wie geht das Programm vor?

    Das Erste ist, den ganzen Text nach <pre>-Tags abzusuchen. Und alles davor unbesehen rauszuschreiben. Wenn das <pre>-Tag kommt, wird's spannend. Dann müssen wir erstmal alles bis zum Endtag beschnüffeln, also einlesen und untersuchen: Ist es ein BASIC-Text oder nicht? Wir nehmen dazu eine ganz einfache Methode: Wir zählen die darin vorkommenden BASIC-Schlüsselworte und wenn es mehr als 2 sind, dann halten wir das einfach mal für BASIC.

    Als Nächstes gehen wir nochmals den ganzen pre-Text, der ja nun als BASIC-Text erkannt wurde, durch. Treffen wir auf ein Schlüsselwort, rahmen wir es mit einem font-Tag ein und konvertieren alles innerhalb in Grossbuchstaben. Ausserdem kontrollieren wir auf einfache und doppelte Anführungszeichen. Alles dazwischen wird auch in ein font-Tag gepackt, einmal mit Farbe "Kommentar" und einem mit Farbe "String". Fertig. Sollte eigentlich nicht schwer sein, oder?

    Wir können in unserer Liste schon ein paar Aufgaben identifizieren, die wir uns genauer anschauen sollten:

    1. Tokenize: Wie kriegen wir überhaupt raus, wo ein Schlüsselwort anfängt und wo es aufhört? Es ist gut, uns eine Methode zu überlegen, einen String in einzelne Worte zu zerlegen. Sowas kann man immer brauchen.
    2. Buffering: Wir brauchen eine Speichereinheit, die einerseits Text zeilenweise abspeichert, andererseits oder eine andere Einheit, die eine Zeile auch gleich in Form einer Wortliste – möglichst noch mit der Information, ob es sich um ein Schlüsselwort handelt.
    3. Keyword: Wie kriegen wir raus, ob ein Schlüsselwort ein BASIC-Schlüsselwort ist?

    Einzelne Aufgaben lösen

    Textspeicher

    Beim Speichern der Texte gilt es zu beachten, dass Texte in der Regel in Absätze unterteilt sind und ein Absatz eine nahezu beliebige Länge haben kann. Ebenso kann ein BASIC-Text ziemlich unterschiedlich lang sein, von 1 bis 1000 Zeilen ist da alles drin. Wir können also nicht einfach ein 2-dimensionales statisches Array hernehmen – das würde 1000 x 100.000 Bytes = 1 GByte benötigen – sau-unschön.

    Hier bietet sich unsere erste Klasse an. Im Idealfall verwaltet sie eine Container-Liste von Zeilen; um hier das Beispiel nicht zu überfrachten, nehmen wir ein Array aus zstring-Zeigern. Das packen wir in eine Klasse und überlassen es dieser, für Allokation und Freigabe des Speicherplatzes belegter Zeilen zu sorgen.

    
    
    type textbuf
      buf AS ZSTRING PTR PTR
    
      nline AS INTEGER
      bufsize AS INTEGER
      NAME AS STRING
    
      'Der wird nur formal gebraucht, wegen "Vererbung" bei tokenbuf
      DECLARE CONSTRUCTOR()
      'Der wird wirklich gebraucht:
      DECLARE CONSTRUCTOR(bufsize AS INTEGER, NAME0 AS STRING = "noname")
      DECLARE SUB init(bufsize AS INTEGER, NAME0 AS STRING = "noname")
      DECLARE DESTRUCTOR()
      DECLARE SUB add(s AS string)
      DECLARE SUB change(i AS INTEGER, s AS string)
      DECLARE FUNCTION OUT(i AS INTEGER) AS STRING
      DECLARE SUB fprint0(fileno AS integer)
      DECLARE SUB print0
      DECLARE SUB CLEAR
    END type
    
    
    ' ------------------------------------------------------------------
    
    SUB brkmess(mess AS string)
    
      PRINT mess
      END
    
    END SUB
    
    
    ' ------------------------------------------------------------------
    
    CONSTRUCTOR textbuf()
    
      'Leer, da der nur formal gebraucht wird, wegen "Vererbung" bei tokenbuf
    
    END CONSTRUCTOR
    
    
    ' ------------------------------------------------------------------
    
    CONSTRUCTOR textbuf(bufsize0 AS INTEGER, NAME0 AS STRING = "noname")
    
      init(bufsize0,NAME0)
    
    END CONSTRUCTOR
    
    
    ' ------------------------------------------------------------------
    
    SUB textbuf.init(bufsize0 AS INTEGER, NAME0 AS STRING = "noname")
    
      THIS.nline=0
      THIS.bufsize=bufsize0
      buf=ALLOCATE(bufsize0*SIZEOF(ZSTRING PTR))
      IF (buf=0) THEN brkmess("textbuf error: allocation failed")
      DIM i AS INTEGER
      FOR i=0 TO bufsize-1:buf[i]=0:NEXT i
    
      THIS.NAME=name0
    
    END SUB
    
    ' ------------------------------------------------------------------
    
    DESTRUCTOR textbuf()
      DIM i AS INTEGER
      FOR i=0 TO nline-1
        DEALLOCATE buf[i]
      NEXT i
      DEALLOCATE THIS.buf
      THIS.buf=0
    
    END DESTRUCTOR
    
    ' ------------------------------------------------------------------
    
    SUB textbuf.add(s AS string)
      IF nline>bufsize THEN brkmess("textbuf error: buffer overflow")
      buf[nline]=ALLOCATE(LEN(s)+10)
      *buf[nline]= s
      nline=nline+1
    END SUB
    
    ' ------------------------------------------------------------------
    
    SUB textbuf.change(i AS INTEGER, s AS string)
    
      IF buf[i]=0 THEN brkmess("error textbuf.change() buf[i] not allocated")
    
      DEALLOCATE buf[i]
      buf[i]=ALLOCATE(LEN(s)+10)
      *buf[i]=s
    
    END SUB
    
    ' ------------------------------------------------------------------
    
    FUNCTION textbuf.OUT(i AS INTEGER) AS STRING
    
      textbuf.OUT=buf[i][0]
    
    END FUNCTION
    
    ' ------------------------------------------------------------------
    
    SUB textbuf.CLEAR()
      DIM i AS INTEGER
      FOR i=0 TO nline-1
        add("")
      NEXT i
      nline=0
    END SUB
    
    
    
    ' ------------------------------------------------------------------
    
    SUB textbuf.print0
    
      DIM i AS INTEGER
    
      PRINT name;":"
      PRINT "----------------------------"
      FOR i=0 TO nline-1
        PRINT i,OUT(i)
      NEXT i
    
    
    END SUB
    
    
    ' ------------------------------------------------------------------
    
    SUB textbuf.fprint0(fileno AS integer)
    
      DIM i AS INTEGER
    
      FOR i=0 TO nline-1
        PRINT #fileno,OUT(i)
      NEXT i
    
    
    END SUB
    
    

    Unsere Klasse hat einen parametrisierten Konstruktor, die Parameter sind Zeilenzahl (bufsize) und ein Name. Letzterer ist für jede Klasse praktisch, wenn man ihre Inhalt auf den Bildschirm oder in ein Logfile schreibt. Allerdings kommt dann nochmal eine init-Routine der gleichen Form vor. Das liegt daran, dass der Aufruf von statisch deklarierten Konstruktoren, wenn sie Parameter benötigen, in Freebasic derzeit noch nicht möglich ist. Der Konstruktor ruft einfach nur die init-Routine auf, damit werden Code-Dopplungen vermieden. Das eigentliche Schicke ist der Destruktor. Er wird automatisch immer aufgerufen, wenn ein statisches, lokales Objekt seine Gültigkeit verliert und gibt die reservierten Zeilen frei.

    Nun wollen wir ja Tags in die Zeilen einfügen. Das klingt ganz einfach, ist es aber dann nicht, wenn wir den Speicherplatz für jede Zeile massgeschneidert reserviert haben. Dann haben wir nämlich im String gar keinen Platz für Einfügungen. Natürlich können wir ganz naiv mit "+" die Strings aneinanderhängen. Aber in dem Moment, in dem wir das in unseren ursprünglichen Stringspeicher zurücksichern, macht's "Peng" – oder etwas später. Daher brauchen wir eine Methode change(), die den alten Speicher freigibt und neuen der passenden Grösse reserviert.

    Tokenspeicher und der Grundgedanke der Vererbung

    Der Textspeicher genügt uns als Zwischenspeicher für den gesamten Programmtext, aber für die Speicherung der Worte, die wir später analysieren wollen, hält er zu wenig Informationen. Wir brauchen etwas, was mehr leistet, eine Klasse, die mehr tut und mehr Informationen bereithält. Dieses "ich brauche im Grunde genommen das, aber mit mehr Leistung" ist der Grundgedanke der Vererbung in der OOP: Man nutzt eine schon vorhandene Klasse als Grundgerüst und fügt noch Eigenschaften und Methoden hinzu. Frage: Warum genügt dafür nicht Copy&Paste? Antwort: Bei Freebasic muss dafür Copy&Paste herhalten und für so kleine Projekte ist das auch gar kein Problem. Aber bei grösseren Projekten hätten wir den Nachteil der Code-Verdopplung. Wir würden die vererbten Methoden mehrfach deklarieren. Fügen wir z.B. zu solch einer Methode ein optionales Argument hinzu, müssten wir den Kopf sowohl in einem Dutzend Klassendeklarationen als auch in den entsprechenden Definitionen ändern. Schöner ist es, wenn die abgeleiteten Klassen sich quasi automatisch anpassen. Richtig mächtig wird das Instrument der Vererbung, wenn die Methoden sich gegenseitig aufrufen. Dann kann es passieren, dass eine Vorfahr-Methode eine Methode aufruft, die im Erben neu definiert wurde. Damit ändert sich dann auch die Funktionsweise der Vorfahr-Methode innerhalb der Erbe-Klasse – vollautomatisch.

    Doch zurück zu unserem Programm. Wir brauchen also eine Klasse, die zu jeder gespeicherten Zeile noch die Position bereithält, an der sie im Originalstring gespeichert war (postab[]). Und eventuell weitere Informationen (Basic-Schlüsselwort, String oder Kommentar?). Ausserdem wäre es praktisch, den Originalstring dort bereitzuhalten:

    
    CONST true=-1
    CONST false=0
    
    type textbuf
      buf AS ZSTRING PTR PTR
    
      nline AS INTEGER
      bufsize AS INTEGER
      NAME AS STRING
    
      'Der wird nur formal gebraucht, wegen "Vererbung" bei tokenbuf
      DECLARE CONSTRUCTOR()
      'Der wird wirklich gebraucht:
      DECLARE CONSTRUCTOR(bufsize AS INTEGER, NAME0 AS STRING = "noname")
      DECLARE SUB init(bufsize AS INTEGER, NAME0 AS STRING = "noname")
      DECLARE DESTRUCTOR()
      DECLARE SUB add(s AS string)
      DECLARE SUB change(i AS INTEGER, s AS string)
      DECLARE FUNCTION OUT(i AS INTEGER) AS STRING
      DECLARE SUB fprint0(fileno AS integer)
      DECLARE SUB print0
      DECLARE SUB CLEAR
    END type
    
    type tokenbuf
    
      tbuf AS textbuf
      orgstring AS STRING
      postab AS INTEGER PTR
      propertytab AS INTEGER PTR
    
      DECLARE CONSTRUCTOR(bufsize AS INTEGER, NAME0 AS STRING = "noname")
      DECLARE DESTRUCTOR()
      DECLARE SUB add(s AS STRING, ipos AS integer)
      DECLARE SUB shiftpos(ishift AS INTEGER, istart AS integer)
      DECLARE FUNCTION OUT(i AS INTEGER) AS STRING
      DECLARE SUB print0
      DECLARE SUB CLEAR
    
    END type
    
    ' ------------------------------------------------------------------
    
    CONSTRUCTOR tokenbuf(bufsize0 AS INTEGER, NAME0 AS STRING = "noname")
    
      tbuf.init(bufsize0,NAME0)
      postab=ALLOCATE(LEN(INTEGER)*bufsize0)
      propertytab=ALLOCATE(LEN(INTEGER)*bufsize0)
    
      DIM i AS INTEGER
      FOR i=0 TO bufsize0-1
        postab[i]=0
        propertytab[i]=0
      NEXT i
    
    END CONSTRUCTOR
    
    
    ' ------------------------------------------------------------------
    
    SUB tokenbuf.add(s AS STRING, ipos AS integer)
      postab[tbuf.nline]=ipos
      tbuf.add(s)
    END SUB
    
    ' ------------------------------------------------------------------
    
    FUNCTION tokenbuf.OUT(i AS INTEGER) AS STRING
    
      tokenbuf.OUT=tbuf.OUT(i)
    
    END FUNCTION
    
    ' ------------------------------------------------------------------
    
    SUB tokenbuf.CLEAR()
    
      DIM i AS INTEGER
      FOR i=0 TO tbuf.nline-1
        postab[i]=0
        propertytab[i]=0
      NEXT i
    
    END SUB
    
    
    
    ' ------------------------------------------------------------------
    
    SUB tokenbuf.print0
    
      DIM i AS INTEGER
    
      PRINT tbuf.NAME
      PRINT "-------------------------------------------"
      PRINT orgstring
      FOR i=0 TO tbuf.nline-1
        ? OUT(i),postab[i],propertytab[i]
      NEXT i
    
    END SUB
    
    ' ------------------------------------------------------------------
    
    SUB tokenbuf.shiftpos(ishift AS INTEGER, istart AS integer)
      'Verschiebt Positionstabelle ab Eintrag istart um ishift
    
      DIM i AS INTEGER
    
      FOR i=istart TO tbuf.nline-1
        postab[i]=postab[i]+ishift
      NEXT i
    
    END SUB
    
    ' ------------------------------------------------------------------
    
    DESTRUCTOR tokenbuf()
    END DESTRUCTOR
    
    

    Wir sehen, dass sich die Methode add() geändert hat – hier übergeben wir nun zusätzlich zum String die Position. clear() befasst sich nur mit postab[] und propertytab[]. Und es gibt eine neue Methode shiftpos(): Falls wir etwas in orgstring eingefügt haben, müssen wir die Positionstabelle nach dieser Position entsprechend verschieben.

    Tokenizing

    Zentrale Teilaufgabe ist es, eine Zeile in ihre Bestandteile zu zerlegen. Das macht die Routine tokenize2(). Sie hat drei Abschnitte. Der erste und zweite kümmert sich um Stringabschnitte, die mit Gänsefüsschen eingeklammert sind. Der dritte Abschnitt hat es etwas in sich:

    'Zunächst fangen wir ab, ob wir uns grade innerhalb eines Strings oder 'Kommentars befinden. Und wir prüfen, ob wir am Zeilenende angelangt sind.
    IF (indoublequote=0 AND insinglequote=0) OR (i=slen) THEN
          'Ein Whitespace stellt einen Begrenzer unseres Wortes dar, das wir
          'suchen. Das Zeilenende natürlich auch.
          IF iswhitespace(mid(s,i,1)) OR (i=slen) THEN
            'Es können auch mehrere Whitespaces hintereinander kommen. Dann
            'sagt uns das neue Whitespace nichts.
            IF NOT ws_before THEN
              'istart zeigt auf den Start des letzten Wortes – falls wir
              'bereits uns innerhalb eines solchen befinden.
              IF (istart>=1)  THEN
                'Nun ist klar, dass ein Wort abgeschlossen ist und
                'abgespeichert werden muss.
                DIM s1 AS STRING
                'Wir holen das Wort aus dem String raus. Leicht andere
                'Positionsangabe, falls wir am Zeilenende sind.
                IF (iTHEN s1=omidstr(s,istart,i-1) ELSE s1=omidstr(s,istart,i)
                '...und fügen das Wort in den tokenbuffer ein.
                buf->add(s1,istart)
              END IF
              ws_before=true
            END IF
          ELSE
            'Hier startet ein neues Wort
            IF ws_before OR i=1 THEN
              ws_before=false
              istart=i
            END IF
          END IF
        END IF
    

    Schlüsselwörter erkennen

    Es gab zwei Möglichkeiten. In jedem Fall muss eine Liste der BASIC-Schlüsselwörter her – die bekommt man leicht über freebasic.de oder freebasic.net. Die dortige Liste lässt sich in eine Tabellenkalkulation übertragen und von dort in eine reine Textliste.

    Die Frage ist nur: Soll man sie als externe Datei einlesen oder soll man sie "hart verkabelt" im Programm verankern? Ich entschied mich für letztere Alternative, da es sich bei unserem Progrämmchen um ein kleines kompaktes Werkzeug handeln soll, das in nur einer exe-Datei besteht. Immer noch eine Textdatei mitrumschleppen wäre unpraktisch.

    Die Liste ist – einfacher geht es nicht mehr – als Serie von if-Anweisungen realisiert, die selbst wieder via Tabellenkalkulation generiert wurde – genauso hätte es ein kurzes BASIC-Programm getan.

    Das ganze Programm

        if (instr(lcase(s),"
    ")>0) then region=0
    
    CONST true=-1
    CONST false=0
    CONST Crecsize=100000
    CONST Ccolor="blue"
    CONST Cstringcolor="teal"
    CONST Cstring=1
    CONST Ccomment=2
    CONST Ccommentcolor="gray"
    
    ' Sonderfaelle end if, Kommentare
    
    type textbuf
      buf AS ZSTRING PTR PTR
    
      nline AS INTEGER
      bufsize AS INTEGER
      NAME AS STRING
    
      'Der wird nur formal gebraucht, wegen "Vererbung" bei tokenbuf
      DECLARE CONSTRUCTOR()
      'Der wird wirklich gebraucht:
      DECLARE CONSTRUCTOR(bufsize AS INTEGER, NAME0 AS STRING = "noname")
      DECLARE SUB init(bufsize AS INTEGER, NAME0 AS STRING = "noname")
      DECLARE DESTRUCTOR()
      DECLARE SUB add(s AS string)
      DECLARE SUB change(i AS INTEGER, s AS string)
      DECLARE FUNCTION OUT(i AS INTEGER) AS STRING
      DECLARE SUB fprint0(fileno AS integer)
      DECLARE SUB print0
      DECLARE SUB CLEAR
    END type
    
    type tokenbuf
    
      tbuf AS textbuf
      orgstring AS STRING
      postab AS INTEGER PTR
      propertytab AS INTEGER PTR
    
      DECLARE CONSTRUCTOR(bufsize AS INTEGER, NAME0 AS STRING = "noname")
      DECLARE DESTRUCTOR()
      DECLARE SUB add(s AS STRING, ipos AS integer)
      DECLARE SUB shiftpos(ishift AS INTEGER, istart AS integer)
      DECLARE FUNCTION OUT(i AS INTEGER) AS STRING
      DECLARE SUB print0
      DECLARE SUB CLEAR
    
    END type
    
    
    
    type tkeyword
      word AS STRING
      COLOR AS STRING
    END type
    
    
    ' ------------------------------------------------------------------
    
    SUB brkmess(mess AS string)
    
      PRINT mess
      END
    
    END SUB
    
    ' ------------------------------------------------------------------
    
    FUNCTION omidstr(s AS STRING, i1 AS INTEGER, i2 AS INTEGER) AS STRING
    
      omidstr=mid(s,i1,i2-i1+1)
    
    END FUNCTION
    
    ' ------------------------------------------------------------------
    
    CONSTRUCTOR textbuf()
    
      'Leer, da der nur formal gebraucht wird, wegen "Vererbung" bei tokenbuf
    
    END CONSTRUCTOR
    
    
    ' ------------------------------------------------------------------
    
    CONSTRUCTOR textbuf(bufsize0 AS INTEGER, NAME0 AS STRING = "noname")
    
      init(bufsize0,NAME0)
    
    END CONSTRUCTOR
    
    
    ' ------------------------------------------------------------------
    
    SUB textbuf.init(bufsize0 AS INTEGER, NAME0 AS STRING = "noname")
    
      THIS.nline=0
      THIS.bufsize=bufsize0
      buf=ALLOCATE(bufsize0*SIZEOF(ZSTRING PTR))
      IF (buf=0) THEN brkmess("textbuf error: allocation failed")
      DIM i AS INTEGER
      FOR i=0 TO bufsize-1:buf[i]=0:NEXT i
    
      THIS.NAME=name0
    
    END SUB
    
    ' ------------------------------------------------------------------
    
    DESTRUCTOR textbuf()
      DIM i AS INTEGER
      FOR i=0 TO nline-1
        DEALLOCATE buf[i]
      NEXT i
      DEALLOCATE THIS.buf
      THIS.buf=0
    
    END DESTRUCTOR
    
    ' ------------------------------------------------------------------
    
    SUB textbuf.add(s AS string)
      IF nline>bufsize THEN brkmess("textbuf error: buffer overflow")
      buf[nline]=ALLOCATE(LEN(s)+10)
      *buf[nline]= s
      nline=nline+1
    END SUB
    
    ' ------------------------------------------------------------------
    
    SUB textbuf.change(i AS INTEGER, s AS string)
    
      IF buf[i]=0 THEN brkmess("error textbuf.change() buf[i] not allocated")
    
      DEALLOCATE buf[i]
      buf[i]=ALLOCATE(LEN(s)+10)
      *buf[i]=s
    
    END SUB
    
    ' ------------------------------------------------------------------
    
    FUNCTION textbuf.OUT(i AS INTEGER) AS STRING
    
      textbuf.OUT=buf[i][0]
    
    END FUNCTION
    
    ' ------------------------------------------------------------------
    
    SUB textbuf.CLEAR()
      DIM i AS INTEGER
      FOR i=0 TO nline-1
        add("")
      NEXT i
      nline=0
    END SUB
    
    
    
    ' ------------------------------------------------------------------
    
    SUB textbuf.print0
    
      DIM i AS INTEGER
    
      PRINT name;":"
      PRINT "----------------------------"
      FOR i=0 TO nline-1
        PRINT i,OUT(i)
      NEXT i
    
    
    END SUB
    
    
    ' ------------------------------------------------------------------
    
    SUB textbuf.fprint0(fileno AS integer)
    
      DIM i AS INTEGER
    
      FOR i=0 TO nline-1
        PRINT #fileno,OUT(i)
      NEXT i
    
    
    END SUB
    
    
    
    ' ------------------------------------------------------------------
    
    CONSTRUCTOR tokenbuf(bufsize0 AS INTEGER, NAME0 AS STRING = "noname")
    
      tbuf.init(bufsize0,NAME0)
      postab=ALLOCATE(LEN(INTEGER)*bufsize0)
      propertytab=ALLOCATE(LEN(INTEGER)*bufsize0)
    
      DIM i AS INTEGER
      FOR i=0 TO bufsize0-1
        postab[i]=0
        propertytab[i]=0
      NEXT i
    
    END CONSTRUCTOR
    
    
    ' ------------------------------------------------------------------
    
    SUB tokenbuf.add(s AS STRING, ipos AS integer)
      postab[tbuf.nline]=ipos
      tbuf.add(s)
    END SUB
    
    ' ------------------------------------------------------------------
    
    FUNCTION tokenbuf.OUT(i AS INTEGER) AS STRING
    
      tokenbuf.OUT=tbuf.OUT(i)
    
    END FUNCTION
    
    ' ------------------------------------------------------------------
    
    SUB tokenbuf.CLEAR()
    
      DIM i AS INTEGER
      FOR i=0 TO tbuf.nline-1
        postab[i]=0
        propertytab[i]=0
      NEXT i
    
    END SUB
    
    
    
    ' ------------------------------------------------------------------
    
    SUB tokenbuf.print0
    
      DIM i AS INTEGER
    
      PRINT tbuf.NAME
      PRINT "-------------------------------------------"
      PRINT orgstring
      FOR i=0 TO tbuf.nline-1
        ? OUT(i),postab[i],propertytab[i]
      NEXT i
    
    END SUB
    
    ' ------------------------------------------------------------------
    
    SUB tokenbuf.shiftpos(ishift AS INTEGER, istart AS integer)
      'Verschiebt Positionstabelle ab Eintrag istart um ishift
    
      DIM i AS INTEGER
    
      FOR i=istart TO tbuf.nline-1
        postab[i]=postab[i]+ishift
      NEXT i
    
    END SUB
    
    ' ------------------------------------------------------------------
    
    DESTRUCTOR tokenbuf()
    END DESTRUCTOR
    
    
    ' ------------------------------------------------------------------
    
    DIM SHARED AS tkeyword keywordlist(500)
    DIM SHARED AS INTEGER nkeywordlist = 0
    
    
    ' ------------------------------------------------------------------
    
    
    FUNCTION iswhitespace(s AS STRING) AS INTEGER
    
      DIM AS STRING wstest=" ,()[]'=$*+/-^:."
      DIM AS INTEGER i,j,slen
      DIM AS INTEGER retval
    
      slen=LEN(wstest)
    
      retval=false
      FOR i=1 TO slen
        IF mid(s,1,1)=mid(wstest,i,1) THEN
          retval=true
          EXIT FOR
        END IF
      NEXT i
      IF (NOT retval) THEN
        IF (ASC(mid(s,1,1))>=48 AND ASC(mid(s,1,1))<=57) OR ASC(mid(s,1,1))=9 THEN retval=true
      END IF
    
      iswhitespace=retval
    
    END FUNCTION
    
    
    ' ------------------------------------------------------------------
    
    FUNCTION tokenizestring(s AS STRING) AS textbuf PTR
    
      DIM AS INTEGER i,j,slen,istart,ws_before
      DIM AS STRING currenttok
      DIM AS textbuf PTR buf
    
      buf=NEW textbuf(1000,"tokenize")
    
      currenttok=""
      istart=-1
      ws_before=false
      slen=LEN(s)
      FOR i=1 TO slen
        IF iswhitespace(mid(s,i,1)) OR (i=slen) THEN
          '? "whiteb>",i
          IF NOT ws_before THEN
            IF (istart>=1) THEN
              DIM s1 AS STRING
              IF (iTHEN s1=omidstr(s,istart,i-1) ELSE s1=omidstr(s,istart,i)
              'print s1
              buf->add(s1)
            END IF
            ws_before=true
          END IF
        ELSE
          IF ws_before OR i=1 THEN
            '? "else>",i
            ws_before=false
            istart=i
          END IF
        END IF
    
      NEXT i
    
      tokenizestring=buf
    
    END FUNCTION
    
    
    ' ------------------------------------------------------------------
    
    ' Das hier ist nicht so ganz schön:
    ' Codeverdopplung von oben. Verbessern Sie die Stelle!
    
    FUNCTION tokenizestring2(s AS STRING) AS tokenbuf PTR
    
      DIM AS INTEGER i,j,slen,istart,ws_before,indoublequote,token_stored,istart_single,istart_double
      DIM AS INTEGER insinglequote
      DIM AS STRING currenttok
      DIM AS tokenbuf PTR buf
    
      buf=NEW tokenbuf(1000,"tokenize")
      IF (buf=0) THEN brkmess("error tokenizestring2() allocation failed")
      buf->orgstring=s
    
      currenttok=""
      istart=-1
      istart_single=-1
      istart_double=-1
      ws_before=false
      slen=LEN(s)
      insinglequote=0
      indoublequote=0
    
      'Double Quotes
      FOR i=1 TO slen
    
        token_stored=false
    
        'Double Quotes
        IF mid(s,i,1)=CHR(34) OR (i=slen) THEN
          IF indoublequote=1 THEN
            IF (istart_double>=1) THEN
              DIM s1 AS STRING
              IF (iTHEN s1=omidstr(s,istart_double,i-1) ELSE s1=omidstr(s,istart_double,i)
              buf->add(s1,istart_double)
              buf->propertytab[buf->tbuf.nline-1]=Cstring
              token_stored=true
            END IF
          ELSE
            istart_double=i
          END IF
          indoublequote=1-indoublequote
        END IF
    
        'Single Quotes
        IF mid(s,i,1)=CHR(39) OR (i=slen) THEN
          IF insinglequote=1 THEN
            IF (istart_single>=1) THEN
              DIM s1 AS STRING
              IF (iTHEN s1=omidstr(s,istart_single,i-1) ELSE s1=omidstr(s,istart_single,i)
              buf->add(s1,istart_single)
              buf->propertytab[buf->tbuf.nline-1]=Ccomment
              token_stored=true
            END IF
          ELSE
            istart_single=i
          END IF
          insinglequote=1-insinglequote
        END IF
    
        IF (indoublequote=0 AND insinglequote=0) OR (i=slen) THEN
          IF iswhitespace(mid(s,i,1)) OR (i=slen) THEN
            IF NOT ws_before THEN
              IF (istart>=1)  THEN
                DIM s1 AS STRING
                IF (iTHEN s1=omidstr(s,istart,i-1) ELSE s1=omidstr(s,istart,i)
                buf->add(s1,istart)
              END IF
              ws_before=true
            END IF
          ELSE
            IF ws_before OR i=1 THEN
              ws_before=false
              istart=i
            END IF
          END IF
        END IF
    
    
      NEXT i
    
      tokenizestring2=buf
    
    END FUNCTION
    
    
    ' ------------------------------------------------------------------
    
    SUB readkeywordlist()
    
            keywordlist(0).word="ABS":keywordlist(0).COLOR="Blue"
            keywordlist(1).word="ACCESS":keywordlist(1).COLOR="Blue"
            keywordlist(2).word="ACOS":keywordlist(2).COLOR="Blue"
            keywordlist(3).word="PUT":keywordlist(3).COLOR="Blue"
            keywordlist(4).word="ALIAS":keywordlist(4).COLOR="Blue"
            keywordlist(5).word="ALLOCATE":keywordlist(5).COLOR="Blue"
            keywordlist(6).word="PUT":keywordlist(6).COLOR="Blue"
            keywordlist(7).word="AND":keywordlist(7).COLOR="Blue"
            keywordlist(8).word="ANDALSO":keywordlist(8).COLOR="Blue"
            keywordlist(9).word="PUT":keywordlist(9).COLOR="Blue"
            keywordlist(10).word="ANY":keywordlist(10).COLOR="Blue"
            keywordlist(11).word="APPEND":keywordlist(11).COLOR="Blue"
            keywordlist(12).word="AS":keywordlist(12).COLOR="Blue"
            keywordlist(13).word="ASSERT":keywordlist(13).COLOR="Blue"
            keywordlist(14).word="ASSERTWARN":keywordlist(14).COLOR="Blue"
            keywordlist(15).word="ASC":keywordlist(15).COLOR="Blue"
            keywordlist(16).word="ASIN":keywordlist(16).COLOR="Blue"
            keywordlist(17).word="ASM":keywordlist(17).COLOR="Blue"
            keywordlist(18).word="ATAN2":keywordlist(18).COLOR="Blue"
            keywordlist(19).word="ATN":keywordlist(19).COLOR="Blue"
            keywordlist(20).word="BEEP":keywordlist(20).COLOR="Blue"
            keywordlist(21).word="BIN":keywordlist(21).COLOR="Blue"
            keywordlist(22).word="BINARY":keywordlist(22).COLOR="Blue"
            keywordlist(23).word="BIT":keywordlist(23).COLOR="Blue"
            keywordlist(24).word="BITRESET":keywordlist(24).COLOR="Blue"
            keywordlist(25).word="BITSET":keywordlist(25).COLOR="Blue"
            keywordlist(26).word="BLOAD":keywordlist(26).COLOR="Blue"
            keywordlist(27).word="BSAVE":keywordlist(27).COLOR="Blue"
            keywordlist(28).word="BYREF":keywordlist(28).COLOR="Blue"
            keywordlist(29).word="BYTE":keywordlist(29).COLOR="Blue"
            keywordlist(30).word="BYVAL":keywordlist(30).COLOR="Blue"
            keywordlist(31).word="CALL":keywordlist(31).COLOR="Blue"
            keywordlist(32).word="CALLOCATE":keywordlist(32).COLOR="Blue"
            keywordlist(33).word="CASE":keywordlist(33).COLOR="Blue"
            keywordlist(34).word="CAST":keywordlist(34).COLOR="Blue"
            keywordlist(35).word="CBYTE":keywordlist(35).COLOR="Blue"
            keywordlist(36).word="CDBL":keywordlist(36).COLOR="Blue"
            keywordlist(37).word="CDECL":keywordlist(37).COLOR="Blue"
            keywordlist(38).word="CHAIN":keywordlist(38).COLOR="Blue"
            keywordlist(39).word="CHDIR":keywordlist(39).COLOR="Blue"
            keywordlist(40).word="CHR":keywordlist(40).COLOR="Blue"
            keywordlist(41).word="CINT":keywordlist(41).COLOR="Blue"
            keywordlist(42).word="CIRCLE":keywordlist(42).COLOR="Blue"
            keywordlist(43).word="CLASS":keywordlist(43).COLOR="Blue"
            keywordlist(44).word="CLEAR":keywordlist(44).COLOR="Blue"
            keywordlist(45).word="CLNG":keywordlist(45).COLOR="Blue"
            keywordlist(46).word="CLNGINT":keywordlist(46).COLOR="Blue"
            keywordlist(47).word="CLOSE":keywordlist(47).COLOR="Blue"
            keywordlist(48).word="CLS":keywordlist(48).COLOR="Blue"
            keywordlist(49).word="COLOR":keywordlist(49).COLOR="Blue"
            keywordlist(50).word="COMMAND":keywordlist(50).COLOR="Blue"
            keywordlist(51).word="COMMON":keywordlist(51).COLOR="Blue"
            keywordlist(52).word="CONDBROADCAST":keywordlist(52).COLOR="Blue"
            keywordlist(53).word="CONDCREATE":keywordlist(53).COLOR="Blue"
            keywordlist(54).word="CONDDESTROY":keywordlist(54).COLOR="Blue"
            keywordlist(55).word="CONDSIGNAL":keywordlist(55).COLOR="Blue"
            keywordlist(56).word="CONDWAIT":keywordlist(56).COLOR="Blue"
            keywordlist(57).word="CONST":keywordlist(57).COLOR="Blue"
            keywordlist(58).word="CONSTRUCTOR":keywordlist(58).COLOR="Blue"
            keywordlist(59).word="CONTINUE":keywordlist(59).COLOR="Blue"
            keywordlist(60).word="COS":keywordlist(60).COLOR="Blue"
            keywordlist(61).word="CPTR":keywordlist(61).COLOR="Blue"
            keywordlist(62).word="CSHORT":keywordlist(62).COLOR="Blue"
            keywordlist(63).word="CSIGN":keywordlist(63).COLOR="Blue"
            keywordlist(64).word="CSNG":keywordlist(64).COLOR="Blue"
            keywordlist(65).word="CSRLIN":keywordlist(65).COLOR="Blue"
            keywordlist(66).word="CUBYTE":keywordlist(66).COLOR="Blue"
            keywordlist(67).word="CUINT":keywordlist(67).COLOR="Blue"
            keywordlist(68).word="CULNG":keywordlist(68).COLOR="Blue"
            keywordlist(69).word="CULNGINT":keywordlist(69).COLOR="Blue"
            keywordlist(70).word="CUNSG":keywordlist(70).COLOR="Blue"
            keywordlist(71).word="CURDIR":keywordlist(71).COLOR="Blue"
            keywordlist(72).word="CUSHORT":keywordlist(72).COLOR="Blue"
            keywordlist(73).word="PUT":keywordlist(73).COLOR="Blue"
            keywordlist(74).word="CVD":keywordlist(74).COLOR="Blue"
            keywordlist(75).word="CVI":keywordlist(75).COLOR="Blue"
            keywordlist(76).word="CVL":keywordlist(76).COLOR="Blue"
            keywordlist(77).word="CVLONGINT":keywordlist(77).COLOR="Blue"
            keywordlist(78).word="CVS":keywordlist(78).COLOR="Blue"
            keywordlist(79).word="CVSHORT":keywordlist(79).COLOR="Blue"
            keywordlist(80).word="DATA":keywordlist(80).COLOR="Blue"
            keywordlist(81).word="DATE":keywordlist(81).COLOR="Blue"
            keywordlist(82).word="DATEADD":keywordlist(82).COLOR="Blue"
            keywordlist(83).word="DATEDIFF":keywordlist(83).COLOR="Blue"
            keywordlist(84).word="DATEPART":keywordlist(84).COLOR="Blue"
            keywordlist(85).word="DATESERIAL":keywordlist(85).COLOR="Blue"
            keywordlist(86).word="DATEVALUE":keywordlist(86).COLOR="Blue"
            keywordlist(87).word="DAY":keywordlist(87).COLOR="Blue"
            keywordlist(88).word="DEALLOCATE":keywordlist(88).COLOR="Blue"
            keywordlist(89).word="DECLARE":keywordlist(89).COLOR="Blue"
            keywordlist(90).word="DEFBYTE":keywordlist(90).COLOR="Blue"
            keywordlist(91).word="DEFDBL":keywordlist(91).COLOR="Blue"
            keywordlist(92).word="DEFINED":keywordlist(92).COLOR="Blue"
            keywordlist(93).word="DEFINT":keywordlist(93).COLOR="Blue"
            keywordlist(94).word="DEFLNG":keywordlist(94).COLOR="Blue"
            keywordlist(95).word="DEFLONGINT":keywordlist(95).COLOR="Blue"
            keywordlist(96).word="DEFSHORT":keywordlist(96).COLOR="Blue"
            keywordlist(97).word="DEFSNG":keywordlist(97).COLOR="Blue"
            keywordlist(98).word="DEFSTR":keywordlist(98).COLOR="Blue"
            keywordlist(99).word="DEFUBYTE":keywordlist(99).COLOR="Blue"
            keywordlist(100).word="DEFUINT":keywordlist(100).COLOR="Blue"
            keywordlist(101).word="DEFULONGINT":keywordlist(101).COLOR="Blue"
            keywordlist(102).word="DEFUSHORT":keywordlist(102).COLOR="Blue"
            keywordlist(103).word="DELETE":keywordlist(103).COLOR="Blue"
            keywordlist(104).word="DESTRUCTOR":keywordlist(104).COLOR="Blue"
            keywordlist(105).word="DIM":keywordlist(105).COLOR="Blue"
            keywordlist(106).word="DIR":keywordlist(106).COLOR="Blue"
            keywordlist(107).word="DO":keywordlist(107).COLOR="Blue"
            keywordlist(108).word="LOOP":keywordlist(108).COLOR="Blue"
            keywordlist(109).word="DOUBLE":keywordlist(109).COLOR="Blue"
            keywordlist(110).word="DRAW":keywordlist(110).COLOR="Blue"
            keywordlist(111).word="STRING":keywordlist(111).COLOR="Blue"
            keywordlist(112).word="DYLIBFREE":keywordlist(112).COLOR="Blue"
            keywordlist(113).word="DYLIBLOAD":keywordlist(113).COLOR="Blue"
            keywordlist(114).word="DYLIBSYMBOL":keywordlist(114).COLOR="Blue"
            keywordlist(115).word="ELSE":keywordlist(115).COLOR="Blue"
            keywordlist(116).word="ELSEIF":keywordlist(116).COLOR="Blue"
            keywordlist(117).word="ENCODING":keywordlist(117).COLOR="Blue"
            keywordlist(118).word="Block":keywordlist(118).COLOR="Blue"
            keywordlist(119).word="IF":keywordlist(119).COLOR="Blue"
            keywordlist(120).word="ENUM":keywordlist(120).COLOR="Blue"
            keywordlist(121).word="ENVIRON":keywordlist(121).COLOR="Blue"
            keywordlist(122).word="EOF":keywordlist(122).COLOR="Blue"
            keywordlist(123).word="EQV":keywordlist(123).COLOR="Blue"
            keywordlist(124).word="ERASE":keywordlist(124).COLOR="Blue"
            keywordlist(125).word="ERFN":keywordlist(125).COLOR="Blue"
            keywordlist(126).word="ERL":keywordlist(126).COLOR="Blue"
            keywordlist(127).word="ERMN":keywordlist(127).COLOR="Blue"
            keywordlist(128).word="ERR":keywordlist(128).COLOR="Blue"
            keywordlist(129).word="ERROR":keywordlist(129).COLOR="Blue"
            keywordlist(130).word="EXEC":keywordlist(130).COLOR="Blue"
            keywordlist(131).word="EXEPATH":keywordlist(131).COLOR="Blue"
            keywordlist(132).word="EXIT":keywordlist(132).COLOR="Blue"
            keywordlist(133).word="EXP":keywordlist(133).COLOR="Blue"
            keywordlist(134).word="EXPORT":keywordlist(134).COLOR="Blue"
            keywordlist(135).word="EXTERN":keywordlist(135).COLOR="Blue"
            keywordlist(136).word="EXTERN":keywordlist(136).COLOR="Blue"
            keywordlist(137).word="FIELD":keywordlist(137).COLOR="Blue"
            keywordlist(138).word="FILEATTR":keywordlist(138).COLOR="Blue"
            keywordlist(139).word="FILECOPY":keywordlist(139).COLOR="Blue"
            keywordlist(140).word="FILEDATETIME":keywordlist(140).COLOR="Blue"
            keywordlist(141).word="FILEEXISTS":keywordlist(141).COLOR="Blue"
            keywordlist(142).word="FILELEN":keywordlist(142).COLOR="Blue"
            keywordlist(143).word="FIX":keywordlist(143).COLOR="Blue"
            keywordlist(144).word="FLIP":keywordlist(144).COLOR="Blue"
            keywordlist(145).word="FOR":keywordlist(145).COLOR="Blue"
            keywordlist(146).word="NEXT":keywordlist(146).COLOR="Blue"
            keywordlist(147).word="FORMAT":keywordlist(147).COLOR="Blue"
            keywordlist(148).word="FRAC":keywordlist(148).COLOR="Blue"
            keywordlist(149).word="FRE":keywordlist(149).COLOR="Blue"
            keywordlist(150).word="FREEFILE":keywordlist(150).COLOR="Blue"
            keywordlist(151).word="FUNCTION":keywordlist(151).COLOR="Blue"
            keywordlist(152).word="GETJOYSTICK":keywordlist(152).COLOR="Blue"
            keywordlist(153).word="GETKEY":keywordlist(153).COLOR="Blue"
            keywordlist(154).word="GETMOUSE":keywordlist(154).COLOR="Blue"
            keywordlist(155).word="GOSUB":keywordlist(155).COLOR="Blue"
            keywordlist(156).word="GOTO":keywordlist(156).COLOR="Blue"
            keywordlist(157).word="HEX":keywordlist(157).COLOR="Blue"
            keywordlist(158).word="HIBYTE":keywordlist(158).COLOR="Blue"
            keywordlist(159).word="HIWORD":keywordlist(159).COLOR="Blue"
            keywordlist(160).word="HOUR":keywordlist(160).COLOR="Blue"
            keywordlist(161).word="THEN":keywordlist(161).COLOR="Blue"
            keywordlist(162).word="IIF":keywordlist(162).COLOR="Blue"
            keywordlist(163).word="IMAGECONVERTROW":keywordlist(163).COLOR="Blue"
            keywordlist(164).word="IMAGECREATE":keywordlist(164).COLOR="Blue"
            keywordlist(165).word="IMAGEDESTROY":keywordlist(165).COLOR="Blue"
            keywordlist(166).word="IMAGEINFO":keywordlist(166).COLOR="Blue"
            keywordlist(167).word="IMP":keywordlist(167).COLOR="Blue"
            keywordlist(168).word="IMPORT":keywordlist(168).COLOR="Blue"
            keywordlist(169).word="INKEY":keywordlist(169).COLOR="Blue"
            keywordlist(170).word="INP":keywordlist(170).COLOR="Blue"
            keywordlist(171).word="INPUT #":keywordlist(171).COLOR="Blue"
            keywordlist(172).word="INPUT$":keywordlist(172).COLOR="Blue"
            keywordlist(173).word="INSTR":keywordlist(173).COLOR="Blue"
            keywordlist(174).word="INSTRREV":keywordlist(174).COLOR="Blue"
            keywordlist(175).word="INT":keywordlist(175).COLOR="Blue"
            keywordlist(176).word="INTEGER":keywordlist(176).COLOR="Blue"
            keywordlist(177).word="IS":keywordlist(177).COLOR="Blue"
            keywordlist(178).word="ISDATE":keywordlist(178).COLOR="Blue"
            keywordlist(179).word="KILL":keywordlist(179).COLOR="Blue"
            keywordlist(180).word="LBOUND":keywordlist(180).COLOR="Blue"
            keywordlist(181).word="LCASE":keywordlist(181).COLOR="Blue"
            keywordlist(182).word="LEFT":keywordlist(182).COLOR="Blue"
            keywordlist(183).word="LEN":keywordlist(183).COLOR="Blue"
            keywordlist(184).word="LET":keywordlist(184).COLOR="Blue"
            keywordlist(185).word="LIB":keywordlist(185).COLOR="Blue"
            keywordlist(186).word="LINE":keywordlist(186).COLOR="Blue"
            keywordlist(187).word="INPUT":keywordlist(187).COLOR="Blue"
            keywordlist(188).word="INPUT #":keywordlist(188).COLOR="Blue"
            keywordlist(189).word="LOBYTE":keywordlist(189).COLOR="Blue"
            keywordlist(190).word="LOC":keywordlist(190).COLOR="Blue"
            keywordlist(191).word="LOCAL":keywordlist(191).COLOR="Blue"
            keywordlist(192).word="LOCATE":keywordlist(192).COLOR="Blue"
            keywordlist(193).word="LOCK":keywordlist(193).COLOR="Blue"
            keywordlist(194).word="LOF":keywordlist(194).COLOR="Blue"
            keywordlist(195).word="LOG":keywordlist(195).COLOR="Blue"
            keywordlist(196).word="LONG":keywordlist(196).COLOR="Blue"
            keywordlist(197).word="LONGINT":keywordlist(197).COLOR="Blue"
            keywordlist(198).word="LOOP":keywordlist(198).COLOR="Blue"
            keywordlist(199).word="LOWORD":keywordlist(199).COLOR="Blue"
            keywordlist(200).word="LPOS":keywordlist(200).COLOR="Blue"
            keywordlist(201).word="LPRINT":keywordlist(201).COLOR="Blue"
            keywordlist(202).word="LSET":keywordlist(202).COLOR="Blue"
            keywordlist(203).word="LTRIM":keywordlist(203).COLOR="Blue"
            keywordlist(204).word="MINUTE":keywordlist(204).COLOR="Blue"
            keywordlist(205).word="MKD":keywordlist(205).COLOR="Blue"
            keywordlist(206).word="MKDIR":keywordlist(206).COLOR="Blue"
            keywordlist(207).word="MKI":keywordlist(207).COLOR="Blue"
            keywordlist(208).word="MKL":keywordlist(208).COLOR="Blue"
            keywordlist(209).word="MKLONGINT":keywordlist(209).COLOR="Blue"
            keywordlist(210).word="MKS":keywordlist(210).COLOR="Blue"
            keywordlist(211).word="MKSHORT":keywordlist(211).COLOR="Blue"
            keywordlist(212).word="MOD":keywordlist(212).COLOR="Blue"
            keywordlist(213).word="MONTH":keywordlist(213).COLOR="Blue"
            keywordlist(214).word="MONTHNAME":keywordlist(214).COLOR="Blue"
            keywordlist(215).word="MULTIKEY":keywordlist(215).COLOR="Blue"
            keywordlist(216).word="MUTEXCREATE":keywordlist(216).COLOR="Blue"
            keywordlist(217).word="MUTEXDESTROY":keywordlist(217).COLOR="Blue"
            keywordlist(218).word="MUTEXLOCK":keywordlist(218).COLOR="Blue"
            keywordlist(219).word="MUTEXUNLOCK":keywordlist(219).COLOR="Blue"
            keywordlist(220).word="NAME":keywordlist(220).COLOR="Blue"
            keywordlist(221).word="NAMESPACE":keywordlist(221).COLOR="Blue"
            keywordlist(222).word="NEXT":keywordlist(222).COLOR="Blue"
            keywordlist(223).word="NEW":keywordlist(223).COLOR="Blue"
            keywordlist(224).word="RESUME":keywordlist(224).COLOR="Blue"
            keywordlist(225).word="NOT":keywordlist(225).COLOR="Blue"
            keywordlist(226).word="NOW":keywordlist(226).COLOR="Blue"
            keywordlist(227).word="OFFSETOF":keywordlist(227).COLOR="Blue"
            keywordlist(228).word="ERROR":keywordlist(228).COLOR="Blue"
            keywordlist(229).word="GOSUB":keywordlist(229).COLOR="Blue"
            keywordlist(230).word="GOTO":keywordlist(230).COLOR="Blue"
            keywordlist(231).word="ONCE":keywordlist(231).COLOR="Blue"
            keywordlist(232).word="OPEN":keywordlist(232).COLOR="Blue"
            keywordlist(233).word="COM":keywordlist(233).COLOR="Blue"
            keywordlist(234).word="CONS":keywordlist(234).COLOR="Blue"
            keywordlist(235).word="ERR":keywordlist(235).COLOR="Blue"
            keywordlist(236).word="LPT":keywordlist(236).COLOR="Blue"
            keywordlist(237).word="PIPE":keywordlist(237).COLOR="Blue"
            keywordlist(238).word="SCRN":keywordlist(238).COLOR="Blue"
            keywordlist(239).word="OPTION(":keywordlist(239).COLOR="Blue"
            keywordlist(240).word="BASE":keywordlist(240).COLOR="Blue"
            keywordlist(241).word="BYVAL":keywordlist(241).COLOR="Blue"
            keywordlist(242).word="DYNAMIC":keywordlist(242).COLOR="Blue"
            keywordlist(243).word="ESCAPE":keywordlist(243).COLOR="Blue"
            keywordlist(244).word="EXPLICIT":keywordlist(244).COLOR="Blue"
            keywordlist(245).word="GOSUB":keywordlist(245).COLOR="Blue"
            keywordlist(246).word="NOGOSUB":keywordlist(246).COLOR="Blue"
            keywordlist(247).word="NOKEYWORD":keywordlist(247).COLOR="Blue"
            keywordlist(248).word="PRIVATE":keywordlist(248).COLOR="Blue"
            keywordlist(249).word="STATIC":keywordlist(249).COLOR="Blue"
            keywordlist(250).word="OPERATOR":keywordlist(250).COLOR="Blue"
            keywordlist(251).word="OR":keywordlist(251).COLOR="Blue"
            keywordlist(252).word="PUT":keywordlist(252).COLOR="Blue"
            keywordlist(253).word="ORELSE":keywordlist(253).COLOR="Blue"
            keywordlist(254).word="OUT":keywordlist(254).COLOR="Blue"
            keywordlist(255).word="OUTPUT":keywordlist(255).COLOR="Blue"
            keywordlist(256).word="OVERLOAD":keywordlist(256).COLOR="Blue"
            keywordlist(257).word="PAINT":keywordlist(257).COLOR="Blue"
            keywordlist(258).word="PALETTE":keywordlist(258).COLOR="Blue"
            keywordlist(259).word="PASCAL":keywordlist(259).COLOR="Blue"
            keywordlist(260).word="PCOPY":keywordlist(260).COLOR="Blue"
            keywordlist(261).word="PEEK":keywordlist(261).COLOR="Blue"
            keywordlist(262).word="PMAP":keywordlist(262).COLOR="Blue"
            keywordlist(263).word="POINT":keywordlist(263).COLOR="Blue"
            keywordlist(264).word="POINTER":keywordlist(264).COLOR="Blue"
            keywordlist(265).word="POKE":keywordlist(265).COLOR="Blue"
            keywordlist(266).word="POS":keywordlist(266).COLOR="Blue"
            keywordlist(267).word="PRESERVE":keywordlist(267).COLOR="Blue"
            keywordlist(268).word="PRESET":keywordlist(268).COLOR="Blue"
            keywordlist(269).word="PRINT":keywordlist(269).COLOR="Blue"
            keywordlist(270).word="PRINT #":keywordlist(270).COLOR="Blue"
            keywordlist(271).word="USING":keywordlist(271).COLOR="Blue"
            keywordlist(272).word="USING #":keywordlist(272).COLOR="Blue"
            keywordlist(273).word="PRIVATE":keywordlist(273).COLOR="Blue"
            keywordlist(274).word="PROCPTR":keywordlist(274).COLOR="Blue"
            keywordlist(275).word="PROPERTY":keywordlist(275).COLOR="Blue"
            keywordlist(276).word="PUT":keywordlist(276).COLOR="Blue"
            keywordlist(277).word="PTR":keywordlist(277).COLOR="Blue"
            keywordlist(278).word="PUBLIC":keywordlist(278).COLOR="Blue"
            keywordlist(279).word="RANDOM":keywordlist(279).COLOR="Blue"
            keywordlist(280).word="RANDOMIZE":keywordlist(280).COLOR="Blue"
            keywordlist(281).word="READ":keywordlist(281).COLOR="Blue"
            keywordlist(282).word="REALLOCATE":keywordlist(282).COLOR="Blue"
            keywordlist(283).word="REDIM":keywordlist(283).COLOR="Blue"
            keywordlist(284).word="REM":keywordlist(284).COLOR="Blue"
            keywordlist(285).word="RESET":keywordlist(285).COLOR="Blue"
            keywordlist(286).word="RESTORE":keywordlist(286).COLOR="Blue"
            keywordlist(287).word="RESUME":keywordlist(287).COLOR="Blue"
            keywordlist(288).word="NEXT":keywordlist(288).COLOR="Blue"
            keywordlist(289).word="RETURN":keywordlist(289).COLOR="Blue"
            keywordlist(290).word="RGB":keywordlist(290).COLOR="Blue"
            keywordlist(291).word="RGBA":keywordlist(291).COLOR="Blue"
            keywordlist(292).word="RIGHT":keywordlist(292).COLOR="Blue"
            keywordlist(293).word="RMDIR":keywordlist(293).COLOR="Blue"
            keywordlist(294).word="RND":keywordlist(294).COLOR="Blue"
            keywordlist(295).word="RSET":keywordlist(295).COLOR="Blue"
            keywordlist(296).word="RTRIM":keywordlist(296).COLOR="Blue"
            keywordlist(297).word="RUN":keywordlist(297).COLOR="Blue"
            keywordlist(298).word="SADD":keywordlist(298).COLOR="Blue"
            keywordlist(299).word="SCOPE":keywordlist(299).COLOR="Blue"
            keywordlist(300).word="SCREEN":keywordlist(300).COLOR="Blue"
            keywordlist(301).word="SCREENCOPY":keywordlist(301).COLOR="Blue"
            keywordlist(302).word="SCREENCONTROL":keywordlist(302).COLOR="Blue"
            keywordlist(303).word="SCREENEVENT":keywordlist(303).COLOR="Blue"
            keywordlist(304).word="SCREENINFO":keywordlist(304).COLOR="Blue"
            keywordlist(305).word="SCREENGLPROC":keywordlist(305).COLOR="Blue"
            keywordlist(306).word="SCREENLIST":keywordlist(306).COLOR="Blue"
            keywordlist(307).word="SCREENLOCK":keywordlist(307).COLOR="Blue"
            keywordlist(308).word="SCREENPTR":keywordlist(308).COLOR="Blue"
            keywordlist(309).word="SCREENRES":keywordlist(309).COLOR="Blue"
            keywordlist(310).word="SCREENSET":keywordlist(310).COLOR="Blue"
            keywordlist(311).word="SCREENSYNC":keywordlist(311).COLOR="Blue"
            keywordlist(312).word="SCREENUNLOCK":keywordlist(312).COLOR="Blue"
            keywordlist(313).word="SECOND":keywordlist(313).COLOR="Blue"
            keywordlist(314).word="CASE":keywordlist(314).COLOR="Blue"
            keywordlist(315).word="SETDATE":keywordlist(315).COLOR="Blue"
            keywordlist(316).word="SETENVIRON":keywordlist(316).COLOR="Blue"
            keywordlist(317).word="SETMOUSE":keywordlist(317).COLOR="Blue"
            keywordlist(318).word="SETTIME":keywordlist(318).COLOR="Blue"
            keywordlist(319).word="SGN":keywordlist(319).COLOR="Blue"
            keywordlist(320).word="SHARED":keywordlist(320).COLOR="Blue"
            keywordlist(321).word="SHELL":keywordlist(321).COLOR="Blue"
            keywordlist(322).word="SHL":keywordlist(322).COLOR="Blue"
            keywordlist(323).word="SHR":keywordlist(323).COLOR="Blue"
            keywordlist(324).word="SHORT":keywordlist(324).COLOR="Blue"
            keywordlist(325).word="SIN":keywordlist(325).COLOR="Blue"
            keywordlist(326).word="SINGLE":keywordlist(326).COLOR="Blue"
            keywordlist(327).word="SIZEOF":keywordlist(327).COLOR="Blue"
            keywordlist(328).word="SLEEP":keywordlist(328).COLOR="Blue"
            keywordlist(329).word="SPACE":keywordlist(329).COLOR="Blue"
            keywordlist(330).word="SPC":keywordlist(330).COLOR="Blue"
            keywordlist(331).word="SQR":keywordlist(331).COLOR="Blue"
            keywordlist(332).word="STATIC":keywordlist(332).COLOR="Blue"
            keywordlist(333).word="STDCALL":keywordlist(333).COLOR="Blue"
            keywordlist(334).word="STEP":keywordlist(334).COLOR="Blue"
            keywordlist(335).word="STICK":keywordlist(335).COLOR="Blue"
            keywordlist(336).word="STOP":keywordlist(336).COLOR="Blue"
            keywordlist(337).word="STR":keywordlist(337).COLOR="Blue"
            keywordlist(338).word="STRIG":keywordlist(338).COLOR="Blue"
            keywordlist(339).word="STRING":keywordlist(339).COLOR="Blue"
            keywordlist(340).word="STRPTR":keywordlist(340).COLOR="Blue"
            keywordlist(341).word="SUB":keywordlist(341).COLOR="Blue"
            keywordlist(342).word="SWAP":keywordlist(342).COLOR="Blue"
            keywordlist(343).word="SYSTEM":keywordlist(343).COLOR="Blue"
            keywordlist(344).word="TAB":keywordlist(344).COLOR="Blue"
            keywordlist(345).word="TAN":keywordlist(345).COLOR="Blue"
            keywordlist(346).word="THEN":keywordlist(346).COLOR="Blue"
            keywordlist(347).word="THIS":keywordlist(347).COLOR="Blue"
            keywordlist(348).word="THREADCREATE":keywordlist(348).COLOR="Blue"
            keywordlist(349).word="THREADWAIT":keywordlist(349).COLOR="Blue"
            keywordlist(350).word="TIME":keywordlist(350).COLOR="Blue"
            keywordlist(351).word="TIMESERIAL":keywordlist(351).COLOR="Blue"
            keywordlist(352).word="TIMEVALUE":keywordlist(352).COLOR="Blue"
            keywordlist(353).word="TIMER":keywordlist(353).COLOR="Blue"
            keywordlist(354).word="TO":keywordlist(354).COLOR="Blue"
            keywordlist(355).word="PUT":keywordlist(355).COLOR="Blue"
            keywordlist(356).word="TRIM":keywordlist(356).COLOR="Blue"
            keywordlist(357).word="UDT":keywordlist(357).COLOR="Blue"
            keywordlist(358).word="TYPEOF":keywordlist(358).COLOR="Blue"
            keywordlist(359).word="UBOUND":keywordlist(359).COLOR="Blue"
            keywordlist(360).word="UBYTE":keywordlist(360).COLOR="Blue"
            keywordlist(361).word="UCASE":keywordlist(361).COLOR="Blue"
            keywordlist(362).word="UINTEGER":keywordlist(362).COLOR="Blue"
            keywordlist(363).word="ULONG":keywordlist(363).COLOR="Blue"
            keywordlist(364).word="ULONGINT":keywordlist(364).COLOR="Blue"
            keywordlist(365).word="UNION":keywordlist(365).COLOR="Blue"
            keywordlist(366).word="UNLOCK":keywordlist(366).COLOR="Blue"
            keywordlist(367).word="UNSIGNED":keywordlist(367).COLOR="Blue"
            keywordlist(368).word="UNTIL":keywordlist(368).COLOR="Blue"
            keywordlist(369).word="USHORT":keywordlist(369).COLOR="Blue"
            keywordlist(370).word="PRINT":keywordlist(370).COLOR="Blue"
            keywordlist(371).word="VA_ARG":keywordlist(371).COLOR="Blue"
            keywordlist(372).word="VA_FIRST":keywordlist(372).COLOR="Blue"
            keywordlist(373).word="VA_NEXT":keywordlist(373).COLOR="Blue"
            keywordlist(374).word="VAL":keywordlist(374).COLOR="Blue"
            keywordlist(375).word="VALLNG":keywordlist(375).COLOR="Blue"
            keywordlist(376).word="VALINT":keywordlist(376).COLOR="Blue"
            keywordlist(377).word="VALUINT":keywordlist(377).COLOR="Blue"
            keywordlist(378).word="VALULNG":keywordlist(378).COLOR="Blue"
            keywordlist(379).word="VAR":keywordlist(379).COLOR="Blue"
            keywordlist(380).word="VARPTR":keywordlist(380).COLOR="Blue"
            keywordlist(381).word="PRINT":keywordlist(381).COLOR="Blue"
            keywordlist(382).word="WAIT":keywordlist(382).COLOR="Blue"
            keywordlist(383).word="WBIN":keywordlist(383).COLOR="Blue"
            keywordlist(384).word="WCHR":keywordlist(384).COLOR="Blue"
            keywordlist(385).word="WEEKDAY":keywordlist(385).COLOR="Blue"
            keywordlist(386).word="WEEKDAYNAME":keywordlist(386).COLOR="Blue"
            keywordlist(387).word="WEND":keywordlist(387).COLOR="Blue"
            keywordlist(388).word="WHILE":keywordlist(388).COLOR="Blue"
            keywordlist(389).word="WEND":keywordlist(389).COLOR="Blue"
            keywordlist(390).word="WHEX":keywordlist(390).COLOR="Blue"
            keywordlist(391).word="WIDTH":keywordlist(391).COLOR="Blue"
            keywordlist(392).word="WINDOW":keywordlist(392).COLOR="Blue"
            keywordlist(393).word="WINDOWTITLE":keywordlist(393).COLOR="Blue"
            keywordlist(394).word="WINPUT":keywordlist(394).COLOR="Blue"
            keywordlist(395).word="WITH":keywordlist(395).COLOR="Blue"
            keywordlist(396).word="WOCT":keywordlist(396).COLOR="Blue"
            keywordlist(397).word="WRITE":keywordlist(397).COLOR="Blue"
            keywordlist(398).word="WRITE #":keywordlist(398).COLOR="Blue"
            keywordlist(399).word="WSPACE":keywordlist(399).COLOR="Blue"
            keywordlist(400).word="WSTR":keywordlist(400).COLOR="Blue"
            keywordlist(401).word="XOR":keywordlist(401).COLOR="Blue"
            keywordlist(402).word="PUT":keywordlist(402).COLOR="Blue"
            keywordlist(403).word="YEAR":keywordlist(403).COLOR="Blue"
            keywordlist(404).word="ZSTRING":keywordlist(404).COLOR="Blue"
            keywordlist(405).word="END":keywordlist(404).COLOR="Blue"
            keywordlist(406).word="#INCLUDE":keywordlist(404).COLOR="Blue"
            keywordlist(407).word="GET":keywordlist(404).COLOR="Blue"
            keywordlist(408).word="PSET":keywordlist(404).COLOR="Blue"
    
      nkeywordlist=409
    
    END SUB
    
    
    ' ------------------------------------------------------------------
    
    
    FUNCTION isbasickeyword(s AS STRING) AS INTEGER
    
      DIM AS INTEGER i, retval
    
      retval=false
      IF (nkeywordlist=0) THEN readkeywordlist
      FOR i=0 TO nkeywordlist-1
        IF UCASE(s)=keywordlist(i).word THEN
          retval=true
          EXIT FOR
        END IF
      NEXT i
    
      isbasickeyword=retval
    
    END FUNCTION
    
    
    ' ------------------------------------------------------------------
    
    FUNCTION test_basic(x AS textbuf) AS INTEGER
    
      DIM AS INTEGER i, j, icountbasic,retval
      DIM AS textbuf PTR tempbuf
    
      icountbasic=0
      FOR i=0 TO x.nline-1
        tempbuf=tokenizestring(x.OUT(i))
        FOR j=0 TO tempbuf->nline-1
          IF isbasickeyword(tempbuf->out(j)) THEN icountbasic=icountbasic+1
        NEXT j
        DELETE tempbuf
      NEXT i
    
      retval=false
      IF (icountbasic>2) THEN retval=true
    
      test_basic=retval
    
    END FUNCTION
    
    ' ------------------------------------------------------------------
    
    SUB test2
    
      DIM AS textbuf x
      x.init(10,"Test")
    
      x.add("Adam Eva Function")
      x.add("Hallo Eins Print Screen Zwei")
      x.add("juhdga 7652 djsh")
      x.add("F 7652")
    
      readkeywordlist
    
      PRINT test_basic(x)
    
    END SUB
    
    ' ------------------------------------------------------------------
    
    SUB insertstr(BYREF s AS STRING, instring AS STRING, ipos AS integer)
    
      s=LEFT(s,ipos-1)+instring+RIGHT(s,LEN(s)-ipos+1)
    
    END SUB
    
    ' ------------------------------------------------------------------
    
    SUB outline_basic(tb AS textbuf)
    
      DIM AS INTEGER i,j
      DIM AS tokenbuf PTR tempbuf
    
      DIM AS STRING starttag="<font color="+Ccolor+"><b>"
      DIM AS STRING endtag="</b></font>"
      DIM AS STRING starttag_str="<font color="+Cstringcolor+"><b>"
      DIM AS STRING starttag_com="<font color="+Ccommentcolor+"><b>"
      DIM AS STRING starttag0
    
    
      '? "outline_basic()"
      'tb.print0
    
      FOR i=0 TO tb.nline-1
        tempbuf=tokenizestring2(tb.OUT(i))
        'tempbuf->print0
        'sleep
        FOR j=0 TO tempbuf->tbuf.nline-1
          starttag0=""
          IF isbasickeyword(tempbuf->out(j)) THEN starttag0=starttag
          IF (tempbuf->propertytab[j]=Cstring) THEN starttag0=starttag_str
          IF (tempbuf->propertytab[j]=Ccomment) THEN starttag0=starttag_com
          IF (LEN(starttag0)>2) THEN
            '? tempbuf->postab[j]
            '? len(tempbuf->out(j))
            'In Grossbuchstaben umwandeln
            IF (tempbuf->propertytab[j]=0) THEN _
            mid(tempbuf->orgstring,tempbuf->postab[j],LEN(tempbuf->out(j)))=UCASE(mid(tempbuf->orgstring,tempbuf->postab[j],LEN(tempbuf->out(j))))
            'Starttag einfuegen
            insertstr(tempbuf->orgstring,starttag0,tempbuf->postab[j])
            'Endtag einfuegen
            insertstr(tempbuf->orgstring,endtag,tempbuf->postab[j]+LEN(tempbuf->out(j))+LEN(starttag0))
            'Positionstabelle nachfuehren
            tempbuf->shiftpos(LEN(starttag0)+LEN(endtag),j+1)
            '? "outline",i,j,tempbuf->orgstring
            'sleep
          END IF
        NEXT j
        tb.change(i,tempbuf->orgstring)
        DELETE tempbuf
      NEXT i
    END SUB
    
    
    ' ------------------------------------------------------------------
    
    
    SUB convert(infileno AS INTEGER, outfileno AS integer)
    
      DIM s AS STRING
      DIM AS INTEGER i,found,region
    
      DIM tb AS textbuf
      tb.init(1000,"get_basicpart")
    
      i=0
      found=false
      region=-1
      WHILE NOT found AND NOT EOF(infileno)
        LINE INPUT #infileno,s
        if (instr(lcase(s),"
    ")>0) then region=-1 if test_basic(tb) then '? "convert() tb.line=",tb.nline outline_basic(tb) '? "hallo" 'tb.print0 'sleep end if tb.fprint0(outfileno) tb.clear() end if 'Eine Zeile nach
     mit dem Einlesen beginnen
        IF region>0 THEN
          tb.add(s)
        END IF
        IF (region>=0) THEN region=region+1
    
        'Rausschreiben
        IF (region<=1) THEN PRINT #outfileno,s
        i=i+1
      WEND
    
    END SUB
    
    
    ' ------------------------------------------------------------------
    
    SUB test3
    
      DIM AS INTEGER infileno,outfileno
    
      infileno=FREEFILE()
      OPEN "Y:\kap36.htm" FOR INPUT AS infileno
      outfileno=FREEFILE()
      OPEN "Y:\kap36col.htm" FOR OUTPUT AS outfileno
    
      convert(infileno,outfileno)
      CLOSE(infileno)
      CLOSE(outfileno)
    
    END SUB
    
    ' ------------------------------------------------------------------
    
    
    SUB main
    
      DIM AS INTEGER infileno,outfileno
    
      IF COMMAND(1)="" THEN brkmess("Syntax: basicoutline ")
    
      infileno=FREEFILE()
      OPEN COMMAND(1) FOR INPUT AS infileno
      outfileno=FREEFILE()
      OPEN "buffer.htm" FOR OUTPUT AS outfileno
    
      convert(infileno,outfileno)
      CLOSE(infileno)
      CLOSE(outfileno)
    
      SHELL("copy buffer.htm "+COMMAND(1))
      SHELL("del buffer.htm")
    
    END SUB
    
    ' ------------------------------------------------------------------
    
    main