347 lines
12 KiB
C++
347 lines
12 KiB
C++
#include "lexer/lexer.hpp"
|
||
|
||
// --------------------------------------------------------------------------
|
||
// beginPosition: Mevcut offset'i yığına kaydet.
|
||
// İç içe çağrılabilir: 3 kere beginPosition → 3 elemanlı yığın.
|
||
// --------------------------------------------------------------------------
|
||
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).
|
||
// --------------------------------------------------------------------------
|
||
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.
|
||
// --------------------------------------------------------------------------
|
||
void Lexer::acceptPosition() {
|
||
int t = offsetMap.back();
|
||
setLastPosition(t);
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// setLastPosition: Yığının tepesini veya offset'i değiştir.
|
||
// --------------------------------------------------------------------------
|
||
void Lexer::setLastPosition(int n) {
|
||
if (offsetMap.empty())
|
||
offset = n;
|
||
else
|
||
offsetMap.back() = n;
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// isEnd: Dosya sonuna gelindi mi? offset >= size.
|
||
// --------------------------------------------------------------------------
|
||
bool Lexer::isEnd() {
|
||
return size <= getOffset();
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// rejectPosition: Yığındaki son konumu at. Başarısız include() denemesi sonrası.
|
||
// --------------------------------------------------------------------------
|
||
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.
|
||
// --------------------------------------------------------------------------
|
||
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.
|
||
// --------------------------------------------------------------------------
|
||
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?
|
||
// --------------------------------------------------------------------------
|
||
bool Lexer::include(std::string_view 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.
|
||
// --------------------------------------------------------------------------
|
||
int Lexer::getOffset() {
|
||
return getLastPosition();
|
||
}
|
||
|
||
int Lexer::setOffset(int n) {
|
||
setLastPosition(n);
|
||
return getLastPosition();
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// getchar(additionalOffset): offset + ek kadar ilerideki karakteri oku.
|
||
// --------------------------------------------------------------------------
|
||
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);
|
||
}
|
||
|
||
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.
|
||
// --------------------------------------------------------------------------
|
||
void Lexer::nextChar() {
|
||
if (!isEnd())
|
||
setOffset(getOffset() + 1);
|
||
}
|
||
|
||
void Lexer::toChar(int n) {
|
||
if (!isEnd())
|
||
setOffset(getOffset() + n);
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// getLocation: Mevcut offset'in SourceLocation'ını döndür.
|
||
// --------------------------------------------------------------------------
|
||
SourceLocation Lexer::getLocation() {
|
||
return sourceFile.offsetToLocation(getOffset());
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// setSourceText: Yeni kaynak kodu yükle ve SourceFile'ı güncelle.
|
||
// --------------------------------------------------------------------------
|
||
void Lexer::setSourceText(const std::string& path, const std::string& text) {
|
||
sourceFile.setText(path, text);
|
||
setText(text);
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// setText: Yeni kaynak kodu yükle. input ve size'ı günceller.
|
||
// --------------------------------------------------------------------------
|
||
void Lexer::setText(std::string text) {
|
||
input = text;
|
||
size = static_cast<int>(text.length());
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// skipWhiteSpace: Boşluk, sekme, satırsonu, satırbaşı karakterlerini atla.
|
||
// --------------------------------------------------------------------------
|
||
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)
|
||
// --------------------------------------------------------------------------
|
||
bool Lexer::isNumeric() {
|
||
char c = getchar();
|
||
return (c >= '0' && c <= '9');
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// readNumeric: Tam bir sayı literal'ı oku.
|
||
// --------------------------------------------------------------------------
|
||
INumber Lexer::readNumeric() {
|
||
INumber num;
|
||
num.start = getLastPosition();
|
||
num.startLoc = getLocation();
|
||
|
||
// --- 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:
|
||
num.end = getLastPosition();
|
||
num.endLoc = getLocation();
|
||
return num;
|
||
}
|
||
} else {
|
||
num.base = 10;
|
||
}
|
||
|
||
// --- Adım 3: Ana okuma döngüsü ---
|
||
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();
|
||
num.endLoc = getLocation();
|
||
return num;
|
||
}
|
||
break;
|
||
case '8': case '9':
|
||
if (num.base >= 10)
|
||
num.token.push_back(c);
|
||
else {
|
||
num.end = getLastPosition();
|
||
num.endLoc = getLocation();
|
||
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();
|
||
num.endLoc = getLocation();
|
||
return num;
|
||
}
|
||
break;
|
||
case '.':
|
||
if (!nextDot) {
|
||
if (num.token.empty())
|
||
num.token += "0.";
|
||
else
|
||
num.token.push_back('.');
|
||
nextDot = true;
|
||
num.isFloat = true;
|
||
} else {
|
||
num.end = getLastPosition();
|
||
num.endLoc = getLocation();
|
||
return num;
|
||
}
|
||
break;
|
||
case 'e': case 'E':
|
||
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();
|
||
if (c == '+' || c == '-') {
|
||
num.token.push_back(c);
|
||
nextChar();
|
||
}
|
||
while (!isEnd()) {
|
||
c = getchar();
|
||
if (c >= '0' && c <= '9') {
|
||
num.token.push_back(c);
|
||
nextChar();
|
||
} else {
|
||
num.end = getLastPosition();
|
||
num.endLoc = getLocation();
|
||
return num;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
num.end = getLastPosition();
|
||
num.endLoc = getLocation();
|
||
return num;
|
||
default:
|
||
num.end = getLastPosition();
|
||
return num;
|
||
}
|
||
nextChar();
|
||
}
|
||
num.end = getLastPosition();
|
||
num.endLoc = getLocation();
|
||
return num;
|
||
}
|