563 lines
23 KiB
C++
563 lines
23 KiB
C++
// ============================================================================
|
||
// saQut Compiler — Lexer (Karakter Seviyesinde Tarayıcı)
|
||
// ============================================================================
|
||
//
|
||
// DİZİN: src/lexer/lexer.hpp
|
||
// KATMAN: Katman 1 — Derleyici pipeline'ının ilk aşaması
|
||
// BAĞIMLI: Yok (sadece standart kütüphane)
|
||
// KULLANAN: Tokenizer (src/tokenizer/tokenizer.hpp)
|
||
//
|
||
// AMAÇ:
|
||
// Ham kaynak kodu (std::string) karakter karakter tarayarak:
|
||
// 1. Karakter konumunu takip eder (offset)
|
||
// 2. Backtracking (geri alma) desteği ile desen eşleme yapar
|
||
// 3. Sayısal literal'ları okur ve sınıflandırır (decimal, hex, binary, octal, float)
|
||
// 4. Boşluk karakterlerini atlar
|
||
// 5. Satır/sütun bilgisi sağlar (hata mesajları için temel)
|
||
//
|
||
// ADR-006: Neden Kendi Lexer'ımız?
|
||
// - std::istringstream veya regex kullanmak yerine, tam kontrol sağlayan
|
||
// sıfırdan bir lexer yazdık.
|
||
// - Backtracking: offsetMap ile konum yığını tutar, denenen bir eşleşme
|
||
// başarısız olursa geri alınabilir. Bu özellik std::istream'de yoktur.
|
||
// - Performans: Sanal fonksiyon çağrısı yok, her şey inline.
|
||
// - Hata ayıklama: Her karakter okuması kontrol edilebilir.
|
||
//
|
||
// TASARIM KARARLARI:
|
||
// 1. offsetMap (std::vector<int>): İç içe backtracking için yığın.
|
||
// beginPosition() → yığına mevcut konumu ekler
|
||
// acceptPosition() → yığındaki son konumu kalıcı yapar
|
||
// rejectPosition() → yığındaki son konumu atar (geri al)
|
||
// Bu sayede "dene, başarısız olursa geri al" patterni çalışır.
|
||
//
|
||
// 2. getchar() iki overload:
|
||
// - getchar(): mevcut konumdaki karakteri okur
|
||
// - getchar(int offset): mevcut konum + offset'teki karakteri okur
|
||
// İkincisi özellikle keyword kontrolünde önemlidir:
|
||
// "do" kelimesini gördükten sonra, bunun "double"ın başlangıcı olmadığını
|
||
// kontrol etmek için keyword sonrası karaktere bakılır.
|
||
//
|
||
// 3. isEnd(): offset >= size ile kontrol. offset her zaman [0, size] aralığında.
|
||
// size konumunda EOF (end of file) anlamına gelir.
|
||
//
|
||
// 4. readNumeric(): C/C++/Java sayı formatlarını destekler:
|
||
// - Decimal: 42, -3, +7
|
||
// - Hex: 0xFF, 0X1A
|
||
// - Binary: 0b1010, 0B1100
|
||
// - Octal: 0777 (0 ile başlayan ve 8-9 içermeyen)
|
||
// - Float: 3.14, .5, 1e10, 2.5E-3
|
||
// - Negatif/Pozitif: -42, +3 (baştaki işaret)
|
||
//
|
||
// BİLİNEN SINIRLAMALAR (TODO):
|
||
// TODO: Satır/sütun takibi eklenmeli (şu anda sadece offset var)
|
||
// TODO: Unicode/UTF-8 desteği (şu anda sadece ASCII)
|
||
// TODO: ' char literal'ı okunamıyor
|
||
// TODO: Sayısal alt çizgi (_) ayracı: 1_000_000 formatı
|
||
// TODO: Binary floating point: 0b1.1p10 formatı (C99 hexfloat)
|
||
//
|
||
// ============================================================================
|
||
|
||
#ifndef SAQUT_LEXER
|
||
#define SAQUT_LEXER
|
||
|
||
#include <iostream>
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
// ============================================================================
|
||
// INumber — Ara Sayısal Veri Yapısı
|
||
// ============================================================================
|
||
//
|
||
// Lexer'ın readNumeric() fonksiyonu tarafından döndürülür.
|
||
// Tokenizer bu yapıyı NumberToken'a dönüştürür.
|
||
//
|
||
// Neden ayrı bir struct? Lexer katmanı Token sınıflarından haberdar değil.
|
||
// Bağımlılık yönü: Lexer ← Tokenizer. Lexer hiçbir üst katmanı include etmez.
|
||
//
|
||
// ALANLAR:
|
||
// start, end : Kaynak koddaki başlangıç/bitiş konumları (offset)
|
||
// token : Sayının ham string hali (örn: "0xFF", "3.14", "1e10")
|
||
// isFloat : Ondalıklı sayı mı? (nokta veya epsilon içeriyor mu)
|
||
// hasEpsilon : Bilimsel gösterim mi? (e/E içeriyor mu)
|
||
// base : Sayı tabanı: 2, 8, 10, veya 16
|
||
// - 0x/0X ile başlarsa 16
|
||
// - 0b/0B ile başlarsa 2
|
||
// - 0 ile başlayıp 8-9 içermiyorsa 8
|
||
// - diğer her şey 10
|
||
// positive : Pozitif mi? (başında - işareti yoksa true)
|
||
//
|
||
struct INumber {
|
||
int start = 0; // Kaynak koddaki başlangıç offset'i
|
||
int end = 0; // Kaynak koddaki bitiş offset'i
|
||
std::string token; // Sayının ham metni (örn: "42", "0xFF", "3.14e-2")
|
||
bool isFloat = false; // true ise float/double literal
|
||
bool hasEpsilon = false; // true ise bilimsel gösterim (örn: 1e10)
|
||
int base = 10; // Sayı tabanı: 2, 8, 10, 16
|
||
bool positive = true; // false ise sayı negatif
|
||
};
|
||
|
||
// ============================================================================
|
||
// Lexer — Karakter Seviyesinde Tarayıcı
|
||
// ============================================================================
|
||
//
|
||
// Derleyici pipeline'ının en alt katmanı. Ham string üzerinde çalışır.
|
||
// Üst katmanlara (Tokenizer) karakter okuma ve konum yönetimi hizmeti sunar.
|
||
//
|
||
// DURUM DEĞİŞKENLERİ:
|
||
// input : Taranan kaynak kodun tamamı (string kopyası, değişmez)
|
||
// size : input.length() önbelleği (performans: her seferinde hesaplamaz)
|
||
// offset : Mevcut okuma konumu. 0 = ilk karakter, size = EOF
|
||
// offsetMap : Backtracking yığını. İç içe beginPosition/acceptPosition/rejectPosition
|
||
//
|
||
// PERFORMANS NOTU:
|
||
// Tüm metotlar inline tanımlanmıştır. Sanal fonksiyon çağrısı yoktur.
|
||
// offset değişiklikleri O(1)'dir.
|
||
// include() metodu O(n) karakter karşılaştırması yapar (n = kelime uzunluğu).
|
||
//
|
||
class Lexer {
|
||
public:
|
||
// --- Ham Veri ---
|
||
std::string input; // Kaynak kodun tamamı
|
||
int size = 0; // input.length() önbelleği
|
||
int offset = 0; // Mevcut okuma konumu (0 = başlangıç)
|
||
std::vector<int> offsetMap; // Backtracking yığını
|
||
|
||
// --- Pozisyon Yönetimi (Backtracking API) ---
|
||
//
|
||
// Kullanım örneği:
|
||
// lexer.beginPosition(); // konumu kaydet
|
||
// if (lexer.include("for", false)) // dene (false = eşleşse de geri al)
|
||
// lexer.acceptPosition(); // başarılı → kalıcı yap
|
||
// else
|
||
// lexer.rejectPosition(); // başarısız → geri al
|
||
|
||
void beginPosition(); // Şu anki konumu yığına kaydet
|
||
int getLastPosition(); // Yığındaki son konumu döndür
|
||
void acceptPosition(); // Yığındaki son konumu kalıcı yap (apply)
|
||
void setLastPosition(int n); // Yığındaki son konumu n olarak değiştir
|
||
void rejectPosition(); // Yığındaki son konumu at (discard)
|
||
|
||
// --- Dosya Sonu ve Pozisyon Sorgulama ---
|
||
bool isEnd(); // offset >= size ise true (EOF)
|
||
int* positionRange(); // [start, end] offset aralığı (tahsis eder, silinmeli!)
|
||
std::string getPositionRange(); // Pozisyon aralığındaki metni döndür
|
||
|
||
// --- Desen Eşleme ---
|
||
// include(): Belirtilen kelime mevcut konumda başlıyor mu?
|
||
// accept=true (varsayılan): eşleşirse konum ilerletilir
|
||
// accept=false: eşleşse bile konum geri alınır (keyword kontrolü için)
|
||
// Örnek: include("for", false) → "for" ile başlıyor mu? konumu değiştirme.
|
||
bool include(std::string word, bool accept = true);
|
||
|
||
// --- Konum Okuma/Yazma ---
|
||
int getOffset(); // Mevcut offset'i döndür
|
||
int setOffset(int n); // Offset'i n olarak ayarla, yeni değeri döndür
|
||
|
||
// --- Karakter Okuma ---
|
||
char getchar(int additionalOffset); // offset + ek'teki karakteri oku
|
||
char getchar(); // Mevcut offset'teki karakteri oku
|
||
void nextChar(); // offset'i 1 ilerlet (EOF kontrolü yapar)
|
||
void toChar(int n); // offset'i n kadar ilerlet
|
||
|
||
// --- Üst Seviye İşlemler ---
|
||
void setText(std::string input); // Yeni kaynak kodu yükle
|
||
void skipWhiteSpace(); // Boşluk/sekme/satırsonu karakterlerini atla
|
||
bool isNumeric(); // Mevcut karakter 0-9 aralığında mı?
|
||
INumber readNumeric(); // Sayı literal'ı oku ve INumber olarak döndür
|
||
};
|
||
|
||
// ============================================================================
|
||
// GERÇEKLEME (Implementation)
|
||
// ============================================================================
|
||
// Tüm metotlar inline olarak aşağıda tanımlanmıştır.
|
||
// Derleme modeli: header-only. main.cpp bu dosyayı include eder.
|
||
// ============================================================================
|
||
|
||
// --------------------------------------------------------------------------
|
||
// beginPosition: Mevcut offset'i yığına kaydet.
|
||
// İç içe çağrılabilir: 3 kere beginPosition → 3 elemanlı yığın.
|
||
// --------------------------------------------------------------------------
|
||
inline void Lexer::beginPosition() {
|
||
offsetMap.push_back(getLastPosition());
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// getLastPosition: Yığının tepesindeki konumu döndür.
|
||
// Yığın boşsa mevcut offset'i döndür (başlangıç durumu).
|
||
// --------------------------------------------------------------------------
|
||
inline int Lexer::getLastPosition() {
|
||
if (offsetMap.empty()) return offset;
|
||
return offsetMap.back();
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// acceptPosition: Yığındaki son geçici konumu kalıcı yap.
|
||
// Örnek: offsetMap=[5,10], offset=15 → offsetMap.back()=10 olur.
|
||
// Bu sayede include() denemesi başarılı olduğunda konum ilerletilmiş olur.
|
||
// --------------------------------------------------------------------------
|
||
inline void Lexer::acceptPosition() {
|
||
int t = offsetMap.back();
|
||
setLastPosition(t);
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// setLastPosition: Yığının tepesini veya offset'i değiştir.
|
||
// --------------------------------------------------------------------------
|
||
inline void Lexer::setLastPosition(int n) {
|
||
if (offsetMap.empty())
|
||
offset = n;
|
||
else
|
||
offsetMap.back() = n;
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// isEnd: Dosya sonuna gelindi mi? offset >= size.
|
||
// --------------------------------------------------------------------------
|
||
inline bool Lexer::isEnd() {
|
||
return size <= getOffset();
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// rejectPosition: Yığındaki son konumu at. Başarısız include() denemesi sonrası.
|
||
// --------------------------------------------------------------------------
|
||
inline void Lexer::rejectPosition() {
|
||
offsetMap.pop_back();
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// positionRange: Yığındaki en dış ve en iç konumu [start, end] olarak döndür.
|
||
// UYARI: new int[2] ile heap'te tahsis eder. Çağıran sorumludur.
|
||
// TODO: std::pair<int,int> veya yapı kullanarak tahsisi kaldır.
|
||
// --------------------------------------------------------------------------
|
||
inline int* Lexer::positionRange() {
|
||
int len = offsetMap.size();
|
||
if (len == 0)
|
||
return new int[2]{0, offset};
|
||
if (len == 1)
|
||
return new int[2]{offset, offsetMap[0]};
|
||
return new int[2]{offsetMap[len - 2], offsetMap[len - 1]};
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// getPositionRange: positionRange() aralığındaki metni string olarak döndür.
|
||
// --------------------------------------------------------------------------
|
||
inline std::string Lexer::getPositionRange() {
|
||
int* a = positionRange();
|
||
std::string mem;
|
||
for (int i = a[0]; i < a[1]; i++)
|
||
mem.push_back(input.at(i));
|
||
return mem;
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// include: Belirtilen kelime mevcut konumda başlıyor mu?
|
||
//
|
||
// Algoritma:
|
||
// 1. beginPosition() ile konumu kaydet
|
||
// 2. Kelimenin her karakterini sırayla karşılaştır
|
||
// 3. Eşleşmezse veya EOF olursa → rejectPosition() ve false dön
|
||
// 4. Tüm karakterler eşleşirse:
|
||
// - accept=true ise → acceptPosition() (konum kalıcı ilerler)
|
||
// - accept=false ise → rejectPosition() (konum eski haline döner)
|
||
// 5. true dön
|
||
//
|
||
// Neden accept parametresi var?
|
||
// Tokenizer scope() fonksiyonu, keyword'leri kontrol ederken accept=false
|
||
// kullanır. Çünkü bir keyword eşleşmesi, aynı zamanda daha uzun bir
|
||
// keyword'ün parçası olabilir (örn: "do", "double"ın başlangıcı).
|
||
// Eğer include("do", true) kullanılırsa, konum ilerler ve geri alınamaz.
|
||
// --------------------------------------------------------------------------
|
||
inline bool Lexer::include(std::string word, bool accept) {
|
||
beginPosition();
|
||
for (size_t i = 0; i < word.size(); i++) {
|
||
if (isEnd()) {
|
||
rejectPosition();
|
||
return false;
|
||
}
|
||
if (word[i] != getchar()) {
|
||
rejectPosition();
|
||
return false;
|
||
}
|
||
nextChar();
|
||
}
|
||
if (accept)
|
||
acceptPosition();
|
||
else
|
||
rejectPosition();
|
||
return true;
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// getOffset / setOffset: Konum erişimcileri.
|
||
// --------------------------------------------------------------------------
|
||
inline int Lexer::getOffset() {
|
||
return getLastPosition();
|
||
}
|
||
|
||
inline int Lexer::setOffset(int n) {
|
||
setLastPosition(n);
|
||
return getLastPosition();
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// getchar(additionalOffset): offset + ek kadar ilerideki karakteri oku.
|
||
// Sınır kontrolü yapar: target >= size ise '\0' döndürür ve hata mesajı basar.
|
||
// Bu metot özellikle keyword sınır kontrolünde kullanılır:
|
||
// "do" eşleşti, sıradaki karakter 'u' ise bu "double" olabilir → keyword değil
|
||
// --------------------------------------------------------------------------
|
||
inline char Lexer::getchar(int additionalOffset) {
|
||
int target = getOffset() + additionalOffset;
|
||
if (target >= size) {
|
||
std::cerr << "Lexer hatası: sınır aşımı\n";
|
||
return '\0';
|
||
}
|
||
return input.at(target);
|
||
}
|
||
|
||
inline char Lexer::getchar() {
|
||
int target = getOffset();
|
||
if (target >= size) {
|
||
std::cerr << "Lexer hatası: sınır aşımı\n";
|
||
return '\0';
|
||
}
|
||
return input.at(target);
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// nextChar / toChar: Konum ilerletme.
|
||
// EOF kontrolü yapar — dosya sonundaysa ilerlemez.
|
||
// --------------------------------------------------------------------------
|
||
inline void Lexer::nextChar() {
|
||
if (!isEnd())
|
||
setOffset(getOffset() + 1);
|
||
}
|
||
|
||
inline void Lexer::toChar(int n) {
|
||
if (!isEnd())
|
||
setOffset(getOffset() + n);
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// setText: Yeni kaynak kodu yükle. input ve size'ı günceller.
|
||
// --------------------------------------------------------------------------
|
||
inline void Lexer::setText(std::string text) {
|
||
input = text;
|
||
size = text.length();
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// skipWhiteSpace: Boşluk, sekme, satırsonu, satırbaşı karakterlerini atla.
|
||
// Yorum satırlarını atlamaz — bu Tokenizer'ın işi.
|
||
// --------------------------------------------------------------------------
|
||
inline void Lexer::skipWhiteSpace() {
|
||
while (!isEnd()) {
|
||
switch (getchar()) {
|
||
case '\r': // carriage return (Windows satırsonu \r\n)
|
||
case '\n': // line feed (Unix satırsonu)
|
||
case '\b': // backspace
|
||
case '\t': // tab
|
||
case ' ': // boşluk
|
||
nextChar();
|
||
break;
|
||
default:
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// isNumeric: Mevcut karakter bir rakam mı? (0-9)
|
||
// Pointer aritmetiği veya ASCII tablosu karşılaştırması yerine basit aralık
|
||
// kontrolü. Performans: O(1), branchless (modern derleyiciler optimize eder).
|
||
// --------------------------------------------------------------------------
|
||
inline bool Lexer::isNumeric() {
|
||
char c = getchar();
|
||
return (c >= '0' && c <= '9');
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// readNumeric: Tam bir sayı literal'ı oku.
|
||
//
|
||
// Desteklenen formatlar (öncelik sırasıyla):
|
||
// 1. İşaret: -42, +3 (baştaki isteğe bağlı işaret)
|
||
// 2. 0x/0X: Hex (0xFF, 0X1A)
|
||
// 3. 0b/0B: Binary (0b1010)
|
||
// 4. 0 ile başlayan: Octal (0777) veya Float (0.5)
|
||
// 5. Ondalık: 42, 3.14
|
||
// 6. Bilimsel: 1e10, 2.5E-3, 1.0e+5
|
||
//
|
||
// Algoritma:
|
||
// 1. İsteğe bağlı işareti oku (+ veya -)
|
||
// 2. İlk karakter '0' ise → özel durum (hex/bin/octal/float kontrolü)
|
||
// 3. Ana döngü: rakamları, hex harflerini (a-f/A-F), nokta (.), epsilon (e/E) oku
|
||
// 4. Her karakterde taban uygunluğunu kontrol et (örn: octal'da 8-9 geçersiz)
|
||
// 5. İlk karakter '0' değilse → doğrudan decimal
|
||
//
|
||
// Özel durum: "0" takip eden karakter yoksa → tek haneli sayı, base=10.
|
||
// "0xFF" → hex, "0b10" → binary, "077" → octal, "0.5" → float.
|
||
//
|
||
// TODO: Hex float desteği (0x1.ffp10) — C99 standardı
|
||
// TODO: Sayısal ayraç: 1_000_000 — C++14/Java 7
|
||
// --------------------------------------------------------------------------
|
||
inline INumber Lexer::readNumeric() {
|
||
INumber num;
|
||
num.start = getLastPosition();
|
||
|
||
// --- Adım 1: İsteğe bağlı işaret ---
|
||
if (getchar() == '-') {
|
||
nextChar();
|
||
num.positive = false;
|
||
} else if (getchar() == '+') {
|
||
nextChar();
|
||
num.positive = true;
|
||
} else {
|
||
num.positive = true;
|
||
}
|
||
|
||
// --- Adım 2: İlk karakter '0' ise özel format kontrolü ---
|
||
bool nextDot = false;
|
||
if (getchar() == '0') {
|
||
num.token.push_back('0');
|
||
nextChar();
|
||
char c = getchar();
|
||
switch (c) {
|
||
case 'x': case 'X': // Hex: 0xFF, 0X1A
|
||
num.token.push_back(c);
|
||
num.base = 16;
|
||
nextChar();
|
||
break;
|
||
case 'b': case 'B': // Binary: 0b1010
|
||
num.token.push_back(c);
|
||
num.base = 2;
|
||
nextChar();
|
||
break;
|
||
case '.': // Float: 0.5, 0.0
|
||
num.token.push_back(c);
|
||
num.base = 10;
|
||
nextDot = true;
|
||
num.isFloat = true;
|
||
nextChar();
|
||
break;
|
||
case '0': case '1': case '2': case '3': case '4':
|
||
case '5': case '6': case '7':
|
||
// Octal: 0777 — sonraki karakter octal rakam ise devam et
|
||
num.base = 8;
|
||
break;
|
||
default:
|
||
// Sadece "0" — takip eden karakter rakam değil.
|
||
// Hemen dön: base=10 (varsayılan).
|
||
// BUG FIX (commit 438bc0e): Eskiden bu dalda sıradaki karakter
|
||
// token'a ekleniyor ve base=8 yapılıyordu. Bu, "0;" durumunda
|
||
// ';' karakterinin sayıya eklenmesine neden oluyordu.
|
||
num.end = getLastPosition();
|
||
return num;
|
||
}
|
||
} else {
|
||
num.base = 10;
|
||
}
|
||
|
||
// --- Adım 3: Ana okuma döngüsü ---
|
||
// Bu döngü, geçerli tabana uygun tüm karakterleri okur.
|
||
// Her karakter tipi için taban uygunluğu kontrol edilir:
|
||
// - 0-1: tüm tabanlar
|
||
// - 2-7: base >= 8
|
||
// - 8-9: base >= 10
|
||
// - a-f/A-F: base >= 16
|
||
// - . (nokta): sadece ondalık, sadece bir kere
|
||
// - e/E: sadece ondalık ve hex (hex'te epsilon yok, direkt okunur)
|
||
while (!isEnd()) {
|
||
char c = getchar();
|
||
switch (c) {
|
||
case '0':
|
||
case '1':
|
||
num.token.push_back(c);
|
||
break;
|
||
case '2': case '3': case '4': case '5':
|
||
case '6': case '7':
|
||
if (num.base >= 8)
|
||
num.token.push_back(c);
|
||
else {
|
||
num.end = getLastPosition();
|
||
return num;
|
||
}
|
||
break;
|
||
case '8': case '9':
|
||
if (num.base >= 10)
|
||
num.token.push_back(c);
|
||
else {
|
||
num.end = getLastPosition();
|
||
return num;
|
||
}
|
||
break;
|
||
case 'a': case 'A': case 'b': case 'B':
|
||
case 'c': case 'C': case 'd': case 'D':
|
||
case 'f': case 'F':
|
||
if (num.base >= 16)
|
||
num.token.push_back(c);
|
||
else {
|
||
num.end = getLastPosition();
|
||
return num;
|
||
}
|
||
break;
|
||
case '.':
|
||
// Nokta: Sadece bir kere izin verilir.
|
||
// .5 gibi başıboş noktalı sayılar için "0." öneki eklenir.
|
||
if (!nextDot) {
|
||
if (num.token.empty())
|
||
num.token += "0.";
|
||
else
|
||
num.token.push_back('.');
|
||
nextDot = true;
|
||
num.isFloat = true;
|
||
} else {
|
||
// İkinci nokta → sayı bitti
|
||
num.end = getLastPosition();
|
||
return num;
|
||
}
|
||
break;
|
||
case 'e': case 'E':
|
||
// Epsilon (bilimsel gösterim):
|
||
// - Hex tabanda: epsilon DEĞİL, hex hanesi olarak okunur.
|
||
// - Decimal tabanda: 1e10, 2.5E-3 formatı.
|
||
if (num.base == 16) {
|
||
num.token.push_back(c);
|
||
break;
|
||
}
|
||
if (num.base == 10) {
|
||
num.hasEpsilon = true;
|
||
num.token.push_back(c);
|
||
nextChar();
|
||
c = getchar();
|
||
// İsteğe bağlı işaret: e+10, E-3
|
||
if (c == '+' || c == '-') {
|
||
num.token.push_back(c);
|
||
nextChar();
|
||
}
|
||
// Epsilon sonrası rakamları oku
|
||
while (!isEnd()) {
|
||
c = getchar();
|
||
if (c >= '0' && c <= '9') {
|
||
num.token.push_back(c);
|
||
nextChar();
|
||
} else {
|
||
num.end = getLastPosition();
|
||
return num;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
num.end = getLastPosition();
|
||
return num;
|
||
default:
|
||
// Tanınmayan karakter → sayı bitti
|
||
num.end = getLastPosition();
|
||
return num;
|
||
}
|
||
nextChar();
|
||
}
|
||
num.end = getLastPosition();
|
||
return num;
|
||
}
|
||
|
||
#endif // SAQUT_LEXER
|