Laden...

Regex-basierter Tokenizer

Erstellt von TheBrainiac vor 13 Jahren Letzter Beitrag vor 13 Jahren 6.966 Views
TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 13 Jahren
Regex-basierter Tokenizer

Hi @ All!

Nach längerer Schaffenspause habe ich mal wieder die Zeit gefunden, ein kleines Projekt zu entwickeln. Entstanden ist diese Idee bei einem Java-Projekts während meinem Studium. Den Tokenizer habe ich auch erst in Java umgesetzt und jetzt nach C# portiert, da ich ihn euch nicht vorenthalten will 😄.

Was kann der Tokenizer?
Er kann beliebige Eingabe-Strings in Tokens zerlegen, wobei die Regeln für die Zerlegung Regex-basiert sind.

Wie konfiguriere ich den Tokenizer?
Dafür gibt es zwei Möglichkeiten:

1. "Zu Fuß":
Angenommen, wir wollen folgendes Snippet farbig hervorheben:

void Test() {
    Console.WriteLine("THIS IS SPARTA!!!");
}

Dafür müssen wir es nur mit folgendem Code in Tokens zerlegen:

var tokenizer = new Tokenizer();

tokenizer.Add("STRING", @"""[^""]*""", 4);
tokenizer.Add("KEYWORD", @"\bvoid\b", 3);
tokenizer.Add("IDENTIFIER", @"\w+", 2);
tokenizer.Add("OPERATOR", @"[(){};.]", 1);
tokenizer.Add("WHITESPACE", @"\s+", 0, true);

var tokens = tokenizer.Tokenize(input);

Nun können einfach wir anhand der Tokens den Text z.b. in einer RichTextBox farbig hervorheben.

2. Per Regel-Datei:
Angenommen, wir wollen folgendes Mathe-Skript in Tokens zerlegen, um es anschließend Parsen zu können:

const PI = 3.14159265358979323846;

function f(x) = sqrt(x ^ 3);

var a = PI*12;

print f(a);

a = a / 4;

print f(a);

Dafür reicht folgende kleine Regel-Datei:

DIGIT       "\d+(\.\d+)?"                     5
OPERATOR    "[()-+*/%=;^]"                    4
KEYWORD     "\b(var|const|function|print)\b"  3
IDENTIFIER  "\w+"                             2
WHITESPACE* "\s+"                             1

Mithilfe dieser Regel-Datei lässt sich nun ganz einfach ein Tokenizer erzeugen:

var tokenizer = Tokenizer.CreateFrom(File.ReadAllText("myRuleFile.txt"));

var tokens = tokenizer.Tokenize(input);

Die Tokens können wir nun zum Parsen verwenden.

Wie sind die Regel aufgebaut?
Eine Regel hat 4 Eigenschaften:*Name: Gibt den Namen der Regel an. *Pattern: Das Pattern, das matchen muss, um ein Token mit der aktuellen Regel erzeugen zu können. *Weight: Das "Gewicht" der Regel - Regeln mit höherem Gewicht werden zuerst angewendet. *Ignore: Gibt an, ob mit dieser Regel erzeugte Tokens ignoriert werden sollen. Wird in einer Regel-Date mit einem \* hinter dem Namen angegeben

Wie sind die Tokens aufgebaut?
Ein Token enthält den gematchten String, den Namen der Regel, mit der es gefunden wurde sowie die Position, an der es gefunden wurde.

Ich hoffe, jemand kann das gebrauchen, mir hat er (bzw. die Java-Version) dicke Bonuspunkte eingebracht 8).

Gruß, Christian.

// Edit: Downloads V1: 3

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
799 Beiträge seit 2007
vor 13 Jahren

Die Java-Version? Wieso nicht gleich den StreamTokenizer verwenden?

Was ganz nett wäre wenn wie bei lex Methoden an die einzelnen Tokens gebunden werden können.

As a man thinketh in his heart, so he is.

  • Jun Fan
    Es gibt nichts Gutes, außer man tut es.
  • Erich Kästner
    Krawutzi-Kaputzi
  • Kasperl
TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 13 Jahren

Die Java-Version? Wieso nicht gleich den StreamTokenizer verwenden?

Weil wir eine Scheme-ähnliche Sprache verarbeiten wollten. Da sind mehr als nur die normalen Zeichen in Identifiern erlaubt, z.b. foo-bar?!=<> ist hier ein vailder Identifier.

Was ganz nett wäre wenn wie bei lex Methoden an die einzelnen Tokens gebunden werden können.

Wie meinst du das?

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
799 Beiträge seit 2007
vor 13 Jahren

Hallo,

siehe einfach Wiki-Page zu lex

Es ist möglich gleich ein bisserl an den Strings herum zu spielen und zwar sofort wenn der Token identifiziert wurde.

As a man thinketh in his heart, so he is.

  • Jun Fan
    Es gibt nichts Gutes, außer man tut es.
  • Erich Kästner
    Krawutzi-Kaputzi
  • Kasperl
TheBrainiac Themenstarter:in
795 Beiträge seit 2006
vor 13 Jahren

So, hab das mal eben angepasst.

Nun ist es möglich, das Token selbst zu erstellen:

tokenizer.Add("STRING", @"""[^""]*""", 4, (type, content, position) => {
    var processed = content.Substring(1, content.Length - 2);

    return new Token(type, content, position, processed);
});
tokenizer.Add("NUMBER", @"\d+", 3, (type, content, position) => new Token(type, content, position, Int32.Parse(content)));

Das Token hat nun eine Eigenschaft mehr, nämlich State. Hierdrin kann nun ein beliebiges Objekt gespeichert werden.

Habe den Download oben erneuert.

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`