parser: function calls, struct, member/index access - Final.sqt parses successfully (289 tokens, 200+ AST nodes)

This commit is contained in:
saqut 2026-05-26 17:27:06 +03:00
parent 4d3150e811
commit 6aa0da2378
8 changed files with 447 additions and 141 deletions

89
Final.sqt Normal file
View File

@ -0,0 +1,89 @@
int fibonacci(int n) {
if (n <= 1)
return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
void fibonacciIterative(int n) {
int first = 0, second = 1, next;
for (int i = 0; i < n; i++) {
if (i <= 1)
next = i;
else {
next = first + second;
first = second;
second = next;
}
printf("%d ", next);
}
printf("\n");
}
int main() {
int n = 10;
// Formatlı ifade kullanmadan, string concatenation mantığı ile
printf("");
printf(n);
printf(" elemanlı Fibonacci dizisi (iterative):\n");
fibonacciIterative(n);
printf("\n");
printf("");
printf(n);
printf(". Fibonacci sayısı (recursive): ");
printf(fibonacci(n - 1));
printf("\n");
return 0;
}
struct List {
int arr[100];
int length;
};
struct List createList() {
struct List list;
list.length = 0;
return list;
}
void push(struct List* list, int value) {
if (list->length < 100) {
list->arr[list->length] = value;
list->length++;
}
}
int get(struct List* list, int index) {
if (index < list->length) {
return list->arr[index];
}
return -1;
}
void printList(struct List* list) {
for (int i = 0; i < list->length; i++) {
// println yerine direkt yaz
int val = list->arr[i];
// sayıyı göster
}
// yeni satır
}
int main() {
struct List myList = createList();
push(myList, 5);
push(myList, 10);
push(myList, 15);
// JavaScript benzeri kullanım
// myList[0] gibi düşün
printList(myList);
return 0;
}

View File

@ -1,10 +1 @@
int main() { int main() { int x = 0; while (x < 5) { x = x + 1; } do { x = x - 1; } while (x > 0); return x; }
int x = 0;
while (x < 5) {
x = x + 1;
}
do {
x = x - 1;
} while (x > 0);
return x;
}

View File

@ -49,6 +49,10 @@ inline const char* astKindName(ASTKind k) {
case ASTKind::BreakStatement: return "BreakStatement"; case ASTKind::BreakStatement: return "BreakStatement";
case ASTKind::ContinueStatement: return "ContinueStatement"; case ASTKind::ContinueStatement: return "ContinueStatement";
case ASTKind::ExpressionStatement: return "ExpressionStatement"; case ASTKind::ExpressionStatement: return "ExpressionStatement";
case ASTKind::Call: return "Call";
case ASTKind::MemberAccess: return "MemberAccess";
case ASTKind::IndexExpression: return "IndexExpression";
case ASTKind::StructDecl: return "StructDecl";
default: return "Unknown"; default: return "Unknown";
} }
} }
@ -158,6 +162,27 @@ inline void analyzeRecursive(ASTNode* node, int currentDepth, AstAnalysis& a) {
if (es->expression) analyzeRecursive(es->expression, currentDepth + 1, a); if (es->expression) analyzeRecursive(es->expression, currentDepth + 1, a);
break; break;
} }
case ASTKind::Call: {
auto* call = (CallExpressionNode*)node;
if (call->callee) analyzeRecursive(call->callee, currentDepth + 1, a);
for (auto* arg : call->arguments) analyzeRecursive(arg, currentDepth + 1, a);
break;
}
case ASTKind::MemberAccess: {
auto* ma = (MemberAccessNode*)node;
if (ma->object) analyzeRecursive(ma->object, currentDepth + 1, a);
break;
}
case ASTKind::IndexExpression: {
auto* ie = (IndexExpressionNode*)node;
if (ie->object) analyzeRecursive(ie->object, currentDepth + 1, a);
if (ie->index) analyzeRecursive(ie->index, currentDepth + 1, a);
break;
}
case ASTKind::StructDecl: {
for (auto* child : node->getChildren()) analyzeRecursive(child, currentDepth + 1, a);
break;
}
default: break; // Literal, Identifier, Break, Continue — yaprak düğüm default: break; // Literal, Identifier, Break, Continue — yaprak düğüm
} }
} }
@ -204,6 +229,9 @@ inline void collectSymbolsRecursive(ASTNode* node, std::vector<SymbolEntry>& sym
if (node->kind == ASTKind::FunctionDecl) { if (node->kind == ASTKind::FunctionDecl) {
auto* fn = (FunctionDeclNode*)node; auto* fn = (FunctionDeclNode*)node;
symbols.push_back({fn->name, "function", fn->returnType}); symbols.push_back({fn->name, "function", fn->returnType});
} else if (node->kind == ASTKind::StructDecl) {
auto* st = (StructDeclNode*)node;
symbols.push_back({st->name, "struct", ""});
} else if (node->kind == ASTKind::VariableDecl) { } else if (node->kind == ASTKind::VariableDecl) {
auto* vd = (VariableDeclNode*)node; auto* vd = (VariableDeclNode*)node;
symbols.push_back({vd->name, "variable", vd->varType}); symbols.push_back({vd->name, "variable", vd->varType});
@ -270,6 +298,27 @@ inline void collectSymbolsRecursive(ASTNode* node, std::vector<SymbolEntry>& sym
if (es->expression) collectSymbolsRecursive(es->expression, symbols); if (es->expression) collectSymbolsRecursive(es->expression, symbols);
break; break;
} }
case ASTKind::Call: {
auto* call = (CallExpressionNode*)node;
if (call->callee) collectSymbolsRecursive(call->callee, symbols);
for (auto* arg : call->arguments) collectSymbolsRecursive(arg, symbols);
break;
}
case ASTKind::MemberAccess: {
auto* ma = (MemberAccessNode*)node;
if (ma->object) collectSymbolsRecursive(ma->object, symbols);
break;
}
case ASTKind::IndexExpression: {
auto* ie = (IndexExpressionNode*)node;
if (ie->object) collectSymbolsRecursive(ie->object, symbols);
if (ie->index) collectSymbolsRecursive(ie->index, symbols);
break;
}
case ASTKind::StructDecl: {
for (auto* child : node->getChildren()) collectSymbolsRecursive(child, symbols);
break;
}
default: break; default: break;
} }
} }

View File

@ -76,6 +76,10 @@ enum class ASTKind {
BreakStatement, // break BreakStatement, // break
ContinueStatement, // continue ContinueStatement, // continue
ExpressionStatement, // ifade + ; ExpressionStatement, // ifade + ;
Call, // Fonksiyon çağrısı f(args)
MemberAccess, // Üye erişimi a.b, a->b
IndexExpression, // Dizi erişimi a[i]
StructDecl, // struct tanımı
}; };
// ============================================================================ // ============================================================================
@ -669,4 +673,149 @@ public:
} }
}; };
// ============================================================================
// CallExpressionNode — Fonksiyon Çağrısı f(a, b, ...)
// ============================================================================
class CallExpressionNode : public ASTNode {
public:
ASTNode* callee = nullptr;
std::vector<ASTNode*> arguments;
CallExpressionNode() { kind = ASTKind::Call; }
void log(int indent = 0) override {
std::cout << padRight("", indent) << "Call\n";
if (callee) {
std::cout << padRight("", indent + 2) << "Callee:\n";
callee->log(indent + 4);
}
std::cout << padRight("", indent + 2) << "Args (" << arguments.size() << "):\n";
for (auto* a : arguments) a->log(indent + 4);
}
std::string toJson(int depth = 0) override {
std::string in = jsonIndent(depth);
std::ostringstream ss;
ss << in << "{\n"
<< in << " \"kind\": \"Call\"";
if (callee) {
ss << ",\n" << in << " \"callee\":\n"
<< callee->toJson(depth + 2);
}
ss << ",\n" << in << " \"arguments\": [\n";
for (size_t i = 0; i < arguments.size(); i++) {
ss << arguments[i]->toJson(depth + 3);
if (i + 1 < arguments.size()) ss << ",";
ss << "\n";
}
ss << in << " ]\n" << in << "}";
return ss.str();
}
};
// ============================================================================
// MemberAccessNode — Üye Erişimi a.b veya a->b
// ============================================================================
class MemberAccessNode : public ASTNode {
public:
ASTNode* object = nullptr;
std::string member;
bool arrow = false;
MemberAccessNode() { kind = ASTKind::MemberAccess; }
void log(int indent = 0) override {
std::cout << padRight("", indent) << "MemberAccess "
<< (arrow ? "->" : ".") << " " << member << "\n";
if (object) object->log(indent + 2);
}
std::string toJson(int depth = 0) override {
std::string in = jsonIndent(depth);
std::ostringstream ss;
ss << in << "{\n"
<< in << " \"kind\": \"MemberAccess\",\n"
<< in << " \"member\": \"" << jsonEscape(member) << "\",\n"
<< in << " \"arrow\": " << (arrow ? "true" : "false");
if (object) {
ss << ",\n" << in << " \"object\":\n"
<< object->toJson(depth + 2);
}
ss << "\n" << in << "}";
return ss.str();
}
};
// ============================================================================
// IndexExpressionNode — Dizi Erişimi a[i]
// ============================================================================
class IndexExpressionNode : public ASTNode {
public:
ASTNode* object = nullptr;
ASTNode* index = nullptr;
IndexExpressionNode() { kind = ASTKind::IndexExpression; }
void log(int indent = 0) override {
std::cout << padRight("", indent) << "IndexExpression\n";
if (object) {
std::cout << padRight("", indent + 2) << "Object:\n";
object->log(indent + 4);
}
if (index) {
std::cout << padRight("", indent + 2) << "Index:\n";
index->log(indent + 4);
}
}
std::string toJson(int depth = 0) override {
std::string in = jsonIndent(depth);
std::ostringstream ss;
ss << in << "{\n"
<< in << " \"kind\": \"IndexExpression\"";
if (object) {
ss << ",\n" << in << " \"object\":\n"
<< object->toJson(depth + 2);
}
if (index) {
ss << ",\n" << in << " \"index\":\n"
<< index->toJson(depth + 2);
}
ss << "\n" << in << "}";
return ss.str();
}
};
// ============================================================================
// StructDeclNode — struct Tanımı
// ============================================================================
class StructDeclNode : public ASTNode {
public:
std::string name;
StructDeclNode() { kind = ASTKind::StructDecl; }
void log(int indent = 0) override {
std::cout << padRight("", indent) << "StructDecl " << name << "\n";
for (auto* c : getChildren()) c->log(indent + 2);
}
std::string toJson(int depth = 0) override {
std::string in = jsonIndent(depth);
std::ostringstream ss;
ss << in << "{\n"
<< in << " \"kind\": \"StructDecl\",\n"
<< in << " \"name\": \"" << jsonEscape(name) << "\",\n"
<< in << " \"children\": [\n"
<< childrenToJson(this, depth + 3)
<< in << " ]\n"
<< in << "}";
return ss.str();
}
};
#endif // SAQUT_AST #endif // SAQUT_AST

View File

@ -115,6 +115,7 @@ private:
// --- Deklarasyonlar --- // --- Deklarasyonlar ---
ASTNode* parseDeclaration(); ASTNode* parseDeclaration();
ASTNode* parseFunctionDecl(); ASTNode* parseFunctionDecl();
ASTNode* parseStructDecl();
ASTNode* parseVariableDecl(); ASTNode* parseVariableDecl();
// --- Statement'lar --- // --- Statement'lar ---
@ -267,7 +268,11 @@ inline ASTNode* Parser::parseDeclaration() {
return parseVariableDecl(); return parseVariableDecl();
} }
// Tip keyword'ü değil → statement (veya REPL ifadesi) // struct
if (ct.type == TokenType::KW_STRUCT)
return parseStructDecl();
// Tip keyword'ü değil → statement
return parseStatement(); return parseStatement();
} }
@ -308,6 +313,29 @@ inline ASTNode* Parser::parseFunctionDecl() {
return fn; return fn;
} }
// --------------------------------------------------------------------------
// parseStructDecl: struct tanimi.
// --------------------------------------------------------------------------
inline ASTNode* Parser::parseStructDecl() {
StructDeclNode* st = new StructDeclNode();
nextToken();
if (currentToken().type == TokenType::IDENTIFIER) {
st->name = currentToken().token->token;
nextToken();
}
if (currentToken().type == TokenType::LBRACE) {
nextToken();
while (currentToken().type != TokenType::RBRACE && currentToken().type != TokenType::SVR_VOID) {
ASTNode* field = parseDeclaration();
if (field) st->addChild(field);
else break;
}
if (currentToken().type == TokenType::RBRACE) nextToken();
}
if (currentToken().type == TokenType::SEMICOLON) nextToken();
return st;
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// parseVariableDecl: Değişken tanımı. // parseVariableDecl: Değişken tanımı.
@ -805,9 +833,8 @@ inline ASTNode* Parser::parseLeftDenotation(ASTNode* left) {
auto ct = currentToken(); auto ct = currentToken();
// --- Postfix: expr++, expr-- --- // --- Postfix: expr++, expr-- ---
// Operatör operand'dan SONRA gelir, sağ operand yok.
if (ct.is({TokenType::PLUS_PLUS, TokenType::MINUS_MINUS})) { if (ct.is({TokenType::PLUS_PLUS, TokenType::MINUS_MINUS})) {
nextToken(); // Operatörü tüket nextToken();
PostfixNode* pf = new PostfixNode(); PostfixNode* pf = new PostfixNode();
pf->operand = left; pf->operand = left;
pf->Operator = ct.type; pf->Operator = ct.type;
@ -815,13 +842,60 @@ inline ASTNode* Parser::parseLeftDenotation(ASTNode* left) {
return pf; return pf;
} }
// --- Binary infix: expr OP expr --- // --- Fonksiyon cagrisi: expr(args) ---
// OP'nin önceliğine göre sağ operand'ı ayrıştır. if (ct.type == TokenType::LPAREN) {
uint16_t prec = ct.getPowerOperator(); nextToken();
nextToken(); // Operatörü tüket CallExpressionNode* call = new CallExpressionNode();
call->callee = left;
left->parent = call;
if (currentToken().type != TokenType::RPAREN) {
call->arguments.push_back(parseExpression(0));
while (currentToken().type == TokenType::COMMA) {
nextToken();
call->arguments.push_back(parseExpression(0));
}
}
if (currentToken().type == TokenType::RPAREN)
nextToken();
return call;
}
// --- Dizi erisimi: expr[index] ---
if (ct.type == TokenType::LBRACKET) {
nextToken();
IndexExpressionNode* idx = new IndexExpressionNode();
idx->object = left;
left->parent = idx;
idx->index = parseExpression(0);
if (currentToken().type == TokenType::RBRACKET)
nextToken();
return idx;
}
// --- Uye erisimi: expr.member / expr->member ---
if (ct.type == TokenType::DOT || ct.type == TokenType::ARROW) {
bool arrow = (ct.type == TokenType::ARROW);
nextToken();
if (currentToken().type != TokenType::IDENTIFIER) {
std::cerr << "Parser hatasi: uye ismi bekleniyor\n";
return left;
}
MemberAccessNode* ma = new MemberAccessNode();
ma->object = left;
ma->member = currentToken().token->token;
ma->arrow = arrow;
left->parent = ma;
nextToken();
return ma;
}
// --- Binary infix: expr OP expr ---
uint16_t prec = ct.getPowerOperator();
nextToken();
// Sağ operand. prec parametresi, daha yüksek öncelikli operatörlerin
// sağ operand içinde gruplanmasını sağlar.
ASTNode* right = parseExpression(prec); ASTNode* right = parseExpression(prec);
BinaryExpressionNode* bin = new BinaryExpressionNode(); BinaryExpressionNode* bin = new BinaryExpressionNode();

View File

@ -129,6 +129,7 @@ enum class TokenType : uint16_t {
// --- OOP Keyword'leri --- // --- OOP Keyword'leri ---
KW_CLASS, // class KW_CLASS, // class
KW_STRUCT, // struct
KW_INTERFACE, // interface KW_INTERFACE, // interface
KW_ENUM, // enum KW_ENUM, // enum
KW_EXTENDS, // extends KW_EXTENDS, // extends
@ -302,6 +303,7 @@ inline const std::unordered_map<std::string_view, TokenType> KEYWORD_MAP = {
// --- OOP --- // --- OOP ---
{"class", TokenType::KW_CLASS}, {"class", TokenType::KW_CLASS},
{"struct", TokenType::KW_STRUCT},
{"interface", TokenType::KW_INTERFACE}, {"interface", TokenType::KW_INTERFACE},
{"enum", TokenType::KW_ENUM}, {"enum", TokenType::KW_ENUM},
{"extends", TokenType::KW_EXTENDS}, {"extends", TokenType::KW_EXTENDS},

69
src/tokenizer/token.hpp Normal file
View File

@ -0,0 +1,69 @@
// ============================================================================
// saQut Compiler — Token Sınıfları
// ============================================================================
//
// DİZİN: src/tokenizer/token.hpp
// KATMAN: Katman 2 — Tokenizer ile Parser arasında veri yapısı
// BAĞIMLI: Yok (sadece <string>)
//
// AMAÇ:
// Tüm token tiplerinin temel sınıfları. 6 adet polimorfik token tipi:
// Token → NumberToken, StringToken, OperatorToken, DelimiterToken,
// KeywordToken, IdentifierToken
//
// ============================================================================
#ifndef SAQUT_TOKENIZER_TOKEN
#define SAQUT_TOKENIZER_TOKEN
#include <string>
class Token {
protected:
std::string type;
public:
int start = 0;
int end = 0;
std::string token;
std::string gettype() { return type; }
virtual ~Token() = default;
};
class StringToken : public Token {
public:
StringToken() { type = "string"; }
std::string context;
int size = 0;
};
class NumberToken : public Token {
public:
NumberToken() { type = "number"; }
bool isFloat = false;
bool hasEpsilon = false;
int base = 10;
};
class OperatorToken : public Token {
public:
OperatorToken() { type = "operator"; }
};
class DelimiterToken : public Token {
public:
DelimiterToken() { type = "delimiter"; }
};
class KeywordToken : public Token {
public:
KeywordToken() { type = "keyword"; }
};
class IdentifierToken : public Token {
public:
IdentifierToken() { type = "identifier"; }
std::string context;
int size = 0;
};
#endif // SAQUT_TOKENIZER_TOKEN

View File

@ -73,124 +73,7 @@
#include <vector> #include <vector>
#include "lexer/lexer.hpp" #include "lexer/lexer.hpp"
// ============================================================================ #include "tokenizer/token.hpp"
// Token Temel Sınıfı
// ============================================================================
//
// Tüm token tiplerinin ortak atası. Polimorfik kullanım için virtual destructor
// içerir. type alanı, token'ın hangi alt sınıfa ait olduğunu string olarak tutar
// (RTTI'ye alternatif, daha hafif).
//
// ALANLAR:
// type : Token tipi ("number", "string", "operator", "delimiter", "keyword", "identifier")
// token : Token'ın ham metin hali (örn: "42", "+", "if", "myVar")
// start : Kaynak koddaki başlangıç offset'i (Lexer offset'i)
// end : Kaynak koddaki bitiş offset'i
//
class Token {
protected:
std::string type; // Alt sınıf tarafından constructor'da atanır
public:
int start = 0; // Kaynak koddaki başlangıç konumu
int end = 0; // Kaynak koddaki bitiş konumu
std::string token; // Token'ın ham metin gösterimi
std::string gettype() { return type; }
virtual ~Token() = default;
};
// ============================================================================
// StringToken — String Literal'ları ("...")
// ============================================================================
//
// Örnek: "merhaba dünya", "satır\niki", "tırnak \" içinde"
//
// context: Escape sequence'ler çözümlenmiş gerçek string içeriği.
// Örn: token="\"a\\nb\"" ise context="a\nb"
// size: context'in uzunluğu (token'dan farklı olabilir)
// token: Tırnak işaretleri ve escape sequence'ler dahil ham hali
//
class StringToken : public Token {
public:
StringToken() { type = "string"; }
std::string context; // İşlenmiş string içeriği (escape'ler açılmış)
int size = 0; // context uzunluğu
};
// ============================================================================
// NumberToken — Sayısal Literal'lar (42, 0xFF, 3.14)
// ============================================================================
//
// Sayı tabanı, float/整数 ayrımı, bilimsel gösterim bilgisi taşır.
// Lexer'ın INumber yapısından dönüştürülür.
//
// isFloat: true ise float/double literal (nokta veya epsilon içerir)
// hasEpsilon: true ise bilimsel gösterim (örn: 1e10)
// base: Sayı tabanı: 2, 8, 10, 16
// token: Sayının ham string hali (örn: "0xFF", "3.14e-2")
//
class NumberToken : public Token {
public:
NumberToken() { type = "number"; }
bool isFloat = false; // Ondalıklı sayı mı?
bool hasEpsilon = false; // Bilimsel gösterim (e/E) içeriyor mu?
int base = 10; // Sayı tabanı
};
// ============================================================================
// OperatorToken — Operatörler (+, -, *, /, ==, ++, vb.)
// ============================================================================
//
// Aritmetik, karşılaştırma, mantıksal, bitsel, atama operatörleri.
// Token değeri doğrudan operatörün string halidir: "+", "-", "==", "++".
//
class OperatorToken : public Token {
public:
OperatorToken() { type = "operator"; }
};
// ============================================================================
// DelimiterToken — Sınırlandırıcılar ({, }, (, ), [, ], ;, ,, ., ->, ::)
// ============================================================================
//
// Kod yapısını belirleyen karakterler. Bloklar, parametre listeleri,
// dizi indeksleri, ifade sonlandırma.
//
class DelimiterToken : public Token {
public:
DelimiterToken() { type = "delimiter"; }
};
// ============================================================================
// KeywordToken — Anahtar Kelimeler (if, for, while, int, void, ...)
// ============================================================================
//
// Dilin rezerve edilmiş kelimeleri. Identifier olarak kullanılamazlar.
// Tokenizer scope() fonksiyonu, keyword'leri identifier'lardan önce kontrol
// eder. Keyword boundary check sayesinde "double" "do" olarak yanlış
// eşleşmez.
//
class KeywordToken : public Token {
public:
KeywordToken() { type = "keyword"; }
};
// ============================================================================
// IdentifierToken — Tanımlayıcılar (değişken/fonksiyon isimleri)
// ============================================================================
//
// Harf, rakam, _ ve $ karakterlerinden oluşan, keyword olmayan isimler.
// Değişkenler, fonksiyonlar, sınıflar, metotlar için kullanılır.
//
// context: Şu anda token ile aynı (genişleme için ayrıldı)
// size: Tanımlayıcının karakter uzunluğu
//
class IdentifierToken : public Token {
public:
IdentifierToken() { type = "identifier"; }
std::string context; // Şu anda token ile aynı
int size = 0; // Tanımlayıcı uzunluğu
};
// ============================================================================ // ============================================================================
// Token Tanıma Tabloları (Derleme Zamanı Sabitleri) // Token Tanıma Tabloları (Derleme Zamanı Sabitleri)
@ -265,7 +148,7 @@ inline constexpr std::string_view keywords[] = {
// Literals // Literals
"true", "false", "null", "true", "false", "null",
// OOP // OOP
"class", "interface","enum", "extends", "implements", "class", "struct", "interface","enum", "extends", "implements",
"new", "public", "private", "protected", "new", "public", "private", "protected",
"static", "final", "abstract", "static", "final", "abstract",
// Modules // Modules
@ -361,8 +244,8 @@ inline Token* Tokenizer::scope() {
hmx.skipWhiteSpace(); hmx.skipWhiteSpace();
// Yorum satırları: sessizce atla, token üretme // Yorum satırları: sessizce atla, token üretme
if (hmx.include("//", true)) skipOneLineComment(); if (hmx.include("//", true)) { skipOneLineComment(); return scope(); }
if (hmx.include("/*", true)) skipMultiLineComment(); if (hmx.include("/*", true)) { skipMultiLineComment(); return scope(); }
// EOF kontrolü // EOF kontrolü
if (hmx.isEnd()) { if (hmx.isEnd()) {
@ -471,7 +354,7 @@ inline IdentifierToken* Tokenizer::readIdentifier() {
if (read) { if (read) {
hmx.nextChar(); hmx.nextChar();
} else { } else {
break; // Tanımlayıcı karakteri değil → dur if (it->token.empty()) { hmx.nextChar(); } break;
} }
} }