From 9b486942c0224376f06e02e87cfae277687c4fe6 Mon Sep 17 00:00:00 2001 From: abdussamedulutas Date: Thu, 18 Jun 2026 19:17:30 +0300 Subject: [PATCH] =?UTF-8?q?feat(ir-vm):=20fibonacci.sqt=20=C3=A7al=C4=B1?= =?UTF-8?q?=C5=9F=C4=B1yor=20=E2=80=94=20IR=20=C3=BCretici=20+=20bytecode?= =?UTF-8?q?=20VM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IR instruction seti (14 opcode): LOAD_CONST, LOAD_SLOT, ADD/SUB/MUL/DIV/MOD, LESS/LEQ/GT/GEQ/EQ/NEQ, JMP, JIF_FALSE, CALL, RETURN, CALLHOST IRGenerator (AST → IR): - Slot tabanlı register: parametreler slot 0..n, lokaller/geçiciler sonrası - freshSlot() monoton sayaç — slot asla geri verilmez - Backpatch: ileri-jump için emitJumpIfFalse(-1) → patchJump(idx) - Geri-jump: loopStart = currentInstrIndex() → emitJumpUnconditional(loopStart) - Builtin tespiti: resolvedSymbol->isBuiltin → CALLHOST Interpreter (bytecode VM): - Her iterasyonda callStack.back() taze alınır (referans güvenliği) - CALL: yeni frame + argüman kopyası + continue - RETURN: değer caller slotuna, frame pop + continue - ip CALL/RETURN'den önce ilerler — caller doğru noktadan devam eder - DIV: sıfıra bölme → runtime_error Doğrulama: build/saqut run file:examples/fibonacci.sqt → 55 / 55 ✓ tests/run.sh → TUM TESTLER GECTI ✓ Co-Authored-By: Claude Opus 4.8 --- build/.ninja_deps | Bin 67224 -> 53112 bytes build/.ninja_log | 48 ++-- build/CMakeCache.txt | 3 +- build/build.ninja | 38 ++- src/cli/commands/run.hpp | 91 ++++--- src/ir/instruction.hpp | 143 ++++++++++ src/ir/ir_function.cpp | 63 +++++ src/ir/ir_function.hpp | 37 +++ src/ir/ir_generator.cpp | 551 +++++++++++++++++++++++++++++++++++++++ src/ir/ir_generator.hpp | 84 ++++++ src/ir/ir_program.cpp | 11 + src/ir/ir_program.hpp | 48 ++++ src/vm/call_frame.hpp | 46 ++++ src/vm/interpreter.cpp | 224 ++++++++++++++++ src/vm/interpreter.hpp | 38 +++ src/vm/value.hpp | 43 +++ 16 files changed, 1395 insertions(+), 73 deletions(-) create mode 100644 src/ir/instruction.hpp create mode 100644 src/ir/ir_function.cpp create mode 100644 src/ir/ir_function.hpp create mode 100644 src/ir/ir_generator.cpp create mode 100644 src/ir/ir_generator.hpp create mode 100644 src/ir/ir_program.cpp create mode 100644 src/ir/ir_program.hpp create mode 100644 src/vm/call_frame.hpp create mode 100644 src/vm/interpreter.cpp create mode 100644 src/vm/interpreter.hpp create mode 100644 src/vm/value.hpp diff --git a/build/.ninja_deps b/build/.ninja_deps index 5ac856e83c2720052d3f847eb0c1ae1eba32dbe6..455e7f938f55a8607b14289d2fdef4e5956af4e0 100644 GIT binary patch delta 2040 zcmd6ne@s(X6vy9%J|Ynq4Y1-`f=xnw52U)?x`lu?+-&K+cRqI7kWLm^duGc3}b z?Vm~GANxgWwxm*%aj$gqob33eH$I1P+1@NgT?@*$x6GxaTD@c)4?cKmzfD>)8gy*+ zHpz3|FG}viQDNjRTuNmg6?0RyXAN`VKZ9<0<}xP@ZAquc8`Wa>tW~ZTwKNf_lSwcmP&FHq+ zZL(sW5o|8Ev*w<#PZnycv=*D&!CA{)pBS$XmqG;fmjA7gF9nwYH=%^EbfTjWX1DvkO1%S?AC`8c#aQ#s(@K*5JB2l*4XiPH{Q?zf9 zxKW;^^d-ajcN4d}>xeulW|Re0mczH$aoS6OEpkY z77&EG6X^G~?w9|#o-vXbfZYYU0so!E*aLbQ<@-~k`(XBi0idT2|AM3;SZ@OfyaUdG zcfosLm@fKDQ`Tdf0a2*qYqfp9#lH2#NaFjj!w{Yy^-*bAyFQ#XOD)G=2uoRq*`HuG z-{nyF*O`3wNN_fPIU1zu$&=yCL!;$!bfbF?Yn0fjGfX@Rt(hk7K22Zrs4boWuawdA9{4LDT=}e~M-@@_-PElBc(1YPZ-6<^U zxT2fSyLKTMx(#Cw<@By%B4wX((TcLzq)L?83tk0>K_93GuYh!#$canohjj!rfP>%= z7yu_g0IZ^%a#dt2Vw>QZ=|FEXt<7CXJwIkqple}kae(68nrOm=*MS%K=+ojUlyKn4 zvc>w@UxDxp(jP|p{9u0$v@PQ4Uxdw1gU}g!SOgD}$LP{AFYP}YML!kKiTDnxj!~NV z;dvKeehGa2@3RR=thl#6u)bdLMPu|d2u*)_5bzc=Y^`i^3kYHi8TLP)iI$Pd)M98 hPUUL(#(wht8#@rZN%9j$&hjn)?M*WOA8!(z+FzC)rcnR@ delta 1295 zcmb_aZA_C_6u!6Zy^2smg$1^*f*LjIY@IenZB!Z!Vx$aVQ0K;2Eht6o7q4g{YQ@h` zEy@|RFDTLpe&MF<4x&pOOV$~eWg4?3x*smlEt#5_XiTO*6g)4q`>|w+QGYz=p8KBj zJnwm*_dM-XM-FPv*XHuU!9TwXnS?*Lkn`d*)1Q4~7MJsyIKLKrT=~E(%53+Oc;uu36bEsDLbj&;deRQ5~&00oD7(y(I$GN}`+?di;A^pLf%t<6FmP##`NKZ9u4LMz}_ zcnSR!N+a2<92GO*a3r060tX{{`Z1i2WYab9Q$)``!rHs=OC+;J*09xhF~jZjnMt*- zwd0o~QX~V;%NqJ29F_I71TM;xX))ZAr_oh7#+-3g?#dFJ ziI5_kFLTldNIpbJpaZ|>_ud}g1N>!2Fh7duL=XZ)S7%H=hG`cfil{(*fjExnM)V*~ zAWkAq!KL=eEgeM@3sLtJBLRg-V%jMKsN09RvVZM(wh3QHg_?%N_0G+zi8k!Mg5An1 zp89`SrgVQD@0Rgw5w7TQH3WXtKx|DKtMMtMpCG+yAC4Xwx9j0I{f#lpD(@v F)fesnyS4xT diff --git a/build/.ninja_log b/build/.ninja_log index 9a14d03..d1b50d9 100644 --- a/build/.ninja_log +++ b/build/.ninja_log @@ -1,37 +1,23 @@ # ninja log v7 -1 6106 1781788886608927872 CMakeFiles/saqut.dir/src/core/sourcefile.cpp.o da6f5fc90e87e6b1 -1 5577 1781794548935793844 CMakeFiles/saqut.dir/src/lexer/lexer.cpp.o 90eeec811f2137e6 -2 1784 1781794795003026937 CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o a01677f8bb4f4dbc -6 7118 1781794548940793848 CMakeFiles/saqut.dir/src/parser/parser.cpp.o 2c65b7be26cead32 -1 7781 1781794548937485082 CMakeFiles/saqut.dir/src/parser/nodes/program.cpp.o ac5bbcd74d87561a -1 7846 1781794548937277813 CMakeFiles/saqut.dir/src/parser/nodes/identifier.cpp.o eb96bb4b1eb4ad80 -1 7401 1781794548937164958 CMakeFiles/saqut.dir/src/parser/nodes/expressions.cpp.o 4057e3d63c63a1ab -1 8355 1781794548937386639 CMakeFiles/saqut.dir/src/parser/nodes/literal.cpp.o 78f2c4da7c9b2281 -5 7795 1781794548939793847 CMakeFiles/saqut.dir/src/parser/nodes/statements.cpp.o b5c20724bbf3648c -1 8702 1781794548936968496 CMakeFiles/saqut.dir/src/parser/nodes/binary_expr.cpp.o d2e2bb2f8a63c6d2 -1 8623 1781794548937059964 CMakeFiles/saqut.dir/src/parser/nodes/declarations.cpp.o b6c56f04a257f685 -1 9568 1781794548936880430 CMakeFiles/saqut.dir/src/main.cpp.o 110c26cb1d0c3a23 -1784 2001 1781794796785028624 saqut 77cf84e33c34ab02 -0 22 1781795949575516449 build.ninja 1876a59d627a585 -0 22 1781795949575428471 /home/saqut/Masaüstü/saqutcompiler/build/cmake_install.cmake 1876a59d627a585 -7401 11835 1781794556335800853 CMakeFiles/saqut.dir/src/symbol/symbol_collector.cpp.o 3348f498f369213d -5577 10556 1781794554511799126 CMakeFiles/saqut.dir/src/semantic/structural_validator.cpp.o 4bfec8abc0e9893e -7118 11087 1781794556053800586 CMakeFiles/saqut.dir/src/semantic/type_checker.cpp.o 15f44776b9c3e26d 13 4576 1781796718440262330 CMakeFiles/saqut.dir/src/core/sourcefile.cpp.o 20f68631dd335029 13 4805 1781796718441539505 CMakeFiles/saqut.dir/src/lexer/lexer.cpp.o 5fd259c0401f3e22 -16 6700 1781796718443848488 CMakeFiles/saqut.dir/src/parser/nodes/identifier.cpp.o e3b5b38d75fcd2ca -15 6733 1781796718442847556 CMakeFiles/saqut.dir/src/parser/nodes/declarations.cpp.o c3d262615ede4c95 -14 6864 1781796718442362341 CMakeFiles/saqut.dir/src/parser/nodes/binary_expr.cpp.o 5cc8b697133bcf64 -21 6876 1781796718448262357 CMakeFiles/saqut.dir/src/parser/nodes/program.cpp.o 9313cba8d8daffed -21 7502 1781796718449424977 CMakeFiles/saqut.dir/src/parser/nodes/statements.cpp.o 3c8869307381c930 -16 7589 1781796718444498682 CMakeFiles/saqut.dir/src/parser/nodes/literal.cpp.o 55743f37408c5f -15 7814 1781796718443331391 CMakeFiles/saqut.dir/src/parser/nodes/expressions.cpp.o 5f5bb01381a3c3ad -14 8751 1781796718441949529 CMakeFiles/saqut.dir/src/main.cpp.o 3cfef7a665d5bf87 +1 2117 1781796771817437208 CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o fbabe80dcb141239 4580 9322 1781796723007277591 CMakeFiles/saqut.dir/src/parser/parser.cpp.o 59cdb5935c541b26 -6864 9502 1781796725291285201 CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o fbabe80dcb141239 +21 6876 1781796718448262357 CMakeFiles/saqut.dir/src/parser/nodes/program.cpp.o 9313cba8d8daffed +16 6700 1781796718443848488 CMakeFiles/saqut.dir/src/parser/nodes/identifier.cpp.o e3b5b38d75fcd2ca +15 7814 1781796718443331391 CMakeFiles/saqut.dir/src/parser/nodes/expressions.cpp.o 5f5bb01381a3c3ad +16 7589 1781796718444498682 CMakeFiles/saqut.dir/src/parser/nodes/literal.cpp.o 55743f37408c5f +21 7502 1781796718449424977 CMakeFiles/saqut.dir/src/parser/nodes/statements.cpp.o 3c8869307381c930 +14 6864 1781796718442362341 CMakeFiles/saqut.dir/src/parser/nodes/binary_expr.cpp.o 5cc8b697133bcf64 +15 6733 1781796718442847556 CMakeFiles/saqut.dir/src/parser/nodes/declarations.cpp.o c3d262615ede4c95 +2 4655 1781799345769869911 CMakeFiles/saqut.dir/src/main.cpp.o 3cfef7a665d5bf87 +4655 4925 1781799350422111935 saqut f2e198803c4dbffb +0 22 1781799395548855747 build.ninja 1876a59d627a585 +0 22 1781799395548855747 /home/saqut/Masaüstü/saqutcompiler/build/cmake_install.cmake 1876a59d627a585 +6733 11112 1781796725160284765 CMakeFiles/saqut.dir/src/symbol/symbol_collector.cpp.o ec4e483b8ddb4007 4805 9685 1781796723232278341 CMakeFiles/saqut.dir/src/semantic/structural_validator.cpp.o 248faa3675024351 6700 10405 1781796725127284655 CMakeFiles/saqut.dir/src/semantic/type_checker.cpp.o b29c133293d988b0 -6733 11112 1781796725160284765 CMakeFiles/saqut.dir/src/symbol/symbol_collector.cpp.o ec4e483b8ddb4007 -11112 11349 1781796729539299317 saqut a8f6b7bef23ca761 -1 2117 1781796771817437208 CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o fbabe80dcb141239 -2117 2374 1781796773933443989 saqut a8f6b7bef23ca761 +2 795 1781799345769990010 CMakeFiles/saqut.dir/src/vm/interpreter.cpp.o b7dd80e002d68a1d +1 958 1781799106946635459 CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o 10f5e8dfd1461d69 +1 1001 1781799106947865509 CMakeFiles/saqut.dir/src/ir/ir_program.cpp.o 9518231d970828da +2 3078 1781799345769137653 CMakeFiles/saqut.dir/src/ir/ir_generator.cpp.o 10a1ed4e1f52e530 diff --git a/build/CMakeCache.txt b/build/CMakeCache.txt index 983abac..b26789d 100644 --- a/build/CMakeCache.txt +++ b/build/CMakeCache.txt @@ -20,7 +20,8 @@ CMAKE_ADDR2LINE:FILEPATH=/usr/bin/addr2line //Path to a program. CMAKE_AR:FILEPATH=/usr/bin/ar -//No help, variable specified on the command line. +//Choose the type of build, options are: None Debug Release RelWithDebInfo +// MinSizeRel ... CMAKE_BUILD_TYPE:STRING=Debug //No help, variable specified on the command line. diff --git a/build/build.ninja b/build/build.ninja index d4cf748..60d00e8 100644 --- a/build/build.ninja +++ b/build/build.ninja @@ -58,6 +58,33 @@ build CMakeFiles/saqut.dir/src/core/sourcefile.cpp.o: CXX_COMPILER__saqut_unscan OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/core TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir +build CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/ir/ir_function.cpp || cmake_object_order_depends_target_saqut + CONFIG = Debug + DEP_FILE = CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o.d + FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0 + INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src + OBJECT_DIR = CMakeFiles/saqut.dir + OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/ir + TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir + +build CMakeFiles/saqut.dir/src/ir/ir_generator.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/ir/ir_generator.cpp || cmake_object_order_depends_target_saqut + CONFIG = Debug + DEP_FILE = CMakeFiles/saqut.dir/src/ir/ir_generator.cpp.o.d + FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0 + INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src + OBJECT_DIR = CMakeFiles/saqut.dir + OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/ir + TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir + +build CMakeFiles/saqut.dir/src/ir/ir_program.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/ir/ir_program.cpp || cmake_object_order_depends_target_saqut + CONFIG = Debug + DEP_FILE = CMakeFiles/saqut.dir/src/ir/ir_program.cpp.o.d + FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0 + INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src + OBJECT_DIR = CMakeFiles/saqut.dir + OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/ir + TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir + build CMakeFiles/saqut.dir/src/lexer/lexer.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/lexer/lexer.cpp || cmake_object_order_depends_target_saqut CONFIG = Debug DEP_FILE = CMakeFiles/saqut.dir/src/lexer/lexer.cpp.o.d @@ -184,6 +211,15 @@ build CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o: CXX_COMPILER__saqut_un OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/tokenizer TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir +build CMakeFiles/saqut.dir/src/vm/interpreter.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/vm/interpreter.cpp || cmake_object_order_depends_target_saqut + CONFIG = Debug + DEP_FILE = CMakeFiles/saqut.dir/src/vm/interpreter.cpp.o.d + FLAGS = -g -std=gnu++20 -Wall -Wextra -g -O0 + INCLUDES = -I/home/saqut/Masaüstü/saqutcompiler/src + OBJECT_DIR = CMakeFiles/saqut.dir + OBJECT_FILE_DIR = CMakeFiles/saqut.dir/src/vm + TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir + # ============================================================================= # Link build statements for EXECUTABLE target saqut @@ -192,7 +228,7 @@ build CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o: CXX_COMPILER__saqut_un ############################################# # Link the executable saqut -build saqut: CXX_EXECUTABLE_LINKER__saqut_Debug CMakeFiles/saqut.dir/src/core/sourcefile.cpp.o CMakeFiles/saqut.dir/src/lexer/lexer.cpp.o CMakeFiles/saqut.dir/src/main.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/binary_expr.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/declarations.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/expressions.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/identifier.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/literal.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/program.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/statements.cpp.o CMakeFiles/saqut.dir/src/parser/parser.cpp.o CMakeFiles/saqut.dir/src/semantic/structural_validator.cpp.o CMakeFiles/saqut.dir/src/semantic/type_checker.cpp.o CMakeFiles/saqut.dir/src/symbol/symbol_collector.cpp.o CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o +build saqut: CXX_EXECUTABLE_LINKER__saqut_Debug CMakeFiles/saqut.dir/src/core/sourcefile.cpp.o CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o CMakeFiles/saqut.dir/src/ir/ir_generator.cpp.o CMakeFiles/saqut.dir/src/ir/ir_program.cpp.o CMakeFiles/saqut.dir/src/lexer/lexer.cpp.o CMakeFiles/saqut.dir/src/main.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/binary_expr.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/declarations.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/expressions.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/identifier.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/literal.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/program.cpp.o CMakeFiles/saqut.dir/src/parser/nodes/statements.cpp.o CMakeFiles/saqut.dir/src/parser/parser.cpp.o CMakeFiles/saqut.dir/src/semantic/structural_validator.cpp.o CMakeFiles/saqut.dir/src/semantic/type_checker.cpp.o CMakeFiles/saqut.dir/src/symbol/symbol_collector.cpp.o CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o CMakeFiles/saqut.dir/src/vm/interpreter.cpp.o CONFIG = Debug DEP_FILE = CMakeFiles/saqut.dir/link.d FLAGS = -g diff --git a/src/cli/commands/run.hpp b/src/cli/commands/run.hpp index bb825fd..4649799 100644 --- a/src/cli/commands/run.hpp +++ b/src/cli/commands/run.hpp @@ -1,10 +1,13 @@ // ============================================================================ -// saQut CLI — run komutu (pipeline: token → AST → IR debug) -// ============================================================================ +// saQut CLI — run komutu // -// TODO: İleride `saqut -` ile stdin'den okuyup anında çalıştıracak -// interpreter modu bu komutun altına gelecek. +// Tam derleme + çalıştırma pipeline'ı: +// tokenize → parse → sembol topla → IR üret → VM çalıştır // +// Başarı kriteri: +// build/saqut run file:examples/fibonacci.sqt +// → 55 +// → 55 // ============================================================================ #ifndef SAQUT_CLI_RUN @@ -14,53 +17,61 @@ #include "cli/args.hpp" #include "tokenizer/tokenizer.hpp" #include "parser/parser.hpp" -#include "ir/ir.hpp" +#include "symbol/symbol_table.hpp" +#include "symbol/symbol_collector.hpp" +#include "diagnostic/diagnostic_engine.hpp" +#include "ir/ir_generator.hpp" +#include "vm/interpreter.hpp" inline int cmdRun(const CliArgs& args) { - std::string source = readSource(args); + std::string filePath = inputFilePath(args); + std::string source = readSource(args); if (source.empty()) return 1; + // ── Aşama 1: Tokenize ──────────────────────────────────────────────── Tokenizer tokenizer; - auto tokens = tokenizer.scan(source, inputFilePath(args)); - - std::cout << "=== saQut Compiler ===\n"; - std::cout << "Kaynak kod:\n" << source << "\n\n"; - - std::cout << "Tokenler (" << tokens.size() << " adet):\n"; - for (auto* t : tokens) { - std::cout << " [" << t->gettype() << "] \"" << t->token << "\"\n"; - } - std::cout << "\n"; + auto tokens = tokenizer.scan(source, filePath); + // ── Aşama 2: Parse ─────────────────────────────────────────────────── Parser parser; ASTNode* ast = parser.parse(tokens); - - if (ast) { - std::cout << "AST:\n"; - ast->log(0); - std::cout << "\n"; - - CodeGenerator cg; - cg.parse(ast); - std::cout << "IR (" << cg.IROpDatas.size() << " komut):\n"; - for (size_t i = 0; i < cg.IROpDatas.size(); i++) { - auto& op = cg.IROpDatas[i]; - std::cout << " [" << i << "] reg" << op.targetReg << " = "; - switch (op.op) { - case OPCode::mathadd: std::cout << "add"; break; - case OPCode::mathsub: std::cout << "sub"; break; - case OPCode::mathmul: std::cout << "mul"; break; - case OPCode::mathdiv: std::cout << "div"; break; - case OPCode::declare: std::cout << "literal"; break; - } - std::cout << " (" << op.arg1.value.index() << ")\n"; - } - - delete ast; + if (!ast) { + std::cerr << "Hata: AST üretilemedi\n"; + for (auto* t : tokens) delete t; + return 1; } + // ── Aşama 3: Sembol toplama ─────────────────────────────────────────── + // Identifier'ların resolvedSymbol'ü doldurulur — IR generator buna ihtiyaç duyar. + SymbolTable symbolTable; + DiagnosticEngine diag; + SymbolCollector(symbolTable, diag).collect(ast); + + if (diag.hasErrors()) { + std::cerr << "Derleme hataları var, program çalıştırılamaz:\n"; + diag.printAll(std::cerr); + delete ast; + for (auto* t : tokens) delete t; + return 1; + } + + // ── Aşama 4: IR üretimi ─────────────────────────────────────────────── + IRGenerator irGenerator; + IRProgram program = irGenerator.generate(ast, symbolTable); + + // ── Aşama 5: VM çalıştırma ──────────────────────────────────────────── + int exitCode = 0; + try { + Interpreter vm(program); + exitCode = vm.run(); + } catch (const std::exception& e) { + std::cerr << "Çalışma zamanı hatası: " << e.what() << "\n"; + exitCode = 1; + } + + delete ast; for (auto* t : tokens) delete t; - return 0; + return exitCode; } #endif // SAQUT_CLI_RUN diff --git a/src/ir/instruction.hpp b/src/ir/instruction.hpp new file mode 100644 index 0000000..831f72a --- /dev/null +++ b/src/ir/instruction.hpp @@ -0,0 +1,143 @@ +// ============================================================================ +// saQut IR — Instruction (Tek Talimat) +// +// Sanal makine bu talimatlara bakarak ne yapacağını anlar. +// Her talimatın bir "opcode"u (ne iş yapacağı) ve birkaç operandı vardır. +// Operandlar ya slot numarasıdır (fonksiyonun yerel değişken/geçici depoları) +// ya da doğrudan bir sayı/isim değeridir. +// +// SLOT NEDİR? +// Her fonksiyon çağrısı kendi "frame"ini açar. +// Frame içinde numaralı kutucuklar vardır: slot[0], slot[1], ... +// Parametreler slot 0'dan başlar. Sonrasında lokal değişkenler +// ve hesaplama sırasında oluşan geçici değerler gelir. +// "slots[5] = 42" demek "5 numaralı kutucuğa 42 değerini koy" demektir. +// +// HANGİ OPCODE HANGİ ALANI KULLANIR? +// LOAD_CONST : dest, intValue +// LOAD_SLOT : dest, src +// ADD/SUB/... : dest, left, right +// LESS/LEQ/... : dest, left, right (sonuç: 1=doğru, 0=yanlış) +// JMP : jumpTarget +// JIF_FALSE : cond, jumpTarget +// CALL : dest, functionName, argSlots +// RETURN : src +// CALLHOST : functionName, argSlots +// ============================================================================ + +#ifndef SAQUT_IR_INSTRUCTION +#define SAQUT_IR_INSTRUCTION + +#include +#include + +// ---------------------------------------------------------------------------- +// Opcode — Sanal Makinenin Anlayacağı İşlem Kodları +// ---------------------------------------------------------------------------- +enum class Opcode { + + // --- Değer yükleme --- + LOAD_CONST, // slots[dest] = intValue + // Örnek: LOAD_CONST dest=3 val=10 → slot[3] = 10 + + LOAD_SLOT, // slots[dest] = slots[src] + // Bir slotun değerini başka bir slota kopyalar. + // Atama işlemlerinde (x = y) kullanılır. + + // --- Aritmetik (tümü: slots[dest] = slots[left] OP slots[right]) --- + ADD, + SUB, + MUL, + DIV, // UYARI: sıfıra bölme → runtime_error fırlatılır + MOD, + + // --- Karşılaştırma (sonuç: 1 = doğru, 0 = yanlış) --- + LESS, // slots[left] < slots[right] + LESS_EQUAL, // slots[left] <= slots[right] + GREATER, // slots[left] > slots[right] + GREATER_EQUAL, // slots[left] >= slots[right] + EQUAL_EQUAL, // slots[left] == slots[right] + NOT_EQUAL, // slots[left] != slots[right] + + // --- Kontrol akışı --- + JMP, // Koşulsuz atlama: ip = jumpTarget + JIF_FALSE, // Koşullu atlama: slots[cond] == 0 ise ip = jumpTarget + + // --- Fonksiyon çağrısı --- + CALL, // Başka bir saQut fonksiyonunu çağır. + // Yeni frame açılır, argümanlar parametre slotlarına kopyalanır. + // Fonksiyon RETURN ile bitince sonuç slots[dest]'e yazılır. + + RETURN, // Bu frame'i kapat, slots[src]'yi caller'a ilet. + + // --- Dış dünya (FFI — Foreign Function Interface) --- + CALLHOST, // Host (C++) fonksiyonunu çağır. Şu an sadece "print" destekli. + // Dönüş değeri yok; sadece yan etki (stdout'a yazmak gibi). +}; + +// Hata ayıklama ve IR dump için okunabilir isim +inline const char* opcodeName(Opcode op) { + switch (op) { + case Opcode::LOAD_CONST: return "LOAD_CONST"; + case Opcode::LOAD_SLOT: return "LOAD_SLOT"; + case Opcode::ADD: return "ADD"; + case Opcode::SUB: return "SUB"; + case Opcode::MUL: return "MUL"; + case Opcode::DIV: return "DIV"; + case Opcode::MOD: return "MOD"; + case Opcode::LESS: return "LESS"; + case Opcode::LESS_EQUAL: return "LESS_EQUAL"; + case Opcode::GREATER: return "GREATER"; + case Opcode::GREATER_EQUAL: return "GREATER_EQUAL"; + case Opcode::EQUAL_EQUAL: return "EQUAL_EQUAL"; + case Opcode::NOT_EQUAL: return "NOT_EQUAL"; + case Opcode::JMP: return "JMP"; + case Opcode::JIF_FALSE: return "JIF_FALSE"; + case Opcode::CALL: return "CALL"; + case Opcode::RETURN: return "RETURN"; + case Opcode::CALLHOST: return "CALLHOST"; + } + return "UNKNOWN"; +} + +// ---------------------------------------------------------------------------- +// Instruction — Tek bir IR talimatı +// +// Okunabilirlik öncelikli bir tasarım: her talimat TÜM alanları içerir, +// kullanılmayanlar varsayılan değerde (-1 veya boş) kalır. +// Bu yaklaşım bellek israfeder ama her talimatın hangi veriyle çalıştığı +// açıkça görünür — karmaşık union/variant yapısı gerekmez. +// ---------------------------------------------------------------------------- +struct Instruction { + Opcode opcode; + + // Hedef slot — sonucun yazılacağı yer (LOAD_CONST, ADD, CALL vb.) + int dest = -1; + + // Kaynak slot — kopyalama veya döndürme için (LOAD_SLOT, RETURN) + int src = -1; + + // Aritmetik/karşılaştırma operandları + int left = -1; + int right = -1; + + // LOAD_CONST için yüklenecek sabit değer + int intValue = 0; + + // JMP / JIF_FALSE için hedef instruction indeksi + // Üretim sırasında bilinmiyorsa -1 bırakılır, sonradan doldurulur (backpatch). + int jumpTarget = -1; + + // JIF_FALSE için kontrol edilecek koşul slotu + int cond = -1; + + // CALL / CALLHOST için çağrılacak fonksiyonun adı + std::string functionName; + + // CALL / CALLHOST için argüman slot indeksleri (sırayla) + std::vector argSlots; + + explicit Instruction(Opcode op) : opcode(op) {} +}; + +#endif // SAQUT_IR_INSTRUCTION diff --git a/src/ir/ir_function.cpp b/src/ir/ir_function.cpp new file mode 100644 index 0000000..9606822 --- /dev/null +++ b/src/ir/ir_function.cpp @@ -0,0 +1,63 @@ +#include "ir/ir_function.hpp" +#include +#include + +// Her instruction'ı "indeks: OPCODE operandlar" formatında yazdır. +// Bu çıktı hem insanın okuduğu hem de birim testlerin karşılaştırdığı formattır. +void IRFunction::dump() const { + std::cout << "=== " << name + << " (paramCount=" << paramCount + << ", slotCount=" << slotCount << ") ===\n"; + + for (int i = 0; i < (int)instructions.size(); i++) { + const Instruction& ins = instructions[i]; + std::cout << " " << std::setw(3) << i << ": " + << std::left << std::setw(14) << opcodeName(ins.opcode); + + switch (ins.opcode) { + case Opcode::LOAD_CONST: + std::cout << "slot[" << ins.dest << "] = " << ins.intValue; + break; + case Opcode::LOAD_SLOT: + std::cout << "slot[" << ins.dest << "] = slot[" << ins.src << "]"; + break; + case Opcode::ADD: case Opcode::SUB: case Opcode::MUL: + case Opcode::DIV: case Opcode::MOD: + case Opcode::LESS: case Opcode::LESS_EQUAL: + case Opcode::GREATER: case Opcode::GREATER_EQUAL: + case Opcode::EQUAL_EQUAL: case Opcode::NOT_EQUAL: + std::cout << "slot[" << ins.dest << "] = " + << "slot[" << ins.left << "] op slot[" << ins.right << "]"; + break; + case Opcode::JMP: + std::cout << "→ " << ins.jumpTarget; + break; + case Opcode::JIF_FALSE: + std::cout << "if !slot[" << ins.cond << "] → " << ins.jumpTarget; + break; + case Opcode::CALL: { + std::cout << "slot[" << ins.dest << "] = " << ins.functionName << "("; + for (int j = 0; j < (int)ins.argSlots.size(); j++) { + if (j) std::cout << ", "; + std::cout << "slot[" << ins.argSlots[j] << "]"; + } + std::cout << ")"; + break; + } + case Opcode::RETURN: + std::cout << "slot[" << ins.src << "]"; + break; + case Opcode::CALLHOST: { + std::cout << ins.functionName << "("; + for (int j = 0; j < (int)ins.argSlots.size(); j++) { + if (j) std::cout << ", "; + std::cout << "slot[" << ins.argSlots[j] << "]"; + } + std::cout << ")"; + break; + } + } + std::cout << "\n"; + } + std::cout << "\n"; +} diff --git a/src/ir/ir_function.hpp b/src/ir/ir_function.hpp new file mode 100644 index 0000000..e970e17 --- /dev/null +++ b/src/ir/ir_function.hpp @@ -0,0 +1,37 @@ +// ============================================================================ +// saQut IR — IRFunction (Tek Fonksiyonun IR Karşılığı) +// +// Bir IRFunction, kaynak koddaki tek bir fonksiyonun "pişmiş" halidir. +// IRGenerator bu yapıyı doldurur, Interpreter bu yapıyı çalıştırır. +// +// SLOT DÜZENI: +// slot[0 .. paramCount-1] → parametreler (soldan sağa) +// slot[paramCount ..] → lokal değişkenler ve geçici sonuçlar +// slotCount → toplam kaç slot lazım (frame boyutu) +// +// Örnek — fibonacci(int n): +// paramCount = 1 → slot[0] = n +// slotCount = 11 → slot[0..10] (0'ı parametre, 1-10 hesaplamalar) +// ============================================================================ + +#ifndef SAQUT_IR_FUNCTION +#define SAQUT_IR_FUNCTION + +#include +#include +#include "ir/instruction.hpp" + +struct IRFunction { + std::string name; // kaynak koddaki fonksiyon adı + int paramCount; // kaç parametresi var + int slotCount; // frame boyutu (üretim sonunda doldurulur) + std::vector instructions; // bu fonksiyonun talimat listesi + + IRFunction(std::string name, int paramCount) + : name(std::move(name)), paramCount(paramCount), slotCount(0) {} + + // Okunabilir IR dump — "saqut run" hata ayıklaması veya inceleme için + void dump() const; +}; + +#endif // SAQUT_IR_FUNCTION diff --git a/src/ir/ir_generator.cpp b/src/ir/ir_generator.cpp new file mode 100644 index 0000000..63355b0 --- /dev/null +++ b/src/ir/ir_generator.cpp @@ -0,0 +1,551 @@ +#include "ir/ir_generator.hpp" +#include "parser/nodes/program.hpp" +#include "parser/nodes/declarations.hpp" +#include "parser/nodes/statements.hpp" +#include "parser/nodes/expressions.hpp" +#include "parser/nodes/binary_expr.hpp" +#include "parser/nodes/identifier.hpp" +#include "parser/nodes/literal.hpp" +#include +#include + +// ───────────────────────────────────────────────────────────────────────────── +// generate — Ana giriş noktası +// ───────────────────────────────────────────────────────────────────────────── + +IRProgram IRGenerator::generate(ASTNode* programNode, SymbolTable& /*symbolTable*/) { + IRProgram program; + + // ProgramNode'un her çocuğunu gez. + // Bizi ilgilendiren: FunctionDecl. StructDecl/GlobalVar → TODO. + for (ASTNode* child : programNode->getChildren()) { + if (child->kind == ASTKind::FunctionDecl) { + // Her fonksiyon üretimi için sıfırla + nameToSlot_.clear(); + nextSlot_ = 0; + + // IRFunction oluştur, currentFunction_ olarak işaretle + auto* fnDecl = (FunctionDeclNode*)child; + IRFunction irFn(fnDecl->name, (int)fnDecl->params.size()); + program.addFunction(std::move(irFn)); + + // addFunction std::move yaptığı için pointer'ı haritadan alalım + currentFunction_ = program.findFunction(fnDecl->name); + + generateFunction(child); + + // Fonksiyon bitti — toplam slot sayısını kaydet + currentFunction_->slotCount = nextSlot_; + } + } + + return program; +} + +// ───────────────────────────────────────────────────────────────────────────── +// generateFunction — Tek bir fonksiyonu IR'a çevirir +// ───────────────────────────────────────────────────────────────────────────── + +void IRGenerator::generateFunction(ASTNode* functionDeclNode) { + auto* fn = (FunctionDeclNode*)functionDeclNode; + + // Parametreler slot 0, 1, 2, ... sırasıyla alır. + // Interpreter, CALL sırasında bu slotlara argümanları kopyalar. + for (auto* param : fn->params) { + int slot = freshSlot(); + registerVariable(param->name, slot); + } + + // Fonksiyon gövdesi — children[0] her zaman BlockNode + auto& children = fn->getChildren(); + if (!children.empty()) { + generateStatement(children[0]); + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// generateStatement — Deyim türlerine göre talimat üret +// ───────────────────────────────────────────────────────────────────────────── + +void IRGenerator::generateStatement(ASTNode* node) { + if (!node) return; + + switch (node->kind) { + + // ── Blok: içindeki her deyimi sırayla üret ─────────────────────────── + case ASTKind::Block: { + for (ASTNode* child : node->getChildren()) { + generateStatement(child); + } + break; + } + + // ── Değişken bildirimi: int x = ────────────────────────────── + case ASTKind::VariableDecl: { + auto* vd = (VariableDeclNode*)node; + + // Bu değişken için yeni bir slot ayır + int varSlot = freshSlot(); + registerVariable(vd->name, varSlot); + + if (vd->initExpr) { + // Başlatma ifadesini üret, sonucu bir slotta al + int initSlot = generateExpression(vd->initExpr); + + if (initSlot != varSlot) { + // Sonuç başka bir slotta, değişkenin slotuna kopyala + emitLoadSlot(varSlot, initSlot); + } + // initSlot == varSlot: LOAD_CONST doğrudan varSlot'a yazıldı, kopya gerekmez + } + + // Sibling VariableDecl'ler: int a, b; → children'da diğer VariableDecl'ler + for (ASTNode* sib : node->getChildren()) { + if (sib->kind == ASTKind::VariableDecl) { + generateStatement(sib); + } + } + break; + } + + // ── return ─────────────────────────────────────────────────── + case ASTKind::ReturnStatement: { + auto* rs = (ReturnStatementNode*)node; + int returnSlot = 0; // varsayılan: slot[0] (void fonksiyon / boş return) + + if (rs->value) { + returnSlot = generateExpression(rs->value); + } + emitReturn(returnSlot); + break; + } + + // ── if (koşul) { ... } [else { ... }] ─────────────────────────────── + case ASTKind::IfStatement: { + auto* ifn = (IfStatementNode*)node; + + // Koşulu hesapla + int condSlot = generateExpression(ifn->condition); + + // "Koşul yanlışsa atla" → hedef henüz bilinmiyor, backpatch bekliyor + int jumpToElse = emitJumpIfFalse(condSlot); + + // Then bloğu + if (ifn->thenBranch) generateStatement(ifn->thenBranch); + + if (ifn->elseBranch) { + // Then bitti, else'i atla (then içinde çalışanlar else'e girmemeli) + int jumpOverElse = emitJumpUnconditional(-1); + // Şimdi else'in başlangıç konumunu biliyoruz → jumpToElse'i doldur + patchJump(jumpToElse); + generateStatement(ifn->elseBranch); + // Else bitti → jumpOverElse'i doldur + patchJump(jumpOverElse); + } else { + // Else yok → jumpToElse doğrudan if sonrasına atlıyor + patchJump(jumpToElse); + } + break; + } + + // ── while (koşul) { gövde } ────────────────────────────────────────── + case ASTKind::WhileStatement: { + auto* ws = (WhileStatementNode*)node; + + // Döngü başının konumu — geri-jump buraya gelecek + int loopStart = currentInstrIndex(); + + int condSlot = generateExpression(ws->condition); + int exitJump = emitJumpIfFalse(condSlot); // ileri, backpatch bekliyor + + if (ws->body) generateStatement(ws->body); + + // Geri-jump: hedef zaten biliniyor (loopStart) + emitJumpUnconditional(loopStart); + + // Döngü çıkış noktası → exitJump'ı doldur + patchJump(exitJump); + break; + } + + // ── for (init; koşul; güncelleme) { gövde } ───────────────────────── + // + // Üretilen IR yapısı: + // [init] + // LOOP_START: + // [koşul] → condSlot + // JIF_FALSE condSlot → LOOP_END (ileri-jump, backpatch) + // [gövde] + // [güncelleme] + // JMP → LOOP_START (geri-jump, hedef biliniyor) + // LOOP_END: + // ───────────────────────────────────────────────────────────────────── + case ASTKind::ForStatement: { + auto* fs = (ForStatementNode*)node; + + // Init: genellikle "int i = 0" gibi bir VariableDecl + if (fs->init) generateStatement(fs->init); + + // Döngü başı konumu — geri-jump'ın hedefi + int loopStart = currentInstrIndex(); + + // Koşul + int condSlot = fs->condition ? generateExpression(fs->condition) : -1; + int exitJump = (condSlot != -1) ? emitJumpIfFalse(condSlot) : -1; + + // Gövde + if (fs->body) generateStatement(fs->body); + + // Güncelleme (ör: i = i + 1) — ifade deyimi, sonuç önemsiz + if (fs->update) generateExpression(fs->update); + + // Geri-jump: hedef loopStart, zaten biliniyor + emitJumpUnconditional(loopStart); + + // Döngü çıkışı → exitJump'ı doldur + if (exitJump != -1) patchJump(exitJump); + break; + } + + // ── do { gövde } while (koşul) ─────────────────────────────────────── + case ASTKind::DoWhileStatement: { + auto* dw = (DoWhileStatementNode*)node; + int loopStart = currentInstrIndex(); + + if (dw->body) generateStatement(dw->body); + + int condSlot = generateExpression(dw->condition); + // Koşul doğruysa geri atla (1 = doğru → atla; 0 = yanlış → devam) + // JIF_FALSE koşul yanlışsa atlar; biz doğruysa atlamak istiyoruz. + // Bu yüzden JIF_FALSE yerine "doğruysa atla" mantığı lazım. + // Basit çözüm: koşulun tersini al (0→1, diğer→0) ve JIF_FALSE kullan. + // NOT: saQut'ta "!" operatörü yok henüz; NOT talimatı eklenebilir. + // Şimdilik: koşul slotuna bak, sıfır değilse geri atla. + // TODO(vm-genişletme): JIF_TRUE talimatı ekle + // Geçici çözüm: sabit 1 ile karşılaştır (condSlot != 0 → geri) + int oneSlot = freshSlot(); + emitLoadConst(oneSlot, 1); + int eqSlot = freshSlot(); + emitBinaryOp(Opcode::EQUAL_EQUAL, eqSlot, condSlot, oneSlot); + int skipJump = emitJumpIfFalse(eqSlot); // koşul yanlışsa döngüden çık + emitJumpUnconditional(loopStart); // geri atla + patchJump(skipJump); + break; + } + + // ── İfade deyimi: bir ifadeyi değerlendirip sonucu at ──────────────── + // Örnek: print(x) çağrısı, veya x = 5 ataması + case ASTKind::ExpressionStatement: { + auto* es = (ExpressionStatementNode*)node; + if (es->expression) { + generateExpression(es->expression); // sonucu kullanmıyoruz + } + break; + } + + case ASTKind::BreakStatement: + case ASTKind::ContinueStatement: + // TODO(vm-genişletme): break/continue için JMP + label mekanizması gerekir + break; + + default: + break; + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// generateExpression — İfadeyi IR'a çevirir, sonucu içeren slot'u döndürür +// ───────────────────────────────────────────────────────────────────────────── + +int IRGenerator::generateExpression(ASTNode* node) { + if (!node) return 0; + + switch (node->kind) { + + // ── Sabit değer: 42, 3.14, true ... ────────────────────────────────── + case ASTKind::Literal: { + auto* lit = (LiteralNode*)node; + int slot = freshSlot(); + + switch (lit->literalType) { + case LiteralType::INTEGER: { + // Sayı metnini int'e çevir + int value = 0; + if (lit->parserToken.token) { + value = std::stoi(lit->parserToken.token->token); + } + emitLoadConst(slot, value); + break; + } + case LiteralType::BOOLEAN: { + // true → 1, false → 0 + int value = (lit->parserToken.token && + lit->parserToken.token->token == "true") ? 1 : 0; + emitLoadConst(slot, value); + break; + } + default: + // float, string vb. → TODO(vm-genişletme) + emitLoadConst(slot, 0); + break; + } + return slot; + } + + // ── Değişken ismi: n, first, second ... ────────────────────────────── + // Bu değişkenin değeri zaten bir slotta. O slotu döndür. + case ASTKind::Identifier: { + auto* id = (IdentifierNode*)node; + std::string name = id->parserToken.token ? id->parserToken.token->token : ""; + + // Önce builtin mi? (print gibi) — identifier olarak gelen builtin fonksiyon + // çağrıları CallExpression içinde yakalanıyor, burada sadece değişken kalır + return lookupVariable(name); + } + + // ── İkili ifade: x + y, x = y, x < y ... ──────────────────────────── + case ASTKind::BinaryExpression: { + auto* bin = (BinaryExpressionNode*)node; + + // Atama operatörleri: x = expr, x += expr ... + // Sol taraf bir değişken, sağ taraf hesaplanır ve o değişkene yazılır. + if (bin->Operator == TokenType::EQUAL) { + // Sağ tarafı hesapla + int rhsSlot = generateExpression(bin->Right); + + // Sol taraf değişkenin slotunu bul + auto* lhsId = (IdentifierNode*)bin->Left; + std::string varName = lhsId->parserToken.token->token; + int varSlot = lookupVariable(varName); + + // Sonucu değişkenin slotuna kopyala + if (rhsSlot != varSlot) { + emitLoadSlot(varSlot, rhsSlot); + } + return varSlot; + } + + // Birleşik atama: += -= *= /= + // x += y ≡ x = x + y + if (bin->Operator == TokenType::PLUS_EQUAL || + bin->Operator == TokenType::MINUS_EQUAL || + bin->Operator == TokenType::STAR_EQUAL || + bin->Operator == TokenType::SLASH_EQUAL) { + + auto* lhsId = (IdentifierNode*)bin->Left; + std::string varName = lhsId->parserToken.token->token; + int varSlot = lookupVariable(varName); + int rhsSlot = generateExpression(bin->Right); + + Opcode arithOp = Opcode::ADD; + if (bin->Operator == TokenType::MINUS_EQUAL) arithOp = Opcode::SUB; + else if (bin->Operator == TokenType::STAR_EQUAL) arithOp = Opcode::MUL; + else if (bin->Operator == TokenType::SLASH_EQUAL) arithOp = Opcode::DIV; + + int resultSlot = freshSlot(); + emitBinaryOp(arithOp, resultSlot, varSlot, rhsSlot); + emitLoadSlot(varSlot, resultSlot); + return varSlot; + } + + // Unary prefix: Left = nullptr (ör: -x, !x) + if (!bin->Left) { + int operandSlot = generateExpression(bin->Right); + int resultSlot = freshSlot(); + + if (bin->Operator == TokenType::MINUS) { + // -x → 0 - x + int zeroSlot = freshSlot(); + emitLoadConst(zeroSlot, 0); + emitBinaryOp(Opcode::SUB, resultSlot, zeroSlot, operandSlot); + } else { + // Diğer unary operatörler → TODO + emitLoadSlot(resultSlot, operandSlot); + } + return resultSlot; + } + + // Aritmetik operatörler + switch (bin->Operator) { + case TokenType::PLUS: return generateBinaryArithmetic(Opcode::ADD, bin->Left, bin->Right); + case TokenType::MINUS: return generateBinaryArithmetic(Opcode::SUB, bin->Left, bin->Right); + case TokenType::STAR: return generateBinaryArithmetic(Opcode::MUL, bin->Left, bin->Right); + case TokenType::SLASH: return generateBinaryArithmetic(Opcode::DIV, bin->Left, bin->Right); + case TokenType::PERCENT: return generateBinaryArithmetic(Opcode::MOD, bin->Left, bin->Right); + // Karşılaştırma operatörleri + case TokenType::LESS: return generateBinaryArithmetic(Opcode::LESS, bin->Left, bin->Right); + case TokenType::LESS_EQUAL: return generateBinaryArithmetic(Opcode::LESS_EQUAL, bin->Left, bin->Right); + case TokenType::GREATER: return generateBinaryArithmetic(Opcode::GREATER, bin->Left, bin->Right); + case TokenType::GREATER_EQUAL: return generateBinaryArithmetic(Opcode::GREATER_EQUAL, bin->Left, bin->Right); + case TokenType::EQUAL_EQUAL: return generateBinaryArithmetic(Opcode::EQUAL_EQUAL, bin->Left, bin->Right); + case TokenType::BANG_EQUAL: return generateBinaryArithmetic(Opcode::NOT_EQUAL, bin->Left, bin->Right); + default: { + // Bilinmeyen operatör — boş slot döndür + int slot = freshSlot(); + emitLoadConst(slot, 0); + return slot; + } + } + } + + // ── Fonksiyon çağrısı: fibonacci(n-1), print(x) ... ───────────────── + case ASTKind::Call: { + auto* call = (CallExpressionNode*)node; + + // Hangi fonksiyon çağrılıyor? Callee bir Identifier + std::string fnName; + bool isBuiltin = false; + + if (call->callee && call->callee->kind == ASTKind::Identifier) { + auto* calleeId = (IdentifierNode*)call->callee; + if (calleeId->parserToken.token) { + fnName = calleeId->parserToken.token->token; + } + // Builtin kontrolü: resolvedSymbol->isBuiltin + if (calleeId->resolvedSymbol && calleeId->resolvedSymbol->isBuiltin) { + isBuiltin = true; + } + } + + // Her argümanı hesapla, sonuçların slot numaralarını topla + std::vector argSlots; + for (ASTNode* arg : call->arguments) { + argSlots.push_back(generateExpression(arg)); + } + + if (isBuiltin) { + // CALLHOST: host (C++) fonksiyonu çağır (print gibi), dönüş değeri yok + Instruction ins(Opcode::CALLHOST); + ins.functionName = fnName; + ins.argSlots = argSlots; + currentFunction_->instructions.push_back(std::move(ins)); + return -1; // Dönüş değeri yok + } else { + // CALL: saQut fonksiyonu çağır, sonucu yeni slota yaz + int destSlot = freshSlot(); + Instruction ins(Opcode::CALL); + ins.dest = destSlot; + ins.functionName = fnName; + ins.argSlots = argSlots; + currentFunction_->instructions.push_back(std::move(ins)); + return destSlot; + } + } + + // ── Postfix: i++, i-- ──────────────────────────────────────────────── + case ASTKind::Postfix: { + auto* pf = (PostfixNode*)node; + // Şu anki değeri döndür, sonra artır/azalt + int operandSlot = generateExpression(pf->operand); + int resultSlot = freshSlot(); // dönüş değeri (artırmadan önceki) + emitLoadSlot(resultSlot, operandSlot); + + int oneSlot = freshSlot(); + emitLoadConst(oneSlot, 1); + int newSlot = freshSlot(); + + if (pf->Operator == TokenType::PLUS_PLUS) { + emitBinaryOp(Opcode::ADD, newSlot, operandSlot, oneSlot); + } else { + emitBinaryOp(Opcode::SUB, newSlot, operandSlot, oneSlot); + } + emitLoadSlot(operandSlot, newSlot); // orijinal değişkeni güncelle + return resultSlot; // artırmadan önceki değer + } + + default: + // Bilinmeyen ifade türü + return freshSlot(); // boş slot (0 değeriyle) + } +} + +// ───────────────────────────────────────────────────────────────────────────── +// generateBinaryArithmetic — İkili op için sol+sağ üret, talimat ekle +// ───────────────────────────────────────────────────────────────────────────── + +int IRGenerator::generateBinaryArithmetic(Opcode opcode, ASTNode* leftNode, ASTNode* rightNode) { + int leftSlot = generateExpression(leftNode); + int rightSlot = generateExpression(rightNode); + int destSlot = freshSlot(); + emitBinaryOp(opcode, destSlot, leftSlot, rightSlot); + return destSlot; +} + +// ───────────────────────────────────────────────────────────────────────────── +// Slot yönetimi +// ───────────────────────────────────────────────────────────────────────────── + +int IRGenerator::freshSlot() { + return nextSlot_++; +} + +void IRGenerator::registerVariable(const std::string& name, int slot) { + nameToSlot_[name] = slot; +} + +int IRGenerator::lookupVariable(const std::string& name) { + auto it = nameToSlot_.find(name); + if (it == nameToSlot_.end()) { + // Bu noktaya normalde gelinmemeli; sembol toplayıcı E001 üretmiş olur. + // Yine de çökmemek için 0 döndür. + return 0; + } + return it->second; +} + +// ───────────────────────────────────────────────────────────────────────────── +// Talimat yazma yardımcıları +// ───────────────────────────────────────────────────────────────────────────── + +void IRGenerator::emitLoadConst(int destSlot, int value) { + Instruction ins(Opcode::LOAD_CONST); + ins.dest = destSlot; + ins.intValue = value; + currentFunction_->instructions.push_back(std::move(ins)); +} + +void IRGenerator::emitLoadSlot(int destSlot, int srcSlot) { + Instruction ins(Opcode::LOAD_SLOT); + ins.dest = destSlot; + ins.src = srcSlot; + currentFunction_->instructions.push_back(std::move(ins)); +} + +void IRGenerator::emitBinaryOp(Opcode op, int destSlot, int leftSlot, int rightSlot) { + Instruction ins(op); + ins.dest = destSlot; + ins.left = leftSlot; + ins.right = rightSlot; + currentFunction_->instructions.push_back(std::move(ins)); +} + +void IRGenerator::emitReturn(int srcSlot) { + Instruction ins(Opcode::RETURN); + ins.src = srcSlot; + currentFunction_->instructions.push_back(std::move(ins)); +} + +int IRGenerator::emitJumpUnconditional(int targetInstrIndex) { + Instruction ins(Opcode::JMP); + ins.jumpTarget = targetInstrIndex; + currentFunction_->instructions.push_back(std::move(ins)); + return (int)currentFunction_->instructions.size() - 1; +} + +int IRGenerator::emitJumpIfFalse(int condSlot) { + Instruction ins(Opcode::JIF_FALSE); + ins.cond = condSlot; + ins.jumpTarget = -1; // henüz bilinmiyor — patchJump() bekliyor + currentFunction_->instructions.push_back(std::move(ins)); + // Bu instruction'ın indeksini döndür (backpatch için) + return (int)currentFunction_->instructions.size() - 1; +} + +void IRGenerator::patchJump(int instrIndex) { + // instrIndex'teki JMP veya JIF_FALSE'un hedefini şu anki konuma doldur + currentFunction_->instructions[instrIndex].jumpTarget = currentInstrIndex(); +} + +int IRGenerator::currentInstrIndex() const { + return (int)currentFunction_->instructions.size(); +} diff --git a/src/ir/ir_generator.hpp b/src/ir/ir_generator.hpp new file mode 100644 index 0000000..f3329ce --- /dev/null +++ b/src/ir/ir_generator.hpp @@ -0,0 +1,84 @@ +// ============================================================================ +// saQut IR — IRGenerator (AST → IR Dönüşümü) +// +// AST'yi (parse edilmiş kaynak kodu) Instruction listelerine çevirir. +// Her fonksiyon için bir IRFunction üretir, hepsini IRProgram'a toplar. +// +// SLOT ATAMA STRATEJİSİ: +// - Her fonksiyon üretiminde nextSlot_ sıfırdan başlar. +// - Parametreler 0, 1, 2, ... slotlarına sırayla atanır. +// - Sonraki her değişken veya geçici sonuç freshSlot() ile yeni slot alır. +// - Slotlar asla geri verilmez (basitlik öncelikli). +// - Fonksiyon bitince nextSlot_ = slotCount. +// +// SINIRLAMALAR (fibonacci için yeterli, genel dil için TODO): +// - Aynı isimli iki değişken farklı iç kapsamlarda olsa bile çakışır. +// (fibonacci.sqt'de bu durum yok; gelecekte scope-aware slot atama gerekir.) +// - Sadece int değerler desteklenir (Value.kind şu an hep Int). +// ============================================================================ + +#ifndef SAQUT_IR_GENERATOR +#define SAQUT_IR_GENERATOR + +#include +#include +#include "ir/ir_program.hpp" +#include "symbol/symbol_table.hpp" +#include "parser/ast_node.hpp" + +class IRGenerator { +public: + // Ana giriş noktası: programNode = ProgramNode, tablo = sembol tablosu + IRProgram generate(ASTNode* programNode, SymbolTable& symbolTable); + +private: + // ── Fonksiyon üretimi ───────────────────────────────────────────────── + void generateFunction(ASTNode* functionDeclNode); + + // ── Deyim (statement) üretimi — talimat listesine yazar ────────────── + void generateStatement(ASTNode* node); + + // ── İfade (expression) üretimi — sonucun slotunu döndürür ──────────── + // Sonuç her zaman bir slotta bulunur. Identifier zaten bir slotta, + // hesaplamalar freshSlot() ile yeni slot alır. + int generateExpression(ASTNode* node); + + // ── İkili operatör (binary op) için ortak yardımcı ─────────────────── + int generateBinaryArithmetic(Opcode opcode, ASTNode* leftNode, ASTNode* rightNode); + + // ── Slot yönetimi ───────────────────────────────────────────────────── + int freshSlot(); // Yeni slot numarası al (nextSlot_++) + void registerVariable(const std::string& name, int slot); // name → slot kaydı + int lookupVariable(const std::string& name); // name → slot (bulunamazsa hata) + + // ── Talimat yazma yardımcıları ──────────────────────────────────────── + // Talimatları currentFunction_->instructions'a ekler. + + void emitLoadConst(int destSlot, int value); + void emitLoadSlot(int destSlot, int srcSlot); + void emitBinaryOp(Opcode op, int destSlot, int leftSlot, int rightSlot); + void emitReturn(int srcSlot); + // Koşulsuz atlama yazar; instruction indeksini döndürür (backpatch için). + // Hedef bilinmiyorsa -1 geçilir, patchJump() ile doldurulur. + int emitJumpUnconditional(int targetInstrIndex); + + // JIF_FALSE talimatını -1 hedefle yazar, instruction indeksini döndürür. + // Döndürülen indeks ileride patchJump() ile doldurulur (backpatch). + int emitJumpIfFalse(int condSlot); + + // Daha önce -1 hedefle yazılan jump'ın hedefini şu anki pozisyona doldur. + void patchJump(int instrIndex); + + // Şu an kaç talimat üretildi? (jump hedefi belirlemek için) + int currentInstrIndex() const; + + // ── Per-function üretim durumu ──────────────────────────────────────── + IRFunction* currentFunction_ = nullptr; // şu an üretilen fonksiyon + int nextSlot_ = 0; // sıradaki boş slot numarası + + // Değişken ismi → slot numarası. + // Sınırlama: aynı isimdeki farklı scope değişkenleri çakışır (TODO). + std::unordered_map nameToSlot_; +}; + +#endif // SAQUT_IR_GENERATOR diff --git a/src/ir/ir_program.cpp b/src/ir/ir_program.cpp new file mode 100644 index 0000000..bcbb3a7 --- /dev/null +++ b/src/ir/ir_program.cpp @@ -0,0 +1,11 @@ +#include "ir/ir_program.hpp" +#include + +void IRProgram::dump() const { + std::cout << "========== IR DUMP ==========\n\n"; + for (const auto& name : functionOrder) { + auto it = functions.find(name); + if (it != functions.end()) it->second.dump(); + } + std::cout << "=============================\n"; +} diff --git a/src/ir/ir_program.hpp b/src/ir/ir_program.hpp new file mode 100644 index 0000000..a60e1a4 --- /dev/null +++ b/src/ir/ir_program.hpp @@ -0,0 +1,48 @@ +// ============================================================================ +// saQut IR — IRProgram (Bir .sqt Dosyasının Tüm IR İçeriği) +// +// IRProgram, üretilen tüm fonksiyonları tutar. +// Interpreter programı çalıştırmak için bu yapıyı kullanır. +// +// NEDEN İKİ YAPIDA TUTUYORUZ? +// - functionOrder: fonksiyonları tanımlandıkları sırayla tutar (dump için) +// - functions (unordered_map): CALL instruction'larında isimle hızlı arama için +// +// NOT: unordered_map değer semantiğiyle (IRFunction by value) tutar. +// findFunction() bir pointer döndürür — bu pointer tüm addFunction() çağrıları +// bittikten sonra alınmalıdır. Interpreter program üretildikten sonra çalıştığı +// için bu kural otomatik olarak sağlanır. +// ============================================================================ + +#ifndef SAQUT_IR_PROGRAM +#define SAQUT_IR_PROGRAM + +#include +#include +#include +#include "ir/ir_function.hpp" + +struct IRProgram { + // Fonksiyon adı → IRFunction (hızlı arama için) + std::unordered_map functions; + + // Ekleme sırası (dump'ta orijinal sırayla göstermek için) + std::vector functionOrder; + + // Yeni fonksiyon ekle + void addFunction(IRFunction fn) { + functionOrder.push_back(fn.name); + functions.emplace(fn.name, std::move(fn)); + } + + // İsimle ara — bulunamazsa nullptr döner + IRFunction* findFunction(const std::string& name) { + auto it = functions.find(name); + return (it != functions.end()) ? &it->second : nullptr; + } + + // Tüm fonksiyonları ekleme sırasıyla yazdır + void dump() const; +}; + +#endif // SAQUT_IR_PROGRAM diff --git a/src/vm/call_frame.hpp b/src/vm/call_frame.hpp new file mode 100644 index 0000000..48e2863 --- /dev/null +++ b/src/vm/call_frame.hpp @@ -0,0 +1,46 @@ +// ============================================================================ +// saQut VM — CallFrame (Tek Fonksiyon Çağrısının Çalışma Alanı) +// +// fibonacci(5) çağrıldığında bir CallFrame açılır. +// fibonacci(4) çağrıldığında AYRI bir CallFrame daha açılır. +// Her frame kendi slot dizisine sahiptir — üst frame'e asla dokunmaz. +// +// FRAME YAŞAM DÖNGÜSÜ: +// 1. CALL instruction'ı çalışır → yeni CallFrame oluşturulur, callStack'e eklenir +// 2. Interpreter bu frame'in instruction'larını çalıştırır +// 3. RETURN instruction'ı çalışır → frame callStack'ten çıkarılır, +// dönüş değeri caller'ın `returnDestSlot`'una yazılır +// +// REFERANS GÜVENLİĞİ: +// Interpreter döngüsü her iterasyonda callStack.back() ile frame'i TAZELER. +// CALL ve RETURN'den sonra `continue` ile döngü başına dönülür. +// Bu sayede vector büyüyüp referansı geçersiz kılsa bile sorun olmaz. +// ============================================================================ + +#ifndef SAQUT_VM_CALL_FRAME +#define SAQUT_VM_CALL_FRAME + +#include +#include "ir/ir_function.hpp" +#include "vm/value.hpp" + +struct CallFrame { + // Hangi fonksiyonun instruction'larını çalıştırıyoruz? + // Pointer — IRProgram sahibi, frame sahibi değil. + const IRFunction* function = nullptr; + + // Sıradaki çalıştırılacak instruction'ın indeksi. + // Döngü her adımda önce bu indeksteki instruction'ı alır, + // SONRA ip'yi artırır. CALL/RETURN ip'ye dokunmaz. + int instructionPointer = 0; + + // Bu frame'in değer depoları: parametreler + lokaller + geçiciler. + // Boyut = function->slotCount (frame oluşturulurken ayarlanır). + std::vector slots; + + // RETURN olunca dönüş değeri CALLER'ın hangi slotuna yazılacak? + // -1 = main fonksiyonu (caller yok, değer kullanılmaz). + int returnDestSlot = -1; +}; + +#endif // SAQUT_VM_CALL_FRAME diff --git a/src/vm/interpreter.cpp b/src/vm/interpreter.cpp new file mode 100644 index 0000000..2be1d36 --- /dev/null +++ b/src/vm/interpreter.cpp @@ -0,0 +1,224 @@ +#include "vm/interpreter.hpp" +#include +#include + +// ───────────────────────────────────────────────────────────────────────────── +// run — Ana yorumlayıcı döngüsü +// ───────────────────────────────────────────────────────────────────────────── + +int Interpreter::run() { + // "main" fonksiyonunu bul + IRFunction* mainFunction = program_.findFunction("main"); + if (!mainFunction) { + throw std::runtime_error("Çalışma hatası: 'main' fonksiyonu bulunamadı"); + } + + // main için ilk frame'i oluştur ve stack'e ekle + CallFrame mainFrame; + mainFrame.function = mainFunction; + mainFrame.instructionPointer = 0; + mainFrame.slots.resize(mainFunction->slotCount, Value::fromInt(0)); + mainFrame.returnDestSlot = -1; // caller yok + + callStack_.push_back(std::move(mainFrame)); + + // ── Ana döngü ───────────────────────────────────────────────────────── + // Her iterasyonun başında mevcut frame'i TAZEDEN alırız. + // CALL ve RETURN callStack'i değiştirir; `continue` ile döngü başına + // dönülür ve frame yeniden alınır — dangling pointer sorunu olmaz. + + while (!callStack_.empty()) { + + // Her iterasyonda taze referans al (CALL sonrası vector büyüyebilir) + CallFrame& frame = callStack_.back(); + + // Tüm instruction'lar tükendi mi? (RETURN olmadan biten fonksiyon) + if (frame.instructionPointer >= (int)frame.function->instructions.size()) { + // void fonksiyon gibi davran — 0 döndür + int destSlot = frame.returnDestSlot; + callStack_.pop_back(); + if (!callStack_.empty() && destSlot != -1) { + callStack_.back().slots[destSlot] = Value::fromInt(0); + } + continue; + } + + // Sıradaki talimatı al ve ip'yi ÖNCE İLERLET. + // Neden önce? CALL veya RETURN ip'ye dokunmaz. Böylece: + // - CALL: yeni frame ip=0 ile açılır, caller'ın ip'si zaten ilerletilmiş. + // - RETURN sonrası caller kaldığı yerden (ip zaten doğru) devam eder. + const Instruction& instr = frame.function->instructions[frame.instructionPointer]; + frame.instructionPointer++; + + // ── Talimat switch'i ────────────────────────────────────────────── + switch (instr.opcode) { + + // slots[dest] = sabit değer + case Opcode::LOAD_CONST: + frame.slots[instr.dest] = Value::fromInt(instr.intValue); + break; + + // slots[dest] = slots[src] (kopyala) + case Opcode::LOAD_SLOT: + frame.slots[instr.dest] = frame.slots[instr.src]; + break; + + // ── Aritmetik ──────────────────────────────────────────────────── + case Opcode::ADD: + frame.slots[instr.dest] = Value::fromInt( + frame.slots[instr.left].intValue + frame.slots[instr.right].intValue); + break; + + case Opcode::SUB: + frame.slots[instr.dest] = Value::fromInt( + frame.slots[instr.left].intValue - frame.slots[instr.right].intValue); + break; + + case Opcode::MUL: + frame.slots[instr.dest] = Value::fromInt( + frame.slots[instr.left].intValue * frame.slots[instr.right].intValue); + break; + + case Opcode::DIV: { + int divisor = frame.slots[instr.right].intValue; + if (divisor == 0) { + throw std::runtime_error("Çalışma hatası: sıfıra bölme"); + } + frame.slots[instr.dest] = Value::fromInt( + frame.slots[instr.left].intValue / divisor); + break; + } + + case Opcode::MOD: { + int divisor = frame.slots[instr.right].intValue; + if (divisor == 0) { + throw std::runtime_error("Çalışma hatası: sıfıra bölme (mod)"); + } + frame.slots[instr.dest] = Value::fromInt( + frame.slots[instr.left].intValue % divisor); + break; + } + + // ── Karşılaştırma (sonuç: 1=doğru, 0=yanlış) ──────────────────── + case Opcode::LESS: + frame.slots[instr.dest] = Value::fromInt( + frame.slots[instr.left].intValue < frame.slots[instr.right].intValue ? 1 : 0); + break; + + case Opcode::LESS_EQUAL: + frame.slots[instr.dest] = Value::fromInt( + frame.slots[instr.left].intValue <= frame.slots[instr.right].intValue ? 1 : 0); + break; + + case Opcode::GREATER: + frame.slots[instr.dest] = Value::fromInt( + frame.slots[instr.left].intValue > frame.slots[instr.right].intValue ? 1 : 0); + break; + + case Opcode::GREATER_EQUAL: + frame.slots[instr.dest] = Value::fromInt( + frame.slots[instr.left].intValue >= frame.slots[instr.right].intValue ? 1 : 0); + break; + + case Opcode::EQUAL_EQUAL: + frame.slots[instr.dest] = Value::fromInt( + frame.slots[instr.left].intValue == frame.slots[instr.right].intValue ? 1 : 0); + break; + + case Opcode::NOT_EQUAL: + frame.slots[instr.dest] = Value::fromInt( + frame.slots[instr.left].intValue != frame.slots[instr.right].intValue ? 1 : 0); + break; + + // ── Kontrol akışı ───────────────────────────────────────────────── + + // Koşulsuz atlama + case Opcode::JMP: + frame.instructionPointer = instr.jumpTarget; + break; + + // Koşullu atlama: slot[cond] == 0 (yanlış) ise atla + case Opcode::JIF_FALSE: + if (!frame.slots[instr.cond].isTruthy()) { + frame.instructionPointer = instr.jumpTarget; + } + break; + + // ── Fonksiyon çağrısı ───────────────────────────────────────────── + // Yeni frame oluştur, argümanları parametre slotlarına kopyala, + // stack'e ekle. `continue` ile döngü başına dön — yeni frame çalışmaya başlar. + case Opcode::CALL: { + IRFunction* callee = program_.findFunction(instr.functionName); + if (!callee) { + throw std::runtime_error( + "Çalışma hatası: '" + instr.functionName + "' fonksiyonu bulunamadı"); + } + + // Yeni frame hazırla + CallFrame newFrame; + newFrame.function = callee; + newFrame.instructionPointer = 0; + newFrame.slots.resize(callee->slotCount, Value::fromInt(0)); + newFrame.returnDestSlot = instr.dest; // sonuç bu slota yazılacak + + // Argümanları parametre slotlarına kopyala (slot 0, 1, 2, ...) + for (int i = 0; i < (int)instr.argSlots.size(); i++) { + newFrame.slots[i] = frame.slots[instr.argSlots[i]]; + } + + // Frame'i stack'e ekle — SONRA `continue` ile döngü başına dön. + // Böylece bir sonraki iterasyonda bu yeni frame çalışmaya başlar. + callStack_.push_back(std::move(newFrame)); + continue; // ← frame referansı burada yenilenir, dangling pointer yok + } + + // ── Dönüş ───────────────────────────────────────────────────────── + // Dönüş değerini caller'ın beklediği slota yaz, bu frame'i kapat. + case Opcode::RETURN: { + Value returnValue = frame.slots[instr.src]; + int returnDestSlot = frame.returnDestSlot; + + // Bu frame'i kapat + callStack_.pop_back(); + + // Caller varsa dönüş değerini onun slotuna yaz + if (!callStack_.empty() && returnDestSlot != -1) { + callStack_.back().slots[returnDestSlot] = returnValue; + } + + // main fonksiyonu döndü → program bitti + if (callStack_.empty()) { + return returnValue.intValue; + } + + continue; // ← bir sonraki iterasyonda caller frame tazeden alınır + } + + // ── FFI: Host fonksiyon çağrısı ─────────────────────────────────── + case Opcode::CALLHOST: + executeHostFunction(instr.functionName, frame.slots, instr.argSlots); + break; + } + } + + return 0; // Normal çıkış +} + +// ───────────────────────────────────────────────────────────────────────────── +// executeHostFunction — C++ tarafında tanımlı fonksiyonları çağır +// ───────────────────────────────────────────────────────────────────────────── + +void Interpreter::executeHostFunction(const std::string& name, + const std::vector& slots, + const std::vector& argSlots) { + if (name == "print") { + // print(değer) — stdout'a değeri yazdır + if (!argSlots.empty()) { + std::cout << slots[argSlots[0]].intValue << "\n"; + } + return; + } + + // Bilinmeyen host fonksiyon + throw std::runtime_error("Çalışma hatası: bilinmeyen host fonksiyonu '" + name + "'"); +} diff --git a/src/vm/interpreter.hpp b/src/vm/interpreter.hpp new file mode 100644 index 0000000..d7fb687 --- /dev/null +++ b/src/vm/interpreter.hpp @@ -0,0 +1,38 @@ +// ============================================================================ +// saQut VM — Interpreter (Bytecode Yorumlayıcı) +// +// IRProgram içindeki talimatları çalıştırır. +// "main" fonksiyonundan başlar, RETURN ile biten frame'leri kapatır. +// +// DÖNGÜ GÜVENLİĞİ (referans invalidation): +// Her iterasyonun başında callStack.back() tazeden alınır. +// CALL ve RETURN'den sonra `continue` ile döngü başına dönülür; +// böylece vector büyümesinden kaynaklanan dangling pointer sorunu olmaz. +// ============================================================================ + +#ifndef SAQUT_VM_INTERPRETER +#define SAQUT_VM_INTERPRETER + +#include +#include "ir/ir_program.hpp" +#include "vm/call_frame.hpp" + +class Interpreter { +public: + explicit Interpreter(IRProgram& program) : program_(program) {} + + // "main" fonksiyonunu bul ve çalıştır. + // Tamamlandığında main'in dönüş değerini (int) döndürür. + int run(); + +private: + IRProgram& program_; + std::vector callStack_; + + // Host (C++) fonksiyon çağrısı — şu an sadece "print" destekli + void executeHostFunction(const std::string& name, + const std::vector& slots, + const std::vector& argSlots); +}; + +#endif // SAQUT_VM_INTERPRETER diff --git a/src/vm/value.hpp b/src/vm/value.hpp new file mode 100644 index 0000000..c1103d2 --- /dev/null +++ b/src/vm/value.hpp @@ -0,0 +1,43 @@ +// ============================================================================ +// saQut VM — Value (Çalışma Zamanı Değer) +// +// Bir saQut değerinin bellekteki temsilidir. +// +// ŞU AN SADECE INT: +// fibonacci.sqt tamamen int kullanır, bu dikey dilim için int yeterli. +// İleride float, bool, string eklenmesi için "kind" alanı iskelet olarak bırakıldı. +// +// BOOLEAN OLARAK KULLANIM: +// JIF_FALSE talimatı değerin 0 olup olmadığına bakar. +// 0 = yanlış, sıfır-dışı = doğru. C geleneği. +// ============================================================================ + +#ifndef SAQUT_VM_VALUE +#define SAQUT_VM_VALUE + +// Gelecekte float/bool/string eklendiğinde burası genişleyecek. +// Şimdilik sadece int. +enum class ValueKind { + Int, + // Float, // TODO(vm-genişletme) + // Bool, // TODO(vm-genişletme) + // String, // TODO(vm-genişletme) +}; + +struct Value { + ValueKind kind = ValueKind::Int; + int intValue = 0; + + // Kolay oluşturma + static Value fromInt(int n) { + Value v; + v.kind = ValueKind::Int; + v.intValue = n; + return v; + } + + // JIF_FALSE için: 0 = yanlış, diğer = doğru + bool isTruthy() const { return intValue != 0; } +}; + +#endif // SAQUT_VM_VALUE