feat: statement parser (if/for/while/do-while/return), keyword tokenizer düzeltmeleri

- Tokenizer: tüm keyword'ler eklendi (int, void, float, vb)
- Keyword boundary check: kısa keyword uzun identifier'la karışmıyor
- Lexer: '0' sonrası karakter kontrolü düzeltildi (0; token sorunu)
- Parser: if/else, for, while, do-while, return, break, continue desteği
- FunctionDecl, VariableDecl, Block, ExpressionStatement tam destek
- null expression koruması (sonsuz döngü engellendi)
This commit is contained in:
abdussamedulutas 2026-05-26 00:05:52 +03:00
parent 40579ca508
commit 438bc0e200
4 changed files with 54 additions and 23 deletions

BIN
saqut

Binary file not shown.

View File

@ -201,27 +201,33 @@ INumber Lexer::readNumeric() {
nextChar(); nextChar();
char c = getchar(); char c = getchar();
switch (c) { switch (c) {
case 'x': case 'x': case 'X':
num.token.push_back(c); num.token.push_back(c);
num.base = 16; num.base = 16;
nextChar();
break; break;
case 'b': case 'b': case 'B':
num.token.push_back(c); num.token.push_back(c);
num.base = 2; num.base = 2;
nextChar();
break;
case '.':
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: continue reading in the main loop
num.base = 8;
break; break;
default: default:
if (c != '.') { // Just "0" — stop here
num.token.push_back(c); num.end = getLastPosition();
num.base = 8; return num;
} else {
num.token.push_back(c);
num.base = 10;
nextDot = true;
num.isFloat = true;
}
break;
} }
nextChar();
} else { } else {
num.base = 10; num.base = 10;
} }

View File

@ -397,7 +397,15 @@ inline ASTNode* Parser::parseContinueStatement() {
inline ASTNode* Parser::parseExpressionStatement() { inline ASTNode* Parser::parseExpressionStatement() {
ExpressionStatementNode* es = new ExpressionStatementNode(); ExpressionStatementNode* es = new ExpressionStatementNode();
es->expression = parseExpression(); es->expression = parseExpression();
if (!es->expression) {
// Parsing failed — skip to next statement boundary
while (currentToken().type != TokenType::SEMICOLON &&
currentToken().type != TokenType::RBRACE &&
currentToken().type != TokenType::SVR_VOID)
nextToken();
if (currentToken().type == TokenType::SEMICOLON)
nextToken();
}
if (currentToken().type == TokenType::SEMICOLON) if (currentToken().type == TokenType::SEMICOLON)
nextToken(); nextToken();

View File

@ -82,14 +82,26 @@ inline constexpr std::string_view delimiters[] = {
}; };
inline constexpr std::string_view keywords[] = { inline constexpr std::string_view keywords[] = {
"implements", "protected", "interface", "continue", // Control flow
"private", "finally", "extends", "default", "if", "else", "for", "while", "do",
"throws", "switch", "return", "public", "switch", "case", "default", "break", "continue",
"assert", "false", "while", "throw", "return", "try", "catch", "finally", "throw",
"class", "catch", "break", "null", "throws", "assert",
"true", "enum", "else", "case", // Types
"new", "try", "for", "if", "void", "int", "float", "double", "char",
"do" "string", "bool",
// Literals
"true", "false", "null",
// OOP
"class", "interface","enum", "extends", "implements",
"new", "public", "private", "protected",
"static", "final", "abstract",
// Modules
"import", "package",
// C/C++
"const", "extern", "typedef", "sizeof",
"auto", "constexpr","noexcept",
"native", "synchronized", "volatile", "transient"
}; };
// ============================================================ // ============================================================
@ -155,9 +167,14 @@ inline Token* Tokenizer::scope() {
return nt; return nt;
} }
// Keywords // Keywords (check boundary: keyword must not be prefix of longer identifier)
for (const auto& kw : keywords) { for (const auto& kw : keywords) {
if (hmx.include(std::string(kw), false)) { if (hmx.include(std::string(kw), false)) {
char next = hmx.getchar(static_cast<int>(kw.size()));
if ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') ||
(next >= '0' && next <= '9') || next == '_' || next == '$') {
continue; // part of longer identifier, not a real keyword
}
KeywordToken* kt = new KeywordToken(); KeywordToken* kt = new KeywordToken();
kt->start = hmx.getOffset(); kt->start = hmx.getOffset();
hmx.toChar(static_cast<int>(kw.size())); hmx.toChar(static_cast<int>(kw.size()));