Derleyici Tasarımı
Yüklüyor...
Arıyor...
Eşleşme Yok
Lexer
identifier
harf/rakam/alt çizgiden oluşan, değişken ve fonksiyon ismi olan token. x, b, func gibi.
punctuation
harf/rakam olmayan karakter grubu. ==, (, ; gibi.

Lexer'ımız iki katmandan oluşacak:

  • Sayı, identifier, punctuation gibi tipleri direkt toplayacak; lexeme üretecek katman. Bu katmanda ürettiğimiz lexemeler, gramer açısından hâlâ gereksiz bilgi içeriyor olacak.
  • Bu lexemelerdeki keyword ve punctuation'ları çıkaracak, soyut tokenler üretecek üst katman.

Lexer katmanında identifier ve punctuation'ların ne anlama geldiğini tam bilmiyoruz, sadece metin parçaları olarak grupluyoruz.

struct lexeme {
const char *seminfo;
size_t seminfo_len;
enum lexeme_kind {
} kind;
};
Ham lexer çıktısı.
Definition lexer.h:21
enum lexeme::lexeme_kind kind
lexeme_kind
Definition lexer.h:29
@ LEXEME_FLOAT
Definition lexer.h:35
@ LEXEME_EOF
Definition lexer.h:31
@ LEXEME_INT
Definition lexer.h:33
@ LEXEME_IDENT
Definition lexer.h:37
@ LEXEME_PUNCT
Definition lexer.h:39
const char * seminfo
Definition lexer.h:23
size_t seminfo_len
Definition lexer.h:26

Bu lexemeleri, lexer ile üreteceğiz:

void lexer_init(struct lexer *lexer, const char *text);
struct lexeme lexer_next(struct lexer *lexer);
struct lexeme lexer_next(struct lexer *lexer)
Sıradaki lexeme.
void lexer_init(struct lexer *lexer, const char *text)
[Lexeme]
lexer.
Definition lexer.h:14

lexer_init() gayet basit bir fonksiyon. Kaynak metni lexer içinde pointera alıp currenti metnin başına hizalıyor:

/* lexer struct'ı içinde start ve current şeklinde iki pointer tutuyordu */
void lexer_init(struct lexer *lexer, const char *text)
{
lexer->start = text;
lexer->current = text;
}
const char * start
Definition lexer.h:15
const char * current
Definition lexer.h:16

Esas DFA yapısı lexer_next() içinde kendini gösteriyor:

struct lexeme lexer_next(struct lexer *lexer)
{
/* Boşlukları görmezden gelip bir sonraki karaktere atlıyoruz. */
while (isspace(peek(lexer)))
advance(lexer);
if (is_at_end(lexer))
char c = peek(lexer);
/* a-z|A-Z|_ */
if (isalpha(c) || c == '_')
return collect_identifier(lexer);
/* 0-9 */
if (isdigit(c)) {
return collect_number(lexer);
}
/* punctuation */
if (ispunct(c))
return collect_punctuation(lexer);
assert(0 && "Tanınmayan karakter."); // GCOVR_EXCL_LINE: not tested
}

lexer_next() fonksiyonunda kodun okunabilirliğini artırmak için birkaç basit fonksiyon tanımladım: Lexer için Yardımcı Fonsiyonlar

Lexeme toplayan fonksiyonları tek tek inceleyelim. Identifier ve punctuation toplama fonksiyonları birebir aynı şekilde çalışıyor. Aralarındaki tek fark, biri ispunct kullanırken diğeri isalnum kullanıyor.

static struct lexeme collect_identifier(struct lexer *lexer)
{
/* a-z|A-Z|_|0-9 */
while (isalnum(peek(lexer)) || peek(lexer) == '_')
advance(lexer);
}
static struct lexeme collect_punctuation(struct lexer *lexer)
{
/* Lexer, birden fazla karakterden oluşan punctuation'ları da
* toplayabilmek adına punctuation'ları gruplar. */
while (ispunct(peek(lexer)))
advance(lexer);
}

Buradaki esas karışıklık sayı tokenlerini toplamada. Hem ondalık hem de tam sayı tokenleri tek bir fonksiyonla toplanıyor:

static struct lexeme collect_number(struct lexer *lexer)
{
/* Tam sayı kısmı topla. */
while (isdigit(peek(lexer)))
advance(lexer);
/* Ondalık kısım varsa, onu da topla. */
if (peek(lexer) == '.' && isdigit(peek_next(lexer))) {
advance(lexer); /* Noktayı topla */
/* Geri kalan sayı kısmı da topla. */
while (isdigit(peek(lexer)))
advance(lexer);
}
return lexeme(lexer, kind);
}

Lexeri hallettiğimize göre, artık Tokenizer yazmaya başlayabiliriz.