/media/sda-magnetic/david/Dokumente-15/fernuni-hagen/cs-i-ii/old-cs-2-03/yun7x/lexer-c-3/lexer.v5.c


/*

Zahlenfolge
Buchstabenfolge
$Buchstabenfolge
BEGIN
END
WHILE
DO
IF
THEN
VAR
PRINT
ELSE
+
-
*
/
$
=
:=
(
)
[
]
<
>
=
==
!=
>=
<=
;
~

Buchstabenfolgen, die zusammen hängen, müssen als zusammenhängende Buchstabenfolge zurückgeliefert werden

Zahlenfolgen, die zusammen hängen, müssen als zusammenhängende Zahlenfolge zurückgeliefert werden

Ein Token hört da auf, wo ein White Space kommt und nach einem White Space fängt ein Token an, wenn da noch Text ist

Wenn auf ein Sonderzeichen eine Text oder Zahlenfolge folgt, bildet das Sondernzeichen und die Text oder Zahlenfolge ein getrenntes Token

Wenn auf einen Text oder Zahlenfolge ein Sonderzeichen folgt, bildet das Sonderzeichen und die Text oder Zahlenfolge ein getrenntes Token

Es gibt Sonderzeichen, die nicht zusammen ein Token bilden, aber aufeinander folgen können, diese bilden getrennte Token

Manche Sonderzeichen hängen zusammen. Der Lexer muss selber erkennen, ist das eine zusammenhängende Folge, oder sind das jeweils einzelne Token

Jedes Token, egal, ob aus einem Zeichen bestehend oder nicht, ist ein String. Der Lexer wird mit einem Stream realisiert. Und der Stream ist genauso ein Stream, wie der von der Fernuniversität in Hagen beschrieben wurde. Mit einem Unterschied, er liefert statt einzelnen Zeichen Strings zurück. 

Es lassen sich zwei Möglichkeiten verwenden. Der eine Lexer ließt, während der Compiler arbeitet, Token für Token zurück. Der andere Lexer speichert alle Token in einem Array. Dabei ist auf zweiteren Lexer nicht unbedingt zu verzichten, der zweitere Lexer kann verwendet werden, aber er ist in Kombination zum ersteren zu verwenden. Das heißt, selbst, wenn er verwendet wird, das Array wird selber mit so einem Lexer erstellt. 

Das gute daran wäre, eine Tabelle mit einer Reihenfolge von Token zu haben, dass man jederzeit auf jedes Token zurückgreifen kann. Jedes Token hat dabei eine ID.

Deswegen werden, egal, wie sie verwendet werden und ersterer auch alleine verwendet werden kann, beide Lexer gleich realisiert. 

Der Vorteil vom Stream ist, bei einem Whitespace ist ein Zeichen auf jeden Fall beendet. Wenn wir einen Stream in einen Stream schicken, können wir zum Beispiel klar entscheiden, hier kommt ein Whitespace, hier endet das Token. Deswegen wäre es sinnvoll eine Zwischestufe ein zu schalten, die darin trennt. Sie liefert nach jedem Whitespace einen neuen String. Dieser String wird durch einen weiteren Stream auf Token untersucht.

Jetzt ist die Frage, wie wir die Token unterscheiden: 

Und dabei ist klar, wir brauchen eine Tabelle mit Token. Das heißt, wir brauchen eine Tabelle, die enthält

BEGIN
WHILE 
DO

Die können heißen, wie sie wollen und die Tabelle ist erweiterbar. Was für Zeichen das sind, das ist egal. Das ist alles egal. Das sind Zeichenfolge.

Besonder Zeichenfolgen sind:

Buchstabenfolgen
$Buchstabenfolgen
Zahlenfolgen

Die Idee ist, man geht den SourceCode durch und sucht nach den existierenden Zeichen. Das heißt, die Tabelle existiert, mit den Zeichenfolgen. Und während man den SourceCode durchgeht, sucht man mit der Tabelle nach den Zeichenfolgen.

Allerdings nimmt man für jede Zeichenfolge eine Funktion. Man speichert zwar, die Zeichenfolgen in einem Array, trotzdem hat man zu jeder Zeichenfolge eine Funktion. 

Jedensfalls schreibt man auf keinen Fall, eine Funktion, die das gesamte Array durchgeht.

Die Funktion findet für die Zeichenfolge, heraus, ob sie es ist.

Besondere Funktionen sind dabei für Buchstabenfolgen, $Buchstabenfolgen, Zahlenfolgen. 

Jetzt ist das entscheidende: Das sind keine Booleschen Funktionen. Das wollen wir in C auch nicht. In sind wir nicht so mit Boolean. Außerdem passt Boolean überhaupt nicht dazu, dass wir Buchstabenfolgen haben.
Deswegen liefern die Funktionen, die Zeichenfolge zurück, so wie sie ist, wenn es sich um sie handelt. Oder sie liefert NULL zurück, wenn es nicht die Zeichenfolge ist.

Jetzt ist das blöd: Weil wir haben ein Array von Zeichenfolgen, mit den ausgezeichneten Zeichenfolgen: Buchstabenfolge, Zahlenfolge und müssen trotzdem immer eine Funktion haben.

Der Lexer weiß nicht bescheid. Das Problem wäre: Wir müssten, wenn wir das erweitern, sowohl die Tabelle erweitern, als auch eine neue Funktion schreiben. Der Lexer, weiß allerdings gar nicht bescheid. Schreiben wir eine Funktion für BEGIN, dann würde er quasi Bescheid. Außer Buchstabenfolgen und Zahlenfolgen, sind das irgendwelche Folgen. Deswegen kann man der Funktionen einen Index übergeben. Die Token sind ja in einer Tabelle. Also können wir den Index in der Tabelle übergeben. Das würde bedeuten: Unser Stream: Schickt jeden Eintrag in der Tabelle an die Funktion und untersucht, bis die Funktion nicht NULL liefert. 

Wir brauchen gute Namen - die Wahl der Namen für die Tabelle zum Beispiel sollten gut überlegt sein.

Kommentare bringen wir auch gleich unter

Wir achten insofern auf die Korrektheit, indem wir für alles, auch zum Beispiel für die Länge von Arrays, also eine Funktion, die die Länge einer Tabelle berechnet. Das muss nicht per Hand hingeschrieben werden - die Funktionen, die zur Verfügung gestellt werden, werden üppig und reichlich zur Verfügung gestellt.

Es reicht die Funktion ein Mal auf zu rufen, indem man zum Beispiel globale Variable nimmt und die ein Mal mit der Funktion initialisiert
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define lex_reset                   lex_init 
#define lexlen                      strlen

#define LEX_BUF_N                   8192*8
#define TOKEN_BUF_N                 LEX_BUF_N
#define TOKEN_LEN                   64

#define ERR_MSG_NOT_ENOUGH_MEMORY   "Not enough memory"
#define ERR_MSG_TOO_LONG_TOKEN      "too long token"
#define ERR_NR_NOT_ENOUGH_MEMORY    -2
#define ERR_NR_TOO_LONG_TOKEN       -3

char *tokens [] = { "BEGIN", "END", "WHILE", "DO", "IF", "THEN", "VAR", "PRINT", "ELSE", "+", "-", "*", "/", "$", "=", ":=", "(", ")", "[", "]", "<", ">", "==", "!=", ">=", "<=", ";", "~", NULL };

char lex_buf [LEX_BUF_N];
int lex_index = 0;



#define TOKEN_I_VAR_NAME        0
#define TOKEN_I_CONST           1
#define TOKEN_I_CHAR_STR        2   
#define TOKEN_I_BEGIN           3
#define TOKEN_I_END             4
#define TOKEN_I_WHILE           5
#define TOKEN_I_DO              6
#define TOKEN_I_IF              7
#define TOKEN_I_THEN            8
#define TOKEN_I_VAR             9
#define TOKEN_I_PRINT           10
#define TOKEN_I_ELSE            11
#define TOKEN_I_ADD             12
#define TOKEN_I_SUB             13
#define TOKEN_I_MUL             14
#define TOKEN_I_DIV             15
#define TOKEN_I_DOLLAR          16
#define TOKEN_I_EQ1             17
#define TOKEN_I_SET             18
#define TOKEN_I_OPEN_RND_BRCKT  19
#define TOKEN_I_CLSD_RND_BRCKT  20
#define TOKEN_I_OPEN_SQR_BRCTK  21
#define TOKEN_I_CLSD_SQR_BRCTK  22
#define TOKEN_I_LESS            23
#define TOKEN_I_GRTR            24
#define TOKEN_I_EQ2             25
#define TOKEN_I_NOT_EQ          26
#define TOKEN_I_GRTR_EQ         27
#define TOKEN_I_LESS_EQ         28
#define TOKEN_I_SEMICOLON       29
#define TOKEN_I_TILDE           30

int tokens_n () {
    int i;
    
    for (i = 0;  tokens [i] != NULL;  i++);
    
    return i;
}

int get_lex_index () {
    return lex_index;
}

void lex_init () {
    lex_index = 0;
}

char *lex_reader () {
    char *token_buf;
    
    if (lex_index == lexlen (lex_buf))
        return NULL;
    else {
        if ((token_buf = (char *) malloc (LEX_BUF_N)) == NULL) {
            perror (ERR_MSG_NOT_ENOUGH_MEMORY);
            exit (ERR_NR_NOT_ENOUGH_MEMORY);
        }
        strcpy (token_buf, lex_buf + lex_index);
        lex_index++;
        return (token_buf);
    }
}

char *lex_reader_white_space_filter () {
    char *token_buf_src;
    char *token_buf_des;
    char *p;
    char *q;
    int i;
    
    if ((token_buf_src = lex_reader ()) == NULL)
        return NULL;
    if ((token_buf_des = (char *) malloc (TOKEN_LEN)) == NULL) {
        perror (ERR_MSG_NOT_ENOUGH_MEMORY);
        exit (ERR_NR_NOT_ENOUGH_MEMORY);
    }

    else {
        p = token_buf_src;
        q = token_buf_des;
        i = 0;

        while ( ((*p) != 0) && \
                (((*p) == ' ') || \
                ((*p) == '\n') || \
                ((*p) == '\t') || \
                ((*p) == '\r'   ))) 
        {
            p++;
            i++;
            if (i > TOKEN_LEN) {
                perror (ERR_MSG_TOO_LONG_TOKEN);
                exit (ERR_NR_TOO_LONG_TOKEN);
            }
        }

        while ( ((*p) != 0) && \
                ((*p) != ' ') && \
                ((*p) != '\n') && \
                ((*p) != '\t') && \
                ((*p) != '\r'   )) 
        {
            *q = *p;
            p++;
            q++;
            i++;
            if (i > TOKEN_LEN) {
                perror (ERR_MSG_TOO_LONG_TOKEN);
                exit (ERR_NR_TOO_LONG_TOKEN);
            }
        }
        *q = 0;        
        if (i > 1) 
            lex_reader_white_space_filter ();
        free (token_buf_src);
        return token_buf_des;
    }
}

char *lex_reader_token_filter () {
    char *token_buf_src;
    char *token_buf_des;
    char tmp [TOKEN_LEN];
    char *q;
    int i;
    
    if ((token_buf_src = lex_reader_white_space_filter ()) == NULL)
        return NULL;
    if ((token_buf_des = (char *) malloc (TOKEN_LEN)) == NULL) {
        perror (ERR_MSG_NOT_ENOUGH_MEMORY);
        exit (ERR_NR_NOT_ENOUGH_MEMORY);
    }

    else {        
        strcpy (tmp, token_buf_src);
        
        if (strlen (tmp) > TOKEN_LEN) {
            perror (ERR_MSG_TOO_LONG_TOKEN);
            exit (ERR_NR_TOO_LONG_TOKEN);
        }
        
        q = tmp;
        for (i = 0;  i < tokens_n ();  i++) {
            if (strncmp (tmp, tokens [i], TOKEN_LEN) == 0)
                return q;
        }
        return NULL;
    }
}

int main (void) {
    lex_init ();
    char *str;
    int i;
    
    strcpy (lex_buf, "WHILE 12312ajajasdjasdasbdas bdasbhdask dbajkasdas jkdaskdaj sdasdaljsd lajsdalsd lasjdaslkdjaldsaldjlkaljskdaldskldsaldasdjlaskldaldskaldsjlsd  - WHILE DO BEGIN := + END");

    while (lex_index < strlen (lex_buf)) {
        for (i = 0;  i < tokens_n ();  i++)
            if ((str = lex_reader_token_filter ()) != NULL) {
                printf ("%s\n", str);
                break;
            }
    }
        
}