Compare commits
7 Commits
master
...
feature/pa
| Author | SHA1 | Date |
|---|---|---|
|
|
03970871db | |
|
|
9a013c53d4 | |
|
|
ee1e5213cf | |
|
|
c354d0b434 | |
|
|
6aa0da2378 | |
|
|
4d3150e811 | |
|
|
eb72680507 |
|
|
@ -0,0 +1,22 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
project(saqut VERSION 0.1 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_compile_options(-Wall -Wextra)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR NOT CMAKE_BUILD_TYPE)
|
||||
add_compile_options(-g -O0)
|
||||
message(STATUS "Debug modu aktif")
|
||||
else()
|
||||
add_compile_options(-O3)
|
||||
message(STATUS "Release modu aktif")
|
||||
endif()
|
||||
|
||||
# Tüm kaynak dosyaları topla
|
||||
file(GLOB_RECURSE SOURCES "src/*.cpp")
|
||||
|
||||
add_executable(saqut ${SOURCES})
|
||||
|
||||
target_include_directories(saqut PRIVATE src)
|
||||
Binary file not shown.
|
|
@ -0,0 +1,16 @@
|
|||
# ninja log v7
|
||||
1 1875 1779866878396024286 CMakeFiles/saqut.dir/src/parser/nodes/identifier.cpp.o eb96bb4b1eb4ad80
|
||||
1 1743 1779866878396133191 CMakeFiles/saqut.dir/src/parser/nodes/literal.cpp.o 78f2c4da7c9b2281
|
||||
1 1835 1779866851291850429 CMakeFiles/saqut.dir/src/parser/nodes/binary_expr.cpp.o d2e2bb2f8a63c6d2
|
||||
3 1976 1779866851293202930 CMakeFiles/saqut.dir/src/parser/nodes/program.cpp.o ac5bbcd74d87561a
|
||||
1 1828 1779866851291959426 CMakeFiles/saqut.dir/src/parser/nodes/declarations.cpp.o b6c56f04a257f685
|
||||
1 1991 1779866851292085983 CMakeFiles/saqut.dir/src/parser/nodes/expressions.cpp.o 4057e3d63c63a1ab
|
||||
6 2108 1779866851296202934 CMakeFiles/saqut.dir/src/parser/nodes/statements.cpp.o b5c20724bbf3648c
|
||||
1 2478 1779866878395241768 CMakeFiles/saqut.dir/src/main.cpp.o 110c26cb1d0c3a23
|
||||
2967 3100 1779866359829193015 saqut 78d8bacf305cfc59
|
||||
1 1450 1779866851291202927 CMakeFiles/saqut.dir/src/lexer/lexer.cpp.o 90eeec811f2137e6
|
||||
0 1481 1779866851290202926 CMakeFiles/saqut.dir/src/core/sourcefile.cpp.o da6f5fc90e87e6b1
|
||||
1 1410 1779866878396365249 CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o a01677f8bb4f4dbc
|
||||
1 1579 1779866878396233551 CMakeFiles/saqut.dir/src/parser/parser.cpp.o 2c65b7be26cead32
|
||||
1 1163 1779866916763301383 CMakeFiles/saqut.dir/src/parser/parser.cpp.o 2c65b7be26cead32
|
||||
1163 1283 1779866917925303269 saqut 89052e51305cb697
|
||||
|
|
@ -0,0 +1,360 @@
|
|||
# This is the CMakeCache file.
|
||||
# For build in directory: /home/saqut/Masaüstü/saqutcompiler/build
|
||||
# It was generated by CMake: /usr/bin/cmake
|
||||
# You can edit this file to change values found and used by cmake.
|
||||
# If you do not want to change any of the values, simply exit the editor.
|
||||
# If you do want to change a value, simply edit, save, and exit the editor.
|
||||
# The syntax for the file is as follows:
|
||||
# KEY:TYPE=VALUE
|
||||
# KEY is the name of a variable in the cache.
|
||||
# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
|
||||
# VALUE is the current value for the KEY.
|
||||
|
||||
########################
|
||||
# EXTERNAL cache entries
|
||||
########################
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_ADDR2LINE:FILEPATH=/usr/bin/addr2line
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_AR:FILEPATH=/usr/bin/ar
|
||||
|
||||
//Choose the type of build, options are: None Debug Release RelWithDebInfo
|
||||
// MinSizeRel ...
|
||||
CMAKE_BUILD_TYPE:STRING=Debug
|
||||
|
||||
//CXX compiler
|
||||
CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/c++
|
||||
|
||||
//A wrapper around 'ar' adding the appropriate '--plugin' option
|
||||
// for the GCC compiler
|
||||
CMAKE_CXX_COMPILER_AR:FILEPATH=/usr/bin/gcc-ar
|
||||
|
||||
//A wrapper around 'ranlib' adding the appropriate '--plugin' option
|
||||
// for the GCC compiler
|
||||
CMAKE_CXX_COMPILER_RANLIB:FILEPATH=/usr/bin/gcc-ranlib
|
||||
|
||||
//Flags used by the CXX compiler during all build types.
|
||||
CMAKE_CXX_FLAGS:STRING=
|
||||
|
||||
//Flags used by the CXX compiler during DEBUG builds.
|
||||
CMAKE_CXX_FLAGS_DEBUG:STRING=-g
|
||||
|
||||
//Flags used by the CXX compiler during MINSIZEREL builds.
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
|
||||
|
||||
//Flags used by the CXX compiler during RELEASE builds.
|
||||
CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
|
||||
|
||||
//Flags used by the CXX compiler during RELWITHDEBINFO builds.
|
||||
CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_DLLTOOL:FILEPATH=CMAKE_DLLTOOL-NOTFOUND
|
||||
|
||||
//Flags used by the linker during all build types.
|
||||
CMAKE_EXE_LINKER_FLAGS:STRING=
|
||||
|
||||
//Flags used by the linker during DEBUG builds.
|
||||
CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING=
|
||||
|
||||
//Flags used by the linker during MINSIZEREL builds.
|
||||
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING=
|
||||
|
||||
//Flags used by the linker during RELEASE builds.
|
||||
CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING=
|
||||
|
||||
//Flags used by the linker during RELWITHDEBINFO builds.
|
||||
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
||||
|
||||
//Enable/Disable output of build database during the build.
|
||||
CMAKE_EXPORT_BUILD_DATABASE:BOOL=
|
||||
|
||||
//Enable/Disable output of compile commands during generation.
|
||||
CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=
|
||||
|
||||
//Value Computed by CMake.
|
||||
CMAKE_FIND_PACKAGE_REDIRECTS_DIR:STATIC=/home/saqut/Masaüstü/saqutcompiler/build/CMakeFiles/pkgRedirects
|
||||
|
||||
//Install path prefix, prepended onto install directories.
|
||||
CMAKE_INSTALL_PREFIX:PATH=/usr/local
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_LINKER:FILEPATH=/usr/bin/ld
|
||||
|
||||
//Program used to build from build.ninja files.
|
||||
CMAKE_MAKE_PROGRAM:FILEPATH=/usr/bin/ninja
|
||||
|
||||
//Flags used by the linker during the creation of modules during
|
||||
// all build types.
|
||||
CMAKE_MODULE_LINKER_FLAGS:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of modules during
|
||||
// DEBUG builds.
|
||||
CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of modules during
|
||||
// MINSIZEREL builds.
|
||||
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of modules during
|
||||
// RELEASE builds.
|
||||
CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of modules during
|
||||
// RELWITHDEBINFO builds.
|
||||
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_NM:FILEPATH=/usr/bin/nm
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_OBJCOPY:FILEPATH=/usr/bin/objcopy
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_OBJDUMP:FILEPATH=/usr/bin/objdump
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_COMPAT_VERSION:STATIC=
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_DESCRIPTION:STATIC=
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_HOMEPAGE_URL:STATIC=
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_NAME:STATIC=saqut
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_SPDX_LICENSE:STATIC=
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_VERSION:STATIC=0.1
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_VERSION_MAJOR:STATIC=0
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_VERSION_MINOR:STATIC=1
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_VERSION_PATCH:STATIC=
|
||||
|
||||
//Value Computed by CMake
|
||||
CMAKE_PROJECT_VERSION_TWEAK:STATIC=
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_RANLIB:FILEPATH=/usr/bin/ranlib
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_READELF:FILEPATH=/usr/bin/readelf
|
||||
|
||||
//Flags used by the linker during the creation of shared libraries
|
||||
// during all build types.
|
||||
CMAKE_SHARED_LINKER_FLAGS:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of shared libraries
|
||||
// during DEBUG builds.
|
||||
CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of shared libraries
|
||||
// during MINSIZEREL builds.
|
||||
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of shared libraries
|
||||
// during RELEASE builds.
|
||||
CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING=
|
||||
|
||||
//Flags used by the linker during the creation of shared libraries
|
||||
// during RELWITHDEBINFO builds.
|
||||
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
||||
|
||||
//If set, runtime paths are not added when installing shared libraries,
|
||||
// but are added when building.
|
||||
CMAKE_SKIP_INSTALL_RPATH:BOOL=NO
|
||||
|
||||
//If set, runtime paths are not added when using shared libraries.
|
||||
CMAKE_SKIP_RPATH:BOOL=NO
|
||||
|
||||
//Flags used by the archiver during the creation of static libraries
|
||||
// during all build types.
|
||||
CMAKE_STATIC_LINKER_FLAGS:STRING=
|
||||
|
||||
//Flags used by the archiver during the creation of static libraries
|
||||
// during DEBUG builds.
|
||||
CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING=
|
||||
|
||||
//Flags used by the archiver during the creation of static libraries
|
||||
// during MINSIZEREL builds.
|
||||
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING=
|
||||
|
||||
//Flags used by the archiver during the creation of static libraries
|
||||
// during RELEASE builds.
|
||||
CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING=
|
||||
|
||||
//Flags used by the archiver during the creation of static libraries
|
||||
// during RELWITHDEBINFO builds.
|
||||
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING=
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_STRIP:FILEPATH=/usr/bin/strip
|
||||
|
||||
//Path to a program.
|
||||
CMAKE_TAPI:FILEPATH=CMAKE_TAPI-NOTFOUND
|
||||
|
||||
//If this value is on, makefiles will be generated without the
|
||||
// .SILENT directive, and all commands will be echoed to the console
|
||||
// during the make. This is useful for debugging only. With Visual
|
||||
// Studio IDE projects all commands are done without /nologo.
|
||||
CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE
|
||||
|
||||
//Value Computed by CMake
|
||||
saqut_BINARY_DIR:STATIC=/home/saqut/Masaüstü/saqutcompiler/build
|
||||
|
||||
//Value Computed by CMake
|
||||
saqut_IS_TOP_LEVEL:STATIC=ON
|
||||
|
||||
//Value Computed by CMake
|
||||
saqut_SOURCE_DIR:STATIC=/home/saqut/Masaüstü/saqutcompiler
|
||||
|
||||
|
||||
########################
|
||||
# INTERNAL cache entries
|
||||
########################
|
||||
|
||||
//ADVANCED property for variable: CMAKE_ADDR2LINE
|
||||
CMAKE_ADDR2LINE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_AR
|
||||
CMAKE_AR-ADVANCED:INTERNAL=1
|
||||
//This is the directory where this CMakeCache.txt was created
|
||||
CMAKE_CACHEFILE_DIR:INTERNAL=/home/saqut/Masaüstü/saqutcompiler/build
|
||||
//Major version of cmake used to create the current loaded cache
|
||||
CMAKE_CACHE_MAJOR_VERSION:INTERNAL=4
|
||||
//Minor version of cmake used to create the current loaded cache
|
||||
CMAKE_CACHE_MINOR_VERSION:INTERNAL=3
|
||||
//Patch version of cmake used to create the current loaded cache
|
||||
CMAKE_CACHE_PATCH_VERSION:INTERNAL=2
|
||||
//Path to CMake executable.
|
||||
CMAKE_COMMAND:INTERNAL=/usr/bin/cmake
|
||||
//Path to cpack program executable.
|
||||
CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack
|
||||
//Path to ctest program executable.
|
||||
CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest
|
||||
//ADVANCED property for variable: CMAKE_CXX_COMPILER
|
||||
CMAKE_CXX_COMPILER-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_CXX_COMPILER_AR
|
||||
CMAKE_CXX_COMPILER_AR-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_CXX_COMPILER_RANLIB
|
||||
CMAKE_CXX_COMPILER_RANLIB-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_CXX_FLAGS
|
||||
CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG
|
||||
CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_DLLTOOL
|
||||
CMAKE_DLLTOOL-ADVANCED:INTERNAL=1
|
||||
//Path to cache edit program executable.
|
||||
CMAKE_EDIT_COMMAND:INTERNAL=/usr/bin/ccmake
|
||||
//Executable file format
|
||||
CMAKE_EXECUTABLE_FORMAT:INTERNAL=ELF
|
||||
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS
|
||||
CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG
|
||||
CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL
|
||||
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE
|
||||
CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_EXPORT_BUILD_DATABASE
|
||||
CMAKE_EXPORT_BUILD_DATABASE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS
|
||||
CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1
|
||||
//Name of external makefile project generator.
|
||||
CMAKE_EXTRA_GENERATOR:INTERNAL=
|
||||
//Name of generator.
|
||||
CMAKE_GENERATOR:INTERNAL=Ninja
|
||||
//Generator instance identifier.
|
||||
CMAKE_GENERATOR_INSTANCE:INTERNAL=
|
||||
//Name of generator platform.
|
||||
CMAKE_GENERATOR_PLATFORM:INTERNAL=
|
||||
//Name of generator toolset.
|
||||
CMAKE_GENERATOR_TOOLSET:INTERNAL=
|
||||
//Source directory with the top level CMakeLists.txt file for this
|
||||
// project
|
||||
CMAKE_HOME_DIRECTORY:INTERNAL=/home/saqut/Masaüstü/saqutcompiler
|
||||
//Install .so files without execute permission.
|
||||
CMAKE_INSTALL_SO_NO_EXE:INTERNAL=0
|
||||
//ADVANCED property for variable: CMAKE_LINKER
|
||||
CMAKE_LINKER-ADVANCED:INTERNAL=1
|
||||
//Name of CMakeLists files to read
|
||||
CMAKE_LIST_FILE_NAME:INTERNAL=CMakeLists.txt
|
||||
//ADVANCED property for variable: CMAKE_MAKE_PROGRAM
|
||||
CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS
|
||||
CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG
|
||||
CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL
|
||||
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE
|
||||
CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_NM
|
||||
CMAKE_NM-ADVANCED:INTERNAL=1
|
||||
//number of local generators
|
||||
CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_OBJCOPY
|
||||
CMAKE_OBJCOPY-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_OBJDUMP
|
||||
CMAKE_OBJDUMP-ADVANCED:INTERNAL=1
|
||||
//Platform information initialized
|
||||
CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_RANLIB
|
||||
CMAKE_RANLIB-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_READELF
|
||||
CMAKE_READELF-ADVANCED:INTERNAL=1
|
||||
//Path to CMake installation.
|
||||
CMAKE_ROOT:INTERNAL=/usr/share/cmake
|
||||
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS
|
||||
CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG
|
||||
CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL
|
||||
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE
|
||||
CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH
|
||||
CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_SKIP_RPATH
|
||||
CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS
|
||||
CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG
|
||||
CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL
|
||||
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE
|
||||
CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_STRIP
|
||||
CMAKE_STRIP-ADVANCED:INTERNAL=1
|
||||
//ADVANCED property for variable: CMAKE_TAPI
|
||||
CMAKE_TAPI-ADVANCED:INTERNAL=1
|
||||
//uname command
|
||||
CMAKE_UNAME:INTERNAL=/usr/bin/uname
|
||||
//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE
|
||||
CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1
|
||||
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
set(CMAKE_CXX_COMPILER "/usr/bin/c++")
|
||||
set(CMAKE_CXX_COMPILER_ARG1 "")
|
||||
set(CMAKE_CXX_COMPILER_ID "GNU")
|
||||
set(CMAKE_CXX_COMPILER_VERSION "16.1.1")
|
||||
set(CMAKE_CXX_COMPILER_VERSION_INTERNAL "")
|
||||
set(CMAKE_CXX_COMPILER_WRAPPER "")
|
||||
set(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT "20")
|
||||
set(CMAKE_CXX_EXTENSIONS_COMPUTED_DEFAULT "ON")
|
||||
set(CMAKE_CXX_STANDARD_LATEST "26")
|
||||
set(CMAKE_CXX_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters;cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates;cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates;cxx_std_17;cxx_std_20;cxx_std_23;cxx_std_26")
|
||||
set(CMAKE_CXX98_COMPILE_FEATURES "cxx_std_98;cxx_template_template_parameters")
|
||||
set(CMAKE_CXX11_COMPILE_FEATURES "cxx_std_11;cxx_alias_templates;cxx_alignas;cxx_alignof;cxx_attributes;cxx_auto_type;cxx_constexpr;cxx_decltype;cxx_decltype_incomplete_return_types;cxx_default_function_template_args;cxx_defaulted_functions;cxx_defaulted_move_initializers;cxx_delegating_constructors;cxx_deleted_functions;cxx_enum_forward_declarations;cxx_explicit_conversions;cxx_extended_friend_declarations;cxx_extern_templates;cxx_final;cxx_func_identifier;cxx_generalized_initializers;cxx_inheriting_constructors;cxx_inline_namespaces;cxx_lambdas;cxx_local_type_template_args;cxx_long_long_type;cxx_noexcept;cxx_nonstatic_member_init;cxx_nullptr;cxx_override;cxx_range_for;cxx_raw_string_literals;cxx_reference_qualified_functions;cxx_right_angle_brackets;cxx_rvalue_references;cxx_sizeof_member;cxx_static_assert;cxx_strong_enums;cxx_thread_local;cxx_trailing_return_types;cxx_unicode_literals;cxx_uniform_initialization;cxx_unrestricted_unions;cxx_user_literals;cxx_variadic_macros;cxx_variadic_templates")
|
||||
set(CMAKE_CXX14_COMPILE_FEATURES "cxx_std_14;cxx_aggregate_default_initializers;cxx_attribute_deprecated;cxx_binary_literals;cxx_contextual_conversions;cxx_decltype_auto;cxx_digit_separators;cxx_generic_lambdas;cxx_lambda_init_captures;cxx_relaxed_constexpr;cxx_return_type_deduction;cxx_variable_templates")
|
||||
set(CMAKE_CXX17_COMPILE_FEATURES "cxx_std_17")
|
||||
set(CMAKE_CXX20_COMPILE_FEATURES "cxx_std_20")
|
||||
set(CMAKE_CXX23_COMPILE_FEATURES "cxx_std_23")
|
||||
set(CMAKE_CXX26_COMPILE_FEATURES "cxx_std_26")
|
||||
|
||||
set(CMAKE_CXX_PLATFORM_ID "Linux")
|
||||
set(CMAKE_CXX_SIMULATE_ID "")
|
||||
set(CMAKE_CXX_COMPILER_FRONTEND_VARIANT "GNU")
|
||||
set(CMAKE_CXX_COMPILER_APPLE_SYSROOT "")
|
||||
set(CMAKE_CXX_SIMULATE_VERSION "")
|
||||
set(CMAKE_CXX_COMPILER_ARCHITECTURE_ID "x86_64")
|
||||
|
||||
|
||||
|
||||
|
||||
set(CMAKE_AR "/usr/bin/ar")
|
||||
set(CMAKE_CXX_COMPILER_AR "/usr/bin/gcc-ar")
|
||||
set(CMAKE_RANLIB "/usr/bin/ranlib")
|
||||
set(CMAKE_CXX_COMPILER_RANLIB "/usr/bin/gcc-ranlib")
|
||||
set(CMAKE_LINKER "/usr/bin/ld")
|
||||
set(CMAKE_LINKER_LINK "")
|
||||
set(CMAKE_LINKER_LLD "")
|
||||
set(CMAKE_CXX_COMPILER_LINKER "/usr/bin/ld")
|
||||
set(CMAKE_CXX_COMPILER_LINKER_ID "GNU")
|
||||
set(CMAKE_CXX_COMPILER_LINKER_VERSION 2.46.0)
|
||||
set(CMAKE_CXX_COMPILER_LINKER_FRONTEND_VARIANT GNU)
|
||||
set(CMAKE_MT "")
|
||||
set(CMAKE_TAPI "CMAKE_TAPI-NOTFOUND")
|
||||
set(CMAKE_COMPILER_IS_GNUCXX 1)
|
||||
set(CMAKE_CXX_COMPILER_LOADED 1)
|
||||
set(CMAKE_CXX_COMPILER_WORKS TRUE)
|
||||
set(CMAKE_CXX_ABI_COMPILED TRUE)
|
||||
|
||||
set(CMAKE_CXX_COMPILER_ENV_VAR "CXX")
|
||||
|
||||
set(CMAKE_CXX_COMPILER_ID_RUN 1)
|
||||
set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;mpp;CPP;ixx;cppm;ccm;cxxm;c++m)
|
||||
set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC)
|
||||
|
||||
foreach (lang IN ITEMS C OBJC OBJCXX)
|
||||
if (CMAKE_${lang}_COMPILER_ID_RUN)
|
||||
foreach(extension IN LISTS CMAKE_${lang}_SOURCE_FILE_EXTENSIONS)
|
||||
list(REMOVE_ITEM CMAKE_CXX_SOURCE_FILE_EXTENSIONS ${extension})
|
||||
endforeach()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(CMAKE_CXX_LINKER_PREFERENCE 30)
|
||||
set(CMAKE_CXX_LINKER_PREFERENCE_PROPAGATES 1)
|
||||
set(CMAKE_CXX_LINKER_DEPFILE_SUPPORTED TRUE)
|
||||
set(CMAKE_LINKER_PUSHPOP_STATE_SUPPORTED TRUE)
|
||||
set(CMAKE_CXX_LINKER_PUSHPOP_STATE_SUPPORTED TRUE)
|
||||
|
||||
# Save compiler ABI information.
|
||||
set(CMAKE_CXX_SIZEOF_DATA_PTR "8")
|
||||
set(CMAKE_CXX_COMPILER_ABI "ELF")
|
||||
set(CMAKE_CXX_BYTE_ORDER "LITTLE_ENDIAN")
|
||||
set(CMAKE_CXX_LIBRARY_ARCHITECTURE "")
|
||||
|
||||
if(CMAKE_CXX_SIZEOF_DATA_PTR)
|
||||
set(CMAKE_SIZEOF_VOID_P "${CMAKE_CXX_SIZEOF_DATA_PTR}")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ABI)
|
||||
set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_CXX_COMPILER_ABI}")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_LIBRARY_ARCHITECTURE)
|
||||
set(CMAKE_LIBRARY_ARCHITECTURE "")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX "")
|
||||
if(CMAKE_CXX_CL_SHOWINCLUDES_PREFIX)
|
||||
set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_CXX_CL_SHOWINCLUDES_PREFIX}")
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
set(CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES "/usr/include/c++/16.1.1;/usr/include/c++/16.1.1/x86_64-pc-linux-gnu;/usr/include/c++/16.1.1/backward;/usr/lib/gcc/x86_64-pc-linux-gnu/16.1.1/include;/usr/local/include;/usr/include")
|
||||
set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "stdc++;m;gcc_s;gcc;atomic_asneeded;c;gcc_s;gcc")
|
||||
set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "/usr/lib/gcc/x86_64-pc-linux-gnu/16.1.1;/usr/lib;/lib")
|
||||
set(CMAKE_CXX_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "")
|
||||
set(CMAKE_CXX_COMPILER_CLANG_RESOURCE_DIR "")
|
||||
|
||||
set(CMAKE_CXX_COMPILER_IMPORT_STD "")
|
||||
set(CMAKE_CXX_COMPILER_IMPORT_STD_ERROR_MESSAGE "Experimental `import std` support not enabled when detecting toolchain; it must be set before `CXX` is enabled (usually a `project()` call)")
|
||||
set(CMAKE_CXX_STDLIB_MODULES_JSON "")
|
||||
Binary file not shown.
|
|
@ -0,0 +1,15 @@
|
|||
set(CMAKE_HOST_SYSTEM "Linux-7.0.9-1-MANJARO")
|
||||
set(CMAKE_HOST_SYSTEM_NAME "Linux")
|
||||
set(CMAKE_HOST_SYSTEM_VERSION "7.0.9-1-MANJARO")
|
||||
set(CMAKE_HOST_SYSTEM_PROCESSOR "x86_64")
|
||||
|
||||
|
||||
|
||||
set(CMAKE_SYSTEM "Linux-7.0.9-1-MANJARO")
|
||||
set(CMAKE_SYSTEM_NAME "Linux")
|
||||
set(CMAKE_SYSTEM_VERSION "7.0.9-1-MANJARO")
|
||||
set(CMAKE_SYSTEM_PROCESSOR "x86_64")
|
||||
|
||||
set(CMAKE_CROSSCOMPILING "FALSE")
|
||||
|
||||
set(CMAKE_SYSTEM_LOADED 1)
|
||||
|
|
@ -0,0 +1,949 @@
|
|||
/* This source file must have a .cpp extension so that all C++ compilers
|
||||
recognize the extension without flags. Borland does not know .cxx for
|
||||
example. */
|
||||
#ifndef __cplusplus
|
||||
# error "A C compiler has been selected for C++."
|
||||
#endif
|
||||
|
||||
#if !defined(__has_include)
|
||||
/* If the compiler does not have __has_include, pretend the answer is
|
||||
always no. */
|
||||
# define __has_include(x) 0
|
||||
#endif
|
||||
|
||||
|
||||
/* Version number components: V=Version, R=Revision, P=Patch
|
||||
Version date components: YYYY=Year, MM=Month, DD=Day */
|
||||
|
||||
#if defined(__INTEL_COMPILER) || defined(__ICC)
|
||||
# define COMPILER_ID "Intel"
|
||||
# if defined(_MSC_VER)
|
||||
# define SIMULATE_ID "MSVC"
|
||||
# endif
|
||||
# if defined(__GNUC__)
|
||||
# define SIMULATE_ID "GNU"
|
||||
# endif
|
||||
/* __INTEL_COMPILER = VRP prior to 2021, and then VVVV for 2021 and later,
|
||||
except that a few beta releases use the old format with V=2021. */
|
||||
# if __INTEL_COMPILER < 2021 || __INTEL_COMPILER == 202110 || __INTEL_COMPILER == 202111
|
||||
# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER/100)
|
||||
# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER/10 % 10)
|
||||
# if defined(__INTEL_COMPILER_UPDATE)
|
||||
# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER_UPDATE)
|
||||
# else
|
||||
# define COMPILER_VERSION_PATCH DEC(__INTEL_COMPILER % 10)
|
||||
# endif
|
||||
# else
|
||||
# define COMPILER_VERSION_MAJOR DEC(__INTEL_COMPILER)
|
||||
# define COMPILER_VERSION_MINOR DEC(__INTEL_COMPILER_UPDATE)
|
||||
/* The third version component from --version is an update index,
|
||||
but no macro is provided for it. */
|
||||
# define COMPILER_VERSION_PATCH DEC(0)
|
||||
# endif
|
||||
# if defined(__INTEL_COMPILER_BUILD_DATE)
|
||||
/* __INTEL_COMPILER_BUILD_DATE = YYYYMMDD */
|
||||
# define COMPILER_VERSION_TWEAK DEC(__INTEL_COMPILER_BUILD_DATE)
|
||||
# endif
|
||||
# if defined(_MSC_VER)
|
||||
/* _MSC_VER = VVRR */
|
||||
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
|
||||
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
|
||||
# endif
|
||||
# if defined(__GNUC__)
|
||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
|
||||
# elif defined(__GNUG__)
|
||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUG__)
|
||||
# endif
|
||||
# if defined(__GNUC_MINOR__)
|
||||
# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
|
||||
# endif
|
||||
# if defined(__GNUC_PATCHLEVEL__)
|
||||
# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
|
||||
# endif
|
||||
|
||||
#elif (defined(__clang__) && defined(__INTEL_CLANG_COMPILER)) || defined(__INTEL_LLVM_COMPILER)
|
||||
# define COMPILER_ID "IntelLLVM"
|
||||
#if defined(_MSC_VER)
|
||||
# define SIMULATE_ID "MSVC"
|
||||
#endif
|
||||
#if defined(__GNUC__)
|
||||
# define SIMULATE_ID "GNU"
|
||||
#endif
|
||||
/* __INTEL_LLVM_COMPILER = VVVVRP prior to 2021.2.0, VVVVRRPP for 2021.2.0 and
|
||||
* later. Look for 6 digit vs. 8 digit version number to decide encoding.
|
||||
* VVVV is no smaller than the current year when a version is released.
|
||||
*/
|
||||
#if __INTEL_LLVM_COMPILER < 1000000L
|
||||
# define COMPILER_VERSION_MAJOR DEC(__INTEL_LLVM_COMPILER/100)
|
||||
# define COMPILER_VERSION_MINOR DEC(__INTEL_LLVM_COMPILER/10 % 10)
|
||||
# define COMPILER_VERSION_PATCH DEC(__INTEL_LLVM_COMPILER % 10)
|
||||
#else
|
||||
# define COMPILER_VERSION_MAJOR DEC(__INTEL_LLVM_COMPILER/10000)
|
||||
# define COMPILER_VERSION_MINOR DEC(__INTEL_LLVM_COMPILER/100 % 100)
|
||||
# define COMPILER_VERSION_PATCH DEC(__INTEL_LLVM_COMPILER % 100)
|
||||
#endif
|
||||
#if defined(_MSC_VER)
|
||||
/* _MSC_VER = VVRR */
|
||||
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
|
||||
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
|
||||
#endif
|
||||
#if defined(__GNUC__)
|
||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
|
||||
#elif defined(__GNUG__)
|
||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUG__)
|
||||
#endif
|
||||
#if defined(__GNUC_MINOR__)
|
||||
# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
|
||||
#endif
|
||||
#if defined(__GNUC_PATCHLEVEL__)
|
||||
# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
|
||||
#endif
|
||||
|
||||
#elif defined(__PATHCC__)
|
||||
# define COMPILER_ID "PathScale"
|
||||
# define COMPILER_VERSION_MAJOR DEC(__PATHCC__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__PATHCC_MINOR__)
|
||||
# if defined(__PATHCC_PATCHLEVEL__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__PATHCC_PATCHLEVEL__)
|
||||
# endif
|
||||
|
||||
#elif defined(__BORLANDC__) && defined(__CODEGEARC_VERSION__)
|
||||
# define COMPILER_ID "Embarcadero"
|
||||
# define COMPILER_VERSION_MAJOR HEX(__CODEGEARC_VERSION__>>24 & 0x00FF)
|
||||
# define COMPILER_VERSION_MINOR HEX(__CODEGEARC_VERSION__>>16 & 0x00FF)
|
||||
# define COMPILER_VERSION_PATCH DEC(__CODEGEARC_VERSION__ & 0xFFFF)
|
||||
|
||||
#elif defined(__BORLANDC__)
|
||||
# define COMPILER_ID "Borland"
|
||||
/* __BORLANDC__ = 0xVRR */
|
||||
# define COMPILER_VERSION_MAJOR HEX(__BORLANDC__>>8)
|
||||
# define COMPILER_VERSION_MINOR HEX(__BORLANDC__ & 0xFF)
|
||||
|
||||
#elif defined(__WATCOMC__) && __WATCOMC__ < 1200
|
||||
# define COMPILER_ID "Watcom"
|
||||
/* __WATCOMC__ = VVRR */
|
||||
# define COMPILER_VERSION_MAJOR DEC(__WATCOMC__ / 100)
|
||||
# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10)
|
||||
# if (__WATCOMC__ % 10) > 0
|
||||
# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10)
|
||||
# endif
|
||||
|
||||
#elif defined(__WATCOMC__)
|
||||
# define COMPILER_ID "OpenWatcom"
|
||||
/* __WATCOMC__ = VVRP + 1100 */
|
||||
# define COMPILER_VERSION_MAJOR DEC((__WATCOMC__ - 1100) / 100)
|
||||
# define COMPILER_VERSION_MINOR DEC((__WATCOMC__ / 10) % 10)
|
||||
# if (__WATCOMC__ % 10) > 0
|
||||
# define COMPILER_VERSION_PATCH DEC(__WATCOMC__ % 10)
|
||||
# endif
|
||||
|
||||
#elif defined(__SUNPRO_CC)
|
||||
# define COMPILER_ID "SunPro"
|
||||
# if __SUNPRO_CC >= 0x5100
|
||||
/* __SUNPRO_CC = 0xVRRP */
|
||||
# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>12)
|
||||
# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xFF)
|
||||
# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF)
|
||||
# else
|
||||
/* __SUNPRO_CC = 0xVRP */
|
||||
# define COMPILER_VERSION_MAJOR HEX(__SUNPRO_CC>>8)
|
||||
# define COMPILER_VERSION_MINOR HEX(__SUNPRO_CC>>4 & 0xF)
|
||||
# define COMPILER_VERSION_PATCH HEX(__SUNPRO_CC & 0xF)
|
||||
# endif
|
||||
|
||||
#elif defined(__HP_aCC)
|
||||
# define COMPILER_ID "HP"
|
||||
/* __HP_aCC = VVRRPP */
|
||||
# define COMPILER_VERSION_MAJOR DEC(__HP_aCC/10000)
|
||||
# define COMPILER_VERSION_MINOR DEC(__HP_aCC/100 % 100)
|
||||
# define COMPILER_VERSION_PATCH DEC(__HP_aCC % 100)
|
||||
|
||||
#elif defined(__DECCXX)
|
||||
# define COMPILER_ID "Compaq"
|
||||
/* __DECCXX_VER = VVRRTPPPP */
|
||||
# define COMPILER_VERSION_MAJOR DEC(__DECCXX_VER/10000000)
|
||||
# define COMPILER_VERSION_MINOR DEC(__DECCXX_VER/100000 % 100)
|
||||
# define COMPILER_VERSION_PATCH DEC(__DECCXX_VER % 10000)
|
||||
|
||||
#elif defined(__IBMCPP__) && defined(__COMPILER_VER__)
|
||||
# define COMPILER_ID "zOS"
|
||||
/* __IBMCPP__ = VRP */
|
||||
# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100)
|
||||
# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10)
|
||||
# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10)
|
||||
|
||||
#elif defined(__open_xl__) && defined(__clang__)
|
||||
# define COMPILER_ID "IBMClang"
|
||||
# define COMPILER_VERSION_MAJOR DEC(__open_xl_version__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__open_xl_release__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__open_xl_modification__)
|
||||
# define COMPILER_VERSION_TWEAK DEC(__open_xl_ptf_fix_level__)
|
||||
# define COMPILER_VERSION_INTERNAL_STR __clang_version__
|
||||
|
||||
|
||||
#elif defined(__ibmxl__) && defined(__clang__)
|
||||
# define COMPILER_ID "XLClang"
|
||||
# define COMPILER_VERSION_MAJOR DEC(__ibmxl_version__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__ibmxl_release__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__ibmxl_modification__)
|
||||
# define COMPILER_VERSION_TWEAK DEC(__ibmxl_ptf_fix_level__)
|
||||
|
||||
|
||||
#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ >= 800
|
||||
# define COMPILER_ID "XL"
|
||||
/* __IBMCPP__ = VRP */
|
||||
# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100)
|
||||
# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10)
|
||||
# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10)
|
||||
|
||||
#elif defined(__IBMCPP__) && !defined(__COMPILER_VER__) && __IBMCPP__ < 800
|
||||
# define COMPILER_ID "VisualAge"
|
||||
/* __IBMCPP__ = VRP */
|
||||
# define COMPILER_VERSION_MAJOR DEC(__IBMCPP__/100)
|
||||
# define COMPILER_VERSION_MINOR DEC(__IBMCPP__/10 % 10)
|
||||
# define COMPILER_VERSION_PATCH DEC(__IBMCPP__ % 10)
|
||||
|
||||
#elif defined(__NVCOMPILER)
|
||||
# define COMPILER_ID "NVHPC"
|
||||
# define COMPILER_VERSION_MAJOR DEC(__NVCOMPILER_MAJOR__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__NVCOMPILER_MINOR__)
|
||||
# if defined(__NVCOMPILER_PATCHLEVEL__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__NVCOMPILER_PATCHLEVEL__)
|
||||
# endif
|
||||
|
||||
#elif defined(__PGI)
|
||||
# define COMPILER_ID "PGI"
|
||||
# define COMPILER_VERSION_MAJOR DEC(__PGIC__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__PGIC_MINOR__)
|
||||
# if defined(__PGIC_PATCHLEVEL__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__PGIC_PATCHLEVEL__)
|
||||
# endif
|
||||
|
||||
#elif defined(__clang__) && defined(__cray__)
|
||||
# define COMPILER_ID "CrayClang"
|
||||
# define COMPILER_VERSION_MAJOR DEC(__cray_major__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__cray_minor__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__cray_patchlevel__)
|
||||
# define COMPILER_VERSION_INTERNAL_STR __clang_version__
|
||||
|
||||
|
||||
#elif defined(_CRAYC)
|
||||
# define COMPILER_ID "Cray"
|
||||
# define COMPILER_VERSION_MAJOR DEC(_RELEASE_MAJOR)
|
||||
# define COMPILER_VERSION_MINOR DEC(_RELEASE_MINOR)
|
||||
|
||||
#elif defined(__TI_COMPILER_VERSION__)
|
||||
# define COMPILER_ID "TI"
|
||||
/* __TI_COMPILER_VERSION__ = VVVRRRPPP */
|
||||
# define COMPILER_VERSION_MAJOR DEC(__TI_COMPILER_VERSION__/1000000)
|
||||
# define COMPILER_VERSION_MINOR DEC(__TI_COMPILER_VERSION__/1000 % 1000)
|
||||
# define COMPILER_VERSION_PATCH DEC(__TI_COMPILER_VERSION__ % 1000)
|
||||
|
||||
#elif defined(__CLANG_FUJITSU)
|
||||
# define COMPILER_ID "FujitsuClang"
|
||||
# define COMPILER_VERSION_MAJOR DEC(__FCC_major__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__FCC_minor__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__FCC_patchlevel__)
|
||||
# define COMPILER_VERSION_INTERNAL_STR __clang_version__
|
||||
|
||||
|
||||
#elif defined(__FUJITSU)
|
||||
# define COMPILER_ID "Fujitsu"
|
||||
# if defined(__FCC_version__)
|
||||
# define COMPILER_VERSION __FCC_version__
|
||||
# elif defined(__FCC_major__)
|
||||
# define COMPILER_VERSION_MAJOR DEC(__FCC_major__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__FCC_minor__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__FCC_patchlevel__)
|
||||
# endif
|
||||
# if defined(__fcc_version)
|
||||
# define COMPILER_VERSION_INTERNAL DEC(__fcc_version)
|
||||
# elif defined(__FCC_VERSION)
|
||||
# define COMPILER_VERSION_INTERNAL DEC(__FCC_VERSION)
|
||||
# endif
|
||||
|
||||
|
||||
#elif defined(__ghs__)
|
||||
# define COMPILER_ID "GHS"
|
||||
/* __GHS_VERSION_NUMBER = VVVVRP */
|
||||
# ifdef __GHS_VERSION_NUMBER
|
||||
# define COMPILER_VERSION_MAJOR DEC(__GHS_VERSION_NUMBER / 100)
|
||||
# define COMPILER_VERSION_MINOR DEC(__GHS_VERSION_NUMBER / 10 % 10)
|
||||
# define COMPILER_VERSION_PATCH DEC(__GHS_VERSION_NUMBER % 10)
|
||||
# endif
|
||||
|
||||
#elif defined(__TASKING__)
|
||||
# define COMPILER_ID "Tasking"
|
||||
# define COMPILER_VERSION_MAJOR DEC(__VERSION__/1000)
|
||||
# define COMPILER_VERSION_MINOR DEC(__VERSION__ % 100)
|
||||
# define COMPILER_VERSION_INTERNAL DEC(__VERSION__)
|
||||
|
||||
#elif defined(__ORANGEC__)
|
||||
# define COMPILER_ID "OrangeC"
|
||||
# define COMPILER_VERSION_MAJOR DEC(__ORANGEC_MAJOR__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__ORANGEC_MINOR__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__ORANGEC_PATCHLEVEL__)
|
||||
|
||||
#elif defined(__RENESAS__)
|
||||
# define COMPILER_ID "Renesas"
|
||||
/* __RENESAS_VERSION__ = 0xVVRRPP00 */
|
||||
# define COMPILER_VERSION_MAJOR HEX(__RENESAS_VERSION__ >> 24 & 0xFF)
|
||||
# define COMPILER_VERSION_MINOR HEX(__RENESAS_VERSION__ >> 16 & 0xFF)
|
||||
# define COMPILER_VERSION_PATCH HEX(__RENESAS_VERSION__ >> 8 & 0xFF)
|
||||
|
||||
#elif defined(__SCO_VERSION__)
|
||||
# define COMPILER_ID "SCO"
|
||||
|
||||
#elif defined(__ARMCC_VERSION) && !defined(__clang__)
|
||||
# define COMPILER_ID "ARMCC"
|
||||
#if __ARMCC_VERSION >= 1000000
|
||||
/* __ARMCC_VERSION = VRRPPPP */
|
||||
# define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/1000000)
|
||||
# define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 100)
|
||||
# define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000)
|
||||
#else
|
||||
/* __ARMCC_VERSION = VRPPPP */
|
||||
# define COMPILER_VERSION_MAJOR DEC(__ARMCC_VERSION/100000)
|
||||
# define COMPILER_VERSION_MINOR DEC(__ARMCC_VERSION/10000 % 10)
|
||||
# define COMPILER_VERSION_PATCH DEC(__ARMCC_VERSION % 10000)
|
||||
#endif
|
||||
|
||||
|
||||
#elif defined(__clang__) && defined(__apple_build_version__)
|
||||
# define COMPILER_ID "AppleClang"
|
||||
# if defined(_MSC_VER)
|
||||
# define SIMULATE_ID "MSVC"
|
||||
# endif
|
||||
# define COMPILER_VERSION_MAJOR DEC(__clang_major__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__clang_minor__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__)
|
||||
# if defined(_MSC_VER)
|
||||
/* _MSC_VER = VVRR */
|
||||
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
|
||||
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
|
||||
# endif
|
||||
# define COMPILER_VERSION_TWEAK DEC(__apple_build_version__)
|
||||
|
||||
#elif defined(__clang__) && defined(__ARMCOMPILER_VERSION)
|
||||
# define COMPILER_ID "ARMClang"
|
||||
# define COMPILER_VERSION_MAJOR DEC(__ARMCOMPILER_VERSION/1000000)
|
||||
# define COMPILER_VERSION_MINOR DEC(__ARMCOMPILER_VERSION/10000 % 100)
|
||||
# define COMPILER_VERSION_PATCH DEC(__ARMCOMPILER_VERSION/100 % 100)
|
||||
# define COMPILER_VERSION_INTERNAL DEC(__ARMCOMPILER_VERSION)
|
||||
|
||||
#elif defined(__clang__) && defined(__ti__)
|
||||
# define COMPILER_ID "TIClang"
|
||||
# define COMPILER_VERSION_MAJOR DEC(__ti_major__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__ti_minor__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__ti_patchlevel__)
|
||||
# define COMPILER_VERSION_INTERNAL DEC(__ti_version__)
|
||||
|
||||
#elif defined(__clang__)
|
||||
# define COMPILER_ID "Clang"
|
||||
# if defined(_MSC_VER)
|
||||
# define SIMULATE_ID "MSVC"
|
||||
# endif
|
||||
# define COMPILER_VERSION_MAJOR DEC(__clang_major__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__clang_minor__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__clang_patchlevel__)
|
||||
# if defined(_MSC_VER)
|
||||
/* _MSC_VER = VVRR */
|
||||
# define SIMULATE_VERSION_MAJOR DEC(_MSC_VER / 100)
|
||||
# define SIMULATE_VERSION_MINOR DEC(_MSC_VER % 100)
|
||||
# endif
|
||||
|
||||
#elif defined(__LCC__) && (defined(__GNUC__) || defined(__GNUG__) || defined(__MCST__))
|
||||
# define COMPILER_ID "LCC"
|
||||
# define COMPILER_VERSION_MAJOR DEC(__LCC__ / 100)
|
||||
# define COMPILER_VERSION_MINOR DEC(__LCC__ % 100)
|
||||
# if defined(__LCC_MINOR__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__LCC_MINOR__)
|
||||
# endif
|
||||
# if defined(__GNUC__) && defined(__GNUC_MINOR__)
|
||||
# define SIMULATE_ID "GNU"
|
||||
# define SIMULATE_VERSION_MAJOR DEC(__GNUC__)
|
||||
# define SIMULATE_VERSION_MINOR DEC(__GNUC_MINOR__)
|
||||
# if defined(__GNUC_PATCHLEVEL__)
|
||||
# define SIMULATE_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||
# define COMPILER_ID "GNU"
|
||||
# if defined(__GNUC__)
|
||||
# define COMPILER_VERSION_MAJOR DEC(__GNUC__)
|
||||
# else
|
||||
# define COMPILER_VERSION_MAJOR DEC(__GNUG__)
|
||||
# endif
|
||||
# if defined(__GNUC_MINOR__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__GNUC_MINOR__)
|
||||
# endif
|
||||
# if defined(__GNUC_PATCHLEVEL__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__GNUC_PATCHLEVEL__)
|
||||
# endif
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
# define COMPILER_ID "MSVC"
|
||||
/* _MSC_VER = VVRR */
|
||||
# define COMPILER_VERSION_MAJOR DEC(_MSC_VER / 100)
|
||||
# define COMPILER_VERSION_MINOR DEC(_MSC_VER % 100)
|
||||
# if defined(_MSC_FULL_VER)
|
||||
# if _MSC_VER >= 1400
|
||||
/* _MSC_FULL_VER = VVRRPPPPP */
|
||||
# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 100000)
|
||||
# else
|
||||
/* _MSC_FULL_VER = VVRRPPPP */
|
||||
# define COMPILER_VERSION_PATCH DEC(_MSC_FULL_VER % 10000)
|
||||
# endif
|
||||
# endif
|
||||
# if defined(_MSC_BUILD)
|
||||
# define COMPILER_VERSION_TWEAK DEC(_MSC_BUILD)
|
||||
# endif
|
||||
|
||||
#elif defined(_ADI_COMPILER)
|
||||
# define COMPILER_ID "ADSP"
|
||||
#if defined(__VERSIONNUM__)
|
||||
/* __VERSIONNUM__ = 0xVVRRPPTT */
|
||||
# define COMPILER_VERSION_MAJOR DEC(__VERSIONNUM__ >> 24 & 0xFF)
|
||||
# define COMPILER_VERSION_MINOR DEC(__VERSIONNUM__ >> 16 & 0xFF)
|
||||
# define COMPILER_VERSION_PATCH DEC(__VERSIONNUM__ >> 8 & 0xFF)
|
||||
# define COMPILER_VERSION_TWEAK DEC(__VERSIONNUM__ & 0xFF)
|
||||
#endif
|
||||
|
||||
#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)
|
||||
# define COMPILER_ID "IAR"
|
||||
# if defined(__VER__) && defined(__ICCARM__)
|
||||
# define COMPILER_VERSION_MAJOR DEC((__VER__) / 1000000)
|
||||
# define COMPILER_VERSION_MINOR DEC(((__VER__) / 1000) % 1000)
|
||||
# define COMPILER_VERSION_PATCH DEC((__VER__) % 1000)
|
||||
# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__)
|
||||
# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__) || defined(__ICCRH850__) || defined(__ICCRL78__) || defined(__ICC430__) || defined(__ICCRISCV__) || defined(__ICCV850__) || defined(__ICC8051__) || defined(__ICCSTM8__))
|
||||
# define COMPILER_VERSION_MAJOR DEC((__VER__) / 100)
|
||||
# define COMPILER_VERSION_MINOR DEC((__VER__) - (((__VER__) / 100)*100))
|
||||
# define COMPILER_VERSION_PATCH DEC(__SUBVERSION__)
|
||||
# define COMPILER_VERSION_INTERNAL DEC(__IAR_SYSTEMS_ICC__)
|
||||
# endif
|
||||
|
||||
#elif defined(__DCC__) && defined(_DIAB_TOOL)
|
||||
# define COMPILER_ID "Diab"
|
||||
# define COMPILER_VERSION_MAJOR DEC(__VERSION_MAJOR_NUMBER__)
|
||||
# define COMPILER_VERSION_MINOR DEC(__VERSION_MINOR_NUMBER__)
|
||||
# define COMPILER_VERSION_PATCH DEC(__VERSION_ARCH_FEATURE_NUMBER__)
|
||||
# define COMPILER_VERSION_TWEAK DEC(__VERSION_BUG_FIX_NUMBER__)
|
||||
|
||||
|
||||
|
||||
/* These compilers are either not known or too old to define an
|
||||
identification macro. Try to identify the platform and guess that
|
||||
it is the native compiler. */
|
||||
#elif defined(__hpux) || defined(__hpua)
|
||||
# define COMPILER_ID "HP"
|
||||
|
||||
#else /* unknown compiler */
|
||||
# define COMPILER_ID ""
|
||||
#endif
|
||||
|
||||
/* Construct the string literal in pieces to prevent the source from
|
||||
getting matched. Store it in a pointer rather than an array
|
||||
because some compilers will just produce instructions to fill the
|
||||
array rather than assigning a pointer to a static array. */
|
||||
char const* info_compiler = "INFO" ":" "compiler[" COMPILER_ID "]";
|
||||
#ifdef SIMULATE_ID
|
||||
char const* info_simulate = "INFO" ":" "simulate[" SIMULATE_ID "]";
|
||||
#endif
|
||||
|
||||
#ifdef __QNXNTO__
|
||||
char const* qnxnto = "INFO" ":" "qnxnto[]";
|
||||
#endif
|
||||
|
||||
#if defined(__CRAYXT_COMPUTE_LINUX_TARGET)
|
||||
char const *info_cray = "INFO" ":" "compiler_wrapper[CrayPrgEnv]";
|
||||
#endif
|
||||
|
||||
#define STRINGIFY_HELPER(X) #X
|
||||
#define STRINGIFY(X) STRINGIFY_HELPER(X)
|
||||
|
||||
/* Identify known platforms by name. */
|
||||
#if defined(__linux) || defined(__linux__) || defined(linux)
|
||||
# define PLATFORM_ID "Linux"
|
||||
|
||||
#elif defined(__MSYS__)
|
||||
# define PLATFORM_ID "MSYS"
|
||||
|
||||
#elif defined(__CYGWIN__)
|
||||
# define PLATFORM_ID "Cygwin"
|
||||
|
||||
#elif defined(__MINGW32__)
|
||||
# define PLATFORM_ID "MinGW"
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
# define PLATFORM_ID "Darwin"
|
||||
|
||||
#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
|
||||
# define PLATFORM_ID "Windows"
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD)
|
||||
# define PLATFORM_ID "FreeBSD"
|
||||
|
||||
#elif defined(__NetBSD__) || defined(__NetBSD)
|
||||
# define PLATFORM_ID "NetBSD"
|
||||
|
||||
#elif defined(__OpenBSD__) || defined(__OPENBSD)
|
||||
# define PLATFORM_ID "OpenBSD"
|
||||
|
||||
#elif defined(__sun) || defined(sun)
|
||||
# define PLATFORM_ID "SunOS"
|
||||
|
||||
#elif defined(_AIX) || defined(__AIX) || defined(__AIX__) || defined(__aix) || defined(__aix__)
|
||||
# define PLATFORM_ID "AIX"
|
||||
|
||||
#elif defined(__hpux) || defined(__hpux__)
|
||||
# define PLATFORM_ID "HP-UX"
|
||||
|
||||
#elif defined(__HAIKU__)
|
||||
# define PLATFORM_ID "Haiku"
|
||||
|
||||
#elif defined(__BeOS) || defined(__BEOS__) || defined(_BEOS)
|
||||
# define PLATFORM_ID "BeOS"
|
||||
|
||||
#elif defined(__QNX__) || defined(__QNXNTO__)
|
||||
# define PLATFORM_ID "QNX"
|
||||
|
||||
#elif defined(__tru64) || defined(_tru64) || defined(__TRU64__)
|
||||
# define PLATFORM_ID "Tru64"
|
||||
|
||||
#elif defined(__riscos) || defined(__riscos__)
|
||||
# define PLATFORM_ID "RISCos"
|
||||
|
||||
#elif defined(__sinix) || defined(__sinix__) || defined(__SINIX__)
|
||||
# define PLATFORM_ID "SINIX"
|
||||
|
||||
#elif defined(__UNIX_SV__)
|
||||
# define PLATFORM_ID "UNIX_SV"
|
||||
|
||||
#elif defined(__bsdos__)
|
||||
# define PLATFORM_ID "BSDOS"
|
||||
|
||||
#elif defined(_MPRAS) || defined(MPRAS)
|
||||
# define PLATFORM_ID "MP-RAS"
|
||||
|
||||
#elif defined(__osf) || defined(__osf__)
|
||||
# define PLATFORM_ID "OSF1"
|
||||
|
||||
#elif defined(_SCO_SV) || defined(SCO_SV) || defined(sco_sv)
|
||||
# define PLATFORM_ID "SCO_SV"
|
||||
|
||||
#elif defined(__ultrix) || defined(__ultrix__) || defined(_ULTRIX)
|
||||
# define PLATFORM_ID "ULTRIX"
|
||||
|
||||
#elif defined(__XENIX__) || defined(_XENIX) || defined(XENIX)
|
||||
# define PLATFORM_ID "Xenix"
|
||||
|
||||
#elif defined(__WATCOMC__)
|
||||
# if defined(__LINUX__)
|
||||
# define PLATFORM_ID "Linux"
|
||||
|
||||
# elif defined(__DOS__)
|
||||
# define PLATFORM_ID "DOS"
|
||||
|
||||
# elif defined(__OS2__)
|
||||
# define PLATFORM_ID "OS2"
|
||||
|
||||
# elif defined(__WINDOWS__)
|
||||
# define PLATFORM_ID "Windows3x"
|
||||
|
||||
# elif defined(__VXWORKS__)
|
||||
# define PLATFORM_ID "VxWorks"
|
||||
|
||||
# else /* unknown platform */
|
||||
# define PLATFORM_ID
|
||||
# endif
|
||||
|
||||
#elif defined(__INTEGRITY)
|
||||
# if defined(INT_178B)
|
||||
# define PLATFORM_ID "Integrity178"
|
||||
|
||||
# else /* regular Integrity */
|
||||
# define PLATFORM_ID "Integrity"
|
||||
# endif
|
||||
|
||||
# elif defined(_ADI_COMPILER)
|
||||
# define PLATFORM_ID "ADSP"
|
||||
|
||||
#else /* unknown platform */
|
||||
# define PLATFORM_ID
|
||||
|
||||
#endif
|
||||
|
||||
/* For windows compilers MSVC and Intel we can determine
|
||||
the architecture of the compiler being used. This is because
|
||||
the compilers do not have flags that can change the architecture,
|
||||
but rather depend on which compiler is being used
|
||||
*/
|
||||
#if defined(_WIN32) && defined(_MSC_VER)
|
||||
# if defined(_M_IA64)
|
||||
# define ARCHITECTURE_ID "IA64"
|
||||
|
||||
# elif defined(_M_ARM64EC)
|
||||
# define ARCHITECTURE_ID "ARM64EC"
|
||||
|
||||
# elif defined(_M_X64) || defined(_M_AMD64)
|
||||
# define ARCHITECTURE_ID "x64"
|
||||
|
||||
# elif defined(_M_IX86)
|
||||
# define ARCHITECTURE_ID "X86"
|
||||
|
||||
# elif defined(_M_ARM64)
|
||||
# define ARCHITECTURE_ID "ARM64"
|
||||
|
||||
# elif defined(_M_ARM)
|
||||
# if _M_ARM == 4
|
||||
# define ARCHITECTURE_ID "ARMV4I"
|
||||
# elif _M_ARM == 5
|
||||
# define ARCHITECTURE_ID "ARMV5I"
|
||||
# else
|
||||
# define ARCHITECTURE_ID "ARMV" STRINGIFY(_M_ARM)
|
||||
# endif
|
||||
|
||||
# elif defined(_M_MIPS)
|
||||
# define ARCHITECTURE_ID "MIPS"
|
||||
|
||||
# elif defined(_M_SH)
|
||||
# define ARCHITECTURE_ID "SHx"
|
||||
|
||||
# else /* unknown architecture */
|
||||
# define ARCHITECTURE_ID ""
|
||||
# endif
|
||||
|
||||
#elif defined(__WATCOMC__)
|
||||
# if defined(_M_I86)
|
||||
# define ARCHITECTURE_ID "I86"
|
||||
|
||||
# elif defined(_M_IX86)
|
||||
# define ARCHITECTURE_ID "X86"
|
||||
|
||||
# else /* unknown architecture */
|
||||
# define ARCHITECTURE_ID ""
|
||||
# endif
|
||||
|
||||
#elif defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)
|
||||
# if defined(__ICCARM__)
|
||||
# define ARCHITECTURE_ID "ARM"
|
||||
|
||||
# elif defined(__ICCRX__)
|
||||
# define ARCHITECTURE_ID "RX"
|
||||
|
||||
# elif defined(__ICCRH850__)
|
||||
# define ARCHITECTURE_ID "RH850"
|
||||
|
||||
# elif defined(__ICCRL78__)
|
||||
# define ARCHITECTURE_ID "RL78"
|
||||
|
||||
# elif defined(__ICCRISCV__)
|
||||
# define ARCHITECTURE_ID "RISCV"
|
||||
|
||||
# elif defined(__ICCAVR__)
|
||||
# define ARCHITECTURE_ID "AVR"
|
||||
|
||||
# elif defined(__ICC430__)
|
||||
# define ARCHITECTURE_ID "MSP430"
|
||||
|
||||
# elif defined(__ICCV850__)
|
||||
# define ARCHITECTURE_ID "V850"
|
||||
|
||||
# elif defined(__ICC8051__)
|
||||
# define ARCHITECTURE_ID "8051"
|
||||
|
||||
# elif defined(__ICCSTM8__)
|
||||
# define ARCHITECTURE_ID "STM8"
|
||||
|
||||
# else /* unknown architecture */
|
||||
# define ARCHITECTURE_ID ""
|
||||
# endif
|
||||
|
||||
#elif defined(__ghs__)
|
||||
# if defined(__PPC64__)
|
||||
# define ARCHITECTURE_ID "PPC64"
|
||||
|
||||
# elif defined(__ppc__)
|
||||
# define ARCHITECTURE_ID "PPC"
|
||||
|
||||
# elif defined(__ARM__)
|
||||
# define ARCHITECTURE_ID "ARM"
|
||||
|
||||
# elif defined(__x86_64__)
|
||||
# define ARCHITECTURE_ID "x64"
|
||||
|
||||
# elif defined(__i386__)
|
||||
# define ARCHITECTURE_ID "X86"
|
||||
|
||||
# else /* unknown architecture */
|
||||
# define ARCHITECTURE_ID ""
|
||||
# endif
|
||||
|
||||
#elif defined(__clang__) && defined(__ti__)
|
||||
# if defined(__ARM_ARCH)
|
||||
# define ARCHITECTURE_ID "ARM"
|
||||
|
||||
# else /* unknown architecture */
|
||||
# define ARCHITECTURE_ID ""
|
||||
# endif
|
||||
|
||||
#elif defined(__TI_COMPILER_VERSION__)
|
||||
# if defined(__TI_ARM__)
|
||||
# define ARCHITECTURE_ID "ARM"
|
||||
|
||||
# elif defined(__MSP430__)
|
||||
# define ARCHITECTURE_ID "MSP430"
|
||||
|
||||
# elif defined(__TMS320C28XX__)
|
||||
# define ARCHITECTURE_ID "TMS320C28x"
|
||||
|
||||
# elif defined(__TMS320C6X__) || defined(_TMS320C6X)
|
||||
# define ARCHITECTURE_ID "TMS320C6x"
|
||||
|
||||
# else /* unknown architecture */
|
||||
# define ARCHITECTURE_ID ""
|
||||
# endif
|
||||
|
||||
# elif defined(__ADSPSHARC__)
|
||||
# define ARCHITECTURE_ID "SHARC"
|
||||
|
||||
# elif defined(__ADSPBLACKFIN__)
|
||||
# define ARCHITECTURE_ID "Blackfin"
|
||||
|
||||
#elif defined(__TASKING__)
|
||||
|
||||
# if defined(__CTC__) || defined(__CPTC__)
|
||||
# define ARCHITECTURE_ID "TriCore"
|
||||
|
||||
# elif defined(__CMCS__)
|
||||
# define ARCHITECTURE_ID "MCS"
|
||||
|
||||
# elif defined(__CARM__) || defined(__CPARM__)
|
||||
# define ARCHITECTURE_ID "ARM"
|
||||
|
||||
# elif defined(__CARC__)
|
||||
# define ARCHITECTURE_ID "ARC"
|
||||
|
||||
# elif defined(__C51__)
|
||||
# define ARCHITECTURE_ID "8051"
|
||||
|
||||
# elif defined(__CPCP__)
|
||||
# define ARCHITECTURE_ID "PCP"
|
||||
|
||||
# else
|
||||
# define ARCHITECTURE_ID ""
|
||||
# endif
|
||||
|
||||
#elif defined(__RENESAS__)
|
||||
# if defined(__CCRX__)
|
||||
# define ARCHITECTURE_ID "RX"
|
||||
|
||||
# elif defined(__CCRL__)
|
||||
# define ARCHITECTURE_ID "RL78"
|
||||
|
||||
# elif defined(__CCRH__)
|
||||
# define ARCHITECTURE_ID "RH850"
|
||||
|
||||
# else
|
||||
# define ARCHITECTURE_ID ""
|
||||
# endif
|
||||
|
||||
#else
|
||||
# define ARCHITECTURE_ID
|
||||
#endif
|
||||
|
||||
/* Convert integer to decimal digit literals. */
|
||||
#define DEC(n) \
|
||||
('0' + (((n) / 10000000)%10)), \
|
||||
('0' + (((n) / 1000000)%10)), \
|
||||
('0' + (((n) / 100000)%10)), \
|
||||
('0' + (((n) / 10000)%10)), \
|
||||
('0' + (((n) / 1000)%10)), \
|
||||
('0' + (((n) / 100)%10)), \
|
||||
('0' + (((n) / 10)%10)), \
|
||||
('0' + ((n) % 10))
|
||||
|
||||
/* Convert integer to hex digit literals. */
|
||||
#define HEX(n) \
|
||||
('0' + ((n)>>28 & 0xF)), \
|
||||
('0' + ((n)>>24 & 0xF)), \
|
||||
('0' + ((n)>>20 & 0xF)), \
|
||||
('0' + ((n)>>16 & 0xF)), \
|
||||
('0' + ((n)>>12 & 0xF)), \
|
||||
('0' + ((n)>>8 & 0xF)), \
|
||||
('0' + ((n)>>4 & 0xF)), \
|
||||
('0' + ((n) & 0xF))
|
||||
|
||||
/* Construct a string literal encoding the version number. */
|
||||
#ifdef COMPILER_VERSION
|
||||
char const* info_version = "INFO" ":" "compiler_version[" COMPILER_VERSION "]";
|
||||
|
||||
/* Construct a string literal encoding the version number components. */
|
||||
#elif defined(COMPILER_VERSION_MAJOR)
|
||||
char const info_version[] = {
|
||||
'I', 'N', 'F', 'O', ':',
|
||||
'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','[',
|
||||
COMPILER_VERSION_MAJOR,
|
||||
# ifdef COMPILER_VERSION_MINOR
|
||||
'.', COMPILER_VERSION_MINOR,
|
||||
# ifdef COMPILER_VERSION_PATCH
|
||||
'.', COMPILER_VERSION_PATCH,
|
||||
# ifdef COMPILER_VERSION_TWEAK
|
||||
'.', COMPILER_VERSION_TWEAK,
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
']','\0'};
|
||||
#endif
|
||||
|
||||
/* Construct a string literal encoding the internal version number. */
|
||||
#ifdef COMPILER_VERSION_INTERNAL
|
||||
char const info_version_internal[] = {
|
||||
'I', 'N', 'F', 'O', ':',
|
||||
'c','o','m','p','i','l','e','r','_','v','e','r','s','i','o','n','_',
|
||||
'i','n','t','e','r','n','a','l','[',
|
||||
COMPILER_VERSION_INTERNAL,']','\0'};
|
||||
#elif defined(COMPILER_VERSION_INTERNAL_STR)
|
||||
char const* info_version_internal = "INFO" ":" "compiler_version_internal[" COMPILER_VERSION_INTERNAL_STR "]";
|
||||
#endif
|
||||
|
||||
/* Construct a string literal encoding the version number components. */
|
||||
#ifdef SIMULATE_VERSION_MAJOR
|
||||
char const info_simulate_version[] = {
|
||||
'I', 'N', 'F', 'O', ':',
|
||||
's','i','m','u','l','a','t','e','_','v','e','r','s','i','o','n','[',
|
||||
SIMULATE_VERSION_MAJOR,
|
||||
# ifdef SIMULATE_VERSION_MINOR
|
||||
'.', SIMULATE_VERSION_MINOR,
|
||||
# ifdef SIMULATE_VERSION_PATCH
|
||||
'.', SIMULATE_VERSION_PATCH,
|
||||
# ifdef SIMULATE_VERSION_TWEAK
|
||||
'.', SIMULATE_VERSION_TWEAK,
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
']','\0'};
|
||||
#endif
|
||||
|
||||
/* Construct the string literal in pieces to prevent the source from
|
||||
getting matched. Store it in a pointer rather than an array
|
||||
because some compilers will just produce instructions to fill the
|
||||
array rather than assigning a pointer to a static array. */
|
||||
char const* info_platform = "INFO" ":" "platform[" PLATFORM_ID "]";
|
||||
char const* info_arch = "INFO" ":" "arch[" ARCHITECTURE_ID "]";
|
||||
|
||||
|
||||
|
||||
#define CXX_STD_98 199711L
|
||||
#define CXX_STD_11 201103L
|
||||
#define CXX_STD_14 201402L
|
||||
#define CXX_STD_17 201703L
|
||||
#define CXX_STD_20 202002L
|
||||
#define CXX_STD_23 202302L
|
||||
|
||||
#if defined(__INTEL_COMPILER) && defined(_MSVC_LANG)
|
||||
# if _MSVC_LANG > CXX_STD_17
|
||||
# define CXX_STD _MSVC_LANG
|
||||
# elif _MSVC_LANG == CXX_STD_17 && defined(__cpp_aggregate_paren_init)
|
||||
# define CXX_STD CXX_STD_20
|
||||
# elif _MSVC_LANG > CXX_STD_14 && __cplusplus > CXX_STD_17
|
||||
# define CXX_STD CXX_STD_20
|
||||
# elif _MSVC_LANG > CXX_STD_14
|
||||
# define CXX_STD CXX_STD_17
|
||||
# elif defined(__INTEL_CXX11_MODE__) && defined(__cpp_aggregate_nsdmi)
|
||||
# define CXX_STD CXX_STD_14
|
||||
# elif defined(__INTEL_CXX11_MODE__)
|
||||
# define CXX_STD CXX_STD_11
|
||||
# else
|
||||
# define CXX_STD CXX_STD_98
|
||||
# endif
|
||||
#elif defined(_MSC_VER) && defined(_MSVC_LANG)
|
||||
# if _MSVC_LANG > __cplusplus
|
||||
# define CXX_STD _MSVC_LANG
|
||||
# else
|
||||
# define CXX_STD __cplusplus
|
||||
# endif
|
||||
#elif defined(__NVCOMPILER)
|
||||
# if __cplusplus == CXX_STD_17 && defined(__cpp_aggregate_paren_init)
|
||||
# define CXX_STD CXX_STD_20
|
||||
# else
|
||||
# define CXX_STD __cplusplus
|
||||
# endif
|
||||
#elif defined(__INTEL_COMPILER) || defined(__PGI)
|
||||
# if __cplusplus == CXX_STD_11 && defined(__cpp_namespace_attributes)
|
||||
# define CXX_STD CXX_STD_17
|
||||
# elif __cplusplus == CXX_STD_11 && defined(__cpp_aggregate_nsdmi)
|
||||
# define CXX_STD CXX_STD_14
|
||||
# else
|
||||
# define CXX_STD __cplusplus
|
||||
# endif
|
||||
#elif (defined(__IBMCPP__) || defined(__ibmxl__)) && defined(__linux__)
|
||||
# if __cplusplus == CXX_STD_11 && defined(__cpp_aggregate_nsdmi)
|
||||
# define CXX_STD CXX_STD_14
|
||||
# else
|
||||
# define CXX_STD __cplusplus
|
||||
# endif
|
||||
#elif __cplusplus == 1 && defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||
# define CXX_STD CXX_STD_11
|
||||
#else
|
||||
# define CXX_STD __cplusplus
|
||||
#endif
|
||||
|
||||
const char* info_language_standard_default = "INFO" ":" "standard_default["
|
||||
#if CXX_STD > CXX_STD_23
|
||||
"26"
|
||||
#elif CXX_STD > CXX_STD_20
|
||||
"23"
|
||||
#elif CXX_STD > CXX_STD_17
|
||||
"20"
|
||||
#elif CXX_STD > CXX_STD_14
|
||||
"17"
|
||||
#elif CXX_STD > CXX_STD_11
|
||||
"14"
|
||||
#elif CXX_STD >= CXX_STD_11
|
||||
"11"
|
||||
#else
|
||||
"98"
|
||||
#endif
|
||||
"]";
|
||||
|
||||
const char* info_language_extensions_default = "INFO" ":" "extensions_default["
|
||||
#if (defined(__clang__) || defined(__GNUC__) || defined(__xlC__) || \
|
||||
defined(__TI_COMPILER_VERSION__) || defined(__RENESAS__)) && \
|
||||
!defined(__STRICT_ANSI__)
|
||||
"ON"
|
||||
#else
|
||||
"OFF"
|
||||
#endif
|
||||
"]";
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int require = 0;
|
||||
require += info_compiler[argc];
|
||||
require += info_platform[argc];
|
||||
require += info_arch[argc];
|
||||
#ifdef COMPILER_VERSION_MAJOR
|
||||
require += info_version[argc];
|
||||
#endif
|
||||
#if defined(COMPILER_VERSION_INTERNAL) || defined(COMPILER_VERSION_INTERNAL_STR)
|
||||
require += info_version_internal[argc];
|
||||
#endif
|
||||
#ifdef SIMULATE_ID
|
||||
require += info_simulate[argc];
|
||||
#endif
|
||||
#ifdef SIMULATE_VERSION_MAJOR
|
||||
require += info_simulate_version[argc];
|
||||
#endif
|
||||
#if defined(__CRAYXT_COMPUTE_LINUX_TARGET)
|
||||
require += info_cray[argc];
|
||||
#endif
|
||||
require += info_language_standard_default[argc];
|
||||
require += info_language_extensions_default[argc];
|
||||
(void)argv;
|
||||
return require;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"InstallScripts" :
|
||||
[
|
||||
"/home/saqut/Masa\u00fcst\u00fc/saqutcompiler/build/cmake_install.cmake"
|
||||
],
|
||||
"Parallel" : false
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
/home/saqut/Masaüstü/saqutcompiler/build/CMakeFiles/saqut.dir
|
||||
/home/saqut/Masaüstü/saqutcompiler/build/CMakeFiles/edit_cache.dir
|
||||
/home/saqut/Masaüstü/saqutcompiler/build/CMakeFiles/rebuild_cache.dir
|
||||
|
|
@ -0,0 +1 @@
|
|||
# This file is generated by cmake for dependency checking of the CMakeCache.txt file
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
# CMAKE generated file: DO NOT EDIT!
|
||||
# Generated by "Ninja" Generator, CMake Version 4.3
|
||||
|
||||
# This file contains all the rules used to get the outputs files
|
||||
# built from the input files.
|
||||
# It is included in the main 'build.ninja'.
|
||||
|
||||
# =============================================================================
|
||||
# Project: saqut
|
||||
# Configurations: Debug
|
||||
# =============================================================================
|
||||
# =============================================================================
|
||||
|
||||
#############################################
|
||||
# Rule for generating CXX dependencies.
|
||||
|
||||
rule CXX_SCAN__saqut_Debug
|
||||
depfile = $DEP_FILE
|
||||
command = /usr/bin/c++ $DEFINES $INCLUDES $FLAGS -E -x c++ $in -MT $DYNDEP_INTERMEDIATE_FILE -MD -MF $DEP_FILE -fmodules-ts -fdeps-file=$DYNDEP_INTERMEDIATE_FILE -fdeps-target=$OBJ_FILE -fdeps-format=p1689r5 -o $PREPROCESSED_OUTPUT_FILE
|
||||
description = Scanning $in for CXX dependencies
|
||||
|
||||
|
||||
#############################################
|
||||
# Rule to generate ninja dyndep files for CXX.
|
||||
|
||||
rule CXX_DYNDEP__saqut_Debug
|
||||
command = /usr/bin/cmake -E cmake_ninja_dyndep --tdi=CMakeFiles/saqut.dir/CXXDependInfo.json --lang=CXX --modmapfmt=gcc --dd=$out @$out.rsp
|
||||
description = Generating CXX dyndep file $out
|
||||
rspfile = $out.rsp
|
||||
rspfile_content = $in
|
||||
restat = 1
|
||||
|
||||
|
||||
#############################################
|
||||
# Rule for compiling CXX files.
|
||||
|
||||
rule CXX_COMPILER__saqut_scanned_Debug
|
||||
depfile = $DEP_FILE
|
||||
deps = gcc
|
||||
command = ${LAUNCHER}${CODE_CHECK}/usr/bin/c++ $DEFINES $INCLUDES $FLAGS -MD -MT $out -MF $DEP_FILE -fmodules-ts -fmodule-mapper=$DYNDEP_MODULE_MAP_FILE -MD -fdeps-format=p1689r5 -x c++ -o $out -c $in
|
||||
description = Building CXX object $out
|
||||
|
||||
|
||||
#############################################
|
||||
# Rule for compiling CXX files.
|
||||
|
||||
rule CXX_COMPILER__saqut_unscanned_Debug
|
||||
depfile = $DEP_FILE
|
||||
deps = gcc
|
||||
command = ${LAUNCHER}${CODE_CHECK}/usr/bin/c++ $DEFINES $INCLUDES $FLAGS -MD -MT $out -MF $DEP_FILE -o $out -c $in
|
||||
description = Building CXX object $out
|
||||
|
||||
|
||||
#############################################
|
||||
# Rule for linking CXX executable.
|
||||
|
||||
rule CXX_EXECUTABLE_LINKER__saqut_Debug
|
||||
depfile = $DEP_FILE
|
||||
deps = gcc
|
||||
command = $PRE_LINK && /usr/bin/c++ $FLAGS $LINK_FLAGS $in -o $TARGET_FILE $LINK_PATH $LINK_LIBRARIES && $POST_BUILD
|
||||
description = Linking CXX executable $TARGET_FILE
|
||||
restat = $RESTAT
|
||||
|
||||
|
||||
#############################################
|
||||
# Rule for running custom commands.
|
||||
|
||||
rule CUSTOM_COMMAND
|
||||
command = $COMMAND
|
||||
description = $DESC
|
||||
|
||||
|
||||
#############################################
|
||||
# Rule for re-running cmake.
|
||||
|
||||
rule RERUN_CMAKE
|
||||
command = /usr/bin/cmake --regenerate-during-build -S/home/saqut/Masaüstü/saqutcompiler -B/home/saqut/Masaüstü/saqutcompiler/build
|
||||
description = Re-running CMake...
|
||||
generator = 1
|
||||
|
||||
|
||||
#############################################
|
||||
# Rule for cleaning all built files.
|
||||
|
||||
rule CLEAN
|
||||
command = /usr/bin/ninja $FILE_ARG -t clean $TARGETS
|
||||
description = Cleaning all built files...
|
||||
|
||||
|
||||
#############################################
|
||||
# Rule for printing all primary targets available.
|
||||
|
||||
rule HELP
|
||||
command = /usr/bin/ninja -t targets
|
||||
description = All primary targets available:
|
||||
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
# CMAKE generated file: DO NOT EDIT!
|
||||
# Generated by "Ninja" Generator, CMake Version 4.3
|
||||
|
||||
# This file contains all the build statements describing the
|
||||
# compilation DAG.
|
||||
|
||||
# =============================================================================
|
||||
# Write statements declared in CMakeLists.txt:
|
||||
#
|
||||
# Which is the root file.
|
||||
# =============================================================================
|
||||
|
||||
# =============================================================================
|
||||
# Project: saqut
|
||||
# Configurations: Debug
|
||||
# =============================================================================
|
||||
|
||||
#############################################
|
||||
# Minimal version of Ninja required by this file
|
||||
|
||||
ninja_required_version = 1.5
|
||||
|
||||
|
||||
#############################################
|
||||
# Set configuration variable for custom commands.
|
||||
|
||||
CONFIGURATION = Debug
|
||||
# =============================================================================
|
||||
# Include auxiliary files.
|
||||
|
||||
|
||||
#############################################
|
||||
# Include rules file.
|
||||
|
||||
include CMakeFiles/rules.ninja
|
||||
|
||||
# =============================================================================
|
||||
|
||||
#############################################
|
||||
# Logical path to working directory; prefix for absolute paths.
|
||||
|
||||
cmake_ninja_workdir = /home/saqut/Masaüstü/saqutcompiler/build/
|
||||
# =============================================================================
|
||||
# Object build statements for EXECUTABLE target saqut
|
||||
|
||||
|
||||
#############################################
|
||||
# Order-only phony target for saqut
|
||||
|
||||
build cmake_object_order_depends_target_saqut: phony || .
|
||||
|
||||
build CMakeFiles/saqut.dir/src/core/sourcefile.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/core/sourcefile.cpp || cmake_object_order_depends_target_saqut
|
||||
CONFIG = Debug
|
||||
DEP_FILE = CMakeFiles/saqut.dir/src/core/sourcefile.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/core
|
||||
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
|
||||
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/lexer
|
||||
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||
|
||||
build CMakeFiles/saqut.dir/src/main.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/main.cpp || cmake_object_order_depends_target_saqut
|
||||
CONFIG = Debug
|
||||
DEP_FILE = CMakeFiles/saqut.dir/src/main.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
|
||||
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||
|
||||
build CMakeFiles/saqut.dir/src/parser/nodes/binary_expr.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/binary_expr.cpp || cmake_object_order_depends_target_saqut
|
||||
CONFIG = Debug
|
||||
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/binary_expr.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/parser/nodes
|
||||
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||
|
||||
build CMakeFiles/saqut.dir/src/parser/nodes/declarations.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/declarations.cpp || cmake_object_order_depends_target_saqut
|
||||
CONFIG = Debug
|
||||
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/declarations.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/parser/nodes
|
||||
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||
|
||||
build CMakeFiles/saqut.dir/src/parser/nodes/expressions.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/expressions.cpp || cmake_object_order_depends_target_saqut
|
||||
CONFIG = Debug
|
||||
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/expressions.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/parser/nodes
|
||||
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||
|
||||
build CMakeFiles/saqut.dir/src/parser/nodes/identifier.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/identifier.cpp || cmake_object_order_depends_target_saqut
|
||||
CONFIG = Debug
|
||||
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/identifier.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/parser/nodes
|
||||
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||
|
||||
build CMakeFiles/saqut.dir/src/parser/nodes/literal.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/literal.cpp || cmake_object_order_depends_target_saqut
|
||||
CONFIG = Debug
|
||||
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/literal.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/parser/nodes
|
||||
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||
|
||||
build CMakeFiles/saqut.dir/src/parser/nodes/program.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/program.cpp || cmake_object_order_depends_target_saqut
|
||||
CONFIG = Debug
|
||||
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/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/parser/nodes
|
||||
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||
|
||||
build CMakeFiles/saqut.dir/src/parser/nodes/statements.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/nodes/statements.cpp || cmake_object_order_depends_target_saqut
|
||||
CONFIG = Debug
|
||||
DEP_FILE = CMakeFiles/saqut.dir/src/parser/nodes/statements.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/parser/nodes
|
||||
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||
|
||||
build CMakeFiles/saqut.dir/src/parser/parser.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/parser/parser.cpp || cmake_object_order_depends_target_saqut
|
||||
CONFIG = Debug
|
||||
DEP_FILE = CMakeFiles/saqut.dir/src/parser/parser.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/parser
|
||||
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||
|
||||
build CMakeFiles/saqut.dir/src/tokenizer/tokenizer.cpp.o: CXX_COMPILER__saqut_unscanned_Debug /home/saqut/Masaüstü/saqutcompiler/src/tokenizer/tokenizer.cpp || cmake_object_order_depends_target_saqut
|
||||
CONFIG = Debug
|
||||
DEP_FILE = CMakeFiles/saqut.dir/src/tokenizer/tokenizer.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/tokenizer
|
||||
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Link build statements for EXECUTABLE target saqut
|
||||
|
||||
|
||||
#############################################
|
||||
# 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/tokenizer/tokenizer.cpp.o
|
||||
CONFIG = Debug
|
||||
DEP_FILE = CMakeFiles/saqut.dir/link.d
|
||||
FLAGS = -g
|
||||
LINK_FLAGS = -Wl,--dependency-file=CMakeFiles/saqut.dir/link.d
|
||||
OBJECT_DIR = CMakeFiles/saqut.dir
|
||||
POST_BUILD = :
|
||||
PRE_LINK = :
|
||||
TARGET_FILE = saqut
|
||||
TARGET_PDB = saqut.dbg
|
||||
TARGET_SUPPORT_DIR = CMakeFiles/saqut.dir
|
||||
|
||||
|
||||
#############################################
|
||||
# Utility command for edit_cache
|
||||
|
||||
build CMakeFiles/edit_cache.util: CUSTOM_COMMAND
|
||||
COMMAND = cd /home/saqut/Masaüstü/saqutcompiler/build && /usr/bin/ccmake -S/home/saqut/Masaüstü/saqutcompiler -B/home/saqut/Masaüstü/saqutcompiler/build
|
||||
DESC = Running CMake cache editor...
|
||||
pool = console
|
||||
restat = 1
|
||||
|
||||
build edit_cache: phony CMakeFiles/edit_cache.util
|
||||
|
||||
|
||||
#############################################
|
||||
# Utility command for rebuild_cache
|
||||
|
||||
build CMakeFiles/rebuild_cache.util: CUSTOM_COMMAND
|
||||
COMMAND = cd /home/saqut/Masaüstü/saqutcompiler/build && /usr/bin/cmake --regenerate-during-build -S/home/saqut/Masaüstü/saqutcompiler -B/home/saqut/Masaüstü/saqutcompiler/build
|
||||
DESC = Running CMake to regenerate build system...
|
||||
pool = console
|
||||
restat = 1
|
||||
|
||||
build rebuild_cache: phony CMakeFiles/rebuild_cache.util
|
||||
|
||||
# =============================================================================
|
||||
# Target aliases.
|
||||
|
||||
# =============================================================================
|
||||
# Folder targets.
|
||||
|
||||
# =============================================================================
|
||||
|
||||
#############################################
|
||||
# Folder: /home/saqut/Masaüstü/saqutcompiler/build
|
||||
|
||||
build all: phony saqut
|
||||
|
||||
# =============================================================================
|
||||
# Built-in targets
|
||||
|
||||
|
||||
#############################################
|
||||
# Re-run CMake if any of its inputs changed.
|
||||
|
||||
build build.ninja /home/saqut/Masaüstü/saqutcompiler/build/cmake_install.cmake: RERUN_CMAKE | /home/saqut/Masaüstü/saqutcompiler/CMakeLists.txt /usr/share/cmake/Modules/CMakeCXXInformation.cmake /usr/share/cmake/Modules/CMakeCommonLanguageInclude.cmake /usr/share/cmake/Modules/CMakeGenericSystem.cmake /usr/share/cmake/Modules/CMakeInitializeConfigs.cmake /usr/share/cmake/Modules/CMakeLanguageInformation.cmake /usr/share/cmake/Modules/CMakeSystemSpecificInformation.cmake /usr/share/cmake/Modules/CMakeSystemSpecificInitialize.cmake /usr/share/cmake/Modules/Compiler/CMakeCommonCompilerMacros.cmake /usr/share/cmake/Modules/Compiler/GNU-CXX.cmake /usr/share/cmake/Modules/Compiler/GNU.cmake /usr/share/cmake/Modules/Internal/CMakeCXXLinkerInformation.cmake /usr/share/cmake/Modules/Internal/CMakeCommonLinkerInformation.cmake /usr/share/cmake/Modules/Linker/GNU-CXX.cmake /usr/share/cmake/Modules/Linker/GNU.cmake /usr/share/cmake/Modules/Platform/Linker/GNU.cmake /usr/share/cmake/Modules/Platform/Linker/Linux-GNU-CXX.cmake /usr/share/cmake/Modules/Platform/Linker/Linux-GNU.cmake /usr/share/cmake/Modules/Platform/Linux-GNU-CXX.cmake /usr/share/cmake/Modules/Platform/Linux-GNU.cmake /usr/share/cmake/Modules/Platform/Linux-Initialize.cmake /usr/share/cmake/Modules/Platform/Linux.cmake /usr/share/cmake/Modules/Platform/UnixPaths.cmake CMakeCache.txt CMakeFiles/4.3.2/CMakeCXXCompiler.cmake CMakeFiles/4.3.2/CMakeSystem.cmake
|
||||
pool = console
|
||||
|
||||
|
||||
#############################################
|
||||
# A missing CMake input file is not an error.
|
||||
|
||||
build /home/saqut/Masaüstü/saqutcompiler/CMakeLists.txt /usr/share/cmake/Modules/CMakeCXXInformation.cmake /usr/share/cmake/Modules/CMakeCommonLanguageInclude.cmake /usr/share/cmake/Modules/CMakeGenericSystem.cmake /usr/share/cmake/Modules/CMakeInitializeConfigs.cmake /usr/share/cmake/Modules/CMakeLanguageInformation.cmake /usr/share/cmake/Modules/CMakeSystemSpecificInformation.cmake /usr/share/cmake/Modules/CMakeSystemSpecificInitialize.cmake /usr/share/cmake/Modules/Compiler/CMakeCommonCompilerMacros.cmake /usr/share/cmake/Modules/Compiler/GNU-CXX.cmake /usr/share/cmake/Modules/Compiler/GNU.cmake /usr/share/cmake/Modules/Internal/CMakeCXXLinkerInformation.cmake /usr/share/cmake/Modules/Internal/CMakeCommonLinkerInformation.cmake /usr/share/cmake/Modules/Linker/GNU-CXX.cmake /usr/share/cmake/Modules/Linker/GNU.cmake /usr/share/cmake/Modules/Platform/Linker/GNU.cmake /usr/share/cmake/Modules/Platform/Linker/Linux-GNU-CXX.cmake /usr/share/cmake/Modules/Platform/Linker/Linux-GNU.cmake /usr/share/cmake/Modules/Platform/Linux-GNU-CXX.cmake /usr/share/cmake/Modules/Platform/Linux-GNU.cmake /usr/share/cmake/Modules/Platform/Linux-Initialize.cmake /usr/share/cmake/Modules/Platform/Linux.cmake /usr/share/cmake/Modules/Platform/UnixPaths.cmake CMakeCache.txt CMakeFiles/4.3.2/CMakeCXXCompiler.cmake CMakeFiles/4.3.2/CMakeSystem.cmake: phony
|
||||
|
||||
|
||||
#############################################
|
||||
# Clean all the built files.
|
||||
|
||||
build clean: CLEAN
|
||||
|
||||
|
||||
#############################################
|
||||
# Print all primary targets available.
|
||||
|
||||
build help: HELP
|
||||
|
||||
|
||||
#############################################
|
||||
# Make the all target the default.
|
||||
|
||||
default all
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
# Install script for directory: /home/saqut/Masaüstü/saqutcompiler
|
||||
|
||||
# Set the install prefix
|
||||
if(NOT DEFINED CMAKE_INSTALL_PREFIX)
|
||||
set(CMAKE_INSTALL_PREFIX "/usr/local")
|
||||
endif()
|
||||
string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||
|
||||
# Set the install configuration name.
|
||||
if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
|
||||
if(BUILD_TYPE)
|
||||
string(REGEX REPLACE "^[^A-Za-z0-9_]+" ""
|
||||
CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}")
|
||||
else()
|
||||
set(CMAKE_INSTALL_CONFIG_NAME "Debug")
|
||||
endif()
|
||||
message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"")
|
||||
endif()
|
||||
|
||||
# Set the component getting installed.
|
||||
if(NOT CMAKE_INSTALL_COMPONENT)
|
||||
if(COMPONENT)
|
||||
message(STATUS "Install component: \"${COMPONENT}\"")
|
||||
set(CMAKE_INSTALL_COMPONENT "${COMPONENT}")
|
||||
else()
|
||||
set(CMAKE_INSTALL_COMPONENT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Install shared libraries without execute permission?
|
||||
if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)
|
||||
set(CMAKE_INSTALL_SO_NO_EXE "0")
|
||||
endif()
|
||||
|
||||
# Is this installation the result of a crosscompile?
|
||||
if(NOT DEFINED CMAKE_CROSSCOMPILING)
|
||||
set(CMAKE_CROSSCOMPILING "FALSE")
|
||||
endif()
|
||||
|
||||
# Set path to fallback-tool for dependency-resolution.
|
||||
if(NOT DEFINED CMAKE_OBJDUMP)
|
||||
set(CMAKE_OBJDUMP "/usr/bin/objdump")
|
||||
endif()
|
||||
|
||||
string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT
|
||||
"${CMAKE_INSTALL_MANIFEST_FILES}")
|
||||
if(CMAKE_INSTALL_LOCAL_ONLY)
|
||||
file(WRITE "/home/saqut/Masaüstü/saqutcompiler/build/install_local_manifest.txt"
|
||||
"${CMAKE_INSTALL_MANIFEST_CONTENT}")
|
||||
endif()
|
||||
if(CMAKE_INSTALL_COMPONENT)
|
||||
if(CMAKE_INSTALL_COMPONENT MATCHES "^[a-zA-Z0-9_.+-]+$")
|
||||
set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt")
|
||||
else()
|
||||
string(MD5 CMAKE_INST_COMP_HASH "${CMAKE_INSTALL_COMPONENT}")
|
||||
set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INST_COMP_HASH}.txt")
|
||||
unset(CMAKE_INST_COMP_HASH)
|
||||
endif()
|
||||
else()
|
||||
set(CMAKE_INSTALL_MANIFEST "install_manifest.txt")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_INSTALL_LOCAL_ONLY)
|
||||
file(WRITE "/home/saqut/Masaüstü/saqutcompiler/build/${CMAKE_INSTALL_MANIFEST}"
|
||||
"${CMAKE_INSTALL_MANIFEST_CONTENT}")
|
||||
endif()
|
||||
|
|
@ -0,0 +1,880 @@
|
|||
# Complete saQut Compiler Issue List (English)
|
||||
|
||||
Copy each issue's title and body directly into Gitea.
|
||||
Issues are ordered by stage, from most urgent to long-term.
|
||||
|
||||
---
|
||||
|
||||
## Aşama 0: Metadata and Location Tracking
|
||||
|
||||
### Issue 0.1 — SourceFile and SourceLocation implementation
|
||||
|
||||
**Title:** Aşama 0.1 — Implement SourceFile and SourceLocation classes
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Create the foundational metadata system so every token and AST node knows its exact origin (file, line, column, offset).
|
||||
|
||||
**Files to create/modify:**
|
||||
- `src/core/location.hpp` — new file
|
||||
- `src/core/sourcefile.hpp` — new file
|
||||
|
||||
**Requirements:**
|
||||
- `SourceLocation` struct with fields: `filePath` (string), `line` (int), `column` (int), `offset` (int).
|
||||
- `SourceFile` class that stores the full source text and a precomputed vector of line-start offsets. Provides `offsetToLocation(int offset) -> SourceLocation` using binary search (O(log n)).
|
||||
- `SourceFile` constructor takes file path and source text; computes line offsets in one pass (O(n)).
|
||||
- Both classes live in the `src/core/` directory.
|
||||
|
||||
**Success criteria:**
|
||||
- Given a source string and offset, `offsetToLocation` returns correct line and column.
|
||||
- Binary search is used, not linear scan.
|
||||
|
||||
---
|
||||
|
||||
### Issue 0.2 — Add location tracking to Lexer
|
||||
|
||||
**Title:** Aşama 0.2 — Add line/column tracking to Lexer
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Lexer updates current line and column on every `nextChar()` call.
|
||||
|
||||
**Files to modify:**
|
||||
- `src/lexer/lexer.hpp`
|
||||
|
||||
**Requirements:**
|
||||
- Add `int currentLine`, `int currentColumn` private fields to `Lexer`.
|
||||
- On `nextChar()`: if character is `\n`, increment line and reset column; otherwise increment column.
|
||||
- Add `SourceLocation getLocation()` method returning current position.
|
||||
- Initialize line=1, column=1 in `setText()`.
|
||||
- Modify `INumber` struct to include `SourceLocation startLoc` and `SourceLocation endLoc` (or keep start/end offsets and add location separately — prefer using `SourceLocation` fields).
|
||||
|
||||
**Success criteria:**
|
||||
- After lexing any source, calling `getLocation()` returns correct line and column.
|
||||
- `INumber` carries source location info.
|
||||
|
||||
---
|
||||
|
||||
### Issue 0.3 — Add SourceLocation to Token base class
|
||||
|
||||
**Title:** Aşama 0.3 — Add SourceLocation to all Token types
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Every token produced by the Tokenizer carries its SourceLocation.
|
||||
|
||||
**Files to modify:**
|
||||
- `src/tokenizer/token.hpp`
|
||||
|
||||
**Requirements:**
|
||||
- Add `SourceLocation loc` field to the base `Token` class.
|
||||
- Remove or deprecate `int start, int end` fields (replace with `loc` data).
|
||||
- Update `StringToken`, `NumberToken`, `IdentifierToken` if they reference start/end directly.
|
||||
- Ensure all token constructors initialize `loc`.
|
||||
|
||||
**Success criteria:**
|
||||
- After tokenizing, every token has a valid SourceLocation.
|
||||
- Old start/end offsets are derivable from SourceLocation if needed (but location is primary).
|
||||
|
||||
---
|
||||
|
||||
### Issue 0.4 — Add SourceLocation to ASTNode base class
|
||||
|
||||
**Title:** Aşama 0.4 — Add SourceLocation to all AST nodes
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Every AST node knows its originating source location.
|
||||
|
||||
**Files to modify:**
|
||||
- `src/parser/ast.hpp`
|
||||
|
||||
**Requirements:**
|
||||
- Add `SourceLocation loc` field to `ASTNode` base class.
|
||||
- Optionally add `SourceLocation endLoc` for range.
|
||||
- Parser must set `loc` when creating AST nodes from tokens.
|
||||
- Update `toJson()` and `log()` methods to include location info.
|
||||
|
||||
**Success criteria:**
|
||||
- JSON output includes `"location": {"file": "...", "line": N, "column": M}` for every node.
|
||||
- Log output shows location when available.
|
||||
|
||||
---
|
||||
|
||||
## Aşama 1: CLI and REPL Mode
|
||||
|
||||
### Issue 1.1 — Implement REPL mode with readline support
|
||||
|
||||
**Title:** Aşama 1.1 — Implement REPL mode (`saqut` without arguments)
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Running `saqut` without arguments enters an interactive REPL loop.
|
||||
|
||||
**Files to create/modify:**
|
||||
- `src/cli/repl.hpp` — new file
|
||||
- `src/main.cpp` — modify to detect no-args and launch REPL
|
||||
|
||||
**Requirements:**
|
||||
- REPL prompt: `> `
|
||||
- Each line is parsed, evaluated, and the result printed.
|
||||
- `.ast` command prints the AST of the last expression.
|
||||
- `.tokens` command prints the token list of the last expression.
|
||||
- `.symbols` command prints the current symbol table.
|
||||
- `.exit` or `.quit` exits the REPL.
|
||||
- Multi-line input support: when a block is started (`{`), keep reading until `}`.
|
||||
|
||||
**Success criteria:**
|
||||
- `./saqut` launches REPL.
|
||||
- `.ast`, `.tokens`, `.symbols` work correctly.
|
||||
- Multi-line input accumulates until balanced braces.
|
||||
|
||||
---
|
||||
|
||||
### Issue 1.2 — Implement stdin mode (`saqut -`)
|
||||
|
||||
**Title:** Aşama 1.2 — Implement stdin reading mode
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** `saqut -` reads source code from standard input.
|
||||
|
||||
**Files to modify:**
|
||||
- `src/cli/args.hpp`
|
||||
|
||||
**Requirements:**
|
||||
- When `-` is passed as positional argument, read all stdin until EOF.
|
||||
- Works with all commands: `saqut run -`, `saqut tokens -`, etc.
|
||||
- Remove the current "TODO" stub and implement fully.
|
||||
|
||||
**Success criteria:**
|
||||
- `echo "int main() { return 42; }" | ./saqut run -` works.
|
||||
- `cat file.sqt | ./saqut tokens -` works.
|
||||
|
||||
---
|
||||
|
||||
### Issue 1.3 — Implement output file support for all commands
|
||||
|
||||
**Title:** Aşama 1.3 — Support `-o/--output` flag for all commands
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** All CLI commands respect `-o outputfile` to write results to a file instead of stdout.
|
||||
|
||||
**Files to modify:**
|
||||
- `src/cli/commands/run.hpp`
|
||||
- `src/cli/commands/tokens.hpp`
|
||||
- `src/cli/commands/symbols.hpp`
|
||||
|
||||
**Requirements:**
|
||||
- `cmdRun`, `cmdTokens`, `cmdSymbols` already partially support `-o`; ensure all do.
|
||||
- If `-o` is provided, write output to the specified file; otherwise stdout.
|
||||
- Handle file open errors gracefully.
|
||||
|
||||
**Success criteria:**
|
||||
- `saqut tokens source.sqt -o tokens.txt` writes to file.
|
||||
- `saqut symbols source.sqt --output=symbols.json` writes JSON to file.
|
||||
|
||||
---
|
||||
|
||||
## Aşama 2: AST — Memory Monster
|
||||
|
||||
### Issue 2.1 — Migrate to unique_ptr for AST-owned tokens
|
||||
|
||||
**Title:** Aşama 2.1 — Use std::unique_ptr for token ownership in AST
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** AST nodes own their tokens via `std::unique_ptr`, eliminating memory leaks.
|
||||
|
||||
**Files to modify:**
|
||||
- `src/parser/ast.hpp`
|
||||
- `src/parser/parser.hpp`
|
||||
- `src/parser/token.hpp`
|
||||
|
||||
**Requirements:**
|
||||
- `ParserToken::token` changes from `Token*` to `std::unique_ptr<Token>`.
|
||||
- All AST nodes that store a `ParserToken` or `Token*` must use `std::unique_ptr`.
|
||||
- Parser transfers ownership when creating nodes.
|
||||
- Remove manual `delete` calls on tokens (they are now owned by AST nodes).
|
||||
|
||||
**Success criteria:**
|
||||
- No memory leaks when parsing and deleting AST.
|
||||
- Valgrind/ASan reports zero leaks on test cases.
|
||||
|
||||
---
|
||||
|
||||
### Issue 2.2 — Implement ASTNode::getSourceText()
|
||||
|
||||
**Title:** Aşama 2.2 — Add getSourceText() and getSourceRange() to ASTNode
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Given any AST node, retrieve the exact source code substring it represents.
|
||||
|
||||
**Files to modify:**
|
||||
- `src/parser/ast.hpp`
|
||||
- `src/core/sourcefile.hpp`
|
||||
|
||||
**Requirements:**
|
||||
- `ASTNode::getSourceText()` returns `std::string` — the original source code for this node.
|
||||
- `ASTNode::getSourceRange()` returns `std::pair<SourceLocation, SourceLocation>` (start and end).
|
||||
- Requires AST nodes to store a reference to the `SourceFile` (or the full source text).
|
||||
- This powers future rich error messages with `^^^^` source highlighting.
|
||||
|
||||
**Success criteria:**
|
||||
- For a BinaryExpression node representing `a + b`, `getSourceText()` returns `"a + b"`.
|
||||
- For an IfStatement, returns the entire `if (...) { ... }` block text.
|
||||
|
||||
---
|
||||
|
||||
### Issue 2.3 — Implement Graphviz DOT format output
|
||||
|
||||
**Title:** Aşama 2.3 — Add --format=dot for AST visualization
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Export the AST as a Graphviz DOT file for graphical visualization.
|
||||
|
||||
**Files to create/modify:**
|
||||
- `src/format/dot.hpp` — new file
|
||||
- `src/cli/commands/ast.hpp` — modify to support `--format=dot`
|
||||
|
||||
**Requirements:**
|
||||
- Implement `astToDot(ASTNode*) -> std::string` function.
|
||||
- Each node becomes a labeled box.
|
||||
- Parent-child relationships become directed edges.
|
||||
- Node labels show kind and name (e.g., `BinaryExpression +`).
|
||||
- Output is valid DOT format, renderable by `dot -Tpng -o ast.png ast.dot`.
|
||||
|
||||
**Success criteria:**
|
||||
- `saqut ast source.sqt --format=dot -o ast.dot` produces a valid DOT file.
|
||||
- The resulting image shows a readable tree.
|
||||
|
||||
---
|
||||
|
||||
## Aşama 3: Symbol Table
|
||||
|
||||
### Issue 3.1 — Implement Symbol and SymbolTable classes
|
||||
|
||||
**Title:** Aşama 3.1 — Implement Symbol struct and SymbolTable class with nested scopes
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Build a full symbol table with nested scope support.
|
||||
|
||||
**Files to create:**
|
||||
- `src/symbol/symbol.hpp` — new file
|
||||
- `src/symbol/symbol_table.hpp` — new file
|
||||
|
||||
**Requirements:**
|
||||
|
||||
`Symbol` struct:
|
||||
- `name` (string)
|
||||
- `kind` (enum: Variable, Function, Parameter, Type, Struct)
|
||||
- `type` (Type* or string for now)
|
||||
- `definitionLoc` (SourceLocation)
|
||||
- `references` (vector of SourceLocation)
|
||||
- `scope` (pointer to parent scope or scope level)
|
||||
- `metadata` (optional map<string,string>)
|
||||
|
||||
`SymbolTable` class:
|
||||
- Nested scope stack: `enterScope()`, `exitScope()`.
|
||||
- `define(Symbol) -> bool` (returns false on duplicate in same scope).
|
||||
- `resolve(name) -> Symbol*` (searches innermost to outermost).
|
||||
- `addReference(name, location)` (appends to symbol's reference list).
|
||||
- `getAllSymbols() -> vector<Symbol*>` (flat list of all symbols in all scopes).
|
||||
- `toJson() -> string` for serialization.
|
||||
|
||||
**Success criteria:**
|
||||
- Nested scopes work: variable in inner scope shadows outer.
|
||||
- Duplicate definition in same scope returns false.
|
||||
- resolve finds symbols across scope boundaries.
|
||||
|
||||
---
|
||||
|
||||
### Issue 3.2 — Implement SymbolCollector AST walker
|
||||
|
||||
**Title:** Aşama 3.2 — Implement SymbolCollector that populates SymbolTable from AST
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Walk the AST and populate the SymbolTable with all definitions and references.
|
||||
|
||||
**Files to create/modify:**
|
||||
- `src/symbol/symbol_collector.hpp` — new file
|
||||
- Replace or refactor the simple `collectSymbolsRecursive` in `src/json.hpp`
|
||||
|
||||
**Requirements:**
|
||||
- `SymbolCollector` class with method `collect(ASTNode* root, SymbolTable* table)`.
|
||||
- Walks all AST node types (Program, FunctionDecl, VariableDecl, Block, etc.).
|
||||
- Calls `table->define()` for declarations.
|
||||
- Calls `table->addReference()` for identifier usages.
|
||||
- Handles all AST node types currently in `ast.hpp` (19 types).
|
||||
- Replaces the ad-hoc `SymbolEntry` vector with proper SymbolTable population.
|
||||
|
||||
**Success criteria:**
|
||||
- After parsing `source.sqt`, SymbolTable contains all functions and variables.
|
||||
- References are collected for each symbol.
|
||||
- `saqut symbols source.sqt` shows the enriched data.
|
||||
|
||||
---
|
||||
|
||||
### Issue 3.3 — Semantic error: undefined variable
|
||||
|
||||
**Title:** Aşama 3.3 — Report "undefined variable" errors using SymbolTable
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** When a variable is used before definition, report a clear error with location.
|
||||
|
||||
**Files to modify:**
|
||||
- `src/symbol/symbol_collector.hpp`
|
||||
- `src/core/diagnostic.hpp` — new file (or extend existing error reporting)
|
||||
|
||||
**Requirements:**
|
||||
- During symbol collection, when an identifier reference has no matching `resolve()`, emit a diagnostic.
|
||||
- Diagnostic includes: error level, SourceLocation, message, optional hint.
|
||||
- `"Variable 'x' is not defined. Did you mean 'xy'?"` if a close match exists (Levenshtein distance < 3).
|
||||
- Diagnostic system supports multiple errors (don't stop at first).
|
||||
|
||||
**Success criteria:**
|
||||
- `int main() { return x; }` reports: `Error: 'x' is not defined at line 1 column 19`.
|
||||
- Typos suggest close matches.
|
||||
|
||||
---
|
||||
|
||||
### Issue 3.4 — Semantic error: duplicate definition
|
||||
|
||||
**Title:** Aşama 3.4 — Report "duplicate definition" errors
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** When a symbol is defined twice in the same scope, report an error.
|
||||
|
||||
**Files to modify:**
|
||||
- `src/symbol/symbol_table.hpp`
|
||||
- `src/symbol/symbol_collector.hpp`
|
||||
|
||||
**Requirements:**
|
||||
- `SymbolTable::define()` returns false on duplicate; collector emits diagnostic.
|
||||
- Error message: `"Function 'main' is already defined. Previous definition at line X."`
|
||||
- Works for variables, functions, structs.
|
||||
|
||||
**Success criteria:**
|
||||
- Two `int main()` definitions produce an error.
|
||||
- Two `int x` in the same block produce an error.
|
||||
- Shadowing in nested scopes is allowed (not an error).
|
||||
|
||||
---
|
||||
|
||||
## Aşama 4: Feature Toggle System
|
||||
|
||||
### Issue 4.1 — Implement CompilerConfig struct and flag parsing
|
||||
|
||||
**Title:** Aşama 4.1 — Implement CompilerConfig struct and --disable-* flags
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Create a configuration system that controls language features at compile time.
|
||||
|
||||
**Files to create/modify:**
|
||||
- `src/core/config.hpp` — new file
|
||||
- `src/cli/args.hpp` — extend to parse feature flags
|
||||
|
||||
**Requirements:**
|
||||
|
||||
`CompilerConfig` struct with boolean fields:
|
||||
- `enableWhile`, `enableFor`, `enableDoWhile`, `enableSwitch`
|
||||
- `enableClass`, `enableInterface`, `enableEnum`
|
||||
- `enableTernary`, `enablePostfix`, `enableUnary`
|
||||
- `optConstantFolding`, `optDeadCodeElim`
|
||||
- `outputFormat` (text/json/dot)
|
||||
- `mode` (run/tokens/ast/symbols/compile/transpile)
|
||||
|
||||
CLI flags:
|
||||
- `--disable-while` sets `enableWhile = false`
|
||||
- `--disable-for` sets `enableFor = false`
|
||||
- `--opt-all` enables all optimizations
|
||||
- `--opt-none` disables all optimizations
|
||||
|
||||
**Success criteria:**
|
||||
- `--disable-while` flag is parsed into CompilerConfig.
|
||||
- Config is passed through to Tokenizer and Parser.
|
||||
|
||||
---
|
||||
|
||||
### Issue 4.2 — Implement keyword toggling in Tokenizer
|
||||
|
||||
**Title:** Aşama 4.2 — Disable keywords based on CompilerConfig
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** When a keyword is disabled in config, the Tokenizer treats it as an identifier.
|
||||
|
||||
**Files to modify:**
|
||||
- `src/tokenizer/tokenizer.hpp`
|
||||
|
||||
**Requirements:**
|
||||
- Tokenizer receives a `CompilerConfig` reference (or copy).
|
||||
- Before matching keywords, check config flags.
|
||||
- Disabled keywords are skipped in keyword matching; they fall through to identifier.
|
||||
- Example: `--disable-while` means `while` becomes a regular identifier.
|
||||
|
||||
**Success criteria:**
|
||||
- With `--disable-while`, `while (true) {}` tokenizes `while` as identifier.
|
||||
- Parser then does not parse it as a while statement (falls through to expression).
|
||||
|
||||
---
|
||||
|
||||
### Issue 4.3 — Implement optimization pass interface
|
||||
|
||||
**Title:** Aşama 4.3 — Implement OptimizationPass interface and OptimizationManager
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Create a framework for pluggable optimization passes.
|
||||
|
||||
**Files to create:**
|
||||
- `src/opt/optimization_pass.hpp` — new file
|
||||
- `src/opt/optimization_manager.hpp` — new file
|
||||
|
||||
**Requirements:**
|
||||
- `OptimizationPass` abstract class with `run(ASTNode* root, SymbolTable* table) -> bool` method.
|
||||
- `OptimizationManager` holds a list of passes, runs them in order based on CompilerConfig.
|
||||
- Initially, two passes: `ConstantFoldingPass` and `DeadCodeEliminationPass` (empty implementations for now, will be filled in Aşama 6).
|
||||
- `--skip-constant-folding` flag skips that pass.
|
||||
|
||||
**Success criteria:**
|
||||
- OptimizationManager runs (even if passes are no-ops for now).
|
||||
- Feature flags control which passes execute.
|
||||
|
||||
---
|
||||
|
||||
## Aşama 5: Backend — Execution
|
||||
|
||||
### Issue 5.1 — Strengthen IR with control flow and function opcodes
|
||||
|
||||
**Title:** Aşama 5.1 — Extend IR with control flow, function, and memory opcodes
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** IR must support control flow (branch, jump, compare), function calls, and memory operations before any backend can work.
|
||||
|
||||
**Files to modify:**
|
||||
- `src/ir/ir.hpp`
|
||||
|
||||
**Requirements:**
|
||||
|
||||
New opcodes:
|
||||
- Control flow: `cmp`, `br`, `br_eq`, `br_lt`, `br_gt`, `jmp`
|
||||
- Function: `call`, `ret`, `param`
|
||||
- Memory: `load`, `store`, `alloca`
|
||||
|
||||
Update `IROpData` if needed to support new parameter types (labels for jump targets, function indices).
|
||||
Add a `label` field or a separate `IRLabel` structure for branch targets.
|
||||
|
||||
**Success criteria:**
|
||||
- All opcodes are defined and documented.
|
||||
- IR can represent a simple if-else and a while loop.
|
||||
- IR can represent a function definition with parameters and return.
|
||||
|
||||
---
|
||||
|
||||
### Issue 5.2 — Implement C Transpile Backend
|
||||
|
||||
**Title:** Aşama 5.2 — Implement C transpile backend (`saqut transpile`)
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Convert saQut IR (or AST) to compilable C source code.
|
||||
|
||||
**Files to create:**
|
||||
- `src/backend/c_transpile.hpp` — new file
|
||||
|
||||
**Requirements:**
|
||||
- Reads IR (or AST) and generates equivalent C code.
|
||||
- Handles: variable declarations, binary expressions, if/for/while/do-while, function definitions, return.
|
||||
- Generates readable C with reasonable indentation.
|
||||
- Embed `#line` directives so GCC/Clang error messages point to original `.sqt` files.
|
||||
- `saqut transpile source.sqt -o output.c` command.
|
||||
|
||||
**Success criteria:**
|
||||
- Given `int main() { return 42; }`, generates compilable C code that returns 42.
|
||||
- Generated C compiles with `gcc -Wall -Werror` without warnings.
|
||||
- `saqut compile source.sqt output:prog` compiles and runs correctly.
|
||||
|
||||
---
|
||||
|
||||
### Issue 5.3 — Implement Interpreter (Tree-walk VM)
|
||||
|
||||
**Title:** Aşama 5.3 — Implement interpreter VM (`saqut run` execution)
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Execute saQut programs by walking the AST or interpreting IR directly.
|
||||
|
||||
**Files to create:**
|
||||
- `src/backend/interpreter.hpp` — new file
|
||||
|
||||
**Requirements:**
|
||||
- `Interpreter` class that walks AST and executes.
|
||||
- Supports: variable declaration and assignment, binary/unary expressions, if/else, while/for/do-while, break/continue, return, function calls (after function parameters are implemented).
|
||||
- Stack-based or register-based value storage.
|
||||
- `saqut run source.sqt` executes and prints program result.
|
||||
- REPL mode (from Issue 1.1) uses the interpreter for evaluation.
|
||||
|
||||
**Success criteria:**
|
||||
- `saqut run source.sqt` executes correctly for arithmetic and control flow.
|
||||
- Variables hold values across statements.
|
||||
- Return value propagates correctly.
|
||||
|
||||
---
|
||||
|
||||
## Aşama 6: Optimization
|
||||
|
||||
### Issue 6.1 — Implement Constant Folding pass
|
||||
|
||||
**Title:** Aşama 6.1 — Implement Constant Folding optimization
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Evaluate constant expressions at compile time.
|
||||
|
||||
**Files to create:**
|
||||
- `src/opt/constant_folding.hpp` — new file
|
||||
|
||||
**Requirements:**
|
||||
- Walk AST, find `BinaryExpression` nodes where both operands are Literals.
|
||||
- Compute the result, replace the subtree with a single Literal node.
|
||||
- Handles: `+`, `-`, `*`, `/`, `%` for integers and floats.
|
||||
- Handles unary `-` on literals.
|
||||
- Guard against division by zero (emit warning, skip folding).
|
||||
|
||||
**Success criteria:**
|
||||
- `4 + 5` becomes `9` in the optimized AST.
|
||||
- `x + 0` is NOT folded (x is not constant).
|
||||
- `1 / 0` emits warning, AST unchanged.
|
||||
|
||||
---
|
||||
|
||||
### Issue 6.2 — Implement Dead Code Elimination pass
|
||||
|
||||
**Title:** Aşama 6.2 — Implement Dead Code Elimination
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Remove code that is provably unreachable.
|
||||
|
||||
**Files to create:**
|
||||
- `src/opt/dead_code_elim.hpp` — new file
|
||||
|
||||
**Requirements:**
|
||||
- Remove statements after `return`, `break`, `continue` within the same block.
|
||||
- Remove `if (false)` branches.
|
||||
- Remove `while (false)` bodies.
|
||||
- Remove unused variable declarations (requires SymbolTable reference counting).
|
||||
|
||||
**Success criteria:**
|
||||
- `return; x = 5;` — assignment is removed.
|
||||
- `if (false) { ... }` — entire block removed.
|
||||
- Unused variable `int y = 10;` removed when y has zero references.
|
||||
|
||||
---
|
||||
|
||||
### Issue 6.3 — Implement Null Check and Type Check Elimination
|
||||
|
||||
**Title:** Aşama 6.3 — Implement Null/Type Check Elimination
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Remove redundant null checks and type checks when the compiler can prove they are unnecessary.
|
||||
|
||||
**Files to create:**
|
||||
- `src/opt/null_check_elim.hpp` — new file
|
||||
- `src/opt/type_check_elim.hpp` — new file
|
||||
|
||||
**Requirements:**
|
||||
- Track which variables have been checked for null in the current path.
|
||||
- If a variable was already null-checked (and not reassigned), skip subsequent checks.
|
||||
- Similarly for type checks (`is` expressions).
|
||||
- Requires dataflow analysis within a function body.
|
||||
|
||||
**Success criteria:**
|
||||
- Two consecutive `if (x != null)` — second check eliminated.
|
||||
- `if (x is int) { ... if (x is int) { ... } }` — inner check eliminated.
|
||||
|
||||
---
|
||||
|
||||
## Aşama 7: Test and Performance
|
||||
|
||||
### Issue 7.1 — Set up unit test framework (Google Test)
|
||||
|
||||
**Title:** Aşama 7.1 — Set up Google Test framework and write initial tests
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Create a proper testing infrastructure.
|
||||
|
||||
**Files to create/modify:**
|
||||
- `tests/` directory
|
||||
- `tests/lexer_test.cpp`
|
||||
- `tests/tokenizer_test.cpp`
|
||||
- `tests/parser_test.cpp`
|
||||
- `tests/symbol_test.cpp`
|
||||
- `CMakeLists.txt` or `Makefile` with test target
|
||||
|
||||
**Requirements:**
|
||||
- Use Google Test (download during build or include as submodule).
|
||||
- Initial tests: lexing numbers, tokenizing keywords, parsing simple expressions, symbol collection.
|
||||
- `make test` or `cmake --build . --target test` runs all tests.
|
||||
|
||||
**Success criteria:**
|
||||
- At least 10 passing tests.
|
||||
- CI-ready: tests can be run from command line.
|
||||
- Failures show expected vs actual.
|
||||
|
||||
---
|
||||
|
||||
### Issue 7.2 — Write snapshot tests for AST output
|
||||
|
||||
**Title:** Aşama 7.2 — Snapshot testing for AST/IR/symbol output
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Ensure compiler output is stable across changes.
|
||||
|
||||
**Files to create:**
|
||||
- `tests/snapshots/` directory
|
||||
- Script or C++ test that runs `saqut ast` and compares to stored JSON.
|
||||
|
||||
**Requirements:**
|
||||
- Store known-good JSON output for `source.sqt`, `Final.sqt`.
|
||||
- Test compares current output to snapshot; fails on difference.
|
||||
- Snapshot update mode to regenerate expected files.
|
||||
|
||||
**Success criteria:**
|
||||
- Changes to parser that affect AST structure are caught.
|
||||
- False positives (formatting changes) are manageable.
|
||||
|
||||
---
|
||||
|
||||
### Issue 7.3 — Implement benchmark suite
|
||||
|
||||
**Title:** Aşama 7.3 — Implement benchmark infrastructure (`saqut bench`)
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Measure compiler performance on large inputs.
|
||||
|
||||
**Files to create/modify:**
|
||||
- `src/cli/commands/bench.hpp` — new file
|
||||
- `benchmarks/` directory with test files
|
||||
|
||||
**Requirements:**
|
||||
- `saqut bench` runs a set of benchmark files and reports parse time, token throughput, memory usage.
|
||||
- Warm-up phase to reduce noise.
|
||||
- Output in machine-readable format (JSON) for tracking over time.
|
||||
|
||||
**Success criteria:**
|
||||
- Parse a 10K-line file and report tokens/second.
|
||||
- Memory usage reported in KB/MB.
|
||||
|
||||
---
|
||||
|
||||
## Aşama 8: Advanced Type System
|
||||
|
||||
### Issue 8.1 — Implement Struct type (user-defined types)
|
||||
|
||||
**Title:** Aşama 8.1 — Full struct support: definition, instantiation, field access
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Users can define and use `struct` types.
|
||||
|
||||
**Files to modify:**
|
||||
- `src/parser/parser.hpp`
|
||||
- `src/parser/ast.hpp`
|
||||
- `src/symbol/`
|
||||
- `src/backend/`
|
||||
|
||||
**Requirements:**
|
||||
- `struct Point { int x; int y; }` defines a type.
|
||||
- `Point p;` declares a variable of that type.
|
||||
- `p.x` accesses a field (already partially supported via MemberAccess).
|
||||
- Struct type checking: field must exist, type must match on assignment.
|
||||
- C transpile and interpreter both support structs.
|
||||
|
||||
**Success criteria:**
|
||||
- Struct definition, instantiation, and field access work end-to-end.
|
||||
- Accessing nonexistent field produces clear error.
|
||||
|
||||
---
|
||||
|
||||
### Issue 8.2 — Implement Array and Pointer types
|
||||
|
||||
**Title:** Aşama 8.2 — Implement array and pointer type support
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Support `int[]`, `int*`, array indexing, and pointer arithmetic.
|
||||
|
||||
**Files to modify:**
|
||||
- `src/parser/parser.hpp`
|
||||
- `src/parser/ast.hpp`
|
||||
- `src/symbol/`
|
||||
- `src/backend/`
|
||||
|
||||
**Requirements:**
|
||||
- `int arr[10];` array declaration.
|
||||
- `arr[i]` indexing (already partially supported).
|
||||
- `int* p;` pointer declaration.
|
||||
- `*p` dereference (unary `*` operator).
|
||||
- `&x` address-of operator.
|
||||
- Type checking for pointer/array operations.
|
||||
|
||||
**Success criteria:**
|
||||
- Array declaration and indexing work.
|
||||
- Pointer declaration, assignment, dereference work.
|
||||
- Pointer arithmetic (`p + 1`) works.
|
||||
|
||||
---
|
||||
|
||||
### Issue 8.3 — Implement standard library foundation (lib/std.sqt)
|
||||
|
||||
**Title:** Aşama 8.3 — Create standard library with basic data structures
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Provide built-in data structures: List, Map, Set, Buffer, String utilities.
|
||||
|
||||
**Files to create:**
|
||||
- `lib/std.sqt` — standard library source file
|
||||
- `lib/collections.sqt`, `lib/io.sqt`, `lib/encoding.sqt` — optional modules
|
||||
|
||||
**Requirements:**
|
||||
- `List<T>` — dynamic array with `add`, `get`, `remove`, `size`.
|
||||
- `Map<K,V>` — hash map with `put`, `get`, `contains`, `remove`.
|
||||
- `Set<T>` — hash set.
|
||||
- `Buffer` — byte buffer for binary data.
|
||||
- `String` methods: `split`, `replace`, `substring`, `toUpper`, `toLower`.
|
||||
- All implemented in saQut itself (or native functions exposed to saQut).
|
||||
|
||||
**Success criteria:**
|
||||
- `import std;` makes these types available.
|
||||
- Basic operations work without crashes.
|
||||
|
||||
---
|
||||
|
||||
## Aşama 9: Ecosystem
|
||||
|
||||
### Issue 9.1 — Implement project initialization (`saqut init`)
|
||||
|
||||
**Title:** Aşama 9.1 — Implement `saqut init` project scaffolding
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** `saqut init my-project` creates a standard project directory.
|
||||
|
||||
**Files to create/modify:**
|
||||
- `src/cli/commands/init.hpp` — new file
|
||||
|
||||
**Requirements:**
|
||||
- Creates directory with:
|
||||
- `project.saqut` manifest file (TOML or JSON format).
|
||||
- `src/` directory with `main.sqt`.
|
||||
- `.gitignore` file.
|
||||
- Manifest contains: project name, version, description, author, dependencies (empty initially).
|
||||
|
||||
**Success criteria:**
|
||||
- `saqut init testproj` creates the expected structure.
|
||||
- `saqut run` inside the project directory finds and runs `src/main.sqt`.
|
||||
|
||||
---
|
||||
|
||||
### Issue 9.2 — Implement built-in test framework (`saqut test`)
|
||||
|
||||
**Title:** Aşama 9.2 — Implement built-in test framework
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** `saqut test` discovers and runs test functions in the project.
|
||||
|
||||
**Files to create/modify:**
|
||||
- `src/cli/commands/test.hpp` — new file
|
||||
- `lib/test.sqt` — test framework library
|
||||
|
||||
**Requirements:**
|
||||
- Functions annotated with `#[test]` or named `test_*` are test functions.
|
||||
- `saqut test` runs all tests, reports pass/fail.
|
||||
- `assert(condition)` and `assert_eq(a, b)` built-in functions.
|
||||
- Output in TAP or JUnit XML format for CI integration.
|
||||
|
||||
**Success criteria:**
|
||||
- `saqut test` in a project with test functions runs them.
|
||||
- Failures report file and line number.
|
||||
- Exit code 0 for all pass, non-zero for any failure.
|
||||
|
||||
---
|
||||
|
||||
## Aşama 10: Package Manager
|
||||
|
||||
### Issue 10.1 — Implement package manager foundation (`saqut add`)
|
||||
|
||||
**Title:** Aşama 10.1 — Implement `saqut add` package manager
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** `saqut add <package>` downloads and installs a package dependency.
|
||||
|
||||
**Files to create/modify:**
|
||||
- `src/cli/commands/add.hpp` — new file
|
||||
- `src/package/registry.hpp` — new file
|
||||
- `src/package/resolver.hpp` — new file
|
||||
|
||||
**Requirements:**
|
||||
- Package registry: a central Git repository or simple HTTP server listing available packages.
|
||||
- `saqut add json` adds the `json` package to `project.saqut` dependencies.
|
||||
- Downloads package source into `packages/` directory (or a cache).
|
||||
- `import json;` in source code finds the installed package.
|
||||
- Semantic versioning support (major.minor.patch).
|
||||
|
||||
**Success criteria:**
|
||||
- `saqut add` adds dependency to manifest.
|
||||
- `import` of installed package works.
|
||||
- Version constraints are enforced.
|
||||
|
||||
---
|
||||
|
||||
## Aşama 11: Language Specification
|
||||
|
||||
### Issue 11.1 — Write language specification document
|
||||
|
||||
**Title:** Aşama 11.1 — Write comprehensive language specification
|
||||
|
||||
**Body:**
|
||||
|
||||
**Goal:** Create `docs/lang_spec.md` that fully defines the saQut language.
|
||||
|
||||
**Files to create:**
|
||||
- `docs/lang_spec.md`
|
||||
|
||||
**Requirements:**
|
||||
- Syntax: full grammar in EBNF or similar notation.
|
||||
- Type system: all built-in types, conversion rules, type inference.
|
||||
- Control flow: semantics of if/else, for, while, do-while, break, continue, return.
|
||||
- Memory model: stack vs heap, pointer rules, array layout.
|
||||
- Standard library: function signatures and contracts for all `lib/std.sqt` functions.
|
||||
- Error handling: exception-like or error-return semantics.
|
||||
|
||||
**Success criteria:**
|
||||
- A developer can implement a saQut compiler from only this document.
|
||||
- All implemented features are documented.
|
||||
- Unimplemented features are clearly marked as "planned" or "future."
|
||||
|
||||
---
|
||||
|
||||
> **Total issues: 30**
|
||||
>
|
||||
> **Order:** Start from 0.1 and work sequentially. Each issue is a milestone toward
|
||||
> the next. Dependencies are explicit: later stages require earlier stages complete.
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# PR Açıklaması: Parser ve AST Bileşenlerinin Modülerleştirilmesi
|
||||
|
||||
## Açıklama
|
||||
Bu PR, `src/parser/` dizini altındaki devasa `ast.hpp` ve `parser.hpp` dosyalarını mantıksal parçalara ayırarak kodun okunabilirliğini ve bakımını kolaylaştırmayı amaçlar. Kodun işlevselliği korunmuş, sadece dosya yapısı modüler hale getirilmiştir.
|
||||
|
||||
## Önemli Değişiklikler
|
||||
|
||||
### 1. AST (Soyut Sözdizim Ağacı) Modülerleştirme
|
||||
`ast.hpp` dosyası artık bir **aggregator** (toplayıcı) görevi görüyor ve aşağıdaki yeni dosyalardan bileşenleri dahil ediyor:
|
||||
- `ast_node.hpp`: Temel `ASTNode` sınıfı ve `ASTKind` enum'u.
|
||||
- `ast_expr.hpp`: `LiteralNode`, `BinaryExpressionNode`, `CallExpressionNode` gibi ifade düğümleri.
|
||||
- `ast_stmt.hpp`: `BlockNode`, `IfStatementNode`, `WhileStatementNode` gibi deyim (statement) düğümleri.
|
||||
- `ast_decl.hpp`: `FunctionDeclNode`, `VariableDeclNode`, `StructDeclNode` gibi deklarasyon düğümleri.
|
||||
- `ast_json.hpp`: JSON serileştirme için yardımcı fonksiyonlar (`childrenToJson`, `jsonEscape` vb.).
|
||||
|
||||
### 2. Parser Modülerleştirme
|
||||
Parser mantığı da benzer şekilde parçalara ayrıldı:
|
||||
- `parser_base.hpp`: `Parser` sınıfı tanımı ve üye değişkenleri.
|
||||
- `parser_core.hpp`: Pratt Parser ana döngüsü, NUD ve LED mantığı.
|
||||
- `parser_decl.hpp`: Fonksiyon, değişken ve struct deklarasyonlarının ayrıştırılması.
|
||||
- `parser_stmt.hpp`: Deyimlerin (if, for, while, return vb.) ayrıştırılması.
|
||||
|
||||
## Teknik Avantajlar
|
||||
- **Okunabilirlik:** Binlerce satırlık dosyalar yerine 100-300 satırlık, spesifik görevleri olan dosyalar oluşturuldu.
|
||||
- **Bakım Kolaylığı:** Belirli bir dil özelliği (örn. yeni bir deyim tipi) eklendiğinde hangi dosyanın değiştirileceği artık çok daha net.
|
||||
- **Derleme Hızı:** (Gelecekte) İncremental build süreçlerinde sadece değişen parçaların derlenmesine olanak sağlar.
|
||||
|
||||
## Notlar
|
||||
- Mevcut tüm testler ve `Final.sqt` gibi örnek dosyaların ayrıştırılması sorunsuz çalışmaya devam etmektedir.
|
||||
- Dosya başlıklarındaki DİZİN ve KATMAN bilgileri güncellenmiştir.
|
||||
|
|
@ -0,0 +1,404 @@
|
|||
# saQut Compiler Toolbox — Yol Haritası ve Yapılacaklar
|
||||
|
||||
> **Felsefe**: saQut bir derleyici değil, bir **derleyici alet çantasıdır**.
|
||||
> Amacı en hızlı kodu üretmek, en güvenli sandbox'u kurmak veya en optimize
|
||||
> binary'yi çıkarmak değildir. Amacı, **derleyicinin kendisidir**.
|
||||
>
|
||||
> Her aşama (Lexer, Parser, Optimizer, Compiler) birbirinden bağımsız,
|
||||
> kendi parametreleriyle yönlendirilebilen, gerektiğinde devre dışı
|
||||
> bırakılabilen birer **araç kutusu modülü** olarak tasarlanır.
|
||||
>
|
||||
> Geliştirici, isterse sadece token listesini alır, isterse AST'yi JSON
|
||||
> olarak çeker, isterse sembol tablosunu inceler, isterse kodu çalıştırır.
|
||||
> Bu, ileride LSP sunucusu yazmayı, IDE eklentileri geliştirmeyi ve
|
||||
> kod hiyerarşisi görselleştirmeyi mümkün kılar.
|
||||
|
||||
---
|
||||
|
||||
## Temel Prensipler
|
||||
|
||||
1. **Her aşama bağımsız**: Lexer → Tokenizer → Parser → Optimizer → IR → Backend.
|
||||
Her biri tek başına çalışabilir, kendi parametrelerini alır.
|
||||
|
||||
2. **Her şey metadata**: Her token ve AST düğümü; dosya adı, satır, sütun,
|
||||
karakter offset'i taşır. Debug ve hata mesajları bu veri üzerine kurulur.
|
||||
|
||||
3. **AST bellek canavarı**: Token'lar AST'de yaşar. Kaynak kodun neredeyse
|
||||
birebir izdüşümü AST üzerinde korunur. Hiçbir bilgi atılmaz.
|
||||
|
||||
4. **Sembol tablosu aynı felsefede**: Basit yapı, çok veri. Her sembolün
|
||||
tanımlandığı yer, tipi, kullanıldığı tüm noktalar, scope bilgisi.
|
||||
|
||||
5. **Feature toggle**: Her keyword, her operatör, her optimizasyon adımı
|
||||
açılıp kapatılabilir. `--disable-while`, `--skip-constant-folding`.
|
||||
|
||||
6. **Çoklu çıktı formatı**: Düz metin, JSON, (ileride) HTML/SVG. Tüm ara
|
||||
gösterimler dışa aktarılabilir.
|
||||
|
||||
7. **IR merkezli backend**: Tüm backend'ler (C transpile, QBE, LLVM, JIT)
|
||||
aynı IR'den beslenir. Yeni backend eklemek = yeni bir IR yürüteci yazmak.
|
||||
|
||||
---
|
||||
|
||||
## Aşama 0: Metadata ve Konum Takibi
|
||||
|
||||
> **Hedef**: Her token ve AST düğümü nereden geldiğini bilsin.
|
||||
|
||||
### 0.1 SourceFile Yöneticisi
|
||||
- [ ] `SourceFile` sınıfı: dosya adı, tam metin, satır başı offset'leri
|
||||
- [ ] `SourceLocation` yapısı: `{file, line, column, offset}`
|
||||
- [ ] `offset → (line, column)` dönüşümü (binary search ile O(log n))
|
||||
- [ ] Çoklu dosya desteği (import/include için temel)
|
||||
|
||||
### 0.2 Lexer'a Konum Ekleme
|
||||
- [ ] Lexer her `nextChar()`'da satır/sütun takibi yapsın
|
||||
- [ ] `getLocation()` → mevcut konumu `SourceLocation` olarak döndürsün
|
||||
- [ ] `INumber` yapısına `SourceLocation startLoc, endLoc` eklensin
|
||||
|
||||
### 0.3 Token'lara Konum Ekleme
|
||||
- [ ] `Token` temel sınıfına `SourceLocation loc` eklensin
|
||||
- [ ] Her token oluşturulurken konumu kaydedilsin
|
||||
- [ ] `start`/`end` offset'leri `SourceLocation` ile değiştirilsin
|
||||
|
||||
### 0.4 AST Düğümlerine Konum Ekleme
|
||||
- [ ] `ASTNode` temel sınıfına `SourceLocation loc` eklensin
|
||||
- [ ] Her düğüm oluşturulurken ilgili token'ın konumu kopyalansın
|
||||
|
||||
---
|
||||
|
||||
## Aşama 1: Interpreter (CLI + Dosya)
|
||||
|
||||
> **Hedef**: `saqut` komutu ile REPL veya dosya çalıştırma.
|
||||
|
||||
### 1.1 CLI Altyapısı
|
||||
- [ ] `argc/argv` ile komut satırı argümanları
|
||||
- [ ] `./saqut` → REPL modu (etkileşimli)
|
||||
- [ ] `./saqut file.sqt` → dosyayı çalıştır
|
||||
- [ ] `./saqut --mode=parse file.sqt` → sadece AST üret
|
||||
- [ ] `./saqut --mode=tokens file.sqt` → sadece token listesi
|
||||
- [ ] `./saqut --mode=ir file.sqt` → sadece IR
|
||||
- [ ] `./saqut --mode=symbols file.sqt` → sembol tablosu
|
||||
- [ ] `./saqut --format=json file.sqt` → JSON çıktı
|
||||
|
||||
### 1.2 REPL Modu
|
||||
- [ ] `>` komut istemi
|
||||
- [ ] Her satır ayrı ayrı parse edilir
|
||||
- [ ] `.ast` komutu → son ifadenin AST'sini göster
|
||||
- [ ] `.tokens` komutu → son ifadenin token'larını göster
|
||||
- [ ] `.symbols` komutu → şu ana kadarki sembolleri göster
|
||||
- [ ] `.exit` / `.quit` → çıkış
|
||||
- [ ] Çok satırlı giriş (bloklar için)
|
||||
|
||||
### 1.3 Dosya Modu
|
||||
- [ ] Kaynak dosyayı oku → pipeline'ı çalıştır
|
||||
- [ ] Çıktıyı seçilen formatta bas
|
||||
- [ ] `-o output.json` → dosyaya yaz
|
||||
|
||||
---
|
||||
|
||||
## Aşama 2: AST'yi Bellek Canavarı Yapma
|
||||
|
||||
> **Hedef**: AST, kaynak kodun tam bir izdüşümü olsun.
|
||||
> Hiçbir bilgi kaybolmasın. Token'lar AST'de yaşasın.
|
||||
|
||||
### 2.1 Token Tutma
|
||||
- [ ] AST düğümleri token'ları **sahiplensin** (unique_ptr veya shared_ptr)
|
||||
- [ ] `LiteralNode`, `IdentifierNode` → ilgili token'ı doğrudan tutar
|
||||
- [ ] `BinaryExpressionNode` → operatör token'ını tutar
|
||||
- [ ] Her statement → ilgili keyword token'ını tutar (`if` token'ı, `for` token'ı)
|
||||
|
||||
### 2.2 Kaynak Kod Parçası Erişimi
|
||||
- [ ] `ASTNode::getSourceText()` → bu düğümün kaynak koddaki ham metni
|
||||
- [ ] `ASTNode::getSourceRange()` → başlangıç/bitiş SourceLocation
|
||||
- [ ] Hata mesajlarında kaynak kod parçası gösterimi:
|
||||
```
|
||||
Hata: test.sqt:5:10: 'x' değişkeni tanımlı değil
|
||||
4 | int main() {
|
||||
5 | y = x + 1;
|
||||
| ^
|
||||
6 | }
|
||||
```
|
||||
|
||||
### 2.3 AST Görselleştirme
|
||||
- [ ] `--format=json` → tam AST'yi JSON olarak dök
|
||||
- [ ] `--format=dot` → Graphviz DOT formatı (görsel ağaç)
|
||||
- [ ] `--format=html` → Etkileşimli HTML ağaç görünümü (ileride)
|
||||
- [ ] JSON çıktısı her düğüm için:
|
||||
```json
|
||||
{
|
||||
"kind": "BinaryExpression",
|
||||
"operator": "PLUS",
|
||||
"location": { "file": "test.sqt", "line": 5, "column": 9 },
|
||||
"sourceText": "x + 1",
|
||||
"left": { ... },
|
||||
"right": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Aşama 3: Sembol Tablosu
|
||||
|
||||
> **Hedef**: AST kadar zengin, basit yapılı bir sembol tablosu.
|
||||
> Her sembolün tüm kullanım noktaları, tip bilgisi, scope'u kayıtlı.
|
||||
|
||||
### 3.1 Symbol Sınıfı
|
||||
- [ ] `Symbol` yapısı:
|
||||
- `name`: sembol adı
|
||||
- `kind`: variable, function, parameter, type, ...
|
||||
- `type`: tip bilgisi (şimdilik string, ileride Type sistemi)
|
||||
- `definitionLoc`: tanımlandığı SourceLocation
|
||||
- `references`: kullanıldığı tüm SourceLocation'ların listesi
|
||||
- `scope`: ait olduğu scope (global, function, block)
|
||||
- `metadata`: opsiyonel anahtar-değer çiftleri
|
||||
|
||||
### 3.2 SymbolTable Sınıfı
|
||||
- [ ] İç içe scope desteği (global → function → block)
|
||||
- [ ] `enterScope()` / `exitScope()`
|
||||
- [ ] `define(name, kind, type, location)` → yeni sembol ekle
|
||||
- [ ] `resolve(name)` → en yakın scope'tan başlayarak ara
|
||||
- [ ] `addReference(name, location)` → kullanım noktası ekle
|
||||
- [ ] `getAllSymbols()` → tüm scope'lardaki tüm semboller (düz liste)
|
||||
- [ ] JSON'a serialize edilebilme
|
||||
|
||||
### 3.3 Sembol Toplama (AST Walker)
|
||||
- [ ] `SymbolCollector` sınıfı: AST'yi dolaşır, sembolleri toplar
|
||||
- [ ] Değişken tanımlarını yakala → `define()`
|
||||
- [ ] Değişken kullanımlarını yakala → `addReference()`
|
||||
- [ ] Fonksiyon tanımlarını yakala
|
||||
- [ ] Hata durumları: tanımsız değişken, çift tanım, tip uyuşmazlığı
|
||||
|
||||
---
|
||||
|
||||
## Aşama 4: Feature Toggle Sistemi
|
||||
|
||||
> **Hedef**: Derleyicinin her özelliği açılıp kapatılabilir olsun.
|
||||
|
||||
### 4.1 CompilerConfig Yapısı
|
||||
- [ ] `CompilerConfig` struct'ı:
|
||||
```cpp
|
||||
struct CompilerConfig {
|
||||
// Dil özellikleri
|
||||
bool enableWhile = true;
|
||||
bool enableFor = true;
|
||||
bool enableDoWhile = true;
|
||||
bool enableSwitch = true;
|
||||
bool enableClass = false; // henüz yok
|
||||
bool enableInterface = false;
|
||||
bool enableEnum = false;
|
||||
|
||||
// Operatörler
|
||||
bool enableTernary = true;
|
||||
bool enablePostfix = true;
|
||||
bool enableUnary = true;
|
||||
|
||||
// Optimizasyon adımları
|
||||
bool optConstantFolding = false;
|
||||
bool optDeadCodeElim = false;
|
||||
bool optUnusedVariable = false;
|
||||
|
||||
// Çıktı
|
||||
std::string outputFormat = "text"; // text, json
|
||||
std::string mode = "run"; // tokens, ast, ir, symbols, run
|
||||
std::string inputFile;
|
||||
std::string outputFile;
|
||||
};
|
||||
```
|
||||
|
||||
### 4.2 Keyword Kontrolü
|
||||
- [ ] Tokenizer, config'te kapalı keyword'leri identifier olarak tanısın
|
||||
- [ ] Parser, kapalı keyword'ler için hata değil, görmezden gelsin
|
||||
- [ ] `--disable-while` → `while` artık bir keyword değil
|
||||
|
||||
### 4.3 Optimizasyon Kontrolü
|
||||
- [ ] Her optimizasyon adımı bir `OptimizationPass` soyut sınıfı
|
||||
- [ ] `OptimizationManager`: config'e göre pas'ları çalıştırır
|
||||
- [ ] `--skip-constant-folding` → o adım atlanır
|
||||
- [ ] `--opt-all` → tüm optimizasyonlar aktif
|
||||
- [ ] `--opt-none` → hiçbiri aktif değil
|
||||
|
||||
---
|
||||
|
||||
## Aşama 5: Çoklu Backend Altyapısı (IR Merkezli)
|
||||
|
||||
> **Hedef**: Tüm backend'ler aynı IR'den beslenir.
|
||||
> Yeni backend eklemek = yeni bir `IRRunner` yazmak.
|
||||
|
||||
### 5.1 IR'yi Güçlendirme
|
||||
- [ ] Kontrol akışı: `br`, `jmp`, `cmp`, `br_eq`, `br_lt`, `br_gt`
|
||||
- [ ] Fonksiyon: `call`, `ret`, `param`
|
||||
- [ ] Bellek: `load`, `store`, `alloca`
|
||||
- [ ] Tip bilgisi: her IR komutu tipleri taşısın
|
||||
- [ ] Debug bilgisi: kaynak satır eşlemesi
|
||||
- [ ] `IRModule` sınıfı: fonksiyon listesi, global değişkenler, IR komutları
|
||||
|
||||
### 5.2 Backend Arayüzü
|
||||
- [ ] `IBackend` soyut sınıfı:
|
||||
```cpp
|
||||
class IBackend {
|
||||
public:
|
||||
virtual bool initialize(CompilerConfig& config) = 0;
|
||||
virtual bool compile(IRModule& ir) = 0;
|
||||
virtual bool run() = 0;
|
||||
virtual std::string getOutput() = 0;
|
||||
virtual ~IBackend() = default;
|
||||
};
|
||||
```
|
||||
|
||||
### 5.3 C Transpile Backend (Aşama 1)
|
||||
- [ ] IR → C kaynak kodu dönüşümü
|
||||
- [ ] Değişken tanımları, if/for/while, fonksiyonlar
|
||||
- [ ] GCC/Clang ile derleme ve çalıştırma
|
||||
- [ ] Üretilen C kodu okunabilir ve debug edilebilir olmalı
|
||||
|
||||
### 5.4 Interpreter Backend (Aşama 1)
|
||||
- [ ] IR'yi doğrudan yürüten sanal makine
|
||||
- [ ] Stack tabanlı veya register tabanlı
|
||||
- [ ] Adım adım çalıştırma (step) desteği
|
||||
- [ ] Değişken değerlerini gösterme
|
||||
|
||||
### 5.5 QBE Backend (Aşama 2)
|
||||
- [ ] QBE C API'si ile entegrasyon
|
||||
- [ ] IR → QBE IR dönüşümü
|
||||
- [ ] Native binary üretimi
|
||||
|
||||
### 5.6 LLVM Backend (Aşama 3, opsiyonel)
|
||||
- [ ] LLVM C API ile entegrasyon
|
||||
- [ ] IR → LLVM IR dönüşümü
|
||||
- [ ] Agresif optimizasyonlar, çok platformlu kod üretimi
|
||||
|
||||
---
|
||||
|
||||
## Aşama 6: Hata Sistemi
|
||||
|
||||
> **Hedef**: Sadece hata mesajı değil, **açıklama**.
|
||||
> Kullanıcıya neyi yanlış yaptığını ve nasıl düzelteceğini söyle.
|
||||
|
||||
### 6.1 Diagnostic Sistemi
|
||||
- [ ] `Diagnostic` yapısı: `{level, location, message, hint, sourceLine}`
|
||||
- [ ] Seviyeler: error, warning, note, hint
|
||||
- [ ] Birden fazla diagnostic toplama (ilk hatada durma)
|
||||
- [ ] `DiagnosticEngine`: diagnostic'leri toplar, formatlar, basar
|
||||
|
||||
### 6.2 Hata Mesajı Formatı
|
||||
- [ ] Kaynak satır gösterimi (^^^^ işaretleme)
|
||||
- [ ] Renkli çıktı (opsiyonel, `--color`)
|
||||
- [ ] JSON formatında da alınabilir
|
||||
- [ ] Hata kodları (örn: E0001, W0002)
|
||||
|
||||
### 6.3 Zengin Hata Açıklamaları
|
||||
- [ ] Tanımsız değişken → "x tanımlı değil. Bunu mu demek istediniz: xy?"
|
||||
- [ ] Tip uyuşmazlığı → "int bekleniyor ama float verilmiş. Cast ekleyin."
|
||||
- [ ] Eksik noktalı virgül → "; eksik. Bu satırın sonuna ekleyin."
|
||||
- [ ] Sembol tablosu kullanarak bağlamlı öneriler
|
||||
|
||||
---
|
||||
|
||||
## Aşama 7: LSP Sunucusu (Uzun Vade)
|
||||
|
||||
> **Hedef**: VS Code ve diğer IDE'ler için tam dil desteği.
|
||||
> Tüm metadata ve çoklu çıktı formatları LSP'yi kolaylaştırır.
|
||||
|
||||
### 7.1 LSP Protokolü Temelleri
|
||||
- [ ] `initialize`, `shutdown`, `exit`
|
||||
- [ ] `textDocument/didOpen`, `didChange`, `didClose`
|
||||
- [ ] JSON-RPC 2.0 üzerinden iletişim
|
||||
|
||||
### 7.2 Dil Özellikleri
|
||||
- [ ] **Syntax highlighting**: Token listesini LSP'e bildir
|
||||
- [ ] **Diagnostics**: Hata/uyarıları anlık bildir
|
||||
- [ ] **Go to definition**: Sembol tablosundan tanım konumuna git
|
||||
- [ ] **Find references**: Sembolün tüm kullanım noktalarını bul
|
||||
- [ ] **Hover**: İmleç üstündeki sembol hakkında bilgi göster
|
||||
- [ ] **Code completion**: Bağlama göre öneriler (sembol tablosundan)
|
||||
- [ ] **Document symbols**: Dosyadaki tüm sembolleri listele (AST'den)
|
||||
- [ ] **Code lens**: Fonksiyon başına reference sayısı
|
||||
|
||||
### 7.3 Kod Hiyerarşisi Görselleştirme
|
||||
- [ ] `--format=html` ile etkileşimli AST ağacı
|
||||
- [ ] AST'yi JSON olarak alıp web UI'da görselleştirme
|
||||
- [ ] Sembol tablosunu grafik olarak gösterme
|
||||
- [ ] Fonksiyon çağrı grafiği (call graph)
|
||||
|
||||
---
|
||||
|
||||
## Aşama 8: Test ve Kalite
|
||||
|
||||
### 8.1 Birim Testleri
|
||||
- [ ] Lexer testleri: her sayı formatı, her operatör, string
|
||||
- [ ] Tokenizer testleri: keyword'ler, delimiter'lar, yorumlar
|
||||
- [ ] Parser testleri: her statement tipi, iç içe ifadeler, hata durumları
|
||||
- [ ] IR testleri: her opcode, doğru register tahsisi
|
||||
- [ ] Sembol tablosu testleri: tanımlama, çözümleme, scope
|
||||
|
||||
### 8.2 Anlık Görüntü (Snapshot) Testleri
|
||||
- [ ] `source.sqt` → `output.json` karşılaştırması
|
||||
- [ ] Her commit'te AST/IR/sembol çıktısı değişmemeli (regresyon)
|
||||
|
||||
### 8.3 Benchmark
|
||||
- [ ] Büyük dosya parse süresi
|
||||
- [ ] Tokenizer throughput (token/saniye)
|
||||
- [ ] Bellek kullanımı
|
||||
|
||||
---
|
||||
|
||||
## Öncelik Sıralaması
|
||||
|
||||
```
|
||||
Şu an (Aşama 0) ──▶ Metadata ve konum takibi
|
||||
│
|
||||
Haftaya (Aşama 1) ──▶ CLI + REPL + dosya modu
|
||||
│
|
||||
2 hafta (Aşama 2) ──▶ Bellek canavarı AST + JSON çıktı
|
||||
│
|
||||
3 hafta (Aşama 3) ──▶ Sembol tablosu
|
||||
│
|
||||
4 hafta (Aşama 4) ──▶ Feature toggle + CompilerConfig
|
||||
│
|
||||
5 hafta (Aşama 5) ──▶ IR güçlendirme + C transpile backend
|
||||
│
|
||||
6 hafta (Aşama 6) ──▶ Diagnostic + zengin hata mesajları
|
||||
│
|
||||
... (Aşama 7-8) ──▶ LSP, testler, benchmark
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Anlık Görevler (Bu Hafta)
|
||||
|
||||
### Bugün / Yarın
|
||||
- [ ] `SourceLocation` yapısını tanımla (`src/core/location.hpp`)
|
||||
- [ ] `SourceFile` sınıfını yaz (offset → satır/sütun dönüşümü)
|
||||
- [ ] Lexer'a satır/sütun takibi ekle
|
||||
- [ ] `INumber` yerine `SourceLocation` kullanan yeni yapı
|
||||
|
||||
### Bu Hafta
|
||||
- [ ] Token'lara `SourceLocation` ekle
|
||||
- [ ] AST düğümlerine `SourceLocation` ekle
|
||||
- [ ] Token'ları AST'nin sahiplenmesi (unique_ptr)
|
||||
- [ ] CLI modülü: argc/argv, temel komutlar
|
||||
|
||||
---
|
||||
|
||||
## Mimari Notlar
|
||||
|
||||
### Neden Bellek Canavarı?
|
||||
Çünkü amaç **bilgiyi korumak**. Derleyiciler genelde parse ettikten sonra
|
||||
kaynak kod bilgisini atar. Biz atmayacağız. Bu sayede:
|
||||
- Hata mesajlarında kaynak kodu gösterebiliriz
|
||||
- IDE'de "go to definition" yapabiliriz
|
||||
- Kodu yeniden oluşturabiliriz (pretty printer)
|
||||
- Dönüşümleri (refactoring) güvenle yapabiliriz
|
||||
|
||||
### Neden Feature Toggle?
|
||||
Çünkü bu bir **toolbox**. Kullanıcı dili özelleştirebilmeli:
|
||||
- Eğitim amaçlı: önce sadece if/else, sonra while ekle
|
||||
- Deney amaçlı: "while olmadan program yazabilir misin?"
|
||||
- Güvenlik: belirli yapıları yasakla (eval, system call)
|
||||
- DSL: kendi alt-dilini oluştur
|
||||
|
||||
### Neden IR Merkezli?
|
||||
IR, tüm backend'lerin ortak dilidir. Yeni bir backend eklemek
|
||||
istediğimizde parser'ı, lexer'ı, optimizer'ı değiştirmemize gerek kalmaz.
|
||||
Sadece IR → Hedef dönüşümü yazarız. Bu, LLVM dışında QBE, Cranelift,
|
||||
hatta kendi JIT'imizi eklemeyi mümkün kılar.
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
int main() { int x = 0; while (x < 5) { x = x + 1; } do { x = x - 1; } while (x > 0); return x; }
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
PROJECT_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." &> /dev/null && pwd )"
|
||||
BUILD_DIR="$PROJECT_ROOT/build"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
cd "$BUILD_DIR"
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
|
||||
ninja
|
||||
echo "Derleme başarılı: build/saqut"
|
||||
10
source.sqt
10
source.sqt
|
|
@ -1,10 +0,0 @@
|
|||
int main() {
|
||||
int x = 0;
|
||||
while (x < 5) {
|
||||
x = x + 1;
|
||||
}
|
||||
do {
|
||||
x = x - 1;
|
||||
} while (x > 0);
|
||||
return x;
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — CLI Argüman Ayrıştırıcı ve Kaynak Okuma
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/cli/args.hpp
|
||||
// BAĞIMLI: Yok (sadece standart kütüphane)
|
||||
//
|
||||
// AMAÇ:
|
||||
// 1. POSIX tarzı argüman ayrıştırma
|
||||
// 2. Kaynak dosya okuma (tüm komutlar tarafından paylaşılır)
|
||||
//
|
||||
// DESTEKLENEN FORMATLAR:
|
||||
// saqut <komut> [dosya] [-o çıktı] [--format json] [--help]
|
||||
// saqut run file:source.sqt (eski sözdizimi)
|
||||
// saqut - (stdin — TODO)
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_CLI_ARGS
|
||||
#define SAQUT_CLI_ARGS
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct CliArgs {
|
||||
std::string command;
|
||||
std::vector<std::string> positional;
|
||||
std::string outputFile;
|
||||
std::string format;
|
||||
bool showHelp = false;
|
||||
bool stdinMode = false;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// parseArgs
|
||||
// ============================================================================
|
||||
inline CliArgs parseArgs(int argc, char* argv[]) {
|
||||
CliArgs args;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::string arg = argv[i];
|
||||
|
||||
if (arg == "-") {
|
||||
args.stdinMode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg == "-h" || arg == "--help") {
|
||||
args.showHelp = true;
|
||||
continue;
|
||||
}
|
||||
if (arg.compare(0, 9, "--output=") == 0) {
|
||||
args.outputFile = arg.substr(9);
|
||||
continue;
|
||||
}
|
||||
if (arg.compare(0, 9, "--format=") == 0) {
|
||||
args.format = arg.substr(9);
|
||||
continue;
|
||||
}
|
||||
if (arg == "--output" || arg == "-o") {
|
||||
if (i + 1 < argc) args.outputFile = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if (arg == "--format") {
|
||||
if (i + 1 < argc) args.format = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if (arg.compare(0, 5, "file:") == 0) {
|
||||
args.positional.push_back(arg.substr(5));
|
||||
continue;
|
||||
}
|
||||
if (arg.compare(0, 7, "output:") == 0) {
|
||||
args.outputFile = arg.substr(7);
|
||||
continue;
|
||||
}
|
||||
if (arg.compare(0, 4, "ast:") == 0) {
|
||||
args.outputFile = arg.substr(4);
|
||||
continue;
|
||||
}
|
||||
|
||||
// İlk argüman komut mu?
|
||||
if (args.command.empty() && i == 1) {
|
||||
if (arg == "run" || arg == "tokens" || arg == "ast" ||
|
||||
arg == "symbols" || arg == "compile" || arg == "parse" ||
|
||||
arg == "transpile" || arg == "interpret") {
|
||||
args.command = arg;
|
||||
continue;
|
||||
}
|
||||
args.command = "run";
|
||||
args.positional.push_back(arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
args.positional.push_back(arg);
|
||||
}
|
||||
|
||||
if (args.command.empty()) args.command = "run";
|
||||
if (args.positional.empty() && !args.stdinMode)
|
||||
args.positional.push_back("source.sqt");
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// readSource: Dosyadan veya stdin'den kaynak kod oku (TODO: stdin)
|
||||
// ============================================================================
|
||||
inline std::string readSource(const CliArgs& args) {
|
||||
if (args.stdinMode) {
|
||||
// TODO: std::cin'den EOF'a kadar oku
|
||||
std::cerr << "TODO: stdin modu henüz desteklenmiyor\n";
|
||||
return "";
|
||||
}
|
||||
if (args.positional.empty()) return "";
|
||||
|
||||
std::string path = args.positional[0];
|
||||
std::ifstream file(path, std::ios::in | std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Hata: '" << path << "' dosyası açılamadı\n";
|
||||
return "";
|
||||
}
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
#endif // SAQUT_CLI_ARGS
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — CLI Dispatcher
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/cli/cli.hpp
|
||||
// BAĞIMLI: args.hpp, commands/*
|
||||
//
|
||||
// AMAÇ:
|
||||
// Komut kaydı ve dağıtımı. Yeni bir komut eklemek için:
|
||||
// 1. src/cli/commands/x.hpp oluştur
|
||||
// 2. registerCommand() ile kaydet
|
||||
//
|
||||
// MİMARİ:
|
||||
// Her komut bir CliCommand struct'ıdır:
|
||||
// - name: "run", "tokens", "ast", ...
|
||||
// - description: Yardım metninde görünür
|
||||
// - hidden: true ise yardımda listelenmez (alias'lar için)
|
||||
// - execute: int(CliArgs&) döndürür (0 = başarılı)
|
||||
//
|
||||
// Komutlar lazy olarak include edilmez — her biri kendi header'ında
|
||||
// inline fonksiyon olarak tanımlanır ve cli.hpp tarafından include edilir.
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_CLI
|
||||
#define SAQUT_CLI
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "cli/args.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// CliCommand — Kayıtlı bir komut
|
||||
// ============================================================================
|
||||
struct CliCommand {
|
||||
std::string name;
|
||||
std::string description;
|
||||
bool hidden = false; // true = yardımda gösterme
|
||||
std::function<int(const CliArgs&)> execute;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// CliDispatcher — Komut kaydı ve çalıştırma
|
||||
// ============================================================================
|
||||
class CliDispatcher {
|
||||
public:
|
||||
void registerCommand(const CliCommand& cmd) {
|
||||
commands.push_back(cmd);
|
||||
}
|
||||
|
||||
int dispatch(const CliArgs& args) const {
|
||||
// Yardım özel durumu
|
||||
if (args.showHelp || args.command == "help") {
|
||||
printHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Komutu bul
|
||||
for (auto& cmd : commands) {
|
||||
if (cmd.name == args.command) {
|
||||
return cmd.execute(args);
|
||||
}
|
||||
}
|
||||
|
||||
// Bilinmeyen komut
|
||||
std::cerr << "Hata: Bilinmeyen komut '" << args.command << "'\n";
|
||||
std::cerr << "Kullanılabilir komutlar için: saqut --help\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
void printHelp() const {
|
||||
std::cout << "saQut Compiler — Dil Bağımsız Derleyici Alet Çantası\n\n";
|
||||
std::cout << "KULLANIM:\n";
|
||||
std::cout << " saqut <komut> [dosya] [seçenekler]\n";
|
||||
std::cout << " saqut - (stdin modu — TODO)\n\n";
|
||||
std::cout << "KOMUTLAR:\n";
|
||||
|
||||
for (auto& cmd : commands) {
|
||||
if (cmd.hidden) continue;
|
||||
std::cout << " " << cmd.name;
|
||||
// 12 karaktere hizala
|
||||
size_t pad = cmd.name.size() < 11 ? 11 - cmd.name.size() : 1;
|
||||
std::cout << std::string(pad, ' ') << cmd.description << "\n";
|
||||
}
|
||||
|
||||
std::cout << "\nSEÇENEKLER:\n";
|
||||
std::cout << " -o, --output <dosya> Çıktı dosyası\n";
|
||||
std::cout << " --format <json|text> Çıktı formatı (varsayılan: text)\n";
|
||||
std::cout << " -h, --help Bu yardım metni\n\n";
|
||||
std::cout << "ÖRNEK:\n";
|
||||
std::cout << " saqut run source.sqt\n";
|
||||
std::cout << " saqut tokens source.sqt\n";
|
||||
std::cout << " saqut ast source.sqt --format=json\n";
|
||||
std::cout << " saqut symbols source.sqt\n";
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<CliCommand> commands;
|
||||
};
|
||||
|
||||
#endif // SAQUT_CLI
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// ============================================================================
|
||||
// saQut CLI — ast komutu (JSON formatında AST hiyerarşisi + analiz)
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_CLI_AST
|
||||
#define SAQUT_CLI_AST
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "cli/args.hpp"
|
||||
#include "tokenizer/tokenizer.hpp"
|
||||
#include "parser/parser.hpp"
|
||||
#include "json.hpp"
|
||||
|
||||
inline int cmdAst(const CliArgs& args) {
|
||||
std::string source = readSource(args);
|
||||
if (source.empty()) return 1;
|
||||
|
||||
Tokenizer tokenizer;
|
||||
auto tokens = tokenizer.scan(source);
|
||||
|
||||
Parser parser;
|
||||
ASTNode* ast = parser.parse(tokens);
|
||||
|
||||
if (!ast) {
|
||||
std::cerr << "Hata: AST üretilemedi\n";
|
||||
for (auto* t : tokens) delete t;
|
||||
return 1;
|
||||
}
|
||||
|
||||
AstAnalysis analysis = analyzeAst(ast);
|
||||
|
||||
// Çıktı hedefi
|
||||
std::ostream* out = &std::cout;
|
||||
std::ofstream outFile;
|
||||
if (!args.outputFile.empty()) {
|
||||
outFile.open(args.outputFile);
|
||||
if (outFile.is_open()) out = &outFile;
|
||||
}
|
||||
|
||||
*out << "{\n"
|
||||
<< " \"ast\":\n"
|
||||
<< astToJson(ast, 2) << ",\n\n"
|
||||
<< " \"analysis\": {\n"
|
||||
<< analysisToJson(analysis) << "\n"
|
||||
<< " }\n"
|
||||
<< "}\n";
|
||||
|
||||
delete ast;
|
||||
for (auto* t : tokens) delete t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // SAQUT_CLI_AST
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// ============================================================================
|
||||
// saQut CLI — run komutu (pipeline: token → AST → IR debug)
|
||||
// ============================================================================
|
||||
//
|
||||
// TODO: İleride `saqut -` ile stdin'den okuyup anında çalıştıracak
|
||||
// interpreter modu bu komutun altına gelecek.
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_CLI_RUN
|
||||
#define SAQUT_CLI_RUN
|
||||
|
||||
#include <iostream>
|
||||
#include "cli/args.hpp"
|
||||
#include "tokenizer/tokenizer.hpp"
|
||||
#include "parser/parser.hpp"
|
||||
#include "ir/ir.hpp"
|
||||
|
||||
inline int cmdRun(const CliArgs& args) {
|
||||
std::string source = readSource(args);
|
||||
if (source.empty()) return 1;
|
||||
|
||||
Tokenizer tokenizer;
|
||||
auto tokens = tokenizer.scan(source);
|
||||
|
||||
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";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
for (auto* t : tokens) delete t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // SAQUT_CLI_RUN
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
// ============================================================================
|
||||
// saQut CLI — symbols komutu (sembol tablosu)
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_CLI_SYMBOLS
|
||||
#define SAQUT_CLI_SYMBOLS
|
||||
|
||||
#include <iostream>
|
||||
#include "cli/args.hpp"
|
||||
#include "tokenizer/tokenizer.hpp"
|
||||
#include "parser/parser.hpp"
|
||||
#include "json.hpp"
|
||||
|
||||
inline int cmdSymbols(const CliArgs& args) {
|
||||
std::string source = readSource(args);
|
||||
if (source.empty()) return 1;
|
||||
|
||||
Tokenizer tokenizer;
|
||||
auto tokens = tokenizer.scan(source);
|
||||
|
||||
Parser parser;
|
||||
ASTNode* ast = parser.parse(tokens);
|
||||
|
||||
if (!ast) {
|
||||
std::cerr << "Hata: AST üretilemedi\n";
|
||||
for (auto* t : tokens) delete t;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto symbols = collectSymbols(ast);
|
||||
|
||||
std::cout << "Sembol Tablosu (" << symbols.size() << " sembol):\n";
|
||||
std::cout << "────────────────────────────────────────────\n";
|
||||
|
||||
if (symbols.empty()) {
|
||||
std::cout << " (sembol bulunamadı)\n";
|
||||
}
|
||||
|
||||
for (auto& s : symbols) {
|
||||
std::cout << " [" << s.kind << "] " << s.type << " " << s.name << "\n";
|
||||
}
|
||||
|
||||
std::cout << "────────────────────────────────────────────\n";
|
||||
|
||||
int fnCount = 0, varCount = 0;
|
||||
for (auto& s : symbols) {
|
||||
if (s.kind == "function") fnCount++;
|
||||
else if (s.kind == "variable") varCount++;
|
||||
}
|
||||
std::cout << "Fonksiyon: " << fnCount
|
||||
<< " | Değişken: " << varCount << "\n";
|
||||
|
||||
delete ast;
|
||||
for (auto* t : tokens) delete t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // SAQUT_CLI_SYMBOLS
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// ============================================================================
|
||||
// saQut CLI — tokens komutu
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_CLI_TOKENS
|
||||
#define SAQUT_CLI_TOKENS
|
||||
|
||||
#include <iostream>
|
||||
#include "cli/args.hpp"
|
||||
#include "tokenizer/tokenizer.hpp"
|
||||
|
||||
inline int cmdTokens(const CliArgs& args) {
|
||||
std::string source = readSource(args);
|
||||
if (source.empty()) return 1;
|
||||
|
||||
Tokenizer tokenizer;
|
||||
auto tokens = tokenizer.scan(source);
|
||||
|
||||
std::cout << "Tokenler (" << tokens.size() << " adet):\n";
|
||||
for (auto* t : tokens) {
|
||||
std::cout << " [" << t->gettype() << "] \"" << t->token << "\"\n";
|
||||
}
|
||||
|
||||
for (auto* t : tokens) delete t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // SAQUT_CLI_TOKENS
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Kaynak Kod Konum Yapısı
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/core/location.hpp
|
||||
// KATMAN: Katman 0 — Tüm katmanlar tarafından kullanılır
|
||||
// BAĞIMLI: Yok (sadece <string>)
|
||||
//
|
||||
// AMAÇ:
|
||||
// Her token ve AST düğümünün kaynak koddaki tam konumunu tutar.
|
||||
// "Hata nerede?" ve "Kullanıcı imleci nerede?" sorularına cevap verir.
|
||||
//
|
||||
// ALANLAR:
|
||||
// filePath : Kaynak dosyanın yolu (bilinmiyorsa boş string)
|
||||
// line : 1-tabanlı satır numarası
|
||||
// column : 1-tabanlı sütun numarası
|
||||
// offset : 0-tabanlı karakter offset'i (dosya başından itibaren)
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_CORE_LOCATION
|
||||
#define SAQUT_CORE_LOCATION
|
||||
|
||||
#include <string>
|
||||
|
||||
// ============================================================================
|
||||
// SourceLocation — Kaynak Koddaki Bir Nokta
|
||||
// ============================================================================
|
||||
//
|
||||
// KULLANIM:
|
||||
// SourceLocation loc{"test.sqt", 5, 10, 134};
|
||||
// std::cout << loc.toString(); // "test.sqt:5:10"
|
||||
// std::cout << loc.shortString(); // "5:10"
|
||||
//
|
||||
// Varsayılan kurucu: geçersiz bir konum üretir (line=0, column=0, offset=-1).
|
||||
// isValid() ile kontrol edilebilir.
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
struct SourceLocation {
|
||||
std::string filePath;
|
||||
int line = 0; // 1-tabanlı, 0 = geçersiz
|
||||
int column = 0; // 1-tabanlı, 0 = geçersiz
|
||||
int offset = -1; // 0-tabanlı, -1 = geçersiz
|
||||
|
||||
SourceLocation() = default;
|
||||
|
||||
SourceLocation(std::string file, int line, int col, int off)
|
||||
: filePath(std::move(file)), line(line), column(col), offset(off) {}
|
||||
|
||||
// Geçerli bir konum mu?
|
||||
bool isValid() const {
|
||||
return line > 0 && column > 0 && offset >= 0;
|
||||
}
|
||||
|
||||
// Tam konum: "dosya.sqt:5:10"
|
||||
std::string toString() const {
|
||||
if (!isValid()) return "<invalid>";
|
||||
return filePath + ":" + std::to_string(line) + ":" + std::to_string(column);
|
||||
}
|
||||
|
||||
// Kısa konum: "5:10"
|
||||
std::string shortString() const {
|
||||
if (!isValid()) return "?:?";
|
||||
return std::to_string(line) + ":" + std::to_string(column);
|
||||
}
|
||||
|
||||
// JSON formatı: {"file":"...","line":5,"column":10,"offset":134}
|
||||
std::string toJson() const {
|
||||
if (!isValid()) return "null";
|
||||
return "{"
|
||||
"\"file\":\"" + filePath + "\","
|
||||
"\"line\":" + std::to_string(line) + ","
|
||||
"\"column\":" + std::to_string(column) + ","
|
||||
"\"offset\":" + std::to_string(offset) +
|
||||
"}";
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SAQUT_CORE_LOCATION
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
#include "core/sourcefile.hpp"
|
||||
|
||||
void SourceFile::setText(const std::string& path, const std::string& source) {
|
||||
filePath = path;
|
||||
text = source;
|
||||
computeLineStarts();
|
||||
}
|
||||
|
||||
int SourceFile::lineCount() const {
|
||||
return static_cast<int>(lineStarts.size());
|
||||
}
|
||||
|
||||
std::string SourceFile::getLine(int line) const {
|
||||
if (line < 1 || line > lineCount()) return "";
|
||||
int start = lineStarts[line - 1];
|
||||
int end;
|
||||
if (line < lineCount()) {
|
||||
end = lineStarts[line] - 1; // Satır sonu (\n) hariç
|
||||
// \r\n varsa bir karakter daha geri
|
||||
if (end > start && text[end - 1] == '\r') end--;
|
||||
} else {
|
||||
end = static_cast<int>(text.length());
|
||||
}
|
||||
return text.substr(start, end - start);
|
||||
}
|
||||
|
||||
SourceLocation SourceFile::offsetToLocation(int offset) const {
|
||||
// Geçersiz offset kontrolü
|
||||
if (offset < 0 || offset > static_cast<int>(text.length())) {
|
||||
return SourceLocation{filePath, 0, 0, -1};
|
||||
}
|
||||
|
||||
// Binary search: offset'in hangi satıra ait olduğunu bul
|
||||
// lineStarts içinde offset'ten büyük ilk elemanı bul
|
||||
auto it = std::upper_bound(lineStarts.begin(), lineStarts.end(), offset);
|
||||
int lineIndex = static_cast<int>(it - lineStarts.begin()) - 1;
|
||||
|
||||
// lineIndex geçerli değilse
|
||||
if (lineIndex < 0) {
|
||||
lineIndex = 0;
|
||||
} else if (lineIndex >= static_cast<int>(lineStarts.size())) {
|
||||
lineIndex = static_cast<int>(lineStarts.size()) - 1;
|
||||
}
|
||||
|
||||
int lineStart = lineStarts[lineIndex];
|
||||
int line = lineIndex + 1; // 1-tabanlı
|
||||
int column = offset - lineStart + 1; // 1-tabanlı
|
||||
|
||||
return SourceLocation{filePath, line, column, offset};
|
||||
}
|
||||
|
||||
SourceFile::LocationRange SourceFile::rangeFromOffsets(int startOffset, int endOffset) const {
|
||||
return {offsetToLocation(startOffset), offsetToLocation(endOffset)};
|
||||
}
|
||||
|
||||
void SourceFile::computeLineStarts() {
|
||||
lineStarts.clear();
|
||||
lineStarts.push_back(0); // 1. satır offset 0
|
||||
|
||||
for (int i = 0; i < static_cast<int>(text.length()); i++) {
|
||||
if (text[i] == '\n') {
|
||||
// \r\n kontrolü: \r'yi atla, \n'den sonraki karakter yeni satır
|
||||
int nextStart = i + 1;
|
||||
if (nextStart < static_cast<int>(text.length())) {
|
||||
lineStarts.push_back(nextStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Kaynak Kod Yöneticisi
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/core/sourcefile.hpp
|
||||
// KATMAN: Katman 0 — Tüm katmanlar tarafından kullanılır
|
||||
// BAĞIMLI: core/location.hpp
|
||||
//
|
||||
// AMAÇ:
|
||||
// Kaynak kodun tamamını ve satır başı offset'lerini tutar.
|
||||
// offset → (line, column) dönüşümü yapar.
|
||||
//
|
||||
// TASARIM:
|
||||
// lineStarts vektörü, her satırın ilk karakterinin offset'ini tutar:
|
||||
// lineStarts[0] = 0 (1. satır, offset 0)
|
||||
// lineStarts[1] = 15 (2. satır, offset 15)
|
||||
// lineStarts[2] = 32 (3. satır, offset 32)
|
||||
//
|
||||
// offsetToLocation() bu dizide binary search yaparak O(log n)'de line/column
|
||||
// bulur. Line-start dizisi bir kere setText()'te O(n)'de hesaplanır.
|
||||
//
|
||||
// PERFORMANS:
|
||||
// setText() : O(n) — line-start dizisi bir kere kurulur
|
||||
// offsetToLocation() : O(log n) — binary search
|
||||
// Bellek : O(n) — lineStarts (en fazla n eleman, her satır için bir int)
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_CORE_SOURCEFILE
|
||||
#define SAQUT_CORE_SOURCEFILE
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "core/location.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// SourceFile — Kaynak Kod Yöneticisi
|
||||
// ============================================================================
|
||||
//
|
||||
// KULLANIM:
|
||||
// SourceFile sf;
|
||||
// sf.setText("deneme.sqt", "int x = 5;\nreturn x;\n");
|
||||
// SourceLocation loc = sf.offsetToLocation(10); // 1:10 (2. satır)
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
class SourceFile {
|
||||
public:
|
||||
std::string filePath; // Kaynak dosyanın yolu
|
||||
std::string text; // Kaynak kodun tamamı
|
||||
std::vector<int> lineStarts; // Her satırın başlangıç offset'i
|
||||
|
||||
SourceFile() = default;
|
||||
|
||||
// text verisini yeni satır dizisini de hesapla
|
||||
void setText(const std::string& path, const std::string& source);
|
||||
|
||||
// Kaynak kodun toplam satır sayısı
|
||||
int lineCount() const;
|
||||
|
||||
// Belirtilen offset'teki satırın tam metnini döndür
|
||||
std::string getLine(int line) const;
|
||||
|
||||
// Offset'ten (line, column) dönüşümü
|
||||
// Binary search ile O(log n)
|
||||
SourceLocation offsetToLocation(int offset) const;
|
||||
|
||||
// Bir aralığın başlangıç ve bitiş konumlarını döndür
|
||||
struct LocationRange {
|
||||
SourceLocation start;
|
||||
SourceLocation end;
|
||||
};
|
||||
|
||||
LocationRange rangeFromOffsets(int startOffset, int endOffset) const;
|
||||
|
||||
private:
|
||||
// lineStarts vektörünü hesapla
|
||||
// Her \n karakterinden sonraki offset bir sonraki satırın başlangıcıdır
|
||||
// İlk satır her zaman offset 0'dan başlar
|
||||
void computeLineStarts();
|
||||
};
|
||||
|
||||
#endif // SAQUT_CORE_SOURCEFILE
|
||||
|
|
@ -0,0 +1,332 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — AST Analizi ve Sembol Toplayıcı
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/json.hpp
|
||||
// KATMAN: Tüm katmanlardan sonra — AST'yi tüketir
|
||||
// BAĞIMLI: AST (src/parser/ast.hpp)
|
||||
// KULLANAN: main.cpp
|
||||
//
|
||||
// AMAÇ:
|
||||
// 1. AST üzerinde analiz yap (düğüm sayısı, derinlik, tip dağılımı)
|
||||
// 2. Sembolleri topla (fonksiyon isimleri, dönüş tipleri, değişkenler)
|
||||
//
|
||||
// NOT: JSON serileştirme ASTNode::toJson() tarafından yapılır.
|
||||
// Bu dosya sadece analiz ve sembol toplama işlemlerini içerir.
|
||||
// Yeni bir AST düğümü eklendiğinde buraya dokunmak gerekmez.
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_JSON
|
||||
#define SAQUT_JSON
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "parser/ast.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// astKindName — ASTKind enum'unu string'e çevir (analiz çıktısı için)
|
||||
// ============================================================================
|
||||
|
||||
inline const char* astKindName(ASTKind k) {
|
||||
switch (k) {
|
||||
case ASTKind::Program: return "Program";
|
||||
case ASTKind::FunctionDecl: return "FunctionDecl";
|
||||
case ASTKind::Block: return "Block";
|
||||
case ASTKind::VariableDecl: return "VariableDecl";
|
||||
case ASTKind::BinaryExpression: return "BinaryExpression";
|
||||
case ASTKind::UnaryExpression: return "UnaryExpression";
|
||||
case ASTKind::Literal: return "Literal";
|
||||
case ASTKind::Identifier: return "Identifier";
|
||||
case ASTKind::Postfix: return "Postfix";
|
||||
case ASTKind::IfStatement: return "IfStatement";
|
||||
case ASTKind::ForStatement: return "ForStatement";
|
||||
case ASTKind::WhileStatement: return "WhileStatement";
|
||||
case ASTKind::DoWhileStatement: return "DoWhileStatement";
|
||||
case ASTKind::ReturnStatement: return "ReturnStatement";
|
||||
case ASTKind::BreakStatement: return "BreakStatement";
|
||||
case ASTKind::ContinueStatement: return "ContinueStatement";
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// AST → JSON (ince sarmalayıcı — asıl iş ASTNode::toJson()'da)
|
||||
// ============================================================================
|
||||
|
||||
inline std::string astToJson(ASTNode* node, int depth = 0) {
|
||||
if (!node) return "null";
|
||||
return node->toJson(depth);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// AST Analizi
|
||||
// ============================================================================
|
||||
|
||||
struct AstAnalysis {
|
||||
int totalNodes = 0;
|
||||
int maxDepth = 0;
|
||||
int functionCount = 0;
|
||||
int variableCount = 0;
|
||||
int ifCount = 0;
|
||||
int loopCount = 0; // for + while + do-while
|
||||
std::map<std::string, int> nodeTypeCounts;
|
||||
};
|
||||
|
||||
inline void analyzeRecursive(ASTNode* node, int currentDepth, AstAnalysis& a) {
|
||||
if (!node) return;
|
||||
|
||||
a.totalNodes++;
|
||||
if (currentDepth > a.maxDepth) a.maxDepth = currentDepth;
|
||||
|
||||
const char* name = astKindName(node->kind);
|
||||
a.nodeTypeCounts[name]++;
|
||||
|
||||
switch (node->kind) {
|
||||
case ASTKind::FunctionDecl: a.functionCount++; break;
|
||||
case ASTKind::VariableDecl: a.variableCount++; break;
|
||||
case ASTKind::IfStatement: a.ifCount++; break;
|
||||
case ASTKind::WhileStatement:
|
||||
case ASTKind::ForStatement:
|
||||
case ASTKind::DoWhileStatement:
|
||||
a.loopCount++; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Tüm çocukları gez
|
||||
switch (node->kind) {
|
||||
case ASTKind::Program:
|
||||
case ASTKind::FunctionDecl:
|
||||
case ASTKind::Block: {
|
||||
for (auto* child : node->getChildren())
|
||||
analyzeRecursive(child, currentDepth + 1, a);
|
||||
break;
|
||||
}
|
||||
case ASTKind::VariableDecl: {
|
||||
auto* vd = (VariableDeclNode*)node;
|
||||
if (vd->initExpr) analyzeRecursive(vd->initExpr, currentDepth + 1, a);
|
||||
break;
|
||||
}
|
||||
case ASTKind::BinaryExpression: {
|
||||
auto* bin = (BinaryExpressionNode*)node;
|
||||
if (bin->Left) analyzeRecursive(bin->Left, currentDepth + 1, a);
|
||||
if (bin->Right) analyzeRecursive(bin->Right, currentDepth + 1, a);
|
||||
break;
|
||||
}
|
||||
case ASTKind::Postfix: {
|
||||
auto* pf = (PostfixNode*)node;
|
||||
if (pf->operand) analyzeRecursive(pf->operand, currentDepth + 1, a);
|
||||
break;
|
||||
}
|
||||
case ASTKind::IfStatement: {
|
||||
auto* ifn = (IfStatementNode*)node;
|
||||
if (ifn->condition) analyzeRecursive(ifn->condition, currentDepth + 1, a);
|
||||
if (ifn->thenBranch) analyzeRecursive(ifn->thenBranch, currentDepth + 1, a);
|
||||
if (ifn->elseBranch) analyzeRecursive(ifn->elseBranch, currentDepth + 1, a);
|
||||
break;
|
||||
}
|
||||
case ASTKind::WhileStatement: {
|
||||
auto* ws = (WhileStatementNode*)node;
|
||||
if (ws->condition) analyzeRecursive(ws->condition, currentDepth + 1, a);
|
||||
if (ws->body) analyzeRecursive(ws->body, currentDepth + 1, a);
|
||||
break;
|
||||
}
|
||||
case ASTKind::ForStatement: {
|
||||
auto* fs = (ForStatementNode*)node;
|
||||
if (fs->init) analyzeRecursive(fs->init, currentDepth + 1, a);
|
||||
if (fs->condition) analyzeRecursive(fs->condition, currentDepth + 1, a);
|
||||
if (fs->update) analyzeRecursive(fs->update, currentDepth + 1, a);
|
||||
if (fs->body) analyzeRecursive(fs->body, currentDepth + 1, a);
|
||||
break;
|
||||
}
|
||||
case ASTKind::DoWhileStatement: {
|
||||
auto* dw = (DoWhileStatementNode*)node;
|
||||
if (dw->body) analyzeRecursive(dw->body, currentDepth + 1, a);
|
||||
if (dw->condition) analyzeRecursive(dw->condition, currentDepth + 1, a);
|
||||
break;
|
||||
}
|
||||
case ASTKind::ReturnStatement: {
|
||||
auto* rs = (ReturnStatementNode*)node;
|
||||
if (rs->value) analyzeRecursive(rs->value, currentDepth + 1, a);
|
||||
break;
|
||||
}
|
||||
case ASTKind::ExpressionStatement: {
|
||||
auto* es = (ExpressionStatementNode*)node;
|
||||
if (es->expression) analyzeRecursive(es->expression, currentDepth + 1, a);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
inline AstAnalysis analyzeAst(ASTNode* root) {
|
||||
AstAnalysis a;
|
||||
analyzeRecursive(root, 0, a);
|
||||
return a;
|
||||
}
|
||||
|
||||
inline std::string analysisToJson(const AstAnalysis& a) {
|
||||
std::ostringstream ss;
|
||||
ss << " \"totalNodes\": " << a.totalNodes << ",\n"
|
||||
<< " \"maxDepth\": " << a.maxDepth << ",\n"
|
||||
<< " \"functionCount\": " << a.functionCount << ",\n"
|
||||
<< " \"variableCount\": " << a.variableCount << ",\n"
|
||||
<< " \"ifCount\": " << a.ifCount << ",\n"
|
||||
<< " \"loopCount\": " << a.loopCount << ",\n"
|
||||
<< " \"nodeTypes\": {\n";
|
||||
bool first = true;
|
||||
for (auto& [name, count] : a.nodeTypeCounts) {
|
||||
if (!first) ss << ",\n";
|
||||
ss << " \"" << name << "\": " << count;
|
||||
first = false;
|
||||
}
|
||||
ss << "\n }";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Sembol Toplayıcı
|
||||
// ============================================================================
|
||||
|
||||
struct SymbolEntry {
|
||||
std::string name;
|
||||
std::string kind; // "function" veya "variable"
|
||||
std::string type; // Dönüş tipi veya değişken tipi
|
||||
};
|
||||
|
||||
inline void collectSymbolsRecursive(ASTNode* node, std::vector<SymbolEntry>& symbols) {
|
||||
if (!node) return;
|
||||
|
||||
// Sadece FunctionDecl ve VariableDecl toplanır — diğerleri gezilir
|
||||
if (node->kind == ASTKind::FunctionDecl) {
|
||||
auto* fn = (FunctionDeclNode*)node;
|
||||
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) {
|
||||
auto* vd = (VariableDeclNode*)node;
|
||||
symbols.push_back({vd->name, "variable", vd->varType});
|
||||
}
|
||||
|
||||
// Çocukları gez
|
||||
switch (node->kind) {
|
||||
case ASTKind::Program:
|
||||
case ASTKind::FunctionDecl:
|
||||
case ASTKind::Block:
|
||||
for (auto* child : node->getChildren())
|
||||
collectSymbolsRecursive(child, symbols);
|
||||
break;
|
||||
case ASTKind::VariableDecl: {
|
||||
auto* vd = (VariableDeclNode*)node;
|
||||
if (vd->initExpr) collectSymbolsRecursive(vd->initExpr, symbols);
|
||||
break;
|
||||
}
|
||||
case ASTKind::BinaryExpression: {
|
||||
auto* bin = (BinaryExpressionNode*)node;
|
||||
if (bin->Left) collectSymbolsRecursive(bin->Left, symbols);
|
||||
if (bin->Right) collectSymbolsRecursive(bin->Right, symbols);
|
||||
break;
|
||||
}
|
||||
case ASTKind::Postfix: {
|
||||
auto* pf = (PostfixNode*)node;
|
||||
if (pf->operand) collectSymbolsRecursive(pf->operand, symbols);
|
||||
break;
|
||||
}
|
||||
case ASTKind::IfStatement: {
|
||||
auto* ifn = (IfStatementNode*)node;
|
||||
if (ifn->condition) collectSymbolsRecursive(ifn->condition, symbols);
|
||||
if (ifn->thenBranch) collectSymbolsRecursive(ifn->thenBranch, symbols);
|
||||
if (ifn->elseBranch) collectSymbolsRecursive(ifn->elseBranch, symbols);
|
||||
break;
|
||||
}
|
||||
case ASTKind::WhileStatement: {
|
||||
auto* ws = (WhileStatementNode*)node;
|
||||
if (ws->condition) collectSymbolsRecursive(ws->condition, symbols);
|
||||
if (ws->body) collectSymbolsRecursive(ws->body, symbols);
|
||||
break;
|
||||
}
|
||||
case ASTKind::ForStatement: {
|
||||
auto* fs = (ForStatementNode*)node;
|
||||
if (fs->init) collectSymbolsRecursive(fs->init, symbols);
|
||||
if (fs->condition) collectSymbolsRecursive(fs->condition, symbols);
|
||||
if (fs->update) collectSymbolsRecursive(fs->update, symbols);
|
||||
if (fs->body) collectSymbolsRecursive(fs->body, symbols);
|
||||
break;
|
||||
}
|
||||
case ASTKind::DoWhileStatement: {
|
||||
auto* dw = (DoWhileStatementNode*)node;
|
||||
if (dw->body) collectSymbolsRecursive(dw->body, symbols);
|
||||
if (dw->condition) collectSymbolsRecursive(dw->condition, symbols);
|
||||
break;
|
||||
}
|
||||
case ASTKind::ReturnStatement: {
|
||||
auto* rs = (ReturnStatementNode*)node;
|
||||
if (rs->value) collectSymbolsRecursive(rs->value, symbols);
|
||||
break;
|
||||
}
|
||||
case ASTKind::ExpressionStatement: {
|
||||
auto* es = (ExpressionStatementNode*)node;
|
||||
if (es->expression) collectSymbolsRecursive(es->expression, symbols);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::vector<SymbolEntry> collectSymbols(ASTNode* root) {
|
||||
std::vector<SymbolEntry> symbols;
|
||||
collectSymbolsRecursive(root, symbols);
|
||||
return symbols;
|
||||
}
|
||||
|
||||
#endif // SAQUT_JSON
|
||||
|
|
@ -0,0 +1,346 @@
|
|||
#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 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;
|
||||
}
|
||||
|
|
@ -15,46 +15,6 @@
|
|||
// 4. Boşluk karakterlerini atlar
|
||||
// 5. Satır/sütun bilgisi sağlar (hata mesajları için temel)
|
||||
//
|
||||
// ADR-006: Neden Kendi Lexer'ımız?
|
||||
// - std::istringstream veya regex kullanmak yerine, tam kontrol sağlayan
|
||||
// sıfırdan bir lexer yazdık.
|
||||
// - Backtracking: offsetMap ile konum yığını tutar, denenen bir eşleşme
|
||||
// başarısız olursa geri alınabilir. Bu özellik std::istream'de yoktur.
|
||||
// - Performans: Sanal fonksiyon çağrısı yok, her şey inline.
|
||||
// - Hata ayıklama: Her karakter okuması kontrol edilebilir.
|
||||
//
|
||||
// TASARIM KARARLARI:
|
||||
// 1. offsetMap (std::vector<int>): İç içe backtracking için yığın.
|
||||
// beginPosition() → yığına mevcut konumu ekler
|
||||
// acceptPosition() → yığındaki son konumu kalıcı yapar
|
||||
// rejectPosition() → yığındaki son konumu atar (geri al)
|
||||
// Bu sayede "dene, başarısız olursa geri al" patterni çalışır.
|
||||
//
|
||||
// 2. getchar() iki overload:
|
||||
// - getchar(): mevcut konumdaki karakteri okur
|
||||
// - getchar(int offset): mevcut konum + offset'teki karakteri okur
|
||||
// İkincisi özellikle keyword kontrolünde önemlidir:
|
||||
// "do" kelimesini gördükten sonra, bunun "double"ın başlangıcı olmadığını
|
||||
// kontrol etmek için keyword sonrası karaktere bakılır.
|
||||
//
|
||||
// 3. isEnd(): offset >= size ile kontrol. offset her zaman [0, size] aralığında.
|
||||
// size konumunda EOF (end of file) anlamına gelir.
|
||||
//
|
||||
// 4. readNumeric(): C/C++/Java sayı formatlarını destekler:
|
||||
// - Decimal: 42, -3, +7
|
||||
// - Hex: 0xFF, 0X1A
|
||||
// - Binary: 0b1010, 0B1100
|
||||
// - Octal: 0777 (0 ile başlayan ve 8-9 içermeyen)
|
||||
// - Float: 3.14, .5, 1e10, 2.5E-3
|
||||
// - Negatif/Pozitif: -42, +3 (baştaki işaret)
|
||||
//
|
||||
// BİLİNEN SINIRLAMALAR (TODO):
|
||||
// TODO: Satır/sütun takibi eklenmeli (şu anda sadece offset var)
|
||||
// TODO: Unicode/UTF-8 desteği (şu anda sadece ASCII)
|
||||
// TODO: ' char literal'ı okunamıyor
|
||||
// TODO: Sayısal alt çizgi (_) ayracı: 1_000_000 formatı
|
||||
// TODO: Binary floating point: 0b1.1p10 formatı (C99 hexfloat)
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_LEXER
|
||||
|
|
@ -63,32 +23,18 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "core/location.hpp"
|
||||
#include "core/sourcefile.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// INumber — Ara Sayısal Veri Yapısı
|
||||
// ============================================================================
|
||||
//
|
||||
// Lexer'ın readNumeric() fonksiyonu tarafından döndürülür.
|
||||
// Tokenizer bu yapıyı NumberToken'a dönüştürür.
|
||||
//
|
||||
// Neden ayrı bir struct? Lexer katmanı Token sınıflarından haberdar değil.
|
||||
// Bağımlılık yönü: Lexer ← Tokenizer. Lexer hiçbir üst katmanı include etmez.
|
||||
//
|
||||
// ALANLAR:
|
||||
// start, end : Kaynak koddaki başlangıç/bitiş konumları (offset)
|
||||
// token : Sayının ham string hali (örn: "0xFF", "3.14", "1e10")
|
||||
// isFloat : Ondalıklı sayı mı? (nokta veya epsilon içeriyor mu)
|
||||
// hasEpsilon : Bilimsel gösterim mi? (e/E içeriyor mu)
|
||||
// base : Sayı tabanı: 2, 8, 10, veya 16
|
||||
// - 0x/0X ile başlarsa 16
|
||||
// - 0b/0B ile başlarsa 2
|
||||
// - 0 ile başlayıp 8-9 içermiyorsa 8
|
||||
// - diğer her şey 10
|
||||
// positive : Pozitif mi? (başında - işareti yoksa true)
|
||||
//
|
||||
|
||||
struct INumber {
|
||||
int start = 0; // Kaynak koddaki başlangıç offset'i
|
||||
int end = 0; // Kaynak koddaki bitiş offset'i
|
||||
SourceLocation startLoc; // Kaynak koddaki başlangıç konumu (line, column)
|
||||
SourceLocation endLoc; // Kaynak koddaki bitiş konumu
|
||||
std::string token; // Sayının ham metni (örn: "42", "0xFF", "3.14e-2")
|
||||
bool isFloat = false; // true ise float/double literal
|
||||
bool hasEpsilon = false; // true ise bilimsel gösterim (örn: 1e10)
|
||||
|
|
@ -99,21 +45,7 @@ struct INumber {
|
|||
// ============================================================================
|
||||
// Lexer — Karakter Seviyesinde Tarayıcı
|
||||
// ============================================================================
|
||||
//
|
||||
// Derleyici pipeline'ının en alt katmanı. Ham string üzerinde çalışır.
|
||||
// Üst katmanlara (Tokenizer) karakter okuma ve konum yönetimi hizmeti sunar.
|
||||
//
|
||||
// DURUM DEĞİŞKENLERİ:
|
||||
// input : Taranan kaynak kodun tamamı (string kopyası, değişmez)
|
||||
// size : input.length() önbelleği (performans: her seferinde hesaplamaz)
|
||||
// offset : Mevcut okuma konumu. 0 = ilk karakter, size = EOF
|
||||
// offsetMap : Backtracking yığını. İç içe beginPosition/acceptPosition/rejectPosition
|
||||
//
|
||||
// PERFORMANS NOTU:
|
||||
// Tüm metotlar inline tanımlanmıştır. Sanal fonksiyon çağrısı yoktur.
|
||||
// offset değişiklikleri O(1)'dir.
|
||||
// include() metodu O(n) karakter karşılaştırması yapar (n = kelime uzunluğu).
|
||||
//
|
||||
|
||||
class Lexer {
|
||||
public:
|
||||
// --- Ham Veri ---
|
||||
|
|
@ -123,14 +55,6 @@ public:
|
|||
std::vector<int> offsetMap; // Backtracking yığını
|
||||
|
||||
// --- Pozisyon Yönetimi (Backtracking API) ---
|
||||
//
|
||||
// Kullanım örneği:
|
||||
// lexer.beginPosition(); // konumu kaydet
|
||||
// if (lexer.include("for", false)) // dene (false = eşleşse de geri al)
|
||||
// lexer.acceptPosition(); // başarılı → kalıcı yap
|
||||
// else
|
||||
// lexer.rejectPosition(); // başarısız → geri al
|
||||
|
||||
void beginPosition(); // Şu anki konumu yığına kaydet
|
||||
int getLastPosition(); // Yığındaki son konumu döndür
|
||||
void acceptPosition(); // Yığındaki son konumu kalıcı yap (apply)
|
||||
|
|
@ -143,10 +67,6 @@ public:
|
|||
std::string getPositionRange(); // Pozisyon aralığındaki metni döndür
|
||||
|
||||
// --- Desen Eşleme ---
|
||||
// include(): Belirtilen kelime mevcut konumda başlıyor mu?
|
||||
// accept=true (varsayılan): eşleşirse konum ilerletilir
|
||||
// accept=false: eşleşse bile konum geri alınır (keyword kontrolü için)
|
||||
// Örnek: include("for", false) → "for" ile başlıyor mu? konumu değiştirme.
|
||||
bool include(std::string word, bool accept = true);
|
||||
|
||||
// --- Konum Okuma/Yazma ---
|
||||
|
|
@ -160,403 +80,14 @@ public:
|
|||
void toChar(int n); // offset'i n kadar ilerlet
|
||||
|
||||
// --- Üst Seviye İşlemler ---
|
||||
SourceFile sourceFile; // Kaynak kod ve satır başı offset'leri
|
||||
SourceLocation getLocation(); // Mevcut offset'in SourceLocation'ını döndür
|
||||
void setSourceText(const std::string& path, const std::string& text);
|
||||
|
||||
void setText(std::string input); // Yeni kaynak kodu yükle
|
||||
void skipWhiteSpace(); // Boşluk/sekme/satırsonu karakterlerini atla
|
||||
bool isNumeric(); // Mevcut karakter 0-9 aralığında mı?
|
||||
INumber readNumeric(); // Sayı literal'ı oku ve INumber olarak döndür
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// GERÇEKLEME (Implementation)
|
||||
// ============================================================================
|
||||
// Tüm metotlar inline olarak aşağıda tanımlanmıştır.
|
||||
// Derleme modeli: header-only. main.cpp bu dosyayı include eder.
|
||||
// ============================================================================
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// beginPosition: Mevcut offset'i yığına kaydet.
|
||||
// İç içe çağrılabilir: 3 kere beginPosition → 3 elemanlı yığın.
|
||||
// --------------------------------------------------------------------------
|
||||
inline void Lexer::beginPosition() {
|
||||
offsetMap.push_back(getLastPosition());
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// getLastPosition: Yığının tepesindeki konumu döndür.
|
||||
// Yığın boşsa mevcut offset'i döndür (başlangıç durumu).
|
||||
// --------------------------------------------------------------------------
|
||||
inline int Lexer::getLastPosition() {
|
||||
if (offsetMap.empty()) return offset;
|
||||
return offsetMap.back();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// acceptPosition: Yığındaki son geçici konumu kalıcı yap.
|
||||
// Örnek: offsetMap=[5,10], offset=15 → offsetMap.back()=10 olur.
|
||||
// Bu sayede include() denemesi başarılı olduğunda konum ilerletilmiş olur.
|
||||
// --------------------------------------------------------------------------
|
||||
inline void Lexer::acceptPosition() {
|
||||
int t = offsetMap.back();
|
||||
setLastPosition(t);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// setLastPosition: Yığının tepesini veya offset'i değiştir.
|
||||
// --------------------------------------------------------------------------
|
||||
inline void Lexer::setLastPosition(int n) {
|
||||
if (offsetMap.empty())
|
||||
offset = n;
|
||||
else
|
||||
offsetMap.back() = n;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// isEnd: Dosya sonuna gelindi mi? offset >= size.
|
||||
// --------------------------------------------------------------------------
|
||||
inline bool Lexer::isEnd() {
|
||||
return size <= getOffset();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// rejectPosition: Yığındaki son konumu at. Başarısız include() denemesi sonrası.
|
||||
// --------------------------------------------------------------------------
|
||||
inline void Lexer::rejectPosition() {
|
||||
offsetMap.pop_back();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// positionRange: Yığındaki en dış ve en iç konumu [start, end] olarak döndür.
|
||||
// UYARI: new int[2] ile heap'te tahsis eder. Çağıran sorumludur.
|
||||
// TODO: std::pair<int,int> veya yapı kullanarak tahsisi kaldır.
|
||||
// --------------------------------------------------------------------------
|
||||
inline int* Lexer::positionRange() {
|
||||
int len = offsetMap.size();
|
||||
if (len == 0)
|
||||
return new int[2]{0, offset};
|
||||
if (len == 1)
|
||||
return new int[2]{offset, offsetMap[0]};
|
||||
return new int[2]{offsetMap[len - 2], offsetMap[len - 1]};
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// getPositionRange: positionRange() aralığındaki metni string olarak döndür.
|
||||
// --------------------------------------------------------------------------
|
||||
inline std::string Lexer::getPositionRange() {
|
||||
int* a = positionRange();
|
||||
std::string mem;
|
||||
for (int i = a[0]; i < a[1]; i++)
|
||||
mem.push_back(input.at(i));
|
||||
return mem;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// include: Belirtilen kelime mevcut konumda başlıyor mu?
|
||||
//
|
||||
// Algoritma:
|
||||
// 1. beginPosition() ile konumu kaydet
|
||||
// 2. Kelimenin her karakterini sırayla karşılaştır
|
||||
// 3. Eşleşmezse veya EOF olursa → rejectPosition() ve false dön
|
||||
// 4. Tüm karakterler eşleşirse:
|
||||
// - accept=true ise → acceptPosition() (konum kalıcı ilerler)
|
||||
// - accept=false ise → rejectPosition() (konum eski haline döner)
|
||||
// 5. true dön
|
||||
//
|
||||
// Neden accept parametresi var?
|
||||
// Tokenizer scope() fonksiyonu, keyword'leri kontrol ederken accept=false
|
||||
// kullanır. Çünkü bir keyword eşleşmesi, aynı zamanda daha uzun bir
|
||||
// keyword'ün parçası olabilir (örn: "do", "double"ın başlangıcı).
|
||||
// Eğer include("do", true) kullanılırsa, konum ilerler ve geri alınamaz.
|
||||
// --------------------------------------------------------------------------
|
||||
inline bool Lexer::include(std::string word, bool accept) {
|
||||
beginPosition();
|
||||
for (size_t i = 0; i < word.size(); i++) {
|
||||
if (isEnd()) {
|
||||
rejectPosition();
|
||||
return false;
|
||||
}
|
||||
if (word[i] != getchar()) {
|
||||
rejectPosition();
|
||||
return false;
|
||||
}
|
||||
nextChar();
|
||||
}
|
||||
if (accept)
|
||||
acceptPosition();
|
||||
else
|
||||
rejectPosition();
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// getOffset / setOffset: Konum erişimcileri.
|
||||
// --------------------------------------------------------------------------
|
||||
inline int Lexer::getOffset() {
|
||||
return getLastPosition();
|
||||
}
|
||||
|
||||
inline int Lexer::setOffset(int n) {
|
||||
setLastPosition(n);
|
||||
return getLastPosition();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// getchar(additionalOffset): offset + ek kadar ilerideki karakteri oku.
|
||||
// Sınır kontrolü yapar: target >= size ise '\0' döndürür ve hata mesajı basar.
|
||||
// Bu metot özellikle keyword sınır kontrolünde kullanılır:
|
||||
// "do" eşleşti, sıradaki karakter 'u' ise bu "double" olabilir → keyword değil
|
||||
// --------------------------------------------------------------------------
|
||||
inline char Lexer::getchar(int additionalOffset) {
|
||||
int target = getOffset() + additionalOffset;
|
||||
if (target >= size) {
|
||||
std::cerr << "Lexer hatası: sınır aşımı\n";
|
||||
return '\0';
|
||||
}
|
||||
return input.at(target);
|
||||
}
|
||||
|
||||
inline char Lexer::getchar() {
|
||||
int target = getOffset();
|
||||
if (target >= size) {
|
||||
std::cerr << "Lexer hatası: sınır aşımı\n";
|
||||
return '\0';
|
||||
}
|
||||
return input.at(target);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// nextChar / toChar: Konum ilerletme.
|
||||
// EOF kontrolü yapar — dosya sonundaysa ilerlemez.
|
||||
// --------------------------------------------------------------------------
|
||||
inline void Lexer::nextChar() {
|
||||
if (!isEnd())
|
||||
setOffset(getOffset() + 1);
|
||||
}
|
||||
|
||||
inline void Lexer::toChar(int n) {
|
||||
if (!isEnd())
|
||||
setOffset(getOffset() + n);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// setText: Yeni kaynak kodu yükle. input ve size'ı günceller.
|
||||
// --------------------------------------------------------------------------
|
||||
inline void Lexer::setText(std::string text) {
|
||||
input = text;
|
||||
size = text.length();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// skipWhiteSpace: Boşluk, sekme, satırsonu, satırbaşı karakterlerini atla.
|
||||
// Yorum satırlarını atlamaz — bu Tokenizer'ın işi.
|
||||
// --------------------------------------------------------------------------
|
||||
inline void Lexer::skipWhiteSpace() {
|
||||
while (!isEnd()) {
|
||||
switch (getchar()) {
|
||||
case '\r': // carriage return (Windows satırsonu \r\n)
|
||||
case '\n': // line feed (Unix satırsonu)
|
||||
case '\b': // backspace
|
||||
case '\t': // tab
|
||||
case ' ': // boşluk
|
||||
nextChar();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// isNumeric: Mevcut karakter bir rakam mı? (0-9)
|
||||
// Pointer aritmetiği veya ASCII tablosu karşılaştırması yerine basit aralık
|
||||
// kontrolü. Performans: O(1), branchless (modern derleyiciler optimize eder).
|
||||
// --------------------------------------------------------------------------
|
||||
inline bool Lexer::isNumeric() {
|
||||
char c = getchar();
|
||||
return (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// readNumeric: Tam bir sayı literal'ı oku.
|
||||
//
|
||||
// Desteklenen formatlar (öncelik sırasıyla):
|
||||
// 1. İşaret: -42, +3 (baştaki isteğe bağlı işaret)
|
||||
// 2. 0x/0X: Hex (0xFF, 0X1A)
|
||||
// 3. 0b/0B: Binary (0b1010)
|
||||
// 4. 0 ile başlayan: Octal (0777) veya Float (0.5)
|
||||
// 5. Ondalık: 42, 3.14
|
||||
// 6. Bilimsel: 1e10, 2.5E-3, 1.0e+5
|
||||
//
|
||||
// Algoritma:
|
||||
// 1. İsteğe bağlı işareti oku (+ veya -)
|
||||
// 2. İlk karakter '0' ise → özel durum (hex/bin/octal/float kontrolü)
|
||||
// 3. Ana döngü: rakamları, hex harflerini (a-f/A-F), nokta (.), epsilon (e/E) oku
|
||||
// 4. Her karakterde taban uygunluğunu kontrol et (örn: octal'da 8-9 geçersiz)
|
||||
// 5. İlk karakter '0' değilse → doğrudan decimal
|
||||
//
|
||||
// Özel durum: "0" takip eden karakter yoksa → tek haneli sayı, base=10.
|
||||
// "0xFF" → hex, "0b10" → binary, "077" → octal, "0.5" → float.
|
||||
//
|
||||
// TODO: Hex float desteği (0x1.ffp10) — C99 standardı
|
||||
// TODO: Sayısal ayraç: 1_000_000 — C++14/Java 7
|
||||
// --------------------------------------------------------------------------
|
||||
inline INumber Lexer::readNumeric() {
|
||||
INumber num;
|
||||
num.start = getLastPosition();
|
||||
|
||||
// --- Adım 1: İsteğe bağlı işaret ---
|
||||
if (getchar() == '-') {
|
||||
nextChar();
|
||||
num.positive = false;
|
||||
} else if (getchar() == '+') {
|
||||
nextChar();
|
||||
num.positive = true;
|
||||
} else {
|
||||
num.positive = true;
|
||||
}
|
||||
|
||||
// --- Adım 2: İlk karakter '0' ise özel format kontrolü ---
|
||||
bool nextDot = false;
|
||||
if (getchar() == '0') {
|
||||
num.token.push_back('0');
|
||||
nextChar();
|
||||
char c = getchar();
|
||||
switch (c) {
|
||||
case 'x': case 'X': // Hex: 0xFF, 0X1A
|
||||
num.token.push_back(c);
|
||||
num.base = 16;
|
||||
nextChar();
|
||||
break;
|
||||
case 'b': case 'B': // Binary: 0b1010
|
||||
num.token.push_back(c);
|
||||
num.base = 2;
|
||||
nextChar();
|
||||
break;
|
||||
case '.': // Float: 0.5, 0.0
|
||||
num.token.push_back(c);
|
||||
num.base = 10;
|
||||
nextDot = true;
|
||||
num.isFloat = true;
|
||||
nextChar();
|
||||
break;
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7':
|
||||
// Octal: 0777 — sonraki karakter octal rakam ise devam et
|
||||
num.base = 8;
|
||||
break;
|
||||
default:
|
||||
// Sadece "0" — takip eden karakter rakam değil.
|
||||
// Hemen dön: base=10 (varsayılan).
|
||||
// BUG FIX (commit 438bc0e): Eskiden bu dalda sıradaki karakter
|
||||
// token'a ekleniyor ve base=8 yapılıyordu. Bu, "0;" durumunda
|
||||
// ';' karakterinin sayıya eklenmesine neden oluyordu.
|
||||
num.end = getLastPosition();
|
||||
return num;
|
||||
}
|
||||
} else {
|
||||
num.base = 10;
|
||||
}
|
||||
|
||||
// --- Adım 3: Ana okuma döngüsü ---
|
||||
// Bu döngü, geçerli tabana uygun tüm karakterleri okur.
|
||||
// Her karakter tipi için taban uygunluğu kontrol edilir:
|
||||
// - 0-1: tüm tabanlar
|
||||
// - 2-7: base >= 8
|
||||
// - 8-9: base >= 10
|
||||
// - a-f/A-F: base >= 16
|
||||
// - . (nokta): sadece ondalık, sadece bir kere
|
||||
// - e/E: sadece ondalık ve hex (hex'te epsilon yok, direkt okunur)
|
||||
while (!isEnd()) {
|
||||
char c = getchar();
|
||||
switch (c) {
|
||||
case '0':
|
||||
case '1':
|
||||
num.token.push_back(c);
|
||||
break;
|
||||
case '2': case '3': case '4': case '5':
|
||||
case '6': case '7':
|
||||
if (num.base >= 8)
|
||||
num.token.push_back(c);
|
||||
else {
|
||||
num.end = getLastPosition();
|
||||
return num;
|
||||
}
|
||||
break;
|
||||
case '8': case '9':
|
||||
if (num.base >= 10)
|
||||
num.token.push_back(c);
|
||||
else {
|
||||
num.end = getLastPosition();
|
||||
return num;
|
||||
}
|
||||
break;
|
||||
case 'a': case 'A': case 'b': case 'B':
|
||||
case 'c': case 'C': case 'd': case 'D':
|
||||
case 'f': case 'F':
|
||||
if (num.base >= 16)
|
||||
num.token.push_back(c);
|
||||
else {
|
||||
num.end = getLastPosition();
|
||||
return num;
|
||||
}
|
||||
break;
|
||||
case '.':
|
||||
// Nokta: Sadece bir kere izin verilir.
|
||||
// .5 gibi başıboş noktalı sayılar için "0." öneki eklenir.
|
||||
if (!nextDot) {
|
||||
if (num.token.empty())
|
||||
num.token += "0.";
|
||||
else
|
||||
num.token.push_back('.');
|
||||
nextDot = true;
|
||||
num.isFloat = true;
|
||||
} else {
|
||||
// İkinci nokta → sayı bitti
|
||||
num.end = getLastPosition();
|
||||
return num;
|
||||
}
|
||||
break;
|
||||
case 'e': case 'E':
|
||||
// Epsilon (bilimsel gösterim):
|
||||
// - Hex tabanda: epsilon DEĞİL, hex hanesi olarak okunur.
|
||||
// - Decimal tabanda: 1e10, 2.5E-3 formatı.
|
||||
if (num.base == 16) {
|
||||
num.token.push_back(c);
|
||||
break;
|
||||
}
|
||||
if (num.base == 10) {
|
||||
num.hasEpsilon = true;
|
||||
num.token.push_back(c);
|
||||
nextChar();
|
||||
c = getchar();
|
||||
// İsteğe bağlı işaret: e+10, E-3
|
||||
if (c == '+' || c == '-') {
|
||||
num.token.push_back(c);
|
||||
nextChar();
|
||||
}
|
||||
// Epsilon sonrası rakamları oku
|
||||
while (!isEnd()) {
|
||||
c = getchar();
|
||||
if (c >= '0' && c <= '9') {
|
||||
num.token.push_back(c);
|
||||
nextChar();
|
||||
} else {
|
||||
num.end = getLastPosition();
|
||||
return num;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
num.end = getLastPosition();
|
||||
return num;
|
||||
default:
|
||||
// Tanınmayan karakter → sayı bitti
|
||||
num.end = getLastPosition();
|
||||
return num;
|
||||
}
|
||||
nextChar();
|
||||
}
|
||||
num.end = getLastPosition();
|
||||
return num;
|
||||
}
|
||||
|
||||
#endif // SAQUT_LEXER
|
||||
|
|
|
|||
174
src/main.cpp
174
src/main.cpp
|
|
@ -3,118 +3,84 @@
|
|||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/main.cpp
|
||||
// KATMAN: En üst — tüm alt katmanları birleştirir
|
||||
// BAĞIMLI: Tokenizer, Parser, IR (ve dolaylı olarak Lexer, AST, Token)
|
||||
//
|
||||
// AMAÇ:
|
||||
// Derleyici pipeline'ını başlatır:
|
||||
// 1. source.sqt dosyasını oku
|
||||
// 2. Lexing + Tokenizing
|
||||
// 3. Parsing (AST üretimi)
|
||||
// 4. IR üretimi
|
||||
// 5. Sonuçları konsola yazdır (debug modu)
|
||||
// KATMAN: En üst — CLI dispatcher'ı başlatır
|
||||
//
|
||||
// KULLANIM:
|
||||
// ./saqut → source.sqt dosyasını derler
|
||||
// echo "1+2" > source.sqt && ./saqut → hızlı test
|
||||
// saqut → yardım
|
||||
// saqut run <dosya> → pipeline debug çıktısı
|
||||
// saqut tokens <dosya> → token listesi
|
||||
// saqut ast <dosya> [-o çıktı] → JSON AST + analiz
|
||||
// saqut symbols <dosya> → sembol tablosu
|
||||
// saqut - → stdin modu (TODO)
|
||||
//
|
||||
// GELECEK:
|
||||
// - Komut satırı argümanları: ./saqut file.sqt -o output
|
||||
// - Mod seçimi: ./saqut --mode=parse|ir|compile|run
|
||||
// - Birden fazla dosya: ./saqut file1.sqt file2.sqt
|
||||
// YENİ KOMUT EKLEMEK İÇİN:
|
||||
// 1. src/cli/commands/x.hpp oluştur
|
||||
// 2. Bu dosyada #include et
|
||||
// 3. cli.registerCommand({...}) ile kaydet
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "tokenizer/tokenizer.hpp"
|
||||
#include "parser/parser.hpp"
|
||||
#include "ir/ir.hpp"
|
||||
#include "cli/args.hpp"
|
||||
#include "cli/cli.hpp"
|
||||
#include "cli/commands/run.hpp"
|
||||
#include "cli/commands/tokens.hpp"
|
||||
#include "cli/commands/ast.hpp"
|
||||
#include "cli/commands/symbols.hpp"
|
||||
|
||||
int main() {
|
||||
// ------------------------------------------------------------------
|
||||
// 1. Kaynak dosyayı oku
|
||||
// ------------------------------------------------------------------
|
||||
// Şimdilik sabit dosya adı: source.sqt.
|
||||
// TODO: argc/argv ile dosya adı al.
|
||||
// ------------------------------------------------------------------
|
||||
std::ifstream file("source.sqt", std::ios::in | std::ios::binary);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Hata: source.sqt dosyası açılamadı\n";
|
||||
return 1;
|
||||
int main(int argc, char* argv[]) {
|
||||
// Komutları kaydet
|
||||
CliDispatcher cli;
|
||||
|
||||
cli.registerCommand({"run",
|
||||
"Pipeline'ı çalıştır (token → AST → IR)",
|
||||
false, cmdRun});
|
||||
|
||||
cli.registerCommand({"tokens",
|
||||
"Token listesini göster",
|
||||
false, cmdTokens});
|
||||
|
||||
cli.registerCommand({"ast",
|
||||
"JSON formatında AST hiyerarşisi ve analiz",
|
||||
false, cmdAst});
|
||||
|
||||
cli.registerCommand({"symbols",
|
||||
"Sembol tablosu (fonksiyonlar, değişkenler)",
|
||||
false, cmdSymbols});
|
||||
|
||||
// --- Gelecek komutlar (TODO) ---
|
||||
cli.registerCommand({"compile",
|
||||
"TODO: Kaynak kodu derle",
|
||||
false, [](const CliArgs&) {
|
||||
std::cerr << "TODO: compile komutu henüz eklenmedi\n"; return 1;
|
||||
}});
|
||||
|
||||
cli.registerCommand({"parse",
|
||||
"TODO: IR üret",
|
||||
false, [](const CliArgs&) {
|
||||
std::cerr << "TODO: parse komutu henüz eklenmedi\n"; return 1;
|
||||
}});
|
||||
|
||||
cli.registerCommand({"transpile",
|
||||
"TODO: C koduna çevir",
|
||||
false, [](const CliArgs&) {
|
||||
std::cerr << "TODO: transpile komutu henüz eklenmedi\n"; return 1;
|
||||
}});
|
||||
|
||||
cli.registerCommand({"interpret",
|
||||
"TODO: Interpreter modu",
|
||||
true, [](const CliArgs&) {
|
||||
std::cerr << "TODO: interpret komutu henüz eklenmedi\n"; return 1;
|
||||
}});
|
||||
|
||||
// Argümanları ayrıştır
|
||||
CliArgs args = parseArgs(argc, argv);
|
||||
|
||||
// Argümansız çağrı → help
|
||||
if (argc <= 1) {
|
||||
cli.printHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
std::string source = buffer.str();
|
||||
file.close();
|
||||
|
||||
std::cout << "=== saQut Compiler ===\n";
|
||||
std::cout << "Kaynak kod:\n" << source << "\n\n";
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 2. Lexing → Tokenizing
|
||||
// ------------------------------------------------------------------
|
||||
// Tokenizer, Lexer'ı içerir. scan() tüm pipeline'ı çalıştırır.
|
||||
// Token'lar heap'te new ile oluşturulur, iş bitince silinmeli.
|
||||
// ------------------------------------------------------------------
|
||||
Tokenizer tokenizer;
|
||||
auto tokens = tokenizer.scan(source);
|
||||
|
||||
std::cout << "Tokenler (" << tokens.size() << " adet):\n";
|
||||
for (auto* t : tokens) {
|
||||
std::cout << " [" << t->gettype() << "] \"" << t->token << "\"\n";
|
||||
}
|
||||
std::cout << "\n";
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 3. Parsing → AST
|
||||
// ------------------------------------------------------------------
|
||||
// Parser, token listesini alır, AST üretir.
|
||||
// parse() artık parseProgram()'ı çağırır — birden fazla deklarasyon
|
||||
// veya statement içeren tam programları ayrıştırabilir.
|
||||
// ------------------------------------------------------------------
|
||||
Parser parser;
|
||||
ASTNode* ast = parser.parse(tokens);
|
||||
|
||||
if (ast) {
|
||||
std::cout << "AST:\n";
|
||||
ast->log(0); // Ağacı girintili olarak yazdır
|
||||
std::cout << "\n";
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 4. IR Üretimi
|
||||
// ------------------------------------------------------------------
|
||||
// CodeGenerator AST'yi dolaşır, sanal register makine komutları üretir.
|
||||
// Şu anda sadece matematik işlemleri ve literal'lar destekleniyor.
|
||||
// ------------------------------------------------------------------
|
||||
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;
|
||||
}
|
||||
// arg1.value.index(): 0=int, 1=float
|
||||
std::cout << " (" << op.arg1.value.index() << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 5. Temizlik
|
||||
// ------------------------------------------------------------------
|
||||
// Token'lar heap'te oluşturuldu, manuel silinmeli.
|
||||
// TODO: std::unique_ptr ile otomatik bellek yönetimi.
|
||||
// ------------------------------------------------------------------
|
||||
for (auto* t : tokens) delete t;
|
||||
|
||||
return 0;
|
||||
return cli.dispatch(args);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,513 +1,13 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Soyut Sözdizim Ağacı (AST)
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/ast.hpp
|
||||
// KATMAN: Katman 3 — Parser'ın ürettiği, IR'nin tükettiği
|
||||
// BAĞIMLI: Token (src/parser/token.hpp), Tools (src/tools.hpp)
|
||||
// KULLANAN: Parser (src/parser/parser.hpp), IR (src/ir/ir.hpp)
|
||||
//
|
||||
// AMAÇ:
|
||||
// Kaynak kodun hiyerarşik, anlamsal gösterimi. Her dil yapısı (ifade,
|
||||
// deyim, fonksiyon) bir AST düğümü ile temsil edilir.
|
||||
//
|
||||
// AST DÜĞÜM HİYERARŞİSİ:
|
||||
// ASTNode (soyut taban)
|
||||
// ├── ProgramNode : Kök düğüm, tüm üst seviye deklarasyonları tutar
|
||||
// ├── FunctionDeclNode : Fonksiyon tanımı (int main() { ... })
|
||||
// ├── BlockNode : { ... } bloğu, statement listesi
|
||||
// ├── VariableDeclNode : Değişken tanımı (int x = 10;)
|
||||
// ├── IfStatementNode : if/else
|
||||
// ├── WhileStatementNode : while döngüsü
|
||||
// ├── ForStatementNode : for döngüsü
|
||||
// ├── DoWhileStatementNode : do-while döngüsü
|
||||
// ├── ReturnStatementNode : return [ifade]
|
||||
// ├── BreakStatementNode : break
|
||||
// ├── ContinueStatementNode : continue
|
||||
// ├── ExpressionStatementNode: ifade + ; (bir statement olarak)
|
||||
// ├── BinaryExpressionNode : İkili işlem (a + b, a * b)
|
||||
// ├── LiteralNode : Sabit değer (42, "hello", true)
|
||||
// ├── IdentifierNode : Değişken/fonksiyon ismi
|
||||
// └── PostfixNode : Son ek işlem (a++, a--)
|
||||
//
|
||||
// TASARIM KARARLARI:
|
||||
// 1. ASTKind enum: Her düğüm tipi için bir enum değeri.
|
||||
// RTTI (dynamic_cast) yerine manuel tip kontrolü sağlar.
|
||||
// Daha hızlı ve hata ayıklaması kolay.
|
||||
//
|
||||
// 2. parent pointer: Her düğüm ebeveynini bilir.
|
||||
// Yukarı doğru gezinme (ör: bir döngü içinde break'in hedefini bulma).
|
||||
//
|
||||
// 3. children vektörü (protected): Sadece addChild() ile ekleme.
|
||||
// ProgramNode, FunctionDeclNode, BlockNode gibi liste tutan düğümler
|
||||
// bu vektörü kullanır. İkili işlem gibi sabit sayıda çocuğu olan
|
||||
// düğümler kendi üye değişkenlerini kullanır (Left, Right).
|
||||
//
|
||||
// 4. log() metodu: Her düğüm kendi alt ağacını girintili olarak yazdırır.
|
||||
// Debug ve test için. Gerçek kod üretimi için kullanılmaz.
|
||||
//
|
||||
// BİLİNEN SINIRLAMALAR (TODO):
|
||||
// TODO: Bellek yönetimi: AST düğümleri heap'te new ile oluşturuluyor,
|
||||
// silme sorumluluğu yok (sızıntı). unique_ptr veya arena allocator.
|
||||
// TODO: Ziyaretçi deseni (Visitor pattern) eklenerek log() ve IR
|
||||
// üretimi ayrı sınıflara taşınabilir.
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_AST
|
||||
#define SAQUT_AST
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "parser/token.hpp"
|
||||
#include "tools.hpp"
|
||||
#include "parser/ast_node.hpp"
|
||||
#include "parser/nodes/program.hpp"
|
||||
#include "parser/nodes/declarations.hpp"
|
||||
#include "parser/nodes/statements.hpp"
|
||||
#include "parser/nodes/binary_expr.hpp"
|
||||
#include "parser/nodes/literal.hpp"
|
||||
#include "parser/nodes/identifier.hpp"
|
||||
#include "parser/nodes/expressions.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// ASTKind — AST Düğüm Tipi Enum'u
|
||||
// ============================================================================
|
||||
//
|
||||
// Her AST düğüm sınıfı, constructor'ında kendi kind değerini atar.
|
||||
// CodeGenerator (IR) ve diğer AST işlemcileri, düğümün tipini bu enum
|
||||
// üzerinden belirler.
|
||||
//
|
||||
// İsimlendirme: Düğüm sınıf adları "Node" ile biter, enum değerleri bitmez.
|
||||
// Örn: sınıf=IfStatementNode, enum=IfStatement
|
||||
//
|
||||
enum class ASTKind {
|
||||
Program, // Kök düğüm
|
||||
FunctionDecl, // Fonksiyon tanımı
|
||||
Block, // { } bloğu
|
||||
VariableDecl, // Değişken tanımı
|
||||
BinaryExpression, // İkili işlem (a + b)
|
||||
UnaryExpression, // Tekli işlem (-a, !a) — ileride kullanılacak
|
||||
Literal, // Sabit değer
|
||||
Identifier, // İsim referansı
|
||||
Postfix, // Son ek (a++)
|
||||
IfStatement, // if/else
|
||||
ForStatement, // for
|
||||
WhileStatement, // while
|
||||
DoWhileStatement, // do-while
|
||||
ReturnStatement, // return
|
||||
BreakStatement, // break
|
||||
ContinueStatement, // continue
|
||||
ExpressionStatement, // ifade + ;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ASTNode — Soyut Temel Sınıf
|
||||
// ============================================================================
|
||||
//
|
||||
// Tüm AST düğümlerinin ortak atası. Minimum arayüz:
|
||||
// - kind: Düğüm tipi (ASTKind enum)
|
||||
// - parent: Ebeveyn düğüm (kök için nullptr)
|
||||
// - addChild() / getChildren(): Çocuk yönetimi
|
||||
// - log(): Debug çıktısı (virtual, her alt sınıf override eder)
|
||||
//
|
||||
class ASTNode {
|
||||
public:
|
||||
ASTKind kind; // Düğüm tipi (alt sınıf constructor'ında atanır)
|
||||
ASTNode* parent = nullptr; // Ebeveyn düğüm (kök = nullptr)
|
||||
|
||||
virtual void log(int indent = 0) {
|
||||
(void)indent; // Kullanılmayan parametre uyarısını sustur
|
||||
std::cout << "<Unknown>\n";
|
||||
}
|
||||
|
||||
// Çocuk ekleme. Otomatik olarak parent pointer'ı ayarlar.
|
||||
void addChild(ASTNode* child) {
|
||||
children.push_back(child);
|
||||
child->parent = this;
|
||||
}
|
||||
|
||||
std::vector<ASTNode*>& getChildren() { return children; }
|
||||
|
||||
virtual ~ASTNode() = default;
|
||||
|
||||
protected:
|
||||
std::vector<ASTNode*> children; // Alt düğümler (liste tipi düğümler için)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ProgramNode — Kök Düğüm
|
||||
// ============================================================================
|
||||
//
|
||||
// Her saQut programı tek bir ProgramNode ile başlar.
|
||||
// Çocukları: FunctionDeclNode, VariableDeclNode (global), ExpressionStatement.
|
||||
//
|
||||
class ProgramNode : public ASTNode {
|
||||
public:
|
||||
ProgramNode() { kind = ASTKind::Program; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "Program\n";
|
||||
for (auto* c : getChildren())
|
||||
c->log(indent + 2);
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// FunctionDeclNode — Fonksiyon Tanımı
|
||||
// ============================================================================
|
||||
//
|
||||
// Örnek: int main() { ... }
|
||||
// returnType: "int", "void", "float", ...
|
||||
// name: "main", "calculate", ...
|
||||
// children: gövde (genellikle tek bir BlockNode)
|
||||
//
|
||||
// TODO: Parametre listesi (şu anda boş)
|
||||
//
|
||||
class FunctionDeclNode : public ASTNode {
|
||||
public:
|
||||
std::string name; // Fonksiyon adı
|
||||
std::string returnType; // Dönüş tipi (string olarak, ileride tip sistemi)
|
||||
|
||||
FunctionDeclNode() { kind = ASTKind::FunctionDecl; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent)
|
||||
<< "FunctionDecl " << returnType << " " << name << "()\n";
|
||||
for (auto* c : getChildren())
|
||||
c->log(indent + 2);
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// BlockNode — Blok { ... }
|
||||
// ============================================================================
|
||||
//
|
||||
// Bir dizi statement'i gruplar. Kendi scope (kapsam) alanı oluşturur.
|
||||
// Örnek: { int x = 1; x = x + 2; }
|
||||
//
|
||||
class BlockNode : public ASTNode {
|
||||
public:
|
||||
BlockNode() { kind = ASTKind::Block; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "Block\n";
|
||||
for (auto* c : getChildren())
|
||||
c->log(indent + 2);
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// VariableDeclNode — Değişken Tanımı
|
||||
// ============================================================================
|
||||
//
|
||||
// Örnek: int x = 10;
|
||||
// varType: "int", "float", "bool", ...
|
||||
// name: "x", "counter", ...
|
||||
// initExpr: Başlangıç değeri (nullptr = tanımsız, örn: int x;)
|
||||
//
|
||||
class VariableDeclNode : public ASTNode {
|
||||
public:
|
||||
std::string varType; // Değişken tipi
|
||||
std::string name; // Değişken adı
|
||||
ASTNode* initExpr = nullptr; // Başlangıç ifadesi (opsiyonel)
|
||||
|
||||
VariableDeclNode() { kind = ASTKind::VariableDecl; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent)
|
||||
<< "VariableDecl " << varType << " " << name;
|
||||
if (initExpr) {
|
||||
std::cout << " =\n";
|
||||
initExpr->log(indent + 4);
|
||||
} else {
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// BinaryExpressionNode — İkili İşlem (a OP b)
|
||||
// ============================================================================
|
||||
//
|
||||
// İki operandlı tüm işlemler: a + b, a * b, a == b, a && b, ...
|
||||
// Unary prefix operatörler de burada temsil edilir (Left = nullptr).
|
||||
//
|
||||
// Operator: İşlem tipi (PLUS, MINUS, STAR, EQUAL_EQUAL, ...)
|
||||
// Left: Sol operand (unary prefix'te nullptr)
|
||||
// Right: Sağ operand (her zaman dolu)
|
||||
//
|
||||
// NEDEN AYRI BİR UnaryExpressionNode YOK?
|
||||
// Pratt parser'da unary ve binary operatörler aynı akışta işlenir.
|
||||
// Left'in null olması unary olduğunu belirtir. Bu, kod tekrarını önler.
|
||||
// İleride AST işlemcisi Left'e bakarak unary/binary ayrımı yapabilir.
|
||||
//
|
||||
class BinaryExpressionNode : public ASTNode {
|
||||
public:
|
||||
TokenType Operator; // İşlem tipi
|
||||
ASTNode* Left = nullptr; // Sol operand
|
||||
ASTNode* Right = nullptr; // Sağ operand
|
||||
|
||||
BinaryExpressionNode() { kind = ASTKind::BinaryExpression; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
// Operatörün enum ismini ve sembolünü göster
|
||||
auto it = OPERATOR_MAP_STRREV.find(Operator);
|
||||
std::string sym = (it != OPERATOR_MAP_STRREV.end()) ? std::string(it->second) : "?";
|
||||
std::string val;
|
||||
auto it2 = OPERATOR_MAP_REV.find(Operator);
|
||||
if (it2 != OPERATOR_MAP_REV.end()) val = std::string(it2->second);
|
||||
|
||||
std::cout << padRight("", indent) << "BinaryExpr " << sym
|
||||
<< " (" << val << ")\n";
|
||||
// Önce sağ, sonra sol yazdır — ağaç görselleştirmesi için
|
||||
if (Right) Right->log(indent + 2);
|
||||
if (Left) Left->log(indent + 2);
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// LiteralNode — Sabit Değer
|
||||
// ============================================================================
|
||||
//
|
||||
// Kaynak kodda doğrudan yazılan değerler: 42, "hello", true, false, null.
|
||||
// lexerToken: Orijinal Token (NumberToken ise isFloat/base bilgisi)
|
||||
// parserToken: Parser'ın atadığı tip bilgisi
|
||||
//
|
||||
class LiteralNode : public ASTNode {
|
||||
public:
|
||||
Token* lexerToken = nullptr; // Tokenizer'dan gelen orijinal token
|
||||
ParserToken parserToken; // Parser tarafından zenginleştirilmiş token
|
||||
|
||||
LiteralNode() { kind = ASTKind::Literal; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent)
|
||||
<< "Literal {" << parserToken.token->token << "}\n";
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// IdentifierNode — Tanımlayıcı Referansı
|
||||
// ============================================================================
|
||||
//
|
||||
// Değişken, fonksiyon, veya tip ismi. Örn: x, myVar, calculate.
|
||||
// İleride symbol table ile çözümlenecek (bu değişken nerede tanımlı?).
|
||||
//
|
||||
class IdentifierNode : public ASTNode {
|
||||
public:
|
||||
Token* lexerToken = nullptr;
|
||||
ParserToken parserToken;
|
||||
|
||||
IdentifierNode() { kind = ASTKind::Identifier; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent)
|
||||
<< "Identifier {" << parserToken.token->token << "}\n";
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// PostfixNode — Son Ek İşlem (a++, a--)
|
||||
// ============================================================================
|
||||
//
|
||||
// Operand'dan SONRA gelen operatör. Şu anda sadece ++ ve --.
|
||||
// operand: İşlem yapılan ifade (genellikle IdentifierNode)
|
||||
// Operator: PLUS_PLUS veya MINUS_MINUS
|
||||
//
|
||||
class PostfixNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* operand = nullptr; // İşlem yapılan ifade
|
||||
TokenType Operator; // PLUS_PLUS veya MINUS_MINUS
|
||||
|
||||
PostfixNode() { kind = ASTKind::Postfix; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
auto it = OPERATOR_MAP_STRREV.find(Operator);
|
||||
std::string sym = (it != OPERATOR_MAP_STRREV.end()) ? std::string(it->second) : "?";
|
||||
|
||||
std::cout << padRight("", indent) << "Postfix " << sym;
|
||||
auto it2 = OPERATOR_MAP_REV.find(Operator);
|
||||
if (it2 != OPERATOR_MAP_REV.end())
|
||||
std::cout << " (" << it2->second << ")";
|
||||
std::cout << "\n";
|
||||
if (operand) operand->log(indent + 2);
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// IfStatementNode — if / else
|
||||
// ============================================================================
|
||||
//
|
||||
// condition: Koşul ifadesi (parantez içindeki)
|
||||
// thenBranch: if gövdesi (BlockNode veya tek statement)
|
||||
// elseBranch: else gövdesi (opsiyonel, nullptr = else yok)
|
||||
//
|
||||
class IfStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* condition = nullptr; // Koşul
|
||||
ASTNode* thenBranch = nullptr; // if gövdesi
|
||||
ASTNode* elseBranch = nullptr; // else gövdesi (opsiyonel)
|
||||
|
||||
IfStatementNode() { kind = ASTKind::IfStatement; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "IfStatement\n";
|
||||
std::cout << padRight("", indent + 2) << "Condition:\n";
|
||||
if (condition) condition->log(indent + 4);
|
||||
std::cout << padRight("", indent + 2) << "Then:\n";
|
||||
if (thenBranch) thenBranch->log(indent + 4);
|
||||
if (elseBranch) {
|
||||
std::cout << padRight("", indent + 2) << "Else:\n";
|
||||
elseBranch->log(indent + 4);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// WhileStatementNode — while Döngüsü
|
||||
// ============================================================================
|
||||
//
|
||||
// while (condition) body
|
||||
//
|
||||
class WhileStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* condition = nullptr; // Döngü koşulu
|
||||
ASTNode* body = nullptr; // Döngü gövdesi
|
||||
|
||||
WhileStatementNode() { kind = ASTKind::WhileStatement; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "WhileStatement\n";
|
||||
std::cout << padRight("", indent + 2) << "Condition:\n";
|
||||
if (condition) condition->log(indent + 4);
|
||||
std::cout << padRight("", indent + 2) << "Body:\n";
|
||||
if (body) body->log(indent + 4);
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ForStatementNode — for Döngüsü
|
||||
// ============================================================================
|
||||
//
|
||||
// for (init; condition; update) body
|
||||
//
|
||||
// init: Başlangıç (VariableDeclNode veya ExpressionStatementNode)
|
||||
// condition: Devam koşulu (nullptr = sonsuz döngü)
|
||||
// update: Her adımda çalışan ifade
|
||||
// body: Döngü gövdesi
|
||||
//
|
||||
class ForStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* init = nullptr; // Başlangıç
|
||||
ASTNode* condition = nullptr; // Koşul
|
||||
ASTNode* update = nullptr; // Güncelleme
|
||||
ASTNode* body = nullptr; // Gövde
|
||||
|
||||
ForStatementNode() { kind = ASTKind::ForStatement; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "ForStatement\n";
|
||||
if (init) {
|
||||
std::cout << padRight("", indent + 2) << "Init:\n";
|
||||
init->log(indent + 4);
|
||||
}
|
||||
if (condition) {
|
||||
std::cout << padRight("", indent + 2) << "Condition:\n";
|
||||
condition->log(indent + 4);
|
||||
}
|
||||
if (update) {
|
||||
std::cout << padRight("", indent + 2) << "Update:\n";
|
||||
update->log(indent + 4);
|
||||
}
|
||||
std::cout << padRight("", indent + 2) << "Body:\n";
|
||||
if (body) body->log(indent + 4);
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// DoWhileStatementNode — do-while Döngüsü
|
||||
// ============================================================================
|
||||
//
|
||||
// do body while (condition);
|
||||
//
|
||||
class DoWhileStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* condition = nullptr;
|
||||
ASTNode* body = nullptr;
|
||||
|
||||
DoWhileStatementNode() { kind = ASTKind::DoWhileStatement; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "DoWhileStatement\n";
|
||||
std::cout << padRight("", indent + 2) << "Body:\n";
|
||||
if (body) body->log(indent + 4);
|
||||
std::cout << padRight("", indent + 2) << "Condition:\n";
|
||||
if (condition) condition->log(indent + 4);
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ReturnStatementNode — return [ifade]
|
||||
// ============================================================================
|
||||
//
|
||||
// value = nullptr ise "return;" (void fonksiyonda)
|
||||
// value dolu ise "return expr;"
|
||||
//
|
||||
class ReturnStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* value = nullptr; // Dönüş değeri (opsiyonel)
|
||||
|
||||
ReturnStatementNode() { kind = ASTKind::ReturnStatement; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "ReturnStatement";
|
||||
if (value) {
|
||||
std::cout << "\n";
|
||||
value->log(indent + 2);
|
||||
} else {
|
||||
std::cout << " (void)\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// BreakStatementNode — break
|
||||
// ============================================================================
|
||||
//
|
||||
// En yakın döngüden veya switch'ten çıkar.
|
||||
//
|
||||
class BreakStatementNode : public ASTNode {
|
||||
public:
|
||||
BreakStatementNode() { kind = ASTKind::BreakStatement; }
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "BreakStatement\n";
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ContinueStatementNode — continue
|
||||
// ============================================================================
|
||||
//
|
||||
// En yakın döngünün başına atlar.
|
||||
//
|
||||
class ContinueStatementNode : public ASTNode {
|
||||
public:
|
||||
ContinueStatementNode() { kind = ASTKind::ContinueStatement; }
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "ContinueStatement\n";
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// ExpressionStatementNode — İfadeyi Statement Olarak Sarma
|
||||
// ============================================================================
|
||||
//
|
||||
// Bir ifadeyi (expression) statement bağlamında kullanmak için sarar.
|
||||
// Örn: x = 5; → ExpressionStatementNode( BinaryExpressionNode(x, =, 5) )
|
||||
//
|
||||
class ExpressionStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* expression = nullptr; // İç ifade
|
||||
|
||||
ExpressionStatementNode() { kind = ASTKind::ExpressionStatement; }
|
||||
|
||||
void log(int indent = 0) override {
|
||||
std::cout << padRight("", indent) << "ExpressionStatement\n";
|
||||
if (expression) expression->log(indent + 2);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SAQUT_AST
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,228 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Temiz JSON Üretici (JsonObject)
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/ast_json.hpp
|
||||
// KATMAN: Katman 3 — Parser (AST JSON serileştirme)
|
||||
// AMAÇ: AST düğümlerinin toJson() metotlarında kullanılan builder pattern
|
||||
// BAĞIMLI: Yok (sadece <string>, <sstream>)
|
||||
//
|
||||
// AMAÇ:
|
||||
// AST düğümlerinin toJson() metotlarını okunabilir kılmak.
|
||||
// stringstream'i el ile yönetmek yerine builder pattern kullanır.
|
||||
//
|
||||
// KULLANIM:
|
||||
// JsonObject obj(depth);
|
||||
// obj.add("kind", "Literal");
|
||||
// obj.add("value", 42);
|
||||
// obj.add("location", loc.toJson()); // ham JSON gömme
|
||||
// return obj.str();
|
||||
//
|
||||
// TASARIM KARARLARI:
|
||||
// 1. Builder pattern: add() çağrıları zincirlenemez ama okunabilirlik kazanır.
|
||||
// Zincirleme için: return obj.add("a",1).add("b",2).str() — tercih edilmedi.
|
||||
// 2. addRaw(): Önceden formatlanmış JSON (alt düğüm çıktısı) gömmek için.
|
||||
// 3. addArray(): Callback ile dizi oluşturma — C++ lambda'ları sayesinde temiz.
|
||||
// 4. addIfNotEmpty/addIfNot: Koşullu alanlar — null alanları JSON'da göstermemek için.
|
||||
// JSON çıktısını temiz tutar.
|
||||
// 5. JSON_INDENT = 2: Standart JSON girinti (4 değil, 2 okunabilir).
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_AST_JSON
|
||||
#define SAQUT_AST_JSON
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
// ============================================================================
|
||||
// JSON_INDENT — JSON girinti miktarı (boşluk sayısı)
|
||||
// ============================================================================
|
||||
// tools.hpp'deki jsonIndent() ile uyumlu olmalıdır.
|
||||
// Her seviyede 2 boşluk içe kaydırılır.
|
||||
#define JSON_INDENT 2
|
||||
|
||||
// jsonEscape ve jsonIndent tools.hpp'de tanımlıdır.
|
||||
|
||||
// ============================================================================
|
||||
// JsonObject — JSON Nesne Builder
|
||||
// ============================================================================
|
||||
//
|
||||
// AST düğümlerini JSON formatına dönüştürmek için kullanılır.
|
||||
// Her çağrıda yeni bir JsonObject oluşturulur, alanlar eklenir ve str() ile
|
||||
// JSON stringi alınır.
|
||||
//
|
||||
// KULLANIM:
|
||||
// JsonObject obj(depth);
|
||||
// obj.add("kind", "FunctionDecl");
|
||||
// obj.add("name", name);
|
||||
// obj.add("returnType", returnType);
|
||||
// obj.addRaw("location", loc.toJson()); // önceden formatlanmış JSON
|
||||
// obj.addArray("children", [&] { // alt düğümler
|
||||
// for (auto* child : children)
|
||||
// obj.addChild(child->toJson(depth + 2));
|
||||
// });
|
||||
// return obj.str();
|
||||
//
|
||||
// ÖRNEK ÇIKTI (depth=0):
|
||||
// {
|
||||
// "kind": "FunctionDecl",
|
||||
// "name": "main",
|
||||
// "returnType": "int",
|
||||
// "children": [ ... ]
|
||||
// }
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
class JsonObject {
|
||||
public:
|
||||
// JsonObject — Yapıcı
|
||||
// PARAMETRE: depth — JSON girinti seviyesi (0 = en dış)
|
||||
// YAN ETKİ: m_ss'e açılış süslü parantezi yazar
|
||||
JsonObject(int depth)
|
||||
: m_indent(jsonIndent(depth)),
|
||||
m_indentInner(jsonIndent(depth + 1))
|
||||
{
|
||||
m_ss << m_indent << "{\n";
|
||||
}
|
||||
|
||||
// add() — String alan ekle
|
||||
// PARAMETRELER:
|
||||
// key — JSON anahtarı (tırnak içinde yazılır)
|
||||
// value — string değer (otomatik tırnaklanır ve escape edilir)
|
||||
// YAN ETKİ: m_hasFields true olur
|
||||
// ÖRN: obj.add("name", "main") → "name": "main"
|
||||
void add(const std::string& key, const std::string& value) {
|
||||
addRaw(key, "\"" + jsonEscape(value) + "\"");
|
||||
}
|
||||
|
||||
// add() — Sayısal alan ekle
|
||||
// PARAMETRELER:
|
||||
// key — JSON anahtarı
|
||||
// value — tamsayı değer (tırnaklanmaz, olduğu gibi yazılır)
|
||||
// ÖRN: obj.add("line", 42) → "line": 42
|
||||
void add(const std::string& key, int value) {
|
||||
addRaw(key, std::to_string(value));
|
||||
}
|
||||
|
||||
// add() — Boolean alan ekle
|
||||
// PARAMETRELER:
|
||||
// key — JSON anahtarı
|
||||
// value — true/false
|
||||
// ÖRN: obj.add("isPublic", true) → "isPublic": true
|
||||
void add(const std::string& key, bool value) {
|
||||
addRaw(key, value ? "true" : "false");
|
||||
}
|
||||
|
||||
// addRaw() — Ham JSON değeri ekle (önceden formatlanmış)
|
||||
// PARAMETRELER:
|
||||
// key — JSON anahtarı
|
||||
// jsonValue — önceden JSON'a çevrilmiş değer (tırnaklanmaz!)
|
||||
// KULLANIM: Alt düğüm toJson() çıktısını gömmek için.
|
||||
// addRaw("location", loc.toJson());
|
||||
void addRaw(const std::string& key, const std::string& jsonValue) {
|
||||
if (m_hasFields) m_ss << ",\n";
|
||||
m_ss << m_indentInner << "\"" << jsonEscape(key) << "\": " << jsonValue;
|
||||
m_hasFields = true;
|
||||
}
|
||||
|
||||
// addNested() — Alt nesne ekle (addRaw alias)
|
||||
// PARAMETRELER: addRaw ile aynı
|
||||
// KULLANIM: addRaw ile aynı. Sadece okunabilirlik için.
|
||||
void addNested(const std::string& key, const std::string& nestedJson) {
|
||||
addRaw(key, nestedJson);
|
||||
}
|
||||
|
||||
// addIfNotEmpty() — Koşullu string alan
|
||||
// PARAMETRELER:
|
||||
// key — JSON anahtarı
|
||||
// value — string değer (sadece boş DEĞİLSE eklenir)
|
||||
// KULLANIM: Opsiyonel alanlar için. JSON çıktısını temiz tutar.
|
||||
// obj.addIfNotEmpty("defaultValue", defaultVal);
|
||||
void addIfNotEmpty(const std::string& key, const std::string& value) {
|
||||
if (!value.empty()) add(key, value);
|
||||
}
|
||||
|
||||
// addIfNot() — Koşullu sayı alan
|
||||
// PARAMETRELER:
|
||||
// key — JSON anahtarı
|
||||
// value — mevcut değer
|
||||
// defaultValue — varsayılan değer
|
||||
// EKLEME KOŞULU: value != defaultValue
|
||||
// KULLANIM: Varsayılan değerler JSON'da tekrarlanmaz.
|
||||
// obj.addIfNot("precedence", 0, 14);
|
||||
void addIfNot(const std::string& key, int value, int defaultValue) {
|
||||
if (value != defaultValue) add(key, value);
|
||||
}
|
||||
|
||||
// addArray() — Dizi alanı (callback ile)
|
||||
// PARAMETRELER:
|
||||
// key — JSON anahtarı
|
||||
// callback — dizi elemanlarını addItem ile ekleyen lambda/fonksiyon
|
||||
// KULLANIM:
|
||||
// obj.addArray("children", [&] {
|
||||
// for (auto* child : children)
|
||||
// obj.addItem(child->toJson(depth + 2));
|
||||
// });
|
||||
// ÖRNEK ÇIKTI:
|
||||
// "children": [
|
||||
// { "kind": "Literal", ... },
|
||||
// { "kind": "Identifier", ... }
|
||||
// ]
|
||||
template<typename Fn>
|
||||
void addArray(const std::string& key, Fn callback) {
|
||||
if (m_hasFields) m_ss << ",\n";
|
||||
m_ss << m_indentInner << "\"" << jsonEscape(key) << "\": [\n";
|
||||
m_arrayDepth++;
|
||||
callback();
|
||||
m_arrayDepth--;
|
||||
m_ss << "\n" << m_indentInner << "]";
|
||||
m_hasFields = true;
|
||||
}
|
||||
|
||||
// addItem() — Diziye eleman ekle
|
||||
// PARAMETRE: itemJson — JSON formatında dizi elemanı
|
||||
// KULLANIM: Sadece addArray callback'i içinde kullanılır.
|
||||
// YAN ETKİ: m_hasArrayItem true olur (virgül kontrolü için)
|
||||
void addItem(const std::string& itemJson) {
|
||||
if (m_hasArrayItem) m_ss << ",";
|
||||
// Öğeler m_indentInner'in bir seviye altında (depth + 2)
|
||||
std::string itemIndent = "";
|
||||
itemIndent.append(m_indentInner.size() + 2, ' ');
|
||||
m_ss << "\n" << itemIndent << itemJson;
|
||||
m_hasArrayItem = true;
|
||||
}
|
||||
|
||||
// str() — JSON nesnesini kapat ve string olarak döndür
|
||||
// DÖNÜŞ: Tam JSON stringi ({"key": "value", ...})
|
||||
// YAN ETKİ: Kapanış süslü parantezini ekler.
|
||||
// KULLANIM:
|
||||
// JsonObject obj(depth);
|
||||
// obj.add("kind", "FunctionDecl");
|
||||
// return obj.str();
|
||||
std::string str() {
|
||||
m_ss << "\n" << m_indent << "}";
|
||||
return m_ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
/* ====== Builder State ====== */
|
||||
std::ostringstream m_ss; // JSON çıktısının biriktirildiği string stream
|
||||
|
||||
std::string m_indent; // Bu nesnenin girinti seviyesi (depth * 2 boşluk)
|
||||
// Örn: depth=0 → "", depth=1 → " "
|
||||
|
||||
std::string m_indentInner; // Bir alt seviye girinti ((depth+1) * 2 boşluk)
|
||||
// Örn: depth=0 → " ", depth=1 → " "
|
||||
|
||||
bool m_hasFields = false; // Alan eklendi mi? (virgül kontrolü için)
|
||||
// true ise bir sonraki alandan önce virgül + newline
|
||||
|
||||
int m_arrayDepth = 0; // İç içe dizi seviyesi (şu anda kullanılmıyor,
|
||||
// ileride çok boyutlu diziler için)
|
||||
|
||||
bool m_hasArrayItem = false; // Diziye eleman eklendi mi? (virgül kontrolü)
|
||||
// true ise bir sonraki elemandan önce virgül
|
||||
};
|
||||
|
||||
#endif // SAQUT_AST_JSON
|
||||
|
|
@ -0,0 +1,280 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — AST Düğüm Tabanı (ASTNode, ASTKind, LiteralType)
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/ast_node.hpp
|
||||
// KATMAN: Katman 3 — Parser (Ayrıştırıcı)
|
||||
// AMAÇ: Tüm AST düğümlerinin taban sınıfını ve temel enum'ları tanımlamak
|
||||
//
|
||||
// BAĞIMLILIKLAR:
|
||||
// - core/location.hpp: Kaynak kod konum bilgisi (SourceLocation)
|
||||
// - parser/token.hpp: Token tipleri (TokenType, ParserToken)
|
||||
// - tools.hpp: Yardımcı fonksiyonlar (jsonIndent vb.)
|
||||
//
|
||||
// MİMARİ KARARLAR:
|
||||
// 1. ASTNode, tüm düğümlerin ortak davranışını (log, toJson, children)
|
||||
// tek bir yerde tanımlar. NVI (Non-Virtual Interface) pattern'i.
|
||||
// 2. virtual log() ve toJson() — her düğüm kendi çıktısını kendisi üretir.
|
||||
// 3. parent pointer — AST'de yukarı doğru gezinme (ör: sembol çözümleme).
|
||||
// 4. children vector — aşağı doğru gezinme (ör: tüm düğümleri ziyaret).
|
||||
// 5. ASTKind enum — switch/case ile tip kontrolü (dynamic_cast yerine).
|
||||
// Performans: dynamic_cast < switch/case < virtual method çağrısı
|
||||
// Ama switch/case ile yeni tip eklemek derleyici uyarısı verir (eksik case).
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_AST_NODE
|
||||
#define SAQUT_AST_NODE
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "core/location.hpp"
|
||||
#include "parser/token.hpp"
|
||||
#include "tools.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// ASTKind — Düğüm Tipi Enum
|
||||
// ============================================================================
|
||||
//
|
||||
// Tüm AST düğüm tiplerini tanımlar. Her düğüm sınıfı bu enum'dan bir değer
|
||||
// alır. enum class olması sayesinde isim çakışması olmaz (ASTKind::Program).
|
||||
//
|
||||
// NEDEN enum class, neden inheritance'daki typeid kullanılmıyor?
|
||||
// - typeid().name() derleyiciye bağlıdır (g++: "4Program", MSVC: "class Program").
|
||||
// - enum class her derleyicide aynıdır, string dönüşümü kolaydır.
|
||||
// - static_cast<uint16_t> ile serileştirilebilir.
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
enum class ASTKind {
|
||||
/* ====== En üst seviye ====== */
|
||||
Program, // Kök düğüm — tüm programı kapsar.
|
||||
// İçindeki children: FunctionDecl, StructDecl, VariableDecl.
|
||||
// Tüm .cpp/.sqt dosyası tek bir Program düğümüdür.
|
||||
|
||||
/* ====== Tanımlar (Declarations) ====== */
|
||||
FunctionDecl, // Fonksiyon tanımı.
|
||||
// children: [returnType?], [name], [params...], [body: Block]
|
||||
// Örn: int main() { ... }
|
||||
StructDecl, // struct tanımı.
|
||||
// children: [name], [members: VariableDecl...]
|
||||
// Örn: struct Point { int x; int y; };
|
||||
VariableDecl, // Değişken tanımı.
|
||||
// children: [type?], [name], [initializer?]
|
||||
// Örn: int x = 5; veya string name;
|
||||
|
||||
/* ====== Kontrol Akışı (Statements) ====== */
|
||||
Block, // { } bloğu — birleşik ifade.
|
||||
// children: [statements...]
|
||||
// Kapsam (scope) oluşturur. Yerel değişkenler burada tanımlanır.
|
||||
IfStatement, // if (koşul) gövde [else gövde].
|
||||
// children: [condition], [thenBranch], [elseBranch?]
|
||||
ForStatement, // for (init; koşul; artım) gövde.
|
||||
// children: [init?], [condition?], [increment?], [body]
|
||||
WhileStatement, // while (koşul) gövde.
|
||||
// children: [condition], [body]
|
||||
DoWhileStatement, // do gövde while (koşul);
|
||||
// children: [body], [condition]
|
||||
ReturnStatement, // return [ifade?];
|
||||
// children: [value?]
|
||||
BreakStatement, // break;
|
||||
// children: yok. Sadece döngü/switch içinde geçerli.
|
||||
ContinueStatement, // continue;
|
||||
// children: yok. Sadece döngü içinde geçerli.
|
||||
ExpressionStatement, // ifade + noktalı virgül (;)
|
||||
// children: [expression]
|
||||
// Örn: x = 5; veya foo();
|
||||
|
||||
/* ====== İfadeler (Expressions) ====== */
|
||||
BinaryExpression, // İkili işlem: sol OP sağ.
|
||||
// children: [left], [right]
|
||||
// OP bilgisi: exprType alanında saklanır.
|
||||
UnaryExpression, // Tekli işlem: OP operand.
|
||||
// children: [operand]
|
||||
// prefix (++x) veya postfix (x++) olabilir.
|
||||
Literal, // Sabit değer: 42, 3.14, "hello", true, null.
|
||||
// children: yok. Değer düğümün kendi alanında.
|
||||
Identifier, // İsim referansı: x, PI, main.
|
||||
// children: yok. İsim string olarak saklanır.
|
||||
Postfix, // Postfix işlem: operand++.
|
||||
// children: [operand]
|
||||
Call, // Fonksiyon/metot çağrısı: f(args).
|
||||
// children: [callee], [args...]
|
||||
MemberAccess, // Üye erişimi: a.b veya a->b.
|
||||
// children: [object], [member]
|
||||
IndexExpression, // Dizi/indeks erişimi: a[i].
|
||||
// children: [object], [index]
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// LiteralType — Sabit Değer Alt Tipleri
|
||||
// ============================================================================
|
||||
//
|
||||
// Literal düğümünün hangi türde bir sabit değer taşıdığını belirler.
|
||||
// uint8_t tabanlı — 256 farklı literal tipi yeterli.
|
||||
//
|
||||
// KULLANIM:
|
||||
// Literal düğümü oluşturulurken tip belirtilir:
|
||||
// Literal lit(LiteralType::INTEGER, "42");
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
enum class LiteralType : uint8_t {
|
||||
INTEGER, // Tamsayı sabiti: 42, 0xFF, 0b1010, 0777
|
||||
// Decimal, hexadecimal (0x), octal (0), binary (0b) desteklenir.
|
||||
// Tokenizer NumberToken ile iletilir.
|
||||
FLOAT, // Ondalıklı sayı: 3.14, 1e-5, 2.0f
|
||||
// Nokta veya üs (e/E) içeren sayılar.
|
||||
STRING, // Metin sabiti: "merhaba dünya"
|
||||
// Çift tırnak içinde. Kaçış dizileri (\n, \t, \") desteklenir.
|
||||
BOOLEAN, // Mantıksal değer: true / false
|
||||
// KW_TRUE veya KW_FALSE token'ından gelir.
|
||||
BOŞ // null sabiti (Türkçe "boş").
|
||||
// KW_NULL token'ından gelir. Nesne/referans türleri için kullanılır.
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// literalTypeToString — LiteralType'ı string'e çevir (log için)
|
||||
// ============================================================================
|
||||
//
|
||||
// PARAMETRE: t — LiteralType enum değeri
|
||||
// DÖNÜŞ: const char* — insan tarafından okunabilir string
|
||||
// KARMAŞIKLIK: O(1) — switch/case (derleyici jump table üretir)
|
||||
//
|
||||
inline const char* literalTypeToString(LiteralType t) {
|
||||
switch (t) {
|
||||
case LiteralType::INTEGER: return "integer";
|
||||
case LiteralType::FLOAT: return "float";
|
||||
case LiteralType::STRING: return "string";
|
||||
case LiteralType::BOOLEAN: return "boolean";
|
||||
case LiteralType::BOŞ: return "null";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ASTNode — Soyut Taban Sınıf
|
||||
// ============================================================================
|
||||
//
|
||||
// Tüm AST düğümleri bu sınıftan türetilir. Her düğüm:
|
||||
// - kind: ASTKind enum — tipini bilir (switch/case için)
|
||||
// - parent: Ebeveyn düğüme işaretçi (ağaçta yukarı gezinme)
|
||||
// - loc: Kaynak koddaki satır/sütun konumu (hata mesajları için)
|
||||
// - children: Alt düğümler (ağaçta aşağı gezinme)
|
||||
// - log(): Konsola hiyerarşik yazdırma
|
||||
// - toJson(): JSON formatında serileştirme
|
||||
//
|
||||
// KALITIM:
|
||||
// Program : ASTNode — Kök düğüm
|
||||
// FunctionDecl : ASTNode — Fonksiyon tanımı
|
||||
// BinaryExpression : ASTNode — İkili işlem
|
||||
// ... (her düğüm tipi ayrı sınıf)
|
||||
//
|
||||
// BELLEK YÖNETİMİ:
|
||||
// Düğümler new ile oluşturulur, delete ile yok edilir.
|
||||
// Sahiplik: Parser oluşturur, çağıran (main/CLI) yok eder.
|
||||
// TODO(Büyük yeniden düzenleme): std::unique_ptr ile RAII.
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
class ASTNode {
|
||||
public:
|
||||
/* ====== Her düğümün tipi ====== */
|
||||
ASTKind kind; // Düğüm tipi (Program, FunctionDecl, ...)
|
||||
// switch(kind) ile tip kontrolü.
|
||||
// Set edilir ve bir daha değişmez.
|
||||
|
||||
/* ====== Ağaç bağlantıları ====== */
|
||||
ASTNode* parent = nullptr; // Ebeveyn düğüm pointerı.
|
||||
// addChild() tarafından otomatik set edilir.
|
||||
// Kullanım: semantic analizde kapsam bulma.
|
||||
// Örn: değişkenin tanımlandığı fonksiyonu bulmak
|
||||
// için parent->parent->... şeklinde yukarı çıkılır.
|
||||
|
||||
/* ====== Kaynak konumu ====== */
|
||||
SourceLocation loc; // Tokenizer'dan gelen satır/sütun bilgisi.
|
||||
// Hata mesajlarında: "satır 5, sütun 12"
|
||||
// TODO: Şu anda tüm düğümlerde dolu değil.
|
||||
|
||||
/* ====== Sanal Metotlar ====== */
|
||||
|
||||
// log() — Düğümü ve alt düğümlerini konsola yazdırır.
|
||||
// PARAMETRE: indent — girinti seviyesi (her seviyede 2 boşluk artar)
|
||||
// KULLANIM: ast->log(0); // tüm ağacı yazdır
|
||||
// KARMAŞIKLIK: O(n) — tüm alt ağacı dolaşır
|
||||
virtual void log(int indent = 0) {
|
||||
(void)indent;
|
||||
std::cout << "<Unknown>\n";
|
||||
}
|
||||
|
||||
// toJson() — Düğümü ve alt düğümlerini JSON formatında döndürür.
|
||||
// PARAMETRE: indent — JSON girinti seviyesi
|
||||
// DÖNÜŞ: JSON stringi
|
||||
// KULLANIM: std::string json = ast->toJson(0);
|
||||
// KARMAŞIKLIK: O(n) — tüm alt ağacı dolaşır, string birleştirme maliyeti
|
||||
virtual std::string toJson(int indent = 0) {
|
||||
(void)indent;
|
||||
return "{\"kind\":\"Unknown\"}";
|
||||
}
|
||||
|
||||
/* ====== Yardımcı Metotlar ====== */
|
||||
|
||||
// addChild() — Alt düğüm ekler ve parent pointer'ını set eder.
|
||||
// PARAMETRE: child — eklenecek alt düğüm (nullptr olmamalı)
|
||||
// YAN ETKİ: child->parent = this (otomatik)
|
||||
// KARMAŞIKLIK: O(1) amortize — vector push_back
|
||||
void addChild(ASTNode* child) {
|
||||
children.push_back(child);
|
||||
child->parent = this;
|
||||
}
|
||||
|
||||
// getChildren() — Alt düğüm vektörüne erişim.
|
||||
// DÖNÜŞ: std::vector<ASTNode*>& — çocuk düğümler listesi
|
||||
// KARMAŞIKLIK: O(1) — referans döndürür
|
||||
std::vector<ASTNode*>& getChildren() { return children; }
|
||||
|
||||
// ~ASTNode() — Sanal yıkıcı (polimorfik silme için)
|
||||
// delete ASTNode* yapıldığında doğru alt sınıf yıkıcısı çağrılır.
|
||||
// Bu olmazsa türetilmiş sınıfların kaynakları sızdırılır.
|
||||
virtual ~ASTNode() = default;
|
||||
|
||||
protected:
|
||||
// children — Alt düğümlerin vektörü.
|
||||
// protected: doğrudan erişim yerine addChild/getChildren kullanılır.
|
||||
// Türetilmiş sınıflar erişebilir (ör: log() içinde çocukları gezme).
|
||||
std::vector<ASTNode*> children;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// childrenToJson — Düğümün çocuklarını JSON array olarak yaz
|
||||
// ============================================================================
|
||||
//
|
||||
// Bir düğümün tüm alt düğümlerini dolaşır ve her birinin toJson() çıktısını
|
||||
// virgülle ayrılmış şekilde birleştirir.
|
||||
//
|
||||
// PARAMETRELER:
|
||||
// node — çocukları yazdırılacak düğüm
|
||||
// depth — JSON girinti seviyesi
|
||||
//
|
||||
// DÖNÜŞ: JSON array içeriği (köşeli parantezler HARİÇ)
|
||||
//
|
||||
// KULLANIM:
|
||||
// std::string json = childrenToJson(this, depth + 1);
|
||||
//
|
||||
// KARMAŞIKLIK: O(n) — n = çocuk sayısı
|
||||
//
|
||||
inline std::string childrenToJson(ASTNode* node, int depth) {
|
||||
std::ostringstream ss;
|
||||
std::string in = jsonIndent(depth);
|
||||
auto& ch = node->getChildren();
|
||||
for (size_t i = 0; i < ch.size(); i++) {
|
||||
ss << ch[i]->toJson(depth);
|
||||
if (i + 1 < ch.size()) ss << ","; // son elemandan sonra virgül yok
|
||||
ss << "\n";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#endif // SAQUT_AST_NODE
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
#include "parser/nodes/binary_expr.hpp"
|
||||
#include "parser/ast_json.hpp"
|
||||
|
||||
BinaryExpressionNode::BinaryExpressionNode() {
|
||||
kind = ASTKind::BinaryExpression;
|
||||
}
|
||||
|
||||
void BinaryExpressionNode::log(int indent) {
|
||||
std::string in = jsonIndent(indent);
|
||||
std::cout << in << "BinaryExpression (" << (OPERATOR_MAP_REV.count(Operator) ? OPERATOR_MAP_REV.at(Operator) : "?") << ")\n";
|
||||
if (Left) Left->log(indent + 1);
|
||||
if (Right) Right->log(indent + 1);
|
||||
}
|
||||
|
||||
std::string BinaryExpressionNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "BinaryExpression");
|
||||
obj.add("operator", std::string(OPERATOR_MAP_REV.count(Operator) ? OPERATOR_MAP_REV.at(Operator) : "?"));
|
||||
if (Left) obj.addRaw("left", Left->toJson(depth + 1));
|
||||
if (Right) obj.addRaw("right", Right->toJson(depth + 1));
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef SAQUT_AST_BINARY_EXPR
|
||||
#define SAQUT_AST_BINARY_EXPR
|
||||
|
||||
#include "parser/ast_node.hpp"
|
||||
|
||||
class BinaryExpressionNode : public ASTNode {
|
||||
public:
|
||||
TokenType Operator;
|
||||
ASTNode* Left = nullptr;
|
||||
ASTNode* Right = nullptr;
|
||||
|
||||
BinaryExpressionNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
#include "parser/nodes/declarations.hpp"
|
||||
#include "parser/ast_json.hpp"
|
||||
|
||||
// FunctionDeclNode
|
||||
FunctionDeclNode::FunctionDeclNode() { kind = ASTKind::FunctionDecl; }
|
||||
void FunctionDeclNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "FunctionDecl (" << name << " : " << returnType << ")\n";
|
||||
for (auto* child : children) child->log(indent + 1);
|
||||
}
|
||||
std::string FunctionDeclNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "FunctionDecl");
|
||||
obj.add("name", name);
|
||||
obj.add("returnType", returnType);
|
||||
obj.addArray("children", [&]() {
|
||||
for (auto* child : children) obj.addItem(child->toJson(depth + 2));
|
||||
});
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
||||
// VariableDeclNode
|
||||
VariableDeclNode::VariableDeclNode() { kind = ASTKind::VariableDecl; }
|
||||
void VariableDeclNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "VariableDecl (" << name << " : " << varType << ")\n";
|
||||
if (initExpr) initExpr->log(indent + 1);
|
||||
}
|
||||
std::string VariableDeclNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "VariableDecl");
|
||||
obj.add("name", name);
|
||||
obj.add("varType", varType);
|
||||
if (initExpr) obj.addRaw("init", initExpr->toJson(depth + 1));
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
||||
// StructDeclNode
|
||||
StructDeclNode::StructDeclNode() { kind = ASTKind::StructDecl; }
|
||||
void StructDeclNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "StructDecl (" << name << ")\n";
|
||||
for (auto* child : children) child->log(indent + 1);
|
||||
}
|
||||
std::string StructDeclNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "StructDecl");
|
||||
obj.add("name", name);
|
||||
obj.addArray("children", [&]() {
|
||||
for (auto* child : children) obj.addItem(child->toJson(depth + 2));
|
||||
});
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef SAQUT_AST_DECL
|
||||
#define SAQUT_AST_DECL
|
||||
|
||||
#include "parser/ast_node.hpp"
|
||||
|
||||
class FunctionDeclNode : public ASTNode {
|
||||
public:
|
||||
std::string name;
|
||||
std::string returnType;
|
||||
FunctionDeclNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
class VariableDeclNode : public ASTNode {
|
||||
public:
|
||||
std::string varType;
|
||||
std::string name;
|
||||
ASTNode* initExpr = nullptr;
|
||||
VariableDeclNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
class StructDeclNode : public ASTNode {
|
||||
public:
|
||||
std::string name;
|
||||
StructDeclNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
#include "parser/nodes/expressions.hpp"
|
||||
#include "parser/ast_json.hpp"
|
||||
|
||||
// PostfixNode
|
||||
PostfixNode::PostfixNode() { kind = ASTKind::Postfix; }
|
||||
void PostfixNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "Postfix (" << (OPERATOR_MAP_REV.count(Operator) ? OPERATOR_MAP_REV.at(Operator) : "?") << ")\n";
|
||||
if (operand) operand->log(indent + 1);
|
||||
}
|
||||
std::string PostfixNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "Postfix");
|
||||
obj.add("operator", std::string(OPERATOR_MAP_REV.count(Operator) ? OPERATOR_MAP_REV.at(Operator) : "?"));
|
||||
if (operand) obj.addRaw("operand", operand->toJson(depth + 1));
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
||||
// CallExpressionNode
|
||||
CallExpressionNode::CallExpressionNode() { kind = ASTKind::Call; }
|
||||
void CallExpressionNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "Call\n";
|
||||
if (callee) callee->log(indent + 1);
|
||||
for (auto* arg : arguments) arg->log(indent + 1);
|
||||
}
|
||||
std::string CallExpressionNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "Call");
|
||||
if (callee) obj.addRaw("callee", callee->toJson(depth + 1));
|
||||
obj.addArray("arguments", [&]() {
|
||||
for (auto* arg : arguments) obj.addItem(arg->toJson(depth + 2));
|
||||
});
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
||||
// MemberAccessNode
|
||||
MemberAccessNode::MemberAccessNode() { kind = ASTKind::MemberAccess; }
|
||||
void MemberAccessNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "MemberAccess (" << (arrow ? "->" : ".") << member << ")\n";
|
||||
if (object) object->log(indent + 1);
|
||||
}
|
||||
std::string MemberAccessNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "MemberAccess");
|
||||
obj.add("member", member);
|
||||
obj.add("arrow", arrow);
|
||||
if (object) obj.addRaw("object", object->toJson(depth + 1));
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
||||
// IndexExpressionNode
|
||||
IndexExpressionNode::IndexExpressionNode() { kind = ASTKind::IndexExpression; }
|
||||
void IndexExpressionNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "IndexExpression\n";
|
||||
if (object) object->log(indent + 1);
|
||||
if (index) index->log(indent + 1);
|
||||
}
|
||||
std::string IndexExpressionNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "IndexExpression");
|
||||
if (object) obj.addRaw("object", object->toJson(depth + 1));
|
||||
if (index) obj.addRaw("index", index->toJson(depth + 1));
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef SAQUT_AST_EXPR_EXT
|
||||
#define SAQUT_AST_EXPR_EXT
|
||||
|
||||
#include "parser/ast_node.hpp"
|
||||
|
||||
class PostfixNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* operand = nullptr;
|
||||
TokenType Operator;
|
||||
PostfixNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
class CallExpressionNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* callee = nullptr;
|
||||
std::vector<ASTNode*> arguments;
|
||||
CallExpressionNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
class MemberAccessNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* object = nullptr;
|
||||
std::string member;
|
||||
bool arrow = false;
|
||||
MemberAccessNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
class IndexExpressionNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* object = nullptr;
|
||||
ASTNode* index = nullptr;
|
||||
IndexExpressionNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
#include "parser/nodes/identifier.hpp"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include "parser/ast_json.hpp"
|
||||
|
||||
IdentifierNode::IdentifierNode() { kind = ASTKind::Identifier; }
|
||||
|
||||
void IdentifierNode::log(int indent) {
|
||||
std::cout << padRight("", indent)
|
||||
<< "Identifier {" << (parserToken.token ? parserToken.token->token : "?") << "}\n";
|
||||
}
|
||||
|
||||
std::string IdentifierNode::toJson(int depth) {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::string name = parserToken.token ? parserToken.token->token : "?";
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"Identifier\",\n"
|
||||
<< in << " \"name\": \"" << jsonEscape(name) << "\",\n"
|
||||
<< in << " \"location\": " << loc.toJson() << "\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef SAQUT_AST_IDENTIFIER
|
||||
#define SAQUT_AST_IDENTIFIER
|
||||
|
||||
#include "parser/ast_node.hpp"
|
||||
|
||||
class IdentifierNode : public ASTNode {
|
||||
public:
|
||||
Token* lexerToken = nullptr;
|
||||
ParserToken parserToken;
|
||||
|
||||
IdentifierNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#include "parser/nodes/literal.hpp"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include "parser/ast_json.hpp"
|
||||
|
||||
LiteralNode::LiteralNode() { kind = ASTKind::Literal; }
|
||||
|
||||
void LiteralNode::log(int indent) {
|
||||
std::cout << padRight("", indent)
|
||||
<< "Literal {" << (parserToken.token ? parserToken.token->token : "?") << "} "
|
||||
<< literalTypeToString(literalType);
|
||||
if (literalType == LiteralType::INTEGER && literalBase != 10)
|
||||
std::cout << " (base " << literalBase << ")";
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
std::string LiteralNode::toJson(int depth) {
|
||||
std::string in = jsonIndent(depth);
|
||||
std::string val = parserToken.token ? parserToken.token->token : "?";
|
||||
std::ostringstream ss;
|
||||
ss << in << "{\n"
|
||||
<< in << " \"kind\": \"Literal\",\n"
|
||||
<< in << " \"literalType\": \"" << literalTypeToString(literalType) << "\",\n"
|
||||
<< in << " \"value\": \"" << jsonEscape(val) << "\"";
|
||||
if (literalType == LiteralType::INTEGER && literalBase != 10) {
|
||||
ss << ",\n" << in << " \"base\": " << literalBase;
|
||||
}
|
||||
if (literalType == LiteralType::FLOAT) {
|
||||
ss << ",\n" << in << " \"isFloat\": true";
|
||||
}
|
||||
ss << ",\n" << in << " \"location\": " << loc.toJson() << "\n"
|
||||
<< in << "}";
|
||||
return ss.str();
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef SAQUT_AST_LITERAL
|
||||
#define SAQUT_AST_LITERAL
|
||||
|
||||
#include "parser/ast_node.hpp"
|
||||
|
||||
class LiteralNode : public ASTNode {
|
||||
public:
|
||||
Token* lexerToken = nullptr;
|
||||
ParserToken parserToken;
|
||||
|
||||
LiteralType literalType = LiteralType::INTEGER;
|
||||
int literalBase = 10;
|
||||
bool isFloatValue = false;
|
||||
|
||||
LiteralNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#include "parser/nodes/program.hpp"
|
||||
#include "parser/ast_json.hpp"
|
||||
|
||||
ProgramNode::ProgramNode() {
|
||||
kind = ASTKind::Program;
|
||||
}
|
||||
|
||||
void ProgramNode::log(int indent) {
|
||||
std::string in = jsonIndent(indent);
|
||||
std::cout << in << "Program\n";
|
||||
for (auto* child : children) {
|
||||
child->log(indent + 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ProgramNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "Program");
|
||||
obj.addArray("children", [&]() {
|
||||
for (auto* child : children) {
|
||||
obj.addItem(child->toJson(depth + 2));
|
||||
}
|
||||
});
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef SAQUT_AST_PROGRAM
|
||||
#define SAQUT_AST_PROGRAM
|
||||
|
||||
#include "parser/ast_node.hpp"
|
||||
|
||||
class ProgramNode : public ASTNode {
|
||||
public:
|
||||
ProgramNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
#include "parser/nodes/statements.hpp"
|
||||
#include "parser/ast_json.hpp"
|
||||
|
||||
// BlockNode
|
||||
BlockNode::BlockNode() { kind = ASTKind::Block; }
|
||||
void BlockNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "Block\n";
|
||||
for (auto* child : children) child->log(indent + 1);
|
||||
}
|
||||
std::string BlockNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "Block");
|
||||
obj.addArray("children", [&]() {
|
||||
for (auto* child : children) obj.addItem(child->toJson(depth + 2));
|
||||
});
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
||||
// IfStatementNode
|
||||
IfStatementNode::IfStatementNode() { kind = ASTKind::IfStatement; }
|
||||
void IfStatementNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "IfStatement\n";
|
||||
if (condition) condition->log(indent + 1);
|
||||
if (thenBranch) thenBranch->log(indent + 1);
|
||||
if (elseBranch) elseBranch->log(indent + 1);
|
||||
}
|
||||
std::string IfStatementNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "IfStatement");
|
||||
if (condition) obj.addRaw("condition", condition->toJson(depth + 1));
|
||||
if (thenBranch) obj.addRaw("then", thenBranch->toJson(depth + 1));
|
||||
if (elseBranch) obj.addRaw("else", elseBranch->toJson(depth + 1));
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
||||
// WhileStatementNode
|
||||
WhileStatementNode::WhileStatementNode() { kind = ASTKind::WhileStatement; }
|
||||
void WhileStatementNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "WhileStatement\n";
|
||||
if (condition) condition->log(indent + 1);
|
||||
if (body) body->log(indent + 1);
|
||||
}
|
||||
std::string WhileStatementNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "WhileStatement");
|
||||
if (condition) obj.addRaw("condition", condition->toJson(depth + 1));
|
||||
if (body) obj.addRaw("body", body->toJson(depth + 1));
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
||||
// ForStatementNode
|
||||
ForStatementNode::ForStatementNode() { kind = ASTKind::ForStatement; }
|
||||
void ForStatementNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "ForStatement\n";
|
||||
if (init) init->log(indent + 1);
|
||||
if (condition) condition->log(indent + 1);
|
||||
if (update) update->log(indent + 1);
|
||||
if (body) body->log(indent + 1);
|
||||
}
|
||||
std::string ForStatementNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "ForStatement");
|
||||
if (init) obj.addRaw("init", init->toJson(depth + 1));
|
||||
if (condition) obj.addRaw("condition", condition->toJson(depth + 1));
|
||||
if (update) obj.addRaw("update", update->toJson(depth + 1));
|
||||
if (body) obj.addRaw("body", body->toJson(depth + 1));
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
||||
// DoWhileStatementNode
|
||||
DoWhileStatementNode::DoWhileStatementNode() { kind = ASTKind::DoWhileStatement; }
|
||||
void DoWhileStatementNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "DoWhileStatement\n";
|
||||
if (body) body->log(indent + 1);
|
||||
if (condition) condition->log(indent + 1);
|
||||
}
|
||||
std::string DoWhileStatementNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "DoWhileStatement");
|
||||
if (condition) obj.addRaw("condition", condition->toJson(depth + 1));
|
||||
if (body) obj.addRaw("body", body->toJson(depth + 1));
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
||||
// ReturnStatementNode
|
||||
ReturnStatementNode::ReturnStatementNode() { kind = ASTKind::ReturnStatement; }
|
||||
void ReturnStatementNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "ReturnStatement\n";
|
||||
if (value) value->log(indent + 1);
|
||||
}
|
||||
std::string ReturnStatementNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "ReturnStatement");
|
||||
if (value) obj.addRaw("value", value->toJson(depth + 1));
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
||||
// BreakStatementNode
|
||||
BreakStatementNode::BreakStatementNode() { kind = ASTKind::BreakStatement; }
|
||||
void BreakStatementNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "BreakStatement\n";
|
||||
}
|
||||
std::string BreakStatementNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "BreakStatement");
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
||||
// ContinueStatementNode
|
||||
ContinueStatementNode::ContinueStatementNode() { kind = ASTKind::ContinueStatement; }
|
||||
void ContinueStatementNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "ContinueStatement\n";
|
||||
}
|
||||
std::string ContinueStatementNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "ContinueStatement");
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
||||
// ExpressionStatementNode
|
||||
ExpressionStatementNode::ExpressionStatementNode() { kind = ASTKind::ExpressionStatement; }
|
||||
void ExpressionStatementNode::log(int indent) {
|
||||
std::cout << jsonIndent(indent) << "ExpressionStatement\n";
|
||||
if (expression) expression->log(indent + 1);
|
||||
}
|
||||
std::string ExpressionStatementNode::toJson(int depth) {
|
||||
JsonObject obj(depth);
|
||||
obj.add("kind", "ExpressionStatement");
|
||||
if (expression) obj.addRaw("expression", expression->toJson(depth + 1));
|
||||
obj.addRaw("location", loc.toJson());
|
||||
return obj.str();
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
#ifndef SAQUT_AST_STMT
|
||||
#define SAQUT_AST_STMT
|
||||
|
||||
#include "parser/ast_node.hpp"
|
||||
|
||||
class BlockNode : public ASTNode {
|
||||
public:
|
||||
BlockNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
class IfStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* condition = nullptr;
|
||||
ASTNode* thenBranch = nullptr;
|
||||
ASTNode* elseBranch = nullptr;
|
||||
IfStatementNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
class WhileStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* condition = nullptr;
|
||||
ASTNode* body = nullptr;
|
||||
WhileStatementNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
class ForStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* init = nullptr;
|
||||
ASTNode* condition = nullptr;
|
||||
ASTNode* update = nullptr;
|
||||
ASTNode* body = nullptr;
|
||||
ForStatementNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
class DoWhileStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* condition = nullptr;
|
||||
ASTNode* body = nullptr;
|
||||
DoWhileStatementNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
class ReturnStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* value = nullptr;
|
||||
ReturnStatementNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
class BreakStatementNode : public ASTNode {
|
||||
public:
|
||||
BreakStatementNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
class ContinueStatementNode : public ASTNode {
|
||||
public:
|
||||
ContinueStatementNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
class ExpressionStatementNode : public ASTNode {
|
||||
public:
|
||||
ASTNode* expression = nullptr;
|
||||
ExpressionStatementNode();
|
||||
void log(int indent = 0) override;
|
||||
std::string toJson(int depth = 0) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,599 @@
|
|||
#include "parser/parser.hpp"
|
||||
#include "parser/nodes/program.hpp"
|
||||
#include "parser/nodes/binary_expr.hpp"
|
||||
#include "parser/nodes/literal.hpp"
|
||||
#include "parser/nodes/identifier.hpp"
|
||||
#include "parser/nodes/expressions.hpp"
|
||||
#include "parser/nodes/statements.hpp"
|
||||
#include "parser/nodes/declarations.hpp"
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseToken: Ham Token'ı ParserToken'a dönüştür.
|
||||
// --------------------------------------------------------------------------
|
||||
ParserToken Parser::parseToken(Token* token) {
|
||||
ParserToken pt;
|
||||
pt.token = token;
|
||||
|
||||
std::string t = token->gettype();
|
||||
if (t == "string")
|
||||
pt.type = TokenType::STRING;
|
||||
else if (t == "number")
|
||||
pt.type = TokenType::NUMBER;
|
||||
else if (t == "operator")
|
||||
pt.type = OPERATOR_MAP.find(pt.token->token)->second;
|
||||
else if (t == "delimiter")
|
||||
pt.type = OPERATOR_MAP.find(pt.token->token)->second;
|
||||
else if (t == "keyword")
|
||||
pt.type = KEYWORD_MAP.find(pt.token->token)->second;
|
||||
else if (t == "identifier")
|
||||
pt.type = TokenType::IDENTIFIER;
|
||||
|
||||
return pt;
|
||||
}
|
||||
|
||||
ParserToken Parser::getToken(int offset) {
|
||||
if ((int)tokens.size() - 1 < current + offset) {
|
||||
ParserToken pt;
|
||||
pt.type = TokenType::SVR_VOID;
|
||||
return pt;
|
||||
}
|
||||
return parseToken(tokens[current + offset]);
|
||||
}
|
||||
|
||||
void Parser::nextToken() {
|
||||
if ((int)tokens.size() >= current + 1)
|
||||
current++;
|
||||
}
|
||||
|
||||
ParserToken Parser::lookahead(uint32_t forward) {
|
||||
return getToken(forward);
|
||||
}
|
||||
|
||||
ParserToken Parser::currentToken() {
|
||||
return getToken(0);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parse(TokenList toks) {
|
||||
tokens = toks;
|
||||
current = 0;
|
||||
return parseProgram();
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseProgram() {
|
||||
ProgramNode* program = new ProgramNode();
|
||||
|
||||
while (currentToken().type != TokenType::SVR_VOID) {
|
||||
ASTNode* decl = parseDeclaration();
|
||||
if (decl)
|
||||
program->addChild(decl);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseDeclaration() {
|
||||
auto ct = currentToken();
|
||||
|
||||
if (ct.is({
|
||||
TokenType::KW_VOID, TokenType::KW_INT, TokenType::KW_FLOAT_TYPE,
|
||||
TokenType::KW_DOUBLE, TokenType::KW_BOOL, TokenType::KW_CHAR,
|
||||
TokenType::KW_STRING_TYPE, TokenType::KW_AUTO
|
||||
})) {
|
||||
auto la1 = lookahead(1);
|
||||
auto la2 = lookahead(2);
|
||||
if (la1.type == TokenType::IDENTIFIER && la2.type == TokenType::LPAREN)
|
||||
return parseFunctionDecl();
|
||||
return parseVariableDecl();
|
||||
}
|
||||
|
||||
if (ct.type == TokenType::KW_STRUCT)
|
||||
return parseStructDecl();
|
||||
|
||||
return parseStatement();
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseExpression() {
|
||||
return parseExpression(0);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseExpression(uint16_t precedence) {
|
||||
if (currentToken().type == TokenType::SVR_VOID)
|
||||
return nullptr;
|
||||
|
||||
ASTNode* left = parseNullDenotation();
|
||||
if (!left) return nullptr;
|
||||
|
||||
while (true) {
|
||||
auto next = currentToken();
|
||||
if (next.type == TokenType::RPAREN ||
|
||||
next.type == TokenType::SEMICOLON ||
|
||||
next.type == TokenType::RBRACE ||
|
||||
next.type == TokenType::COMMA)
|
||||
break;
|
||||
|
||||
if (precedence < next.getPowerOperator()) {
|
||||
left = parseLeftDenotation(left);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseNullDenotation() {
|
||||
auto ct = currentToken();
|
||||
|
||||
if (ct.type == TokenType::SVR_VOID) {
|
||||
std::cerr << "Parser hatası: beklenmeyen dosya sonu\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ct.type == TokenType::LPAREN) {
|
||||
nextToken();
|
||||
ASTNode* expr = parseExpression(0);
|
||||
if (currentToken().type == TokenType::RPAREN)
|
||||
nextToken();
|
||||
return expr;
|
||||
}
|
||||
|
||||
if (ct.is({
|
||||
TokenType::PLUS_PLUS, TokenType::MINUS_MINUS,
|
||||
TokenType::PLUS, TokenType::MINUS,
|
||||
TokenType::BANG, TokenType::TILDE
|
||||
})) {
|
||||
nextToken();
|
||||
ASTNode* right = parseExpression(ct.getPowerOperator());
|
||||
BinaryExpressionNode* bin = new BinaryExpressionNode();
|
||||
bin->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||
bin->Right = right;
|
||||
bin->Left = nullptr;
|
||||
bin->Operator = ct.type;
|
||||
if (right) right->parent = bin;
|
||||
return bin;
|
||||
}
|
||||
|
||||
if (ct.type == TokenType::NUMBER) {
|
||||
nextToken();
|
||||
LiteralNode* lit = new LiteralNode();
|
||||
lit->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||
lit->lexerToken = ct.token;
|
||||
lit->parserToken = ct;
|
||||
if (auto* nt = dynamic_cast<NumberToken*>(ct.token)) {
|
||||
lit->literalBase = nt->base;
|
||||
lit->isFloatValue = nt->isFloat;
|
||||
lit->literalType = nt->isFloat ? LiteralType::FLOAT : LiteralType::INTEGER;
|
||||
}
|
||||
return lit;
|
||||
}
|
||||
|
||||
if (ct.type == TokenType::STRING) {
|
||||
nextToken();
|
||||
LiteralNode* lit = new LiteralNode();
|
||||
lit->literalType = LiteralType::STRING;
|
||||
lit->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||
lit->lexerToken = ct.token;
|
||||
lit->parserToken = ct;
|
||||
return lit;
|
||||
}
|
||||
|
||||
if (ct.is({TokenType::KW_TRUE, TokenType::KW_FALSE, TokenType::KW_NULL})) {
|
||||
nextToken();
|
||||
LiteralNode* lit = new LiteralNode();
|
||||
if (ct.is({TokenType::KW_TRUE, TokenType::KW_FALSE}))
|
||||
lit->literalType = LiteralType::BOOLEAN;
|
||||
else
|
||||
lit->literalType = LiteralType::BOŞ;
|
||||
lit->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||
lit->lexerToken = ct.token;
|
||||
lit->parserToken = ct;
|
||||
return lit;
|
||||
}
|
||||
|
||||
if (ct.type == TokenType::IDENTIFIER) {
|
||||
nextToken();
|
||||
IdentifierNode* id = new IdentifierNode();
|
||||
id->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||
id->lexerToken = ct.token;
|
||||
id->parserToken = ct;
|
||||
return id;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseLeftDenotation(ASTNode* left) {
|
||||
auto ct = currentToken();
|
||||
|
||||
if (ct.is({TokenType::PLUS_PLUS, TokenType::MINUS_MINUS})) {
|
||||
nextToken();
|
||||
PostfixNode* pf = new PostfixNode();
|
||||
pf->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||
pf->operand = left;
|
||||
pf->Operator = ct.type;
|
||||
left->parent = pf;
|
||||
return pf;
|
||||
}
|
||||
|
||||
if (ct.type == TokenType::LPAREN) {
|
||||
nextToken();
|
||||
CallExpressionNode* call = new CallExpressionNode();
|
||||
call->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||
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;
|
||||
}
|
||||
|
||||
if (ct.type == TokenType::LBRACKET) {
|
||||
nextToken();
|
||||
IndexExpressionNode* idx = new IndexExpressionNode();
|
||||
idx->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||
idx->object = left;
|
||||
left->parent = idx;
|
||||
idx->index = parseExpression(0);
|
||||
if (currentToken().type == TokenType::RBRACKET)
|
||||
nextToken();
|
||||
return idx;
|
||||
}
|
||||
|
||||
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->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||
ma->object = left;
|
||||
ma->member = currentToken().token->token;
|
||||
ma->arrow = arrow;
|
||||
left->parent = ma;
|
||||
nextToken();
|
||||
return ma;
|
||||
}
|
||||
|
||||
uint16_t prec = ct.getPowerOperator();
|
||||
nextToken();
|
||||
|
||||
ASTNode* right = parseExpression(prec);
|
||||
|
||||
BinaryExpressionNode* bin = new BinaryExpressionNode();
|
||||
bin->loc = ct.token ? ct.token->loc : SourceLocation{};
|
||||
bin->Left = left;
|
||||
bin->Right = right;
|
||||
bin->Operator = ct.type;
|
||||
if (left) left->parent = bin;
|
||||
if (right) right->parent = bin;
|
||||
return bin;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseFunctionDecl() {
|
||||
FunctionDeclNode* fn = new FunctionDeclNode();
|
||||
fn->loc = currentToken().token->loc;
|
||||
fn->returnType = currentToken().token->token;
|
||||
nextToken();
|
||||
|
||||
fn->name = currentToken().token->token;
|
||||
nextToken();
|
||||
|
||||
if (currentToken().type == TokenType::LPAREN) {
|
||||
nextToken();
|
||||
while (currentToken().type != TokenType::RPAREN &&
|
||||
currentToken().type != TokenType::SVR_VOID)
|
||||
nextToken();
|
||||
if (currentToken().type == TokenType::RPAREN)
|
||||
nextToken();
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::LBRACE) {
|
||||
ASTNode* body = parseBlock();
|
||||
fn->addChild(body);
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseStructDecl() {
|
||||
StructDeclNode* st = new StructDeclNode();
|
||||
st->loc = currentToken().token->loc;
|
||||
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;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseVariableDecl() {
|
||||
VariableDeclNode* vd = new VariableDeclNode();
|
||||
vd->loc = currentToken().token->loc;
|
||||
vd->varType = currentToken().token->token;
|
||||
nextToken();
|
||||
|
||||
if (currentToken().type != TokenType::IDENTIFIER) {
|
||||
std::cerr << "Parser hatası: değişken ismi bekleniyor\n";
|
||||
return vd;
|
||||
}
|
||||
|
||||
vd->name = currentToken().token->token;
|
||||
nextToken();
|
||||
|
||||
if (currentToken().type == TokenType::LBRACKET) {
|
||||
nextToken();
|
||||
while (currentToken().type != TokenType::RBRACKET &&
|
||||
currentToken().type != TokenType::SEMICOLON &&
|
||||
currentToken().type != TokenType::SVR_VOID)
|
||||
nextToken();
|
||||
if (currentToken().type == TokenType::RBRACKET)
|
||||
nextToken();
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::EQUAL) {
|
||||
nextToken();
|
||||
vd->initExpr = parseExpression();
|
||||
}
|
||||
|
||||
while (currentToken().type == TokenType::COMMA) {
|
||||
nextToken();
|
||||
|
||||
if (currentToken().type != TokenType::IDENTIFIER) {
|
||||
std::cerr << "Parser hatası: virgülden sonra değişken ismi bekleniyor\n";
|
||||
break;
|
||||
}
|
||||
|
||||
VariableDeclNode* sibling = new VariableDeclNode();
|
||||
sibling->loc = currentToken().token->loc;
|
||||
sibling->varType = vd->varType;
|
||||
sibling->name = currentToken().token->token;
|
||||
nextToken();
|
||||
|
||||
if (currentToken().type == TokenType::LBRACKET) {
|
||||
nextToken();
|
||||
while (currentToken().type != TokenType::RBRACKET &&
|
||||
currentToken().type != TokenType::SEMICOLON &&
|
||||
currentToken().type != TokenType::SVR_VOID)
|
||||
nextToken();
|
||||
if (currentToken().type == TokenType::RBRACKET)
|
||||
nextToken();
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::EQUAL) {
|
||||
nextToken();
|
||||
sibling->initExpr = parseExpression();
|
||||
}
|
||||
|
||||
vd->addChild(sibling);
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken();
|
||||
|
||||
return vd;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseStatement() {
|
||||
auto ct = currentToken();
|
||||
|
||||
if (ct.type == TokenType::LBRACE)
|
||||
return parseBlock();
|
||||
|
||||
if (ct.type == TokenType::KW_IF)
|
||||
return parseIfStatement();
|
||||
|
||||
if (ct.type == TokenType::KW_WHILE)
|
||||
return parseWhileStatement();
|
||||
|
||||
if (ct.type == TokenType::KW_FOR)
|
||||
return parseForStatement();
|
||||
|
||||
if (ct.type == TokenType::KW_DO)
|
||||
return parseDoWhileStatement();
|
||||
|
||||
if (ct.type == TokenType::KW_RETURN)
|
||||
return parseReturnStatement();
|
||||
|
||||
if (ct.type == TokenType::KW_BREAK)
|
||||
return parseBreakStatement();
|
||||
|
||||
if (ct.type == TokenType::KW_CONTINUE)
|
||||
return parseContinueStatement();
|
||||
|
||||
if (ct.is({
|
||||
TokenType::KW_VOID, TokenType::KW_INT, TokenType::KW_FLOAT_TYPE,
|
||||
TokenType::KW_DOUBLE, TokenType::KW_BOOL, TokenType::KW_CHAR,
|
||||
TokenType::KW_STRING_TYPE
|
||||
})) {
|
||||
return parseVariableDecl();
|
||||
}
|
||||
|
||||
if (ct.type == TokenType::KW_STRUCT)
|
||||
return parseStructDecl();
|
||||
|
||||
return parseExpressionStatement();
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseBlock() {
|
||||
BlockNode* block = new BlockNode();
|
||||
block->loc = currentToken().token ? currentToken().token->loc : SourceLocation{};
|
||||
|
||||
if (currentToken().type == TokenType::LBRACE)
|
||||
nextToken();
|
||||
|
||||
while (currentToken().type != TokenType::RBRACE &&
|
||||
currentToken().type != TokenType::SVR_VOID) {
|
||||
ASTNode* stmt = parseStatement();
|
||||
if (stmt)
|
||||
block->addChild(stmt);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::RBRACE)
|
||||
nextToken();
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseIfStatement() {
|
||||
IfStatementNode* ifNode = new IfStatementNode();
|
||||
ifNode->loc = currentToken().token->loc;
|
||||
nextToken();
|
||||
|
||||
if (currentToken().type == TokenType::LPAREN) {
|
||||
nextToken();
|
||||
ifNode->condition = parseExpression();
|
||||
if (currentToken().type == TokenType::RPAREN)
|
||||
nextToken();
|
||||
}
|
||||
|
||||
ifNode->thenBranch = parseStatement();
|
||||
|
||||
if (currentToken().type == TokenType::KW_ELSE) {
|
||||
nextToken();
|
||||
ifNode->elseBranch = parseStatement();
|
||||
}
|
||||
|
||||
return ifNode;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseWhileStatement() {
|
||||
WhileStatementNode* ws = new WhileStatementNode();
|
||||
ws->loc = currentToken().token->loc;
|
||||
nextToken();
|
||||
|
||||
if (currentToken().type == TokenType::LPAREN) {
|
||||
nextToken();
|
||||
ws->condition = parseExpression();
|
||||
if (currentToken().type == TokenType::RPAREN)
|
||||
nextToken();
|
||||
}
|
||||
|
||||
ws->body = parseStatement();
|
||||
return ws;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseForStatement() {
|
||||
ForStatementNode* fs = new ForStatementNode();
|
||||
fs->loc = currentToken().token->loc;
|
||||
nextToken();
|
||||
|
||||
if (currentToken().type == TokenType::LPAREN)
|
||||
nextToken();
|
||||
|
||||
if (currentToken().type != TokenType::SEMICOLON)
|
||||
fs->init = parseStatement();
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken();
|
||||
|
||||
if (currentToken().type != TokenType::SEMICOLON)
|
||||
fs->condition = parseExpression();
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken();
|
||||
|
||||
if (currentToken().type != TokenType::RPAREN)
|
||||
fs->update = parseExpression();
|
||||
if (currentToken().type == TokenType::RPAREN)
|
||||
nextToken();
|
||||
|
||||
fs->body = parseStatement();
|
||||
|
||||
return fs;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseDoWhileStatement() {
|
||||
DoWhileStatementNode* dw = new DoWhileStatementNode();
|
||||
dw->loc = currentToken().token->loc;
|
||||
nextToken();
|
||||
|
||||
dw->body = parseStatement();
|
||||
|
||||
if (currentToken().type == TokenType::KW_WHILE) {
|
||||
nextToken();
|
||||
if (currentToken().type == TokenType::LPAREN) {
|
||||
nextToken();
|
||||
dw->condition = parseExpression();
|
||||
if (currentToken().type == TokenType::RPAREN)
|
||||
nextToken();
|
||||
}
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken();
|
||||
}
|
||||
|
||||
return dw;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseReturnStatement() {
|
||||
ReturnStatementNode* rs = new ReturnStatementNode();
|
||||
rs->loc = currentToken().token->loc;
|
||||
nextToken();
|
||||
|
||||
if (currentToken().type != TokenType::SEMICOLON &&
|
||||
currentToken().type != TokenType::RBRACE) {
|
||||
rs->value = parseExpression();
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken();
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseBreakStatement() {
|
||||
BreakStatementNode* bs = new BreakStatementNode();
|
||||
bs->loc = currentToken().token->loc;
|
||||
nextToken();
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken();
|
||||
return bs;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseContinueStatement() {
|
||||
ContinueStatementNode* cs = new ContinueStatementNode();
|
||||
cs->loc = currentToken().token->loc;
|
||||
nextToken();
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken();
|
||||
return cs;
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseExpressionStatement() {
|
||||
ExpressionStatementNode* es = new ExpressionStatementNode();
|
||||
es->loc = currentToken().token ? currentToken().token->loc : SourceLocation{};
|
||||
es->expression = parseExpression();
|
||||
if (!es->expression) {
|
||||
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)
|
||||
nextToken();
|
||||
|
||||
return es;
|
||||
}
|
||||
|
|
@ -1,836 +1,6 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Parser (Sözdizimi Ayrıştırıcı)
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/parser.hpp
|
||||
// KATMAN: Katman 3 — Tokenizer'ı tüketir, AST üretir
|
||||
// BAĞIMLI: Token (token.hpp), AST (ast.hpp)
|
||||
// KULLANAN: main.cpp
|
||||
//
|
||||
// AMAÇ:
|
||||
// Tokenizer'ın ürettiği düz token listesini alıp, dilin gramer kurallarına
|
||||
// göre hiyerarşik bir AST (Abstract Syntax Tree) üretir.
|
||||
//
|
||||
// İKİ AYRI PARSER STRATEJİSİ:
|
||||
// 1. Recursive Descent (ifadeler için Pratt parser):
|
||||
// - parseNullDenotation() (NUD): Prefix ifadeleri (sayılar, -, !, parantez)
|
||||
// - parseLeftDenotation() (LED): Infix/Postfix ifadeler (+, *, ++)
|
||||
// - parseExpression(precedence): Pratt'ın ana döngüsü
|
||||
//
|
||||
// 2. Recursive Descent (statement/deklarasyon için):
|
||||
// - parseDeclaration(): Fonksiyon mu, değişken mi, statement mı?
|
||||
// - parseStatement(): if/for/while/do/return/block/expression
|
||||
// - Her statement tipi kendi parse fonksiyonuna sahip
|
||||
//
|
||||
// ADR-002 (devam): Neden Hibrit Yaklaşım?
|
||||
// Pratt parser, operatör önceliğini merkezi bir tabloda yönetir ve yeni
|
||||
// operatör eklemeyi kolaylaştırır. Ancak statement'lar (if, for, while)
|
||||
// operatör değildir; kendi özel sözdizimleri vardır. Bu nedenle statement
|
||||
// tarafında klasik recursive descent kullanıyoruz. Bu, her iki dünyanın
|
||||
// en iyisini birleştirir.
|
||||
//
|
||||
// PARSER AKIŞI:
|
||||
// parse(tokens)
|
||||
// └── parseProgram()
|
||||
// └── parseDeclaration() [döngü, SVR_VOID gelene kadar]
|
||||
// ├── parseFunctionDecl() → tip + isim + ( ) + { gövde }
|
||||
// ├── parseVariableDecl() → tip + isim [+ = ifade] + ;
|
||||
// └── parseStatement()
|
||||
// ├── parseBlock() → { statement* }
|
||||
// ├── parseIfStatement() → if (expr) stmt [else stmt]
|
||||
// ├── parseWhileStatement() → while (expr) stmt
|
||||
// ├── parseForStatement() → for (stmt; expr; expr) stmt
|
||||
// ├── parseDoWhileStatement() → do stmt while (expr);
|
||||
// ├── parseReturnStatement() → return [expr];
|
||||
// ├── parseBreakStatement() → break;
|
||||
// ├── parseContinueStatement() → continue;
|
||||
// ├── parseVariableDecl() → tip + isim ...
|
||||
// └── parseExpressionStatement() → expr;
|
||||
// └── parseExpression() [Pratt]
|
||||
// ├── parseNullDenotation()
|
||||
// │ ├── LPAREN → ( expr )
|
||||
// │ ├── Unary prefix → !expr, -expr, ++expr
|
||||
// │ ├── NUMBER → Literal
|
||||
// │ ├── STRING → Literal
|
||||
// │ ├── true/false/null → Literal
|
||||
// │ └── IDENTIFIER → Identifier
|
||||
// └── parseLeftDenotation() [döngü]
|
||||
// ├── Postfix → expr++, expr--
|
||||
// └── Binary infix → expr + expr
|
||||
//
|
||||
// BİLİNEN SINIRLAMALAR (TODO):
|
||||
// TODO: else-if zincirleri (şu anda else'den sonra if gelirse düzgün çalışır mı?)
|
||||
// TODO: Hata kurtarma (panic mode): ilk hatada durmak yerine senkronizasyon
|
||||
// TODO: Fonksiyon parametreleri
|
||||
// TODO: Dizi erişimi: a[i]
|
||||
// TODO: Fonksiyon çağrısı: f(x, y)
|
||||
// TODO: Üye erişimi: a.b, a->b
|
||||
// TODO: Ternary: a ? b : c
|
||||
// TODO: Tip kontrolü ve sembol tablosu
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_PARSER
|
||||
#define SAQUT_PARSER
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include "parser/token.hpp"
|
||||
#include "parser/ast.hpp"
|
||||
#include "tools.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// Parser — Sözdizimi Ayrıştırıcı
|
||||
// ============================================================================
|
||||
//
|
||||
// Durum bilgisi:
|
||||
// tokens: Tokenizer'dan gelen token listesi (referans değil, kopya değil)
|
||||
// current: Şu anki token'ın indeksi (0 = ilk token)
|
||||
//
|
||||
// Token navigasyon metotları:
|
||||
// currentToken(): tokens[current] döndürür, ilerlemez
|
||||
// nextToken(): current++ (sonraki token'a geç)
|
||||
// lookahead(n): tokens[current + n] döndürür, ilerlemez
|
||||
// getToken(offset): tokens[current + offset] döndürür
|
||||
//
|
||||
class Parser {
|
||||
public:
|
||||
ASTNode* parse(TokenList tokens);
|
||||
|
||||
private:
|
||||
TokenList tokens; // Tokenizer'dan gelen token listesi
|
||||
int current = 0; // Şu anki token indeksi
|
||||
|
||||
// --- Token navigasyonu ---
|
||||
ParserToken currentToken();
|
||||
void nextToken();
|
||||
ParserToken lookahead(uint32_t forward);
|
||||
ParserToken parseToken(Token* token);
|
||||
ParserToken getToken(int offset);
|
||||
|
||||
// --- Üst seviye ---
|
||||
ASTNode* parseProgram();
|
||||
|
||||
// --- Deklarasyonlar ---
|
||||
ASTNode* parseDeclaration();
|
||||
ASTNode* parseFunctionDecl();
|
||||
ASTNode* parseVariableDecl();
|
||||
|
||||
// --- Statement'lar ---
|
||||
ASTNode* parseStatement();
|
||||
ASTNode* parseBlock();
|
||||
ASTNode* parseIfStatement();
|
||||
ASTNode* parseWhileStatement();
|
||||
ASTNode* parseForStatement();
|
||||
ASTNode* parseDoWhileStatement();
|
||||
ASTNode* parseReturnStatement();
|
||||
ASTNode* parseBreakStatement();
|
||||
ASTNode* parseContinueStatement();
|
||||
ASTNode* parseExpressionStatement();
|
||||
|
||||
// --- İfadeler (Pratt parser) ---
|
||||
ASTNode* parseExpression();
|
||||
ASTNode* parseExpression(uint16_t precedence);
|
||||
ASTNode* parseNullDenotation();
|
||||
ASTNode* parseLeftDenotation(ASTNode* left);
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Token Navigasyonu
|
||||
// ============================================================================
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseToken: Ham Token'ı ParserToken'a dönüştür.
|
||||
//
|
||||
// Tokenizer'ın string tabanlı tip sistemini ("number", "operator", ...)
|
||||
// Parser'ın anlamsal tip sistemine (NUMBER, PLUS, KW_IF, ...) çevirir.
|
||||
//
|
||||
// BUG FIX (commit 40579ca): pt.token = token (pointer ataması).
|
||||
// Eskiden pt.token = *token (değer kopyası) object slicing yapıyordu.
|
||||
// --------------------------------------------------------------------------
|
||||
inline ParserToken Parser::parseToken(Token* token) {
|
||||
ParserToken pt;
|
||||
pt.token = token; // Pointer — değer kopyası DEĞİL
|
||||
|
||||
std::string t = token->gettype();
|
||||
if (t == "string")
|
||||
pt.type = TokenType::STRING;
|
||||
else if (t == "number")
|
||||
pt.type = TokenType::NUMBER;
|
||||
else if (t == "operator")
|
||||
pt.type = OPERATOR_MAP.find(pt.token->token)->second;
|
||||
else if (t == "delimiter")
|
||||
pt.type = OPERATOR_MAP.find(pt.token->token)->second;
|
||||
else if (t == "keyword")
|
||||
pt.type = KEYWORD_MAP.find(pt.token->token)->second;
|
||||
else if (t == "identifier")
|
||||
pt.type = TokenType::IDENTIFIER;
|
||||
|
||||
return pt;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// getToken: Güvenli token erişimi. Sınır dışı = SVR_VOID.
|
||||
// --------------------------------------------------------------------------
|
||||
inline ParserToken Parser::getToken(int offset) {
|
||||
if ((int)tokens.size() - 1 < current + offset) {
|
||||
ParserToken pt;
|
||||
pt.type = TokenType::SVR_VOID;
|
||||
return pt;
|
||||
}
|
||||
return parseToken(tokens[current + offset]);
|
||||
}
|
||||
|
||||
inline void Parser::nextToken() {
|
||||
if ((int)tokens.size() >= current + 1)
|
||||
current++;
|
||||
}
|
||||
|
||||
inline ParserToken Parser::lookahead(uint32_t forward) {
|
||||
return getToken(forward);
|
||||
}
|
||||
|
||||
inline ParserToken Parser::currentToken() {
|
||||
return getToken(0);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Üst Seviye
|
||||
// ============================================================================
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parse: Parser'ın ana giriş noktası. Token listesini alır, AST döndürür.
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parse(TokenList toks) {
|
||||
tokens = toks;
|
||||
current = 0;
|
||||
return parseProgram();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseProgram: Tüm üst seviye deklarasyonları/statement'ları ayrıştırır.
|
||||
//
|
||||
// Program ::= Declaration*
|
||||
// EOF'a (SVR_VOID) kadar parseDeclaration() çağrılır.
|
||||
//
|
||||
// BUG FIX (commit 438bc0e): Eskiden parseExpression() doğrudan çağrılıyordu,
|
||||
// bu sadece tek bir ifadeyi ayrıştırabiliyordu. Şimdi tam program desteği var.
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseProgram() {
|
||||
ProgramNode* program = new ProgramNode();
|
||||
|
||||
while (currentToken().type != TokenType::SVR_VOID) {
|
||||
ASTNode* decl = parseDeclaration();
|
||||
if (decl)
|
||||
program->addChild(decl);
|
||||
else
|
||||
break; // Hata durumunda döngüden çık
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Deklarasyonlar
|
||||
// ============================================================================
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseDeclaration: Üst seviye deklarasyon ayrıştırıcı.
|
||||
//
|
||||
// Strateji:
|
||||
// 1. Mevcut token bir tip keyword'ü mü (int, void, float, ...)?
|
||||
// - Evet → lookahead(2) '(' ise → fonksiyon tanımı
|
||||
// - Evet → değilse → değişken tanımı
|
||||
// 2. Değilse → statement (REPL modunda ifade de olabilir)
|
||||
//
|
||||
// LOOKAHEAD KULLANIMI:
|
||||
// "int main()" ve "int x = 10" ayrımı için 2 ileriye bakarız:
|
||||
// - int main() → lookahead(1)=identifier, lookahead(2)='('
|
||||
// - int x = 10 → lookahead(1)=identifier, lookahead(2)='='
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseDeclaration() {
|
||||
auto ct = currentToken();
|
||||
|
||||
// Tip keyword'ü ile başlayan → fonksiyon veya değişken
|
||||
if (ct.is({
|
||||
TokenType::KW_VOID, TokenType::KW_INT, TokenType::KW_FLOAT_TYPE,
|
||||
TokenType::KW_DOUBLE, TokenType::KW_BOOL, TokenType::KW_CHAR,
|
||||
TokenType::KW_STRING_TYPE, TokenType::KW_AUTO
|
||||
})) {
|
||||
auto la1 = lookahead(1);
|
||||
auto la2 = lookahead(2);
|
||||
// int main( ... ) → fonksiyon
|
||||
if (la1.type == TokenType::IDENTIFIER && la2.type == TokenType::LPAREN)
|
||||
return parseFunctionDecl();
|
||||
// int x ... → değişken
|
||||
return parseVariableDecl();
|
||||
}
|
||||
|
||||
// Tip keyword'ü değil → statement (veya REPL ifadesi)
|
||||
return parseStatement();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseFunctionDecl: Fonksiyon tanımı.
|
||||
//
|
||||
// Sözdizimi: Type Identifier ( [ParamList] ) Block
|
||||
// Örnek: int main() { ... }
|
||||
//
|
||||
// TODO: Parametre listesi ayrıştırma
|
||||
// TODO: Dönüş tipi doğrulama (şu anda string olarak saklanıyor)
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseFunctionDecl() {
|
||||
FunctionDeclNode* fn = new FunctionDeclNode();
|
||||
fn->returnType = currentToken().token->token; // "int", "void", ...
|
||||
nextToken(); // Dönüş tipini tüket
|
||||
|
||||
fn->name = currentToken().token->token; // "main", "calculate", ...
|
||||
nextToken(); // İsmi tüket
|
||||
|
||||
// Parametre listesi: ( ... )
|
||||
if (currentToken().type == TokenType::LPAREN) {
|
||||
nextToken(); // '(' tüket
|
||||
// TODO: Parametreleri ayrıştır
|
||||
// Şimdilik ')' gelene kadar atla
|
||||
while (currentToken().type != TokenType::RPAREN &&
|
||||
currentToken().type != TokenType::SVR_VOID)
|
||||
nextToken();
|
||||
if (currentToken().type == TokenType::RPAREN)
|
||||
nextToken(); // ')' tüket
|
||||
}
|
||||
|
||||
// Gövde: { ... }
|
||||
if (currentToken().type == TokenType::LBRACE) {
|
||||
ASTNode* body = parseBlock();
|
||||
fn->addChild(body);
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseVariableDecl: Değişken tanımı.
|
||||
//
|
||||
// Sözdizimi: Type Identifier [= Expression] ;
|
||||
// Örnek: int x = 10;
|
||||
// float y; (initExpr = nullptr)
|
||||
//
|
||||
// TODO: Çoklu değişken: int x = 1, y = 2;
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseVariableDecl() {
|
||||
VariableDeclNode* vd = new VariableDeclNode();
|
||||
vd->varType = currentToken().token->token; // "int", "float", ...
|
||||
nextToken(); // Tipi tüket
|
||||
|
||||
if (currentToken().type != TokenType::IDENTIFIER) {
|
||||
std::cerr << "Parser hatası: değişken ismi bekleniyor\n";
|
||||
return vd; // Hatalı düğüm, çağıran kontrol etmeli
|
||||
}
|
||||
|
||||
vd->name = currentToken().token->token; // "x", "counter", ...
|
||||
nextToken(); // İsmi tüket
|
||||
|
||||
// Opsiyonel başlangıç değeri: = expression
|
||||
if (currentToken().type == TokenType::EQUAL) {
|
||||
nextToken(); // '=' tüket
|
||||
vd->initExpr = parseExpression();
|
||||
}
|
||||
|
||||
// Noktalı virgül (opsiyonel — parser hoşgörülü)
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken();
|
||||
|
||||
return vd;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Statement'lar — Recursive Descent
|
||||
// ============================================================================
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseStatement: Statement ayrıştırıcı (dispatcher).
|
||||
//
|
||||
// Mevcut token'a göre uygun parse fonksiyonuna yönlendirir.
|
||||
// Sıralama önemli: LBRACE, keyword'ler, değişken tanımı, ifade.
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseStatement() {
|
||||
auto ct = currentToken();
|
||||
|
||||
if (ct.type == TokenType::LBRACE)
|
||||
return parseBlock();
|
||||
|
||||
if (ct.type == TokenType::KW_IF)
|
||||
return parseIfStatement();
|
||||
|
||||
if (ct.type == TokenType::KW_WHILE)
|
||||
return parseWhileStatement();
|
||||
|
||||
if (ct.type == TokenType::KW_FOR)
|
||||
return parseForStatement();
|
||||
|
||||
if (ct.type == TokenType::KW_DO)
|
||||
return parseDoWhileStatement();
|
||||
|
||||
if (ct.type == TokenType::KW_RETURN)
|
||||
return parseReturnStatement();
|
||||
|
||||
if (ct.type == TokenType::KW_BREAK)
|
||||
return parseBreakStatement();
|
||||
|
||||
if (ct.type == TokenType::KW_CONTINUE)
|
||||
return parseContinueStatement();
|
||||
|
||||
// Değişken tanımı? (tip keyword'ü ile başlayan)
|
||||
if (ct.is({
|
||||
TokenType::KW_VOID, TokenType::KW_INT, TokenType::KW_FLOAT_TYPE,
|
||||
TokenType::KW_DOUBLE, TokenType::KW_BOOL, TokenType::KW_CHAR,
|
||||
TokenType::KW_STRING_TYPE
|
||||
})) {
|
||||
return parseVariableDecl();
|
||||
}
|
||||
|
||||
// Hiçbiri değilse → ifade statement'ı (atama, fonksiyon çağrısı, ...)
|
||||
return parseExpressionStatement();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseBlock: { statement* }
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseBlock() {
|
||||
BlockNode* block = new BlockNode();
|
||||
|
||||
if (currentToken().type == TokenType::LBRACE)
|
||||
nextToken(); // '{' tüket
|
||||
|
||||
while (currentToken().type != TokenType::RBRACE &&
|
||||
currentToken().type != TokenType::SVR_VOID) {
|
||||
ASTNode* stmt = parseStatement();
|
||||
if (stmt)
|
||||
block->addChild(stmt);
|
||||
else
|
||||
break; // Hata durumunda döngüden çık
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::RBRACE)
|
||||
nextToken(); // '}' tüket
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseIfStatement: if (expression) statement [else statement]
|
||||
//
|
||||
// Süslü parantez zorunlu DEĞİL — tek statement de olabilir.
|
||||
// if (x > 5) return x; ← geçerli
|
||||
// if (x > 5) { ... } ← geçerli
|
||||
//
|
||||
// TODO: Sallantılı else (dangling else) sorunu:
|
||||
// if (a) if (b) x; else y; ← else hangi if'e ait?
|
||||
// Mevcut implementasyon doğru: else en yakın if'e bağlanır.
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseIfStatement() {
|
||||
IfStatementNode* ifNode = new IfStatementNode();
|
||||
nextToken(); // 'if' tüket
|
||||
|
||||
// Koşul: ( expression )
|
||||
if (currentToken().type == TokenType::LPAREN) {
|
||||
nextToken(); // '(' tüket
|
||||
ifNode->condition = parseExpression();
|
||||
if (currentToken().type == TokenType::RPAREN)
|
||||
nextToken(); // ')' tüket
|
||||
}
|
||||
|
||||
// Then gövdesi
|
||||
ifNode->thenBranch = parseStatement();
|
||||
|
||||
// Opsiyonel else
|
||||
if (currentToken().type == TokenType::KW_ELSE) {
|
||||
nextToken(); // 'else' tüket
|
||||
ifNode->elseBranch = parseStatement();
|
||||
}
|
||||
|
||||
return ifNode;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseWhileStatement: while (expression) statement
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseWhileStatement() {
|
||||
WhileStatementNode* ws = new WhileStatementNode();
|
||||
nextToken(); // 'while' tüket
|
||||
|
||||
if (currentToken().type == TokenType::LPAREN) {
|
||||
nextToken(); // '(' tüket
|
||||
ws->condition = parseExpression();
|
||||
if (currentToken().type == TokenType::RPAREN)
|
||||
nextToken(); // ')' tüket
|
||||
}
|
||||
|
||||
ws->body = parseStatement();
|
||||
return ws;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseForStatement: for (init; condition; update) statement
|
||||
//
|
||||
// for'un 3 parçası da isteğe bağlıdır:
|
||||
// for (;;) { ... } ← sonsuz döngü (geçerli)
|
||||
//
|
||||
// init: VariableDeclNode veya ExpressionStatementNode
|
||||
// for (int i = 0; ...) → VariableDecl
|
||||
// for (i = 0; ...) → ExpressionStatement
|
||||
// condition: ifade (nullptr = yok)
|
||||
// update: ifade (nullptr = yok)
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseForStatement() {
|
||||
ForStatementNode* fs = new ForStatementNode();
|
||||
nextToken(); // 'for' tüket
|
||||
|
||||
if (currentToken().type == TokenType::LPAREN)
|
||||
nextToken(); // '(' tüket
|
||||
|
||||
// Init (opsiyonel)
|
||||
if (currentToken().type != TokenType::SEMICOLON)
|
||||
fs->init = parseStatement();
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken(); // ';' tüket
|
||||
|
||||
// Condition (opsiyonel)
|
||||
if (currentToken().type != TokenType::SEMICOLON)
|
||||
fs->condition = parseExpression();
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken(); // ';' tüket
|
||||
|
||||
// Update (opsiyonel)
|
||||
if (currentToken().type != TokenType::RPAREN)
|
||||
fs->update = parseExpression();
|
||||
if (currentToken().type == TokenType::RPAREN)
|
||||
nextToken(); // ')' tüket
|
||||
|
||||
// Body
|
||||
fs->body = parseStatement();
|
||||
|
||||
return fs;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseDoWhileStatement: do statement while (expression) ;
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseDoWhileStatement() {
|
||||
DoWhileStatementNode* dw = new DoWhileStatementNode();
|
||||
nextToken(); // 'do' tüket
|
||||
|
||||
// Gövde
|
||||
dw->body = parseStatement();
|
||||
|
||||
// while (expression) ;
|
||||
if (currentToken().type == TokenType::KW_WHILE) {
|
||||
nextToken(); // 'while' tüket
|
||||
if (currentToken().type == TokenType::LPAREN) {
|
||||
nextToken(); // '(' tüket
|
||||
dw->condition = parseExpression();
|
||||
if (currentToken().type == TokenType::RPAREN)
|
||||
nextToken(); // ')' tüket
|
||||
}
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken(); // ';' tüket
|
||||
}
|
||||
|
||||
return dw;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseReturnStatement: return [expression] ;
|
||||
//
|
||||
// return; ← value = nullptr (void fonksiyon)
|
||||
// return x + 1; ← value = BinaryExpression
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseReturnStatement() {
|
||||
ReturnStatementNode* rs = new ReturnStatementNode();
|
||||
nextToken(); // 'return' tüket
|
||||
|
||||
// Opsiyonel dönüş değeri
|
||||
// Eğer sıradaki token ; veya } ise → return;
|
||||
if (currentToken().type != TokenType::SEMICOLON &&
|
||||
currentToken().type != TokenType::RBRACE) {
|
||||
rs->value = parseExpression();
|
||||
}
|
||||
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken(); // ';' tüket
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseBreakStatement / parseContinueStatement
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseBreakStatement() {
|
||||
BreakStatementNode* bs = new BreakStatementNode();
|
||||
nextToken(); // 'break' tüket
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken();
|
||||
return bs;
|
||||
}
|
||||
|
||||
inline ASTNode* Parser::parseContinueStatement() {
|
||||
ContinueStatementNode* cs = new ContinueStatementNode();
|
||||
nextToken(); // 'continue' tüket
|
||||
if (currentToken().type == TokenType::SEMICOLON)
|
||||
nextToken();
|
||||
return cs;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseExpressionStatement: expression ;
|
||||
//
|
||||
// Bir ifadeyi statement olarak kullanır. Örn: x = 5; foo();
|
||||
//
|
||||
// HATA KURTARMA:
|
||||
// Eğer parseExpression() başarısız olursa (nullptr), sonraki ; veya }
|
||||
// veya EOF'a kadar token'ları atlayarak senkronize olur. Bu, tek bir
|
||||
// hatalı ifadenin tüm parser'ı kilitlemesini önler.
|
||||
//
|
||||
// BUG FIX (commit 438bc0e): Eskiden hatalı ifade durumunda sonsuz
|
||||
// döngüye giriyordu (parseProgram her seferinde aynı ifadeyi okuyordu).
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseExpressionStatement() {
|
||||
ExpressionStatementNode* es = new ExpressionStatementNode();
|
||||
es->expression = parseExpression();
|
||||
if (!es->expression) {
|
||||
// Hata kurtarma: sonraki güvenli noktaya atla
|
||||
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)
|
||||
nextToken();
|
||||
|
||||
return es;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// İfadeler — Pratt Parser (Top-Down Operator Precedence)
|
||||
// ============================================================================
|
||||
//
|
||||
// Pratt parser'ın temel fikri: Her operatörün bir "bağlanma gücü" (precedence)
|
||||
// vardır. Parser, bu güce göre operatörleri doğru sırada gruplar.
|
||||
//
|
||||
// NUD (Null Denotation): Prefix ifadeleri (sayılar, -, !, parantez)
|
||||
// LED (Left Denotation): Infix/Postfix ifadeler (+, *, ++)
|
||||
//
|
||||
// ÖRNEK: 1 + 2 * 3
|
||||
// 1. NUD: 1 → Literal(1)
|
||||
// 2. LED(+): prec=13, right'i parseExpression(13) ile ayrıştır
|
||||
// 2a. NUD: 2 → Literal(2)
|
||||
// 2b. LED(*): prec=14 > 13 → parseExpression(14)
|
||||
// 3a. NUD: 3 → Literal(3)
|
||||
// 3b. LED yok → dön
|
||||
// 2c. BinaryExpr(*, 2, 3) dön
|
||||
// 3. BinaryExpr(+, 1, BinaryExpr(*, 2, 3))
|
||||
// Sonuç: 1 + (2 * 3) ✓
|
||||
//
|
||||
// BUG FIX (commit 40579ca): Ana döngü lookahead(1) yerine currentToken()
|
||||
// kullanıyor. NUD artık token'ı tüketip ilerliyor, bu sayede currentToken()
|
||||
// her zaman bir sonraki operatörü gösterir.
|
||||
//
|
||||
// BUG FIX (commit 438bc0e): Atom'lar (sayı, string, identifier) NUD'da
|
||||
// nextToken() ile tüketiliyor. Eskiden tüketilmediği için sonsuz döngü
|
||||
// oluyordu.
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseExpression(): Öncelik 0'dan başla (en düşük bağlanma)
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseExpression() {
|
||||
return parseExpression(0);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseExpression(precedence): Pratt'ın ana döngüsü.
|
||||
//
|
||||
// Algoritma:
|
||||
// 1. NUD ile ilk operand'ı ayrıştır (prefix)
|
||||
// 2. Mevcut token bir operatör mü?
|
||||
// - Evet ve önceliği > precedence ise → LED ile infix ayrıştır
|
||||
// - Hayır veya öncelik <= precedence ise → dur, sol operand'ı döndür
|
||||
// 3. LED'in döndürdüğü düğüm yeni sol operand olur, 2. adıma dön
|
||||
//
|
||||
// DURMA KOŞULLARI:
|
||||
// - RPAREN, SEMICOLON, RBRACE, COMMA: İfade sonu sinyali
|
||||
// - Operatörün önceliği <= mevcut öncelik: Daha sıkı bağlanamaz
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseExpression(uint16_t precedence) {
|
||||
if (currentToken().type == TokenType::SVR_VOID)
|
||||
return nullptr;
|
||||
|
||||
// 1. Prefix (NUD)
|
||||
ASTNode* left = parseNullDenotation();
|
||||
if (!left) return nullptr;
|
||||
|
||||
// 2. Infix/Postfix döngüsü (LED)
|
||||
while (true) {
|
||||
auto next = currentToken();
|
||||
|
||||
// İfade sonu sinyalleri → dur
|
||||
if (next.type == TokenType::RPAREN ||
|
||||
next.type == TokenType::SEMICOLON ||
|
||||
next.type == TokenType::RBRACE ||
|
||||
next.type == TokenType::COMMA)
|
||||
break;
|
||||
|
||||
// Operatörün bağlanma gücü yetersiz → dur
|
||||
// (daha yüksek öncelikli bir bağlamdayız, bu operatör oraya ait değil)
|
||||
if (precedence < next.getPowerOperator()) {
|
||||
left = parseLeftDenotation(left);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseNullDenotation (NUD): Prefix ifadeleri.
|
||||
//
|
||||
// İşlenen prefix tipleri:
|
||||
// - Parantez: ( expression )
|
||||
// - Unary: +expr, -expr, !expr, ~expr, ++expr, --expr
|
||||
// - Literal: 42, "hello", true, false, null
|
||||
// - Identifier: x, myVar
|
||||
//
|
||||
// DÖNÜŞ: Ayrıştırılmış AST düğümü. Token TÜKETİLMİŞ olur (current ilerlemiş).
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseNullDenotation() {
|
||||
auto ct = currentToken();
|
||||
|
||||
if (ct.type == TokenType::SVR_VOID) {
|
||||
std::cerr << "Parser hatası: beklenmeyen dosya sonu\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// --- Parantezli ifade: ( expr ) ---
|
||||
// Önceliği sıfırlar — parantez içinde yeni bir ifade başlar.
|
||||
if (ct.type == TokenType::LPAREN) {
|
||||
nextToken(); // '(' tüket
|
||||
ASTNode* expr = parseExpression(0); // Öncelik sıfırla
|
||||
if (currentToken().type == TokenType::RPAREN)
|
||||
nextToken(); // ')' tüket
|
||||
return expr;
|
||||
}
|
||||
|
||||
// --- Unary prefix operatörler: +, -, !, ~, ++, -- ---
|
||||
// PLUS ve MINUS burada UNARY olarak işlenir.
|
||||
// Binary olarak işlenmesi LED tarafından yapılır.
|
||||
//
|
||||
// ÖNEMLİ: PLUS ve MINUS için getPowerOperator() 13 döndürür (binary öncelik).
|
||||
// Ama burada unary olarak kullanılıyor. parseExpression(16) çağırmak daha
|
||||
// doğru olurdu ancak mevcut çalışma şekli de doğru sonuç veriyor.
|
||||
// TODO: Unary için ayrı öncelik seviyesi (örn: 16)
|
||||
if (ct.is({
|
||||
TokenType::PLUS_PLUS, TokenType::MINUS_MINUS,
|
||||
TokenType::PLUS, TokenType::MINUS,
|
||||
TokenType::BANG, TokenType::TILDE
|
||||
})) {
|
||||
nextToken(); // Operatörü tüket
|
||||
// Sağ operand'ı ayrıştır. Unary prefix sağdan sola bağlanır.
|
||||
ASTNode* right = parseExpression(ct.getPowerOperator());
|
||||
BinaryExpressionNode* bin = new BinaryExpressionNode();
|
||||
bin->Right = right;
|
||||
bin->Left = nullptr; // Unary işaretçisi
|
||||
bin->Operator = ct.type;
|
||||
if (right) right->parent = bin;
|
||||
return bin;
|
||||
}
|
||||
|
||||
// --- Sayısal literal: 42, 0xFF, 3.14 ---
|
||||
if (ct.type == TokenType::NUMBER) {
|
||||
nextToken(); // Token'ı tüket
|
||||
LiteralNode* lit = new LiteralNode();
|
||||
lit->lexerToken = ct.token;
|
||||
lit->parserToken = ct;
|
||||
return lit;
|
||||
}
|
||||
|
||||
// --- String literal: "hello" ---
|
||||
if (ct.type == TokenType::STRING) {
|
||||
nextToken();
|
||||
LiteralNode* lit = new LiteralNode();
|
||||
lit->lexerToken = ct.token;
|
||||
lit->parserToken = ct;
|
||||
return lit;
|
||||
}
|
||||
|
||||
// --- Boolean/null literal: true, false, null ---
|
||||
if (ct.is({TokenType::KW_TRUE, TokenType::KW_FALSE, TokenType::KW_NULL})) {
|
||||
nextToken();
|
||||
LiteralNode* lit = new LiteralNode();
|
||||
lit->lexerToken = ct.token;
|
||||
lit->parserToken = ct;
|
||||
return lit;
|
||||
}
|
||||
|
||||
// --- Identifier: x, myVar ---
|
||||
if (ct.type == TokenType::IDENTIFIER) {
|
||||
nextToken();
|
||||
IdentifierNode* id = new IdentifierNode();
|
||||
id->lexerToken = ct.token;
|
||||
id->parserToken = ct;
|
||||
return id;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// parseLeftDenotation (LED): Infix ve Postfix ifadeler.
|
||||
//
|
||||
// Sol operand zaten ayrıştırılmış olarak gelir (left).
|
||||
// Mevcut token operatördür.
|
||||
//
|
||||
// İşlenen tipler:
|
||||
// - Postfix: expr++, expr--
|
||||
// - Binary infix: expr + expr, expr * expr, expr == expr, ...
|
||||
//
|
||||
// TASARIM NOTU: Postfix ve Binary aynı fonksiyonda işlenir çünkü ikisi de
|
||||
// "sol operand + operatör" pattern'ini takip eder. Postfix'te sağ operand
|
||||
// yoktur.
|
||||
// --------------------------------------------------------------------------
|
||||
inline ASTNode* Parser::parseLeftDenotation(ASTNode* left) {
|
||||
auto ct = currentToken();
|
||||
|
||||
// --- Postfix: expr++, expr-- ---
|
||||
// Operatör operand'dan SONRA gelir, sağ operand yok.
|
||||
if (ct.is({TokenType::PLUS_PLUS, TokenType::MINUS_MINUS})) {
|
||||
nextToken(); // Operatörü tüket
|
||||
PostfixNode* pf = new PostfixNode();
|
||||
pf->operand = left;
|
||||
pf->Operator = ct.type;
|
||||
left->parent = pf;
|
||||
return pf;
|
||||
}
|
||||
|
||||
// --- Binary infix: expr OP expr ---
|
||||
// OP'nin önceliğine göre sağ operand'ı ayrıştır.
|
||||
uint16_t prec = ct.getPowerOperator();
|
||||
nextToken(); // Operatörü tüket
|
||||
|
||||
// Sağ operand. prec parametresi, daha yüksek öncelikli operatörlerin
|
||||
// sağ operand içinde gruplanmasını sağlar.
|
||||
ASTNode* right = parseExpression(prec);
|
||||
|
||||
BinaryExpressionNode* bin = new BinaryExpressionNode();
|
||||
bin->Left = left;
|
||||
bin->Right = right;
|
||||
bin->Operator = ct.type;
|
||||
if (left) left->parent = bin;
|
||||
if (right) right->parent = bin;
|
||||
return bin;
|
||||
}
|
||||
#include "parser/parser_base.hpp"
|
||||
|
||||
#endif // SAQUT_PARSER
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Parser Sınıf Tanımı
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/parser_base.hpp
|
||||
// İÇERİK: Parser sınıf tanımı + include'lar. Metot gövdeleri yok.
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_PARSER_BASE
|
||||
#define SAQUT_PARSER_BASE
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include "parser/token.hpp"
|
||||
#include "parser/ast.hpp"
|
||||
#include "tools.hpp"
|
||||
class Parser {
|
||||
public:
|
||||
ASTNode* parse(TokenList tokens);
|
||||
|
||||
private:
|
||||
TokenList tokens; // Tokenizer'dan gelen token listesi
|
||||
int current = 0; // Şu anki token indeksi
|
||||
|
||||
// --- Token navigasyonu ---
|
||||
ParserToken currentToken();
|
||||
void nextToken();
|
||||
ParserToken lookahead(uint32_t forward);
|
||||
ParserToken parseToken(Token* token);
|
||||
ParserToken getToken(int offset);
|
||||
|
||||
// --- Üst seviye ---
|
||||
ASTNode* parseProgram();
|
||||
|
||||
// --- Deklarasyonlar ---
|
||||
ASTNode* parseDeclaration();
|
||||
ASTNode* parseFunctionDecl();
|
||||
ASTNode* parseStructDecl();
|
||||
ASTNode* parseVariableDecl();
|
||||
|
||||
// --- Statement'lar ---
|
||||
ASTNode* parseStatement();
|
||||
ASTNode* parseBlock();
|
||||
ASTNode* parseIfStatement();
|
||||
ASTNode* parseWhileStatement();
|
||||
ASTNode* parseForStatement();
|
||||
ASTNode* parseDoWhileStatement();
|
||||
ASTNode* parseReturnStatement();
|
||||
ASTNode* parseBreakStatement();
|
||||
ASTNode* parseContinueStatement();
|
||||
ASTNode* parseExpressionStatement();
|
||||
|
||||
// --- İfadeler (Pratt parser) ---
|
||||
ASTNode* parseExpression();
|
||||
ASTNode* parseExpression(uint16_t precedence);
|
||||
ASTNode* parseNullDenotation();
|
||||
ASTNode* parseLeftDenotation(ASTNode* left);
|
||||
};
|
||||
|
||||
#endif // SAQUT_PARSER_BASE
|
||||
|
|
@ -1,65 +1,43 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Parser Token Tipleri ve Operatör Öncelik Tablosu
|
||||
// saQut Compiler — Parser Token Tipleri, Operatör Öncelik Tablosu ve ParserToken
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/parser/token.hpp
|
||||
// KATMAN: Katman 3 — Tokenizer ile Parser arasında köprü
|
||||
// BAĞIMLI: Tokenizer (src/tokenizer/tokenizer.hpp)
|
||||
// KULLANAN: AST (src/parser/ast.hpp), Parser (src/parser/parser.hpp)
|
||||
// KATMAN: Katman 3 — Tokenizer ile Parser Arasında Köprü
|
||||
// AMAÇ: Tokenizer'ın ham token'larını anlamsal tiplere dönüştürmek,
|
||||
// operatör öncelik ve birleşme kurallarını merkezi olarak tanımlamak
|
||||
//
|
||||
// AMAÇ:
|
||||
// Tokenizer'ın ürettiği ham Token'ları (string tipli) Parser'ın anlayacağı
|
||||
// anlamsal tiplere (TokenType enum) dönüştürür. Ayrıca operatör önceliğini
|
||||
// (precedence) ve birleşme yönünü (associativity) merkezi olarak tanımlar.
|
||||
// BAĞIMLILIKLAR:
|
||||
// - tokenizer/tokenizer.hpp: Token sınıf hiyerarşisi (Token, NumberToken, vs.)
|
||||
// - KULLANAN: parser/ast.hpp, parser/parser.hpp
|
||||
//
|
||||
// Bu dosya, Pratt parser'ın "kalbi"dir — tüm operatör önceliği ve birleşme
|
||||
// kuralları burada tek bir yerde tanımlanır.
|
||||
// BU DOSYANIN İÇERDİKLERİ:
|
||||
// 1. TokenType enum (uint16_t): 100+ token tipi (keyword'ler, operatörler, delimiter'lar)
|
||||
// 2. KEYWORD_MAP: string → TokenType (keyword çözümleme)
|
||||
// 3. OPERATOR_MAP: string → TokenType (operatör çözümleme)
|
||||
// 4. OPERATOR_MAP_REV: TokenType → string (log çıktısı için ters harita)
|
||||
// 5. OPERATOR_MAP_STRREV: TokenType → string (enum ismi, debug için)
|
||||
// 6. TokenPrecedence(): Öncelik tablosu (18 seviye, Pratt parser'ın kalbi)
|
||||
// 7. RightAssociative(): Sağ birleşme kontrolü (atama, üs, ternary)
|
||||
// 8. ParserToken: Parser'ın kullandığı token yapısı (Token* + TokenType)
|
||||
//
|
||||
// ADR-002: Neden Merkezi Operatör Öncelik Tablosu?
|
||||
// Recursive descent parser'larda operatör önceliği, her seviye için ayrı
|
||||
// bir fonksiyon yazılarak (parseAddExpr, parseMulExpr, ...) kod tekrarına
|
||||
// neden olur. Yeni bir operatör eklemek için yeni fonksiyon + mevcut
|
||||
// fonksiyonları değiştirmek gerekir.
|
||||
// TASARIM KARARLARI (ADR-002):
|
||||
// Neden TokenType enum'ı burada tanımlı, Tokenizer'da değil?
|
||||
// -> Tokenizer sadece ham token'lar üretir. Anlamsal tipler Parser'ın işidir.
|
||||
// -> Tokenizer'ın Tokenizer'ın ham yapısını değiştirmeden yeni diller eklenebilir.
|
||||
//
|
||||
// Pratt parser'da tüm öncelik bilgisi TEK BİR TABLODA (TokenPrecedence)
|
||||
// toplanır. Yeni operatör eklemek = tabloya bir satır eklemek.
|
||||
// Neden uint16_t tabanlı enum?
|
||||
// -> 65K token tipi fazlasıyla yeterli. 2 byte = bellek tasarrufu.
|
||||
// -> Her AST düğümünde TokenType saklanabilir (opsiyonel).
|
||||
//
|
||||
// TASARIM KARARLARI:
|
||||
// 1. TokenType enum: uint16_t tabanlı. Neden? 65K'dan fazla token tipi
|
||||
// olmayacak, 2 byte yeterli. Bellek tasarrufu AST'de fark eder.
|
||||
// Neden dört ayrı map?
|
||||
// -> unordered_map tek yönlüdür. Her yön için ayrı map gerekir.
|
||||
// -> OPERATOR_MAP_REV: log çıktısında "+" göstermek için.
|
||||
// -> OPERATOR_MAP_STRREV: enum ismini string olarak (debug, AST dump).
|
||||
//
|
||||
// 2. Üç harita (KEYWORD_MAP, OPERATOR_MAP, OPERATOR_MAP_REV, OPERATOR_MAP_STRREV):
|
||||
// - KEYWORD_MAP: "if" → KW_IF, string'den TokenType'a
|
||||
// - OPERATOR_MAP: "+" → PLUS, operatör string'inden TokenType'a
|
||||
// - OPERATOR_MAP_REV: PLUS → "+", log çıktısı için ters harita
|
||||
// - OPERATOR_MAP_STRREV: PLUS → "PLUS", enum ismini string olarak verir
|
||||
// Neden dört harita? Çünkü std::unordered_map tek yönlüdür.
|
||||
// bidirectional_map kütüphanesi kullanılabilirdi ama bağımlılık istemedik.
|
||||
//
|
||||
// 3. TokenPrecedence(): 18 seviyeli öncelik sistemi.
|
||||
// C/C++/Java standartlarına uygun. Yüksek sayı = yüksek öncelik.
|
||||
// Seviye 18 (en yüksek): üye erişimi (., ->, [], (), ::)
|
||||
// Seviye 1 (en düşük): virgül (,)
|
||||
// Seviye 0: önceliksiz (değerler, EOF, vb.)
|
||||
//
|
||||
// 4. RightAssociative(): Hangi operatörler sağdan sola birleşir?
|
||||
// - Atama (=, +=, vb.)
|
||||
// - Üs alma (**, ^) — matematiksel sağ birleşme: a^b^c = a^(b^c)
|
||||
// - Ternary (?:)
|
||||
// Diğer tüm operatörler soldan sağa birleşir.
|
||||
//
|
||||
// 5. ParserToken yapısı:
|
||||
// Token* token: Tokenizer'ın ürettiği Token'a pointer. Değer kopyası
|
||||
// DEĞİL. Neden pointer? Çünkü Token polimorfik (NumberToken, StringToken,
|
||||
// vb.) ve değer kopyası object slicing'e neden olur.
|
||||
// BUG FIX (commit 40579ca): Eskiden Token token (değer) vardı.
|
||||
// TokenType type: Token'ın anlamsal tipi.
|
||||
// is() / getPowerOperator() / isRightAssociative(): kolaylık metotları.
|
||||
//
|
||||
// BİLİNEN SINIRLAMALAR (TODO):
|
||||
// TODO: Özel operatörler: ?., ??, |>, >>=, vb. (ileride eklenebilir)
|
||||
// TODO: Kullanıcı tanımlı operatör önceliği (DSL'ler için)
|
||||
// TODO: Token konum bilgisi (satır/sütun) ParserToken'a eklenmeli
|
||||
// Neden bu kadar çok keyword?
|
||||
// -> saQut hem C/C++ hem Java hem de kendi sözdizimini destekler.
|
||||
// -> Tüm keyword'ler tek enum'da toplanmıştır.
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
|
|
@ -107,184 +85,334 @@ typedef std::vector<Token*> TokenList;
|
|||
// NEDEN uint16_t? Bellek optimizasyonu. Her AST düğümü bir TokenType taşır.
|
||||
// Binlerce düğümde 2 byte vs 4 byte fark eder.
|
||||
//
|
||||
/* ================================================================
|
||||
* TokenType — Anlamsal Token Tipleri
|
||||
* ================================================================
|
||||
*
|
||||
* Tokenizer'ın ürettiği ham token'ları (string tipli) Parser'ın
|
||||
* anlayacağı anlamsal tiplere dönüştürür.
|
||||
*
|
||||
* uint16_t tabanlı — 65K token tipi yeterli.
|
||||
* Bellek: AST düğümlerinde taşınabilir (2 byte).
|
||||
*
|
||||
* KATEGORİLER (öncelik sırasına göre):
|
||||
* 1. Değerler: IDENTIFIER, NUMBER, STRING, SVR_VOID
|
||||
* 2. Keyword'ler: KW_IF ... KW_NOEXCEPT (C/C++/Java ortak)
|
||||
* 3. Operatörler: DOT(18) ... COMMA(1) (Pratt öncelik seviyesi)
|
||||
* 4. Delimiter'lar: LBRACE, RBRACE, SEMICOLON, vb.
|
||||
* 5. Özel: END_OF_FILE, UNKNOWN, COMMENT, PREPROCESSOR
|
||||
*
|
||||
* ================================================================ */
|
||||
enum class TokenType : uint16_t {
|
||||
// --- Değerler ve Tanımlayıcılar ---
|
||||
IDENTIFIER, // değişken/fonksiyon ismi
|
||||
NUMBER, // 42, 0xFF, 0b1010, 3.14
|
||||
STRING, // "merhaba"
|
||||
SVR_VOID, // Geçersiz/EOF sinyali (Parser içinde kullanılır)
|
||||
/* ====== Değerler ve Tanımlayıcılar ====== */
|
||||
IDENTIFIER, // Değişken/fonksiyon/sınıf ismi.
|
||||
// Tokenizer'da IdentifierToken olarak üretilir.
|
||||
// Örn: x, main, Point, calculateAverage
|
||||
NUMBER, // Sayısal sabit: 42, 0xFF, 0b1010, 3.14, 1e-5
|
||||
// Tokenizer'da NumberToken olarak üretilir.
|
||||
// .isFloat alanı ile tamsayı/ondalık ayrımı yapılır.
|
||||
STRING, // Metin sabiti: "merhaba", "selam"
|
||||
// Tokenizer'da StringToken olarak üretilir.
|
||||
// Kaçış dizileri (\n, \t, \") tokenizer'da çözülür.
|
||||
SVR_VOID, // Geçersiz/EOF sinyali.
|
||||
// Parser içinde kullanılır. Tokenizer ÜRETMEZ.
|
||||
// currentToken() geçersiz indeks gösterdiğinde döner.
|
||||
|
||||
// --- Kontrol Akışı Keyword'leri ---
|
||||
KW_IF, // if
|
||||
KW_ELSE, // else
|
||||
KW_FOR, // for
|
||||
KW_WHILE, // while
|
||||
KW_DO, // do
|
||||
KW_SWITCH, // switch
|
||||
KW_CASE, // case
|
||||
KW_DEFAULT, // default
|
||||
KW_BREAK, // break
|
||||
KW_CONTINUE, // continue
|
||||
KW_RETURN, // return
|
||||
/* ====== Kontrol Akışı Keyword'leri ====== */
|
||||
KW_IF, // if (koşullu dal)
|
||||
// Sözdizimi: if (koşul) gövde [else gövde]
|
||||
KW_ELSE, // else (if'in alternatif dalı)
|
||||
// Sözdizimi: if (...) ... else ...
|
||||
// Parser'da if'ten sonra else opsiyoneldir.
|
||||
KW_FOR, // for (tekrarlı döngü)
|
||||
// Sözdizimi: for (init; koşul; artım) gövde
|
||||
KW_WHILE, // while (koşullu döngü)
|
||||
// Sözdizimi: while (koşul) gövde
|
||||
KW_DO, // do (en az bir kez çalışan döngü)
|
||||
// Sözdizimi: do gövde while (koşul);
|
||||
KW_SWITCH, // switch (çoklu dal — henüz implemente edilmedi)
|
||||
KW_CASE, // case (switch dalı — henüz implemente edilmedi)
|
||||
KW_DEFAULT, // default (switch varsayılan — henüz implemente edilmedi)
|
||||
KW_BREAK, // break (döngü/switch'ten çık)
|
||||
// Sadece döngü veya switch içinde geçerlidir.
|
||||
KW_CONTINUE, // continue (döngünün bir sonraki iterasyonuna geç)
|
||||
// Sadece döngü içinde geçerlidir.
|
||||
KW_RETURN, // return (fonksiyondan dön)
|
||||
// İsteğe bağlı dönüş değeri: return expr;
|
||||
|
||||
// --- OOP Keyword'leri ---
|
||||
KW_CLASS, // class
|
||||
KW_INTERFACE, // interface
|
||||
KW_ENUM, // enum
|
||||
KW_EXTENDS, // extends
|
||||
KW_IMPLEMENTS, // implements
|
||||
KW_NEW, // new
|
||||
KW_PUBLIC, // public
|
||||
KW_PRIVATE, // private
|
||||
KW_PROTECTED, // protected
|
||||
KW_STATIC, // static
|
||||
KW_FINAL, // final
|
||||
KW_ABSTRACT, // abstract
|
||||
/* ====== OOP Keyword'leri ====== */
|
||||
KW_CLASS, // class (sınıf tanımı — Java/C++ tarzı)
|
||||
KW_STRUCT, // struct (yapı tanımı — C tarzı)
|
||||
// saQut'ta class ve struct ikisi de desteklenir.
|
||||
KW_INTERFACE, // interface (soyut tip — Java tarzı)
|
||||
KW_ENUM, // enum (sabit listesi — C/C++/Java tarzı)
|
||||
KW_EXTENDS, // extends (kalıtım — Java tarzı)
|
||||
KW_IMPLEMENTS, // implements (interface gerçekleme — Java tarzı)
|
||||
KW_NEW, // new (nesne oluşturma — Java/C++ tarzı)
|
||||
KW_PUBLIC, // public (erişim belirteci)
|
||||
KW_PRIVATE, // private (erişim belirteci)
|
||||
KW_PROTECTED, // protected (erişim belirteci — alt sınıflara açık)
|
||||
KW_STATIC, // static (sınıf üyesi / dosya içi bağlantı)
|
||||
KW_FINAL, // final (değiştirilemez — Java tarzı)
|
||||
KW_ABSTRACT, // abstract (soyut sınıf/metot — Java tarzı)
|
||||
|
||||
// --- Tip Keyword'leri ---
|
||||
KW_VOID, // void
|
||||
KW_BOOL, // bool
|
||||
KW_INT, // int
|
||||
KW_FLOAT_TYPE, // float (FLOAT math.h'de tanımlı olabilir, TYPE eki var)
|
||||
KW_DOUBLE, // double
|
||||
KW_CHAR, // char
|
||||
KW_STRING_TYPE, // string
|
||||
/* ====== Tip Keyword'leri ====== */
|
||||
KW_VOID, // void (değer döndürmeyen fonksiyon / tip yok)
|
||||
// C/C++/Java uyumluluğu için.
|
||||
KW_BOOL, // bool (mantıksal tip: true/false)
|
||||
// C++ bool ile aynı.
|
||||
KW_INT, // int (tamsayı tipi: 32-bit işaretli)
|
||||
// Varsayılan tamsayı tipi.
|
||||
KW_FLOAT_TYPE, // float (32-bit ondalıklı sayı)
|
||||
// FLOAT_MATH hatasından kaçınmak için _TYPE eki.
|
||||
// math.h'deki float tanımıyla çakışmaz.
|
||||
KW_DOUBLE, // double (64-bit ondalıklı sayı)
|
||||
KW_CHAR, // char (8-bit karakter)
|
||||
// Tek tırnak içindeki karakterler için: 'A'
|
||||
KW_STRING_TYPE, // string (metin tipi)
|
||||
// string.h'daki string işlevleriyle çakışmaz.
|
||||
|
||||
// --- Literal Keyword'ler ---
|
||||
KW_TRUE, // true
|
||||
KW_FALSE, // false
|
||||
KW_NULL, // null
|
||||
/* ====== Literal Keyword'ler ====== */
|
||||
KW_TRUE, // true (mantıksal doğru sabiti)
|
||||
// Boolean literal: if (true) { ... }
|
||||
KW_FALSE, // false (mantıksal yanlış sabiti)
|
||||
// Boolean literal: while (false) { ... }
|
||||
KW_NULL, // null (boş referans sabiti)
|
||||
// Pointer/referans tipleri için: Object obj = null;
|
||||
|
||||
// --- İstisna Yönetimi ---
|
||||
KW_TRY, // try
|
||||
KW_CATCH, // catch
|
||||
KW_FINALLY, // finally
|
||||
KW_THROW, // throw
|
||||
KW_THROWS, // throws
|
||||
KW_ASSERT, // assert
|
||||
/* ====== İstisna Yönetimi ====== */
|
||||
KW_TRY, // try (istisna deneme bloğu — Java/C++ tarzı)
|
||||
// Sözdizimi: try { ... } catch (Ex e) { ... }
|
||||
KW_CATCH, // catch (istisna yakalama bloğu)
|
||||
KW_FINALLY, // finally (her durumda çalışan blok — Java tarzı)
|
||||
KW_THROW, // throw (istisna fırlatma — C++/Java tarzı)
|
||||
// Sözdizimi: throw new Exception("hata");
|
||||
KW_THROWS, // throws (metot imzasında istisna bildirimi — Java)
|
||||
KW_ASSERT, // assert (debug assertions — C/Java tarzı)
|
||||
|
||||
// --- Modül/Paket ---
|
||||
KW_IMPORT, // import
|
||||
KW_PACKAGE, // package
|
||||
/* ====== Modül/Paket ====== */
|
||||
KW_IMPORT, // import (modül içe aktarma — Java/Python tarzı)
|
||||
// Sözdizimi: import java.util.List;
|
||||
KW_PACKAGE, // package (modül bildirimi — Java tarzı)
|
||||
// Sözdizimi: package com.saqut.compiler;
|
||||
|
||||
// --- C/C++ Ekleri ---
|
||||
KW_NATIVE, // native (JNI)
|
||||
KW_SYNCHRONIZED, // synchronized (Java)
|
||||
KW_VOLATILE, // volatile
|
||||
KW_TRANSIENT, // transient
|
||||
KW_CONST, // const
|
||||
KW_EXTERN, // extern
|
||||
KW_TYPEDEF, // typedef
|
||||
KW_SIZEOF, // sizeof
|
||||
KW_ALIGNOF, // alignof
|
||||
KW_DECLTYPE, // decltype
|
||||
KW_AUTO, // auto
|
||||
KW_CONSTEXPR, // constexpr
|
||||
KW_NOEXCEPT, // noexcept
|
||||
/* ====== C/C++ Ekleri ====== */
|
||||
KW_NATIVE, // native (yerel kod bildirimi — JNI)
|
||||
// Java native metotları için.
|
||||
KW_SYNCHRONIZED, // synchronized (iş parçacığı senkronizasyonu — Java)
|
||||
KW_VOLATILE, // volatile (derleyici optimizasyonunu engelle — C/C++/Java)
|
||||
KW_TRANSIENT, // transient (serileştirmeyi atla — Java)
|
||||
KW_CONST, // const (değişmez değer — C/C++ tarzı)
|
||||
// Örn: const int MAX = 100;
|
||||
KW_EXTERN, // extern (harici bağlantı — C/C++ tarzı)
|
||||
KW_TYPEDEF, // typedef (tip takma adı — C/C++ tarzı)
|
||||
KW_SIZEOF, // sizeof (tip/boyut sorgulama — C/C++ tarzı)
|
||||
// Sözdizimi: sizeof(int) veya sizeof x
|
||||
KW_ALIGNOF, // alignof (hizalama sorgulama — C++11)
|
||||
KW_DECLTYPE, // decltype (ifade tipi çıkarımı — C++11)
|
||||
KW_AUTO, // auto (otomatik tip çıkarımı — C++11)
|
||||
KW_CONSTEXPR, // constexpr (derleme zamanı sabiti — C++11)
|
||||
KW_NOEXCEPT, // noexcept (istisna fırlatmayan bildirimi — C++11)
|
||||
|
||||
// ================================================================
|
||||
// Operatörler — Öncelik sırasına göre gruplanmış
|
||||
// ================================================================
|
||||
/* ================================================================
|
||||
* Operatörler — Öncelik sırasına göre gruplanmış
|
||||
*
|
||||
* Her operatörün yanında Pratt parser öncelik seviyesi yazılıdır.
|
||||
* Yüksek sayı = daha sıkı bağlanma (önce işlenir).
|
||||
*
|
||||
* Seviye 18 (en yüksek): Üye erişimi ve çağrı
|
||||
* Seviye 17: Postfix ++ --
|
||||
* Seviye 16: Unary prefix + - ! ~
|
||||
* Seviye 15: Üs alma ** ^
|
||||
* Seviye 14: Çarpma/Bölme * / %
|
||||
* Seviye 13: Toplama/Çıkarma + -
|
||||
* Seviye 12: Bitsel kaydırma << >>
|
||||
* Seviye 11: İlişkisel < <= > >=
|
||||
* Seviye 10: Eşitlik == !=
|
||||
* Seviye 9: Bitsel VE &
|
||||
* Seviye 8: Bitsel XOR ^ (CARET üs olarak 15'te)
|
||||
* Seviye 7: Bitsel VEYA |
|
||||
* Seviye 6: Mantıksal VE &&
|
||||
* Seviye 5: Mantıksal VEYA ||
|
||||
* Seviye 4: Ternary ?
|
||||
* Seviye 3: Ternary else :
|
||||
* Seviye 2: Atama = += -= vb.
|
||||
* Seviye 1 (en düşük): Virgül ,
|
||||
* ================================================================ */
|
||||
|
||||
// Seviye 1 (18): Üye erişimi ve çağrı — En yüksek öncelik
|
||||
DOT, // .
|
||||
ARROW, // ->
|
||||
LBRACKET, // [
|
||||
RBRACKET, // ]
|
||||
LPAREN, // (
|
||||
RPAREN, // )
|
||||
// Seviye 18: Üye erişimi ve çağrı — En yüksek öncelik
|
||||
DOT, // . (üye erişimi) — obj.field
|
||||
// Öncelik 18. En sıkı bağlanan operatör.
|
||||
ARROW, // -> (pointer üye erişimi) — ptr->field
|
||||
// C++ tarzı. Öncelik 18.
|
||||
LBRACKET, // [ (dizi/indeks erişimi başlangıcı) — a[i]
|
||||
// Açılış köşeli parantez. Öncelik 18.
|
||||
RBRACKET, // ] (dizi/indeks erişimi bitişi) — a[i]
|
||||
// Kapanış köşeli parantez. Tek başına kullanılmaz.
|
||||
LPAREN, // ( (fonksiyon çağrısı/grouping başlangıcı)
|
||||
// İki anlamı: f(args) çağrı, (expr) gruplama.
|
||||
// Öncelik 18.
|
||||
RPAREN, // ) (fonksiyon çağrısı/grouping bitişi)
|
||||
// Kapanış parantez. Tek başına kullanılmaz.
|
||||
|
||||
// Seviye 2 (17): Postfix
|
||||
PLUS_PLUS, // ++ (postfix)
|
||||
MINUS_MINUS, // -- (postfix)
|
||||
// Seviye 17: Postfix — Soldaki ifadeye sonradan uygulanan operatörler
|
||||
PLUS_PLUS, // ++ (postfix artım) — x++
|
||||
// Önce x'in değerini döndür, sonra artır.
|
||||
// Öncelik 17. Sağ birleşmeli DEĞİL.
|
||||
MINUS_MINUS, // -- (postfix azaltım) — x--
|
||||
// Önce x'in değerini döndür, sonra azalt.
|
||||
// Öncelik 17.
|
||||
|
||||
// Seviye 3 (16): Unary Prefix
|
||||
PLUS, // + (unary)
|
||||
MINUS, // - (unary)
|
||||
BANG, // ! (mantıksal değil)
|
||||
TILDE, // ~ (bitsel değil)
|
||||
// Seviye 16: Unary Prefix — Sağındaki ifadeye uygulanan tekli operatörler
|
||||
PLUS, // + (unary plus / binary toplama)
|
||||
// Unary: +x (pozitif işareti, genelde etkisiz).
|
||||
// Binary: a + b (toplama, öncelik 13).
|
||||
// Hangi anlamda kullanıldığı parse bağlamında belirlenir.
|
||||
MINUS, // - (unary minus / binary çıkarma)
|
||||
// Unary: -x (negatif yap).
|
||||
// Binary: a - b (çıkarma, öncelik 13).
|
||||
BANG, // ! (mantıksal değil) — !x
|
||||
// Örn: if (!flag) { ... }
|
||||
// Sadece prefix. Öncelik 16.
|
||||
TILDE, // ~ (bitsel değil) — ~x
|
||||
// Bitwise NOT. Sadece prefix. Öncelik 16.
|
||||
|
||||
// Seviye 4 (15): Üs alma
|
||||
STAR_STAR, // ** (Python tarzı üs)
|
||||
CARET, // ^ (bazı dillerde üs)
|
||||
// Seviye 15: Üs alma — Sağ birleşmeli
|
||||
STAR_STAR, // ** (üs alma) — a ** b = a^b
|
||||
// Python tarzı. Öncelik 15. Sağ birleşmeli.
|
||||
// 2 ** 3 ** 2 = 2 ** (3 ** 2) = 512
|
||||
CARET, // ^ (üs alma veya bitsel XOR)
|
||||
// saQut'ta varsayılan: üs alma (öncelik 15).
|
||||
// C/C++'da XOR (öncelik 8) — bağlama göre değişebilir.
|
||||
|
||||
// Seviye 5 (14): Çarpma/Bölme
|
||||
STAR, // *
|
||||
SLASH, // /
|
||||
PERCENT, // %
|
||||
// Seviye 14: Çarpma/Bölme — Sol birleşmeli
|
||||
STAR, // * (çarpma) — a * b
|
||||
// Öncelik 14.
|
||||
SLASH, // / (bölme) — a / b
|
||||
// Tamsayı bölmesi: int / int = int.
|
||||
// Ondalık bölme: float / float = float.
|
||||
PERCENT, // % (mod alma) — a % b
|
||||
// Sadece tamsayılar için.
|
||||
|
||||
// Seviye 6 (13): Toplama/Çıkarma — PLUS ve MINUS yukarıda (unary + binary)
|
||||
// Seviye 7 (12): Bitsel kaydırma
|
||||
LSHIFT, // <<
|
||||
RSHIFT, // >>
|
||||
// Seviye 13: Toplama/Çıkarma
|
||||
// PLUS ve MINUS yukarıda tanımlandı (hem unary 16 hem binary 13).
|
||||
// Pratt parser bağlama göre doğru önceliği kullanır.
|
||||
|
||||
// Seviye 8 (11): İlişkisel karşılaştırma
|
||||
LESS, // <
|
||||
LESS_EQUAL, // <=
|
||||
GREATER, // >
|
||||
GREATER_EQUAL, // >=
|
||||
// Seviye 12: Bitsel kaydırma
|
||||
LSHIFT, // << (sola kaydırma) — a << b
|
||||
// a * 2^b. Öncelik 12.
|
||||
RSHIFT, // >> (sağa kaydırma) — a >> b
|
||||
// a / 2^b (işaretli: arithmetic, işaretsiz: logical).
|
||||
|
||||
// Seviye 9 (10): Eşitlik
|
||||
EQUAL_EQUAL, // ==
|
||||
BANG_EQUAL, // !=
|
||||
// Seviye 11: İlişkisel karşılaştırma
|
||||
LESS, // < (küçüktür) — a < b
|
||||
// true/false döndürür.
|
||||
LESS_EQUAL, // <= (küçük eşittir) — a <= b
|
||||
GREATER, // > (büyüktür) — a > b
|
||||
GREATER_EQUAL, // >= (büyük eşittir) — a >= b
|
||||
|
||||
// Seviye 10 (9): Bitsel VE
|
||||
AMPERSAND, // &
|
||||
// Seviye 10: Eşitlik
|
||||
EQUAL_EQUAL, // == (eşittir) — a == b
|
||||
// Değer eşitliği. Öncelik 10.
|
||||
BANG_EQUAL, // != (eşit değildir) — a != b
|
||||
|
||||
// Seviye 11 (8): Bitsel XOR — CARET yukarıda (üs veya XOR)
|
||||
// Seviye 9: Bitsel VE
|
||||
AMPERSAND, // & (bitsel VE) — a & b
|
||||
// Bitwise AND. Öncelik 9.
|
||||
|
||||
// Seviye 12 (7): Bitsel VEYA
|
||||
PIPE, // |
|
||||
// Seviye 8: Bitsel XOR
|
||||
// ^ (CARET) yukarıda (üs olarak seviye 15'te).
|
||||
// Gelecekte XOR için ayrı token eklenebilir.
|
||||
|
||||
// Seviye 13 (6): Mantıksal VE
|
||||
AMPERSAND_AMPERSAND, // &&
|
||||
// Seviye 7: Bitsel VEYA
|
||||
PIPE, // | (bitsel VEYA) — a | b
|
||||
// Bitwise OR. Öncelik 7.
|
||||
|
||||
// Seviye 14 (5): Mantıksal VEYA
|
||||
PIPE_PIPE, // ||
|
||||
// Seviye 6: Mantıksal VE
|
||||
AMPERSAND_AMPERSAND, // && (mantıksal VE) — a && b
|
||||
// Kısa devre (short-circuit): a false ise b değerlendirilmez.
|
||||
// Öncelik 6.
|
||||
|
||||
// Seviye 15 (4): Üçlü koşul (ternary)
|
||||
TERNARY, // ?
|
||||
COLON, // : (ternary ve etiket için)
|
||||
// Seviye 5: Mantıksal VEYA
|
||||
PIPE_PIPE, // || (mantıksal VEYA) — a || b
|
||||
// Kısa devre: a true ise b değerlendirilmez.
|
||||
// Öncelik 5.
|
||||
|
||||
// Seviye 16 (3): Atama
|
||||
EQUAL, // =
|
||||
PLUS_EQUAL, // +=
|
||||
MINUS_EQUAL, // -=
|
||||
STAR_EQUAL, // *=
|
||||
SLASH_EQUAL, // /=
|
||||
PERCENT_EQUAL, // %=
|
||||
AMPERSAND_EQUAL, // &=
|
||||
PIPE_EQUAL, // |=
|
||||
CARET_EQUAL, // ^=
|
||||
LSHIFT_EQUAL, // <<=
|
||||
RSHIFT_EQUAL, // >>=
|
||||
// Seviye 4: Üçlü koşul (ternary) — Sağ birleşmeli
|
||||
TERNARY, // ? (ternary if) — a ? b : c
|
||||
// Öncelik 4. Sağ birleşmeli.
|
||||
// a ? b : c ? d : e = a ? b : (c ? d : e)
|
||||
COLON, // : (ternary else / etiket) — ternary'in ikinci kısmı
|
||||
// Ternary'de öncelik 3 (0 değil!).
|
||||
// Ayrıca switch/case etiketleri için de kullanılır.
|
||||
|
||||
// --- Diğer Semboller ---
|
||||
LBRACE, // {
|
||||
RBRACE, // }
|
||||
SEMICOLON, // ;
|
||||
COMMA, // ,
|
||||
COLON_COLON, // ::
|
||||
// Seviye 2: Atama — Sağ birleşmeli
|
||||
EQUAL, // = (basit atama) — a = b
|
||||
// Öncelik 2. Sağ birleşmeli.
|
||||
// a = b = 5 = a = (b = 5)
|
||||
PLUS_EQUAL, // += (topla ve ata) — a += b → a = a + b
|
||||
MINUS_EQUAL, // -= (çıkar ve ata) — a -= b → a = a - b
|
||||
STAR_EQUAL, // *= (çarp ve ata) — a *= b → a = a * b
|
||||
SLASH_EQUAL, // /= (böl ve ata) — a /= b → a = a / b
|
||||
PERCENT_EQUAL, // %= (mod al ve ata) — a %= b → a = a % b
|
||||
AMPERSAND_EQUAL, // &= (bitsel VE ve ata) — a &= b → a = a & b
|
||||
PIPE_EQUAL, // |= (bitsel VEYA ve ata) — a |= b → a = a | b
|
||||
CARET_EQUAL, // ^= (XOR ve ata) — a ^= b → a = a ^ b
|
||||
LSHIFT_EQUAL, // <<= (sola kaydır ve ata) — a <<= b → a = a << b
|
||||
RSHIFT_EQUAL, // >>= (sağa kaydır ve ata) — a >>= b → a = a >> b
|
||||
|
||||
// --- Özel ---
|
||||
END_OF_FILE, // Dosya sonu
|
||||
UNKNOWN, // Bilinmeyen karakter
|
||||
COMMENT, // Yorum (// veya /* */) — şu anda token üretilmez
|
||||
PREPROCESSOR, // Önişlemci (#) — şu anda kullanılmıyor
|
||||
/* ====== Diğer Semboller ====== */
|
||||
LBRACE, // { (açılış süslü parantez) — blok başlangıcı
|
||||
// Sözdizimi: { statement1; statement2; }
|
||||
RBRACE, // } (kapanış süslü parantez) — blok bitişi
|
||||
SEMICOLON, // ; (noktalı virgül) — ifade sonu belirteci
|
||||
// C/C++/Java tarzında her ifade ; ile biter.
|
||||
COMMA, // , (virgül) — ifade ayırıcı
|
||||
// Örn: int a, b, c; veya f(1, 2, 3)
|
||||
// Öncelik 1 (en düşük).
|
||||
COLON_COLON, // :: (kapsam çözümleme) — Class::method
|
||||
// C++ tarzı. Şu anda sadece token tanımlı, parse yok.
|
||||
|
||||
/* ====== Özel Token'lar ====== */
|
||||
END_OF_FILE, // Dosya sonu belirteci.
|
||||
// Tokenizer dosya sonuna gelindiğinde üretir.
|
||||
// Parser'ın durma koşuludur.
|
||||
UNKNOWN, // Bilinmeyen/tanınamayan karakter.
|
||||
// Tokenizer'ın çözemediği her şey.
|
||||
// Hata raporlamada kullanılır.
|
||||
COMMENT, // Yorum token'ı (// veya /* */).
|
||||
// ŞU ANDA TOKEN ÜRETİLMEZ — tokenizer yorumları atlar.
|
||||
// Gelecekte belge yorumları (///, /** */) için kullanılabilir.
|
||||
PREPROCESSOR, // Önişlemci direktifi (#).
|
||||
// ŞU ANDA KULLANILMIYOR — C önişlemcisi yok.
|
||||
// Gelecekte #include, #define için.
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// KEYWORD_MAP — Keyword String → TokenType
|
||||
// KEYWORD_MAP — Keyword String → TokenType Dönüşüm Haritası
|
||||
// ============================================================================
|
||||
//
|
||||
// Tokenizer'ın ürettiği KeywordToken'ların token değerini (örn: "if")
|
||||
// Parser'ın anlayacağı TokenType'a (KW_IF) dönüştürür.
|
||||
// AMAÇ: Tokenizer'ın ürettiği KeywordToken'ların token değerini (örn: "if")
|
||||
// Parser'ın anlayacağı TokenType'a (KW_IF) dönüştürür.
|
||||
//
|
||||
// std::unordered_map: O(1) ortalama arama. const: derleme zamanı sabiti.
|
||||
// std::string_view: string kopyalamadan kaçınır.
|
||||
// ANAHTAR: std::string_view — keyword string'i (kopyalanmaz, salt okunur)
|
||||
// DEĞER: TokenType — Parser'ın anlayacağı anlamsal tip
|
||||
//
|
||||
// NOT: Bu harita, Tokenizer'daki keywords[] dizisi ile eşleşmelidir.
|
||||
// Birinde ekleme yapılırsa diğerine de eklenmelidir.
|
||||
// VERİ YAPISI: std::unordered_map<string_view, TokenType>
|
||||
// - O(1) ortalama arama süresi
|
||||
// - constexpr: derleme zamanı sabiti (derleyici tabloya gömer)
|
||||
// - std::string_view: string kopyalamadan kaçınır (performans)
|
||||
//
|
||||
// BOYUT: ~60 girdi (tüm keyword'ler)
|
||||
// NEDEN unordered_map, neden map değil?
|
||||
// - Arama sıklığı: her token için bir kez
|
||||
// - unordered_map O(1) vs map O(log n) — fark küçük ama var
|
||||
// - Sıralı erişim gerekmez
|
||||
//
|
||||
// SENKRONİZASYON UYARISI:
|
||||
// Bu harita, Tokenizer'daki keywords[] dizisi İLE EŞLEŞMELİDİR.
|
||||
// Birinde ekleme yapılırsa diğerine de eklenmelidir.
|
||||
// TODO: İki listeyi ortak bir kaynaktan üretecek bir makro/kod üreteci.
|
||||
//
|
||||
inline const std::unordered_map<std::string_view, TokenType> KEYWORD_MAP = {
|
||||
// --- Control flow ---
|
||||
|
|
@ -302,6 +430,7 @@ inline const std::unordered_map<std::string_view, TokenType> KEYWORD_MAP = {
|
|||
|
||||
// --- OOP ---
|
||||
{"class", TokenType::KW_CLASS},
|
||||
{"struct", TokenType::KW_STRUCT},
|
||||
{"interface", TokenType::KW_INTERFACE},
|
||||
{"enum", TokenType::KW_ENUM},
|
||||
{"extends", TokenType::KW_EXTENDS},
|
||||
|
|
@ -357,16 +486,30 @@ inline const std::unordered_map<std::string_view, TokenType> KEYWORD_MAP = {
|
|||
};
|
||||
|
||||
// ============================================================================
|
||||
// OPERATOR_MAP — Operatör/Delimiter String → TokenType
|
||||
// OPERATOR_MAP — Operatör/Delimiter String → TokenType Dönüşüm Haritası
|
||||
// ============================================================================
|
||||
//
|
||||
// Tokenizer'ın ürettiği OperatorToken ve DelimiterToken'ları TokenType'a
|
||||
// dönüştürür. Her iki token tipi de aynı haritayı kullanır çünkü parser
|
||||
// seviyesinde delimiter'lar da operatör gibi işlenir.
|
||||
// AMAÇ: Tokenizer'ın ürettiği OperatorToken ve DelimiterToken'ları TokenType'a
|
||||
// dönüştürür. Her iki token tipi de aynı haritayı kullanır çünkü
|
||||
// Parser seviyesinde delimiter'lar da operatör gibi işlenir.
|
||||
//
|
||||
// SIRALAMA ÖNEMLİ DEĞİL (unordered_map).
|
||||
// Ama Tokenizer'daki operators[] ve delimiters[] dizilerindeki sıralama
|
||||
// önemlidir — çok karakterliler önce gelmelidir.
|
||||
// ANAHTAR: std::string_view — operatör/delimiter string'i (örn: "+", "->", "{")
|
||||
// DEĞER: TokenType — Parser'ın anlayacağı anlamsal tip
|
||||
//
|
||||
// VERİ YAPISI: std::unordered_map<string_view, TokenType>
|
||||
// - O(1) ortalama arama
|
||||
// - const: derleme zamanı sabiti
|
||||
// - Boyut: ~40 girdi
|
||||
//
|
||||
// NEDEN İKİ AYRI HARİTA DEĞİL (operator + delimiter)?
|
||||
// - Parser seviyesinde fark yok: {, }, ; hepsi operatör gibi işlenir.
|
||||
// - Tek harita = tek arama = daha basit kod.
|
||||
//
|
||||
// SIRALAMA UYARISI:
|
||||
// Bu haritada sıralama önemli DEĞİL (unordered_map).
|
||||
// ANCAK Tokenizer'daki operators[] ve delimiters[] dizilerindeki
|
||||
// sıralama ÖNEMLİDİR — çok karakterliler önce gelmelidir!
|
||||
// Örn: "->" önce, "-" sonra kontrol edilmelidir.
|
||||
//
|
||||
inline const std::unordered_map<std::string_view, TokenType> OPERATOR_MAP = {
|
||||
// --- 2 karakterli ---
|
||||
|
|
@ -426,12 +569,21 @@ inline const std::unordered_map<std::string_view, TokenType> OPERATOR_MAP = {
|
|||
};
|
||||
|
||||
// ============================================================================
|
||||
// OPERATOR_MAP_REV — TokenType → Operatör String (Log için)
|
||||
// OPERATOR_MAP_REV — TokenType → Operatör String (Log/Görüntüleme İçin)
|
||||
// ============================================================================
|
||||
//
|
||||
// AST ağacını konsola yazdırırken (log) TokenType enum değerini insan
|
||||
// tarafından okunabilir operatör sembolüne dönüştürür.
|
||||
// Örn: TokenType::PLUS → "+"
|
||||
// AMAÇ: TokenType enum değerini insan tarafından okunabilir operatör
|
||||
// sembolüne dönüştürür. Log çıktısı ve hata mesajları için kullanılır.
|
||||
//
|
||||
// ANAHTAR: TokenType — enum değeri (örn: TokenType::PLUS)
|
||||
// DEĞER: std::string_view — operatör sembolü (örn: "+")
|
||||
//
|
||||
// KULLANIM:
|
||||
// auto it = OPERATOR_MAP_REV.find(type);
|
||||
// if (it != OPERATOR_MAP_REV.end()) std::cout << it->second;
|
||||
//
|
||||
// NOT: TokenType::IDENTIFIER, NUMBER, STRING, KW_* ve özel token'lar
|
||||
// bu haritada YOKTUR (operatör değiller). Onlar için ayrı dönüşüm gerekir.
|
||||
//
|
||||
inline const std::unordered_map<TokenType, std::string_view> OPERATOR_MAP_REV = {
|
||||
{TokenType::ARROW, "->"},
|
||||
|
|
@ -484,11 +636,24 @@ inline const std::unordered_map<TokenType, std::string_view> OPERATOR_MAP_REV =
|
|||
};
|
||||
|
||||
// ============================================================================
|
||||
// OPERATOR_MAP_STRREV — TokenType → Enum İsmi (Log için)
|
||||
// OPERATOR_MAP_STRREV — TokenType → Enum İsmi (Debug/Log İçin)
|
||||
// ============================================================================
|
||||
//
|
||||
// AST log çıktısında operatörün enum ismini gösterir.
|
||||
// Örn: TokenType::PLUS → "PLUS"
|
||||
// AMAÇ: AST log çıktısında operatörün enum ismini (string olarak) gösterir.
|
||||
// OPERATOR_MAP_REV'den farkı: sembol yerine enum adı döndürür.
|
||||
//
|
||||
// KULLANIM:
|
||||
// AST dump/debug çıktısı: TokenPrecedence(PLUS) yerine "PLUS(13)" gösterimi.
|
||||
//
|
||||
// ANAHTAR: TokenType — enum değeri (örn: TokenType::PLUS)
|
||||
// DEĞER: std::string_view — enum ismi (örn: "PLUS")
|
||||
//
|
||||
// ÖRN: TokenType::PLUS → "PLUS"
|
||||
// TokenType::PLUS_EQUAL → "PLUS_EQUAL"
|
||||
//
|
||||
// NOT: İki harita da (REV ve STRREV) aynı anahtarları içerir ama farklı değerler.
|
||||
// REV: "+" (operatör sembolü)
|
||||
// STRREV: "PLUS" (enum ismi)
|
||||
//
|
||||
inline const std::unordered_map<TokenType, std::string_view> OPERATOR_MAP_STRREV = {
|
||||
{TokenType::ARROW, "ARROW"},
|
||||
|
|
@ -541,40 +706,50 @@ inline const std::unordered_map<TokenType, std::string_view> OPERATOR_MAP_STRREV
|
|||
};
|
||||
|
||||
// ============================================================================
|
||||
// TokenPrecedence — Operatör Öncelik Tablosu
|
||||
// TokenPrecedence — Operatör Öncelik Tablosu (Pratt Parser'ın Kalbi)
|
||||
// ============================================================================
|
||||
//
|
||||
// Pratt parser'ın kalbi. Her TokenType için bir öncelik seviyesi döndürür.
|
||||
// Yüksek sayı = daha sıkı bağlanma (daha yüksek öncelik).
|
||||
// AMAÇ: Her TokenType için bir öncelik seviyesi döndürür.
|
||||
// Yüksek sayı = daha sıkı bağlanma (önce işlenir).
|
||||
//
|
||||
// PARAMETRE: type — sorgulanan token tipi
|
||||
// DÖNÜŞ: uint16_t — öncelik seviyesi (0-18)
|
||||
// KARMAŞIKLIK: O(1) — switch/case (derleyici jump table üretir)
|
||||
//
|
||||
// KULLANIM:
|
||||
// uint16_t prec = TokenPrecedence(current.type);
|
||||
// if (prec >= minPrec) { parseLeftDenotation(left); }
|
||||
//
|
||||
// ÖNCELİK SEVİYELERİ (yüksekten düşüğe):
|
||||
// 18: Üye erişimi . -> [ ] ( )
|
||||
// 18: Üye erişimi . -> [ ] ( ) — En yüksek
|
||||
// 17: Postfix ++ --
|
||||
// 16: Unary prefix ! ~
|
||||
// 15: Üs alma ** ^
|
||||
// 16: Unary prefix ! ~ + -
|
||||
// 15: Üs alma ** ^ — Sağ birleşmeli
|
||||
// 14: Çarpma/Bölme * / %
|
||||
// 13: Toplama/Çıkarma + -
|
||||
// 12: Bitsel kaydırma << >>
|
||||
// 11: İlişkisel < <= > >=
|
||||
// 10: Eşitlik == !=
|
||||
// 9: Bitsel VE &
|
||||
// 8: Bitsel XOR ^ (üs olarak 15'te de var — bağlama göre)
|
||||
// 8: Bitsel XOR ^ (şu anda üs olarak 15'te)
|
||||
// 7: Bitsel VEYA |
|
||||
// 6: Mantıksal VE &&
|
||||
// 5: Mantıksal VEYA ||
|
||||
// 4: Ternary ?
|
||||
// 3: Ternary else :
|
||||
// 2: Atama = += -= vb.
|
||||
// 2: Atama = += -= vb. — Sağ birleşmeli
|
||||
// 1: Virgül ,
|
||||
// 0: Önceliksiz (değerler, EOF, bilinmeyen)
|
||||
//
|
||||
// NOT: C/C++'da ^ operatörü bitsel XOR'tur (seviye 8), ama Python'da üs (seviye 15).
|
||||
// saQut'ta ^ hem üs hem XOR olarak kullanılabilir (AST'de bağlam belirler).
|
||||
// Şimdilik ^ seviye 15 (üs) olarak ayarlı.
|
||||
// KARAR: Neden ^ (CARET) seviye 15 (üs) olarak ayarlı?
|
||||
// - C/C++'da ^ bitsel XOR'tur (seviye 8).
|
||||
// - Python'da ** üs, ^ XOR'tur.
|
||||
// - saQut'ta ^ varsayılan olarak üs alma olarak kullanılır.
|
||||
// - Gelecekte XOR için ayrı token (CARET_CARET ^^) eklenebilir.
|
||||
//
|
||||
// BUG FIX (commit 438bc0e): Seviye 8'deki ölü kod (CARET için case olmadan
|
||||
// return 8) temizlendi. CARET zaten seviye 15'te STAR_STAR ile birlikte
|
||||
// işleniyor.
|
||||
// BUG FIX (commit 438bc0e):
|
||||
// Seviye 8'de CARET için ölü kod (case olmadan return 8) vardı.
|
||||
// Temizlendi. CARET zaten seviye 15'te STAR_STAR ile birlikte işleniyor.
|
||||
//
|
||||
inline uint16_t TokenPrecedence(TokenType type) {
|
||||
switch (type) {
|
||||
|
|
@ -675,18 +850,29 @@ inline uint16_t TokenPrecedence(TokenType type) {
|
|||
}
|
||||
|
||||
// ============================================================================
|
||||
// RightAssociative — Sağdan Sola Birleşme Kontrolü
|
||||
// RightAssociative — Sağdan Sola Birleşme (Associativity) Kontrolü
|
||||
// ============================================================================
|
||||
//
|
||||
// Hangi operatörler sağdan sola birleşir?
|
||||
// AMAÇ: Bir operatörün sağdan sola mı, yoksa soldan sağa mı birleştiğini
|
||||
// belirler. Pratt parser'da doğru ağaç yapısını oluşturmak için kritik.
|
||||
//
|
||||
// PARAMETRE: type — sorgulanan operatör tipi
|
||||
// DÖNÜŞ: bool — true: sağ birleşmeli, false: sol birleşmeli
|
||||
// KARMAŞIKLIK: O(1) — switch/case
|
||||
//
|
||||
// Sağ birleşmeli operatörler (a OP b OP c = a OP (b OP c)):
|
||||
// - Üs alma: **, ^ (matematiksel: 2^3^2 = 2^(3^2) = 2^9 = 512)
|
||||
// - Atama: =, +=, -=, vb. (a = b = 5 → a = (b = 5))
|
||||
// - Ternary: ?: (a ? b : c ? d : e → a ? b : (c ? d : e))
|
||||
// - STAR_STAR (üs alma): 2 ** 3 ** 2 = 2 ** (3 ** 2) = 2^9 = 512
|
||||
// (matematiksel kural: üs sağdan sola birleşir)
|
||||
// - CARET (üs alma): 2 ^ 3 ^ 2 = 2 ^ (3 ^ 2) = 512
|
||||
// - EQUAL (atama): a = b = 5 → a = (b = 5)
|
||||
// (önce b = 5 çalışır, sonra a = b)
|
||||
// - +=, -=, *=, vb. (birleşik atama): a += b += 5 → a += (b += 5)
|
||||
// - TERNARY (üçlü koşul): a ? b : c ? d : e → a ? b : (c ? d : e)
|
||||
// (iç içe ternary'lerde sağdan sola)
|
||||
//
|
||||
// Sol birleşmeli operatörler (a OP b OP c = (a OP b) OP c):
|
||||
// - Tüm diğerleri: +, -, *, /, ==, &&, vb.
|
||||
// - Tüm diğerleri: +, -, *, /, ==, &&, ||, vb.
|
||||
// (a + b + c = (a + b) + c, yani önce a+b, sonuç + c)
|
||||
//
|
||||
inline bool RightAssociative(TokenType type) {
|
||||
switch (type) {
|
||||
|
|
@ -711,52 +897,103 @@ inline bool RightAssociative(TokenType type) {
|
|||
}
|
||||
|
||||
// ============================================================================
|
||||
// ParserToken — Parser'ın Kullandığı Token Yapısı
|
||||
// ParserToken — Parser'ın Kullandığı Token Yapısı (Köprü)
|
||||
// ============================================================================
|
||||
//
|
||||
// Tokenizer'ın ürettiği ham Token ile Parser'ın ihtiyaç duyduğu anlamsal
|
||||
// tipi (TokenType) bir arada tutar.
|
||||
// AMAÇ: Tokenizer'ın ürettiği ham Token ile Parser'ın ihtiyaç duyduğu
|
||||
// anlamsal tipi (TokenType) bir arada tutar. İki katman arasında
|
||||
// köprü görevi görür.
|
||||
//
|
||||
// ALANLAR:
|
||||
// token (Token*): Tokenizer'dan gelen orijinal token. Neden pointer?
|
||||
// Çünkü Token polimorfik bir sınıf hiyerarşisidir. Değer kopyası (Token)
|
||||
// object slicing'e neden olur — alt sınıf verileri (NumberToken.isFloat,
|
||||
// StringToken.context) kaybolur.
|
||||
// BUG FIX (commit 40579ca): Eskiden Token token (değer) tutuyordu.
|
||||
//
|
||||
// type (TokenType): Token'ın anlamsal tipi. Örn: NUMBER, PLUS, KW_IF.
|
||||
// token (Token*):
|
||||
// Tokenizer'dan gelen orijinal token'a pointer.
|
||||
// Neden pointer, neden değer (Token token) değil?
|
||||
//
|
||||
// Çünkü Token polimorfik bir sınıf hiyerarşisidir:
|
||||
// Token (base)
|
||||
// +-- NumberToken (isFloat, numberValue alanları)
|
||||
// +-- StringToken (context alanı)
|
||||
// +-- IdentifierToken
|
||||
// +-- OperatorToken
|
||||
// +-- DelimiterToken
|
||||
// +-- KeywordToken
|
||||
//
|
||||
// Değer kopyası (Token token) OBJECT SLICING'e neden olur:
|
||||
// NumberToken → Token'a kopyalanırken isFloat, numberValue KAYBOLUR.
|
||||
//
|
||||
// BUG FIX (commit 40579ca):
|
||||
// Eskiden "Token token" (değer) olarak tanımlanmıştı.
|
||||
// NumberToken.isFloat her zaman false dönüyordu çünkü slicing oluyordu.
|
||||
// "Token* token" (pointer) olarak değiştirildi.
|
||||
//
|
||||
// type (TokenType):
|
||||
// Token'ın anlamsal tipi. Örn: NUMBER, PLUS, KW_IF.
|
||||
// Tokeni parselerken parseToken() tarafından atanır.
|
||||
//
|
||||
// METOTLAR:
|
||||
// is(TokenType): Bu token belirtilen tipte mi?
|
||||
// is({...}): Bu token listedeki tiplerden biri mi?
|
||||
// getPowerOperator(): Bu token bir operatör ise önceliğini döndür.
|
||||
// isRightAssociative(): Bu operatör sağ birleşmeli mi?
|
||||
// is(TokenType): Tek tip kontrolü (O(1))
|
||||
// is(initializer_list): Çoklu tip kontrolü (O(k), k = liste boyutu)
|
||||
// getPowerOperator(): Öncelik sorgulama (O(1), TokenPrecedence'a yönlendirir)
|
||||
// isRightAssociative(): Birleşme yönü sorgulama (O(1))
|
||||
//
|
||||
struct ParserToken {
|
||||
Token* token = nullptr; // Tokenizer'dan gelen orijinal token
|
||||
TokenType type = TokenType::SVR_VOID; // Anlamsal tip
|
||||
/* ====== Alanlar ====== */
|
||||
|
||||
// Tek tip kontrolü
|
||||
// Tokenizer'dan gelen orijinal token pointer'ı.
|
||||
// nullptr olabilir mi? Hayır — geçerli bir token her zaman vardır.
|
||||
// SVR_VOID durumunda token nullptr olabilir (EOF sinyali).
|
||||
Token* token = nullptr;
|
||||
|
||||
// Token'ın anlamsal tipi.
|
||||
// Varsayılan: SVR_VOID (geçersiz/başlangıç değeri).
|
||||
// parseToken() tarafından atanır.
|
||||
TokenType type = TokenType::SVR_VOID;
|
||||
|
||||
/* ====== Kolaylık Metotları ====== */
|
||||
|
||||
// is() — Tek tip kontrolü
|
||||
// PARAMETRE: t — sorgulanan token tipi
|
||||
// DÖNÜŞ: true — bu token t tipinde
|
||||
// KARMAŞIKLIK: O(1)
|
||||
// KULLANIM: if (current.is(TokenType::SEMICOLON)) { ... }
|
||||
bool is(TokenType t) const {
|
||||
return type == t;
|
||||
}
|
||||
|
||||
// Çoklu tip kontrolü — örn: is({KW_INT, KW_FLOAT, KW_VOID})
|
||||
// is() — Çoklu tip kontrolü
|
||||
// PARAMETRE: types — kontrol edilecek tipler listesi
|
||||
// DÖNÜŞ: true — bu token listedeki tiplerden birine aitse
|
||||
// KARMAŞIKLIK: O(k) — k = liste boyutu
|
||||
// KULLANIM:
|
||||
// if (current.is({KW_INT, KW_FLOAT, KW_VOID})) { ... }
|
||||
// if (current.is({TokenType::SEMICOLON, TokenType::RPAREN})) { ... }
|
||||
bool is(std::initializer_list<TokenType> types) const {
|
||||
for (TokenType t : types)
|
||||
if (type == t) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Operatör önceliği (Pratt parser için)
|
||||
// getPowerOperator() — Operatör önceliği sorgulama (Pratt parser için)
|
||||
// DÖNÜŞ: uint16_t — öncelik seviyesi (0-18)
|
||||
// KARMAŞIKLIK: O(1) — TokenPrecedence'a yönlendirir
|
||||
// KULLANIM:
|
||||
// uint16_t prec = current.getPowerOperator();
|
||||
// while (prec >= minPrec) { ... parseLeftDenotation(left); }
|
||||
uint16_t getPowerOperator() const {
|
||||
return TokenPrecedence(type);
|
||||
}
|
||||
|
||||
// Sağ birleşmeli mi?
|
||||
// isRightAssociative() — Birleşme yönü sorgulama
|
||||
// DÖNÜŞ: true — sağ birleşmeli (atama, üs, ternary)
|
||||
// false — sol birleşmeli (toplama, çarpma, vb.)
|
||||
// KARMAŞIKLIK: O(1) — RightAssociative'a yönlendirir
|
||||
// KULLANIM:
|
||||
// bool rightAssoc = current.isRightAssociative();
|
||||
// uint16_t nextPrec = rightAssoc ? prec : prec + 1;
|
||||
bool isRightAssociative() const {
|
||||
return RightAssociative(type);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SAQUT_PARSER_TOKEN
|
||||
#endif // SAQUT_PARSER_TOKEN
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// ============================================================================
|
||||
// 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>
|
||||
#include "core/location.hpp"
|
||||
|
||||
class Token {
|
||||
protected:
|
||||
std::string type;
|
||||
public:
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
SourceLocation loc; // Token'ın kaynak koddaki konumu
|
||||
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
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
#include "tokenizer/tokenizer.hpp"
|
||||
|
||||
std::vector<Token*> Tokenizer::scan(std::string input) {
|
||||
std::vector<Token*> tokens;
|
||||
hmx.setSourceText("", input);
|
||||
while (true) {
|
||||
Token* token = scope();
|
||||
if (token->token == "EOL") break;
|
||||
tokens.push_back(token);
|
||||
if (hmx.isEnd()) break;
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
Token* Tokenizer::scope() {
|
||||
hmx.skipWhiteSpace();
|
||||
|
||||
if (hmx.include("//", true)) { skipOneLineComment(); return scope(); }
|
||||
if (hmx.include("/*", true)) { skipMultiLineComment(); return scope(); }
|
||||
|
||||
if (hmx.isEnd()) {
|
||||
Token* t = new Token();
|
||||
t->token = "EOL";
|
||||
return t;
|
||||
}
|
||||
|
||||
if (hmx.getchar() == '"')
|
||||
return readString();
|
||||
|
||||
if (hmx.isNumeric()) {
|
||||
INumber lem = hmx.readNumeric();
|
||||
NumberToken* nt = new NumberToken();
|
||||
nt->loc = lem.startLoc;
|
||||
nt->base = lem.base;
|
||||
nt->start = lem.start;
|
||||
nt->end = lem.end;
|
||||
nt->hasEpsilon = lem.hasEpsilon;
|
||||
nt->isFloat = lem.isFloat;
|
||||
nt->token = lem.token;
|
||||
return nt;
|
||||
}
|
||||
|
||||
for (const auto& kw : keywords) {
|
||||
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;
|
||||
}
|
||||
KeywordToken* kt = new KeywordToken();
|
||||
kt->start = hmx.getOffset();
|
||||
kt->loc = hmx.getLocation();
|
||||
hmx.toChar(static_cast<int>(kw.size()));
|
||||
kt->end = hmx.getOffset();
|
||||
kt->token = kw;
|
||||
return kt;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& del : delimiters) {
|
||||
if (hmx.include(std::string(del), false)) {
|
||||
DelimiterToken* dt = new DelimiterToken();
|
||||
dt->start = hmx.getOffset();
|
||||
dt->loc = hmx.getLocation();
|
||||
hmx.toChar(static_cast<int>(del.size()));
|
||||
dt->end = hmx.getOffset();
|
||||
dt->token = del;
|
||||
return dt;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& op : operators) {
|
||||
if (hmx.include(std::string(op), false)) {
|
||||
OperatorToken* ot = new OperatorToken();
|
||||
ot->start = hmx.getOffset();
|
||||
ot->loc = hmx.getLocation();
|
||||
hmx.toChar(static_cast<int>(op.size()));
|
||||
ot->end = hmx.getOffset();
|
||||
ot->token = op;
|
||||
return ot;
|
||||
}
|
||||
}
|
||||
|
||||
return readIdentifier();
|
||||
}
|
||||
|
||||
IdentifierToken* Tokenizer::readIdentifier() {
|
||||
hmx.beginPosition();
|
||||
IdentifierToken* it = new IdentifierToken();
|
||||
it->start = hmx.getOffset();
|
||||
|
||||
while (!hmx.isEnd()) {
|
||||
char c = hmx.getchar();
|
||||
bool read = false;
|
||||
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
|
||||
read = true;
|
||||
it->token.push_back(c);
|
||||
} else if (c == '_' || c == '$') {
|
||||
read = true;
|
||||
it->token.push_back(c);
|
||||
}
|
||||
|
||||
if (read) {
|
||||
hmx.nextChar();
|
||||
} else {
|
||||
if (it->token.empty()) { hmx.nextChar(); } break;
|
||||
}
|
||||
}
|
||||
|
||||
it->end = hmx.getOffset();
|
||||
it->size = static_cast<int>(it->context.size());
|
||||
it->loc = hmx.sourceFile.offsetToLocation(it->start);
|
||||
hmx.acceptPosition();
|
||||
return it;
|
||||
}
|
||||
|
||||
StringToken* Tokenizer::readString() {
|
||||
hmx.beginPosition();
|
||||
StringToken* st = new StringToken();
|
||||
bool started = false;
|
||||
bool ended = false;
|
||||
st->start = hmx.getOffset();
|
||||
|
||||
while (!hmx.isEnd()) {
|
||||
char c = hmx.getchar();
|
||||
st->token.push_back(c);
|
||||
switch (c) {
|
||||
case '"':
|
||||
if (!started) {
|
||||
started = true;
|
||||
} else {
|
||||
ended = true;
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
hmx.nextChar();
|
||||
c = hmx.getchar();
|
||||
st->token.push_back(c);
|
||||
st->context.push_back(c);
|
||||
break;
|
||||
default:
|
||||
st->context.push_back(c);
|
||||
break;
|
||||
}
|
||||
hmx.nextChar();
|
||||
if (ended) break;
|
||||
}
|
||||
|
||||
st->end = hmx.getOffset();
|
||||
st->size = static_cast<int>(st->context.size());
|
||||
st->loc = hmx.sourceFile.offsetToLocation(st->start);
|
||||
hmx.acceptPosition();
|
||||
return st;
|
||||
}
|
||||
|
||||
void Tokenizer::skipOneLineComment() {
|
||||
while (!hmx.isEnd()) {
|
||||
if (hmx.getchar() == '\n') {
|
||||
hmx.nextChar();
|
||||
hmx.skipWhiteSpace();
|
||||
return;
|
||||
}
|
||||
hmx.nextChar();
|
||||
}
|
||||
}
|
||||
|
||||
void Tokenizer::skipMultiLineComment() {
|
||||
while (!hmx.isEnd()) {
|
||||
if (hmx.include("*/", true)) {
|
||||
hmx.skipWhiteSpace();
|
||||
return;
|
||||
}
|
||||
hmx.nextChar();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,577 +1,60 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Tokenizer (Token Seviyesinde Tarayıcı)
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/tokenizer/tokenizer.hpp
|
||||
// KATMAN: Katman 2 — Lexer üzerine kurulu
|
||||
// BAĞIMLI: Lexer (src/lexer/lexer.hpp)
|
||||
// KULLANAN: Parser (src/parser/parser.hpp), ParserToken (src/parser/token.hpp)
|
||||
//
|
||||
// AMAÇ:
|
||||
// Lexer tarafından sağlanan karakter akışını alıp anlamlı token'lara dönüştürür.
|
||||
// Token'lar derleyicinin "kelime"leridir — parser'ın anlayacağı en küçük birim.
|
||||
//
|
||||
// Üretilen token tipleri (6 adet polimorfik sınıf):
|
||||
// ┌─────────────────┬──────────────────────────────────┐
|
||||
// │ Sınıf │ Örnek token'lar │
|
||||
// ├─────────────────┼──────────────────────────────────┤
|
||||
// │ NumberToken │ 42, 0xFF, 3.14, 1e10 │
|
||||
// │ StringToken │ "merhaba", "satır\niki" │
|
||||
// │ OperatorToken │ +, -, *, /, ==, !=, ++, -- │
|
||||
// │ DelimiterToken │ (, ), {, }, [, ], ;, ,, ., -> │
|
||||
// │ KeywordToken │ if, for, while, int, void │
|
||||
// │ IdentifierToken │ x, myVar, _private │
|
||||
// └─────────────────┴──────────────────────────────────┘
|
||||
//
|
||||
// ADR-004: Neden Polimorfik Token Sınıfları?
|
||||
// Seçenek 1 — Tagged union (std::variant): Tüm veriyi tek struct'ta
|
||||
// +: Bellek tek parça, cache-friendly
|
||||
// -: Tip eklemek için union'ı değiştirmek gerek
|
||||
// Seçenek 2 — Class hierarchy (seçilen): Base Token, alt sınıflar
|
||||
// +: Yeni token tipi eklemek kolay (yeni sınıf türet)
|
||||
// +: Her token kendi verisini taşır (NumberToken.isFloat, StringToken.context)
|
||||
// -: Heap tahsisi (new) gerektirir
|
||||
// -: virtual destructor çağrısı (maliyet: 1 vtable lookup)
|
||||
//
|
||||
// Karar: Class hierarchy. Derleyici gibi bir araçta kod netliği ve
|
||||
// genişletilebilirlik, mikro-performanstan daha önemlidir.
|
||||
//
|
||||
// TASARIM KARARLARI:
|
||||
// 1. Tablolar (operators, delimiters, keywords): constexpr std::string_view dizileri.
|
||||
// Derleme zamanında sabit, heap tahsisi yok. Sıralama önceliği:
|
||||
// - Önce keyword'ler: if/for/while gibi kelimeler identifier'lardan önce yakalanmalı
|
||||
// - Sonra delimiter'lar: -> ve :: gibi 2 karakterliler önce, tek karakterliler sonra
|
||||
// - Sonra operator'ler: != ve == gibi 2 karakterliler önce, tek karakterliler sonra
|
||||
// - En son identifier: yukarıdakilerden hiçbirine uymayan her şey
|
||||
//
|
||||
// 2. Keyword boundary check: "do" keyword'ü "double" ile karışmasın diye,
|
||||
// keyword eşleşmesinden sonraki karakter kontrol edilir. Sonraki karakter
|
||||
// harf/rakam/_/$ ise bu bir keyword değil, identifier'dır.
|
||||
//
|
||||
// 3. scope() metodu: Her çağrıldığında bir sonraki token'ı döndürür.
|
||||
// EOF'da "EOL" isimli özel bir token döndürür (Token tipi, özel değil).
|
||||
// Bu, boş token listesi sorununu çözer (parser her zaman bir token görür).
|
||||
//
|
||||
// 4. Yorum satırları: // (tek satır) ve /* */ (çok satırlı) desteklenir.
|
||||
// Yorumlar token üretmez, sessizce atlanır.
|
||||
// NOT: İç içe /* */ yorumları desteklenmez (C standardı gibi).
|
||||
//
|
||||
// BİLİNEN SINIRLAMALAR (TODO):
|
||||
// TODO: String escape sequence'leri tam değil (\x, \u, \U eksik)
|
||||
// TODO: Char literal: 'a' formatı okunamıyor
|
||||
// TODO: Raw string: R"(...)" formatı yok
|
||||
// TODO: Token konum bilgisi (satır/sütun) token'lara eklenmeli
|
||||
// TODO: Bellek sızıntısı: Token'lar heap'te new ile oluşturuluyor, silme sorumluluğu çağıranda
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_TOKENIZER
|
||||
#define SAQUT_TOKENIZER
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "lexer/lexer.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)
|
||||
// ============================================================================
|
||||
//
|
||||
// Bu tablolar, Tokenizer::scope() tarafından ham karakterlerden token üretmek
|
||||
// için kullanılır. constexpr std::string_view ile tanımlanmıştır, böylece
|
||||
// heap tahsisi yapılmaz ve derleme zamanında optimize edilir.
|
||||
//
|
||||
// SIRALAMA ÖNEMLİDİR!
|
||||
// scope() fonksiyonu bu tabloları sırasıyla tarar ve İLK eşleşmede durur.
|
||||
// Bu nedenle:
|
||||
// - Çok karakterli operatörler (==) tek karakterlilerden (=) ÖNCE gelmeli
|
||||
// - Çok karakterli delimiter'lar (->) tek karakterlilerden (.) ÖNCE gelmeli
|
||||
// - Keyword'ler, identifier'lardan ÖNCE kontrol edilmeli
|
||||
//
|
||||
// Mevcut sıralama: keywords → delimiters → operators → identifier (fallback)
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#include <string_view>
|
||||
#include "lexer/lexer.hpp"
|
||||
#include "tokenizer/token.hpp"
|
||||
|
||||
// Operatör tablosu. Çok karakterliler (==, !=, ++, +=, vb.) önce gelir.
|
||||
// NOT: Bu tablo ParserToken'daki OPERATOR_MAP ile eşleşmelidir.
|
||||
inline constexpr std::string_view operators[] = {
|
||||
// --- 2 karakterli: karşılaştırma ---
|
||||
"==", "!=", "<=", ">=", "&&", "||",
|
||||
// --- 2 karakterli: aritmetik ---
|
||||
"++", "--", "<<", ">>",
|
||||
// --- 2 karakterli: birleşik atama ---
|
||||
"+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=",
|
||||
// --- 1 karakterli: aritmetik ---
|
||||
"+", "-", "*", "/", "%", "<", ">",
|
||||
// --- 1 karakterli: bitsel/mantıksal ---
|
||||
"^", "!", "~", "&", "|",
|
||||
// --- 1 karakterli: temel atama ---
|
||||
"="
|
||||
};
|
||||
|
||||
// Delimiter tablosu. Çok karakterliler (->, ::) önce gelir.
|
||||
inline constexpr std::string_view delimiters[] = {
|
||||
"->", "::", // 2 karakterli bağlayıcılar
|
||||
"[", "]", "(", ")", "{", "}", // gruplama
|
||||
";", ",", ":", // ayırıcılar
|
||||
"." // üye erişimi
|
||||
"->", "::",
|
||||
"[", "]", "(", ")", "{", "}",
|
||||
";", ",", ":",
|
||||
"."
|
||||
};
|
||||
|
||||
// Keyword tablosu. Dilin tüm rezerve edilmiş kelimeleri.
|
||||
// Gruplandırılmıştır:
|
||||
// - Kontrol akışı: if, else, for, while, do, switch, case, vb.
|
||||
// - Tipler: void, int, float, double, char, string, bool
|
||||
// - Literal'lar: true, false, null
|
||||
// - OOP: class, interface, enum, extends, public, private, vb.
|
||||
// - Modüller: import, package
|
||||
// - C/C++ ekleri: const, extern, typedef, sizeof, auto, vb.
|
||||
//
|
||||
// BUG FIX (commit 438bc0e):
|
||||
// Eskiden tip keyword'leri bu listede yoktu. int, float gibi kelimeler
|
||||
// identifier olarak tokenize ediliyordu. Parser'da KW_INT gibi tipler
|
||||
// tanımlı olmasına rağmen tokenizer'dan gelmediği için değişken tanımlama
|
||||
// çalışmıyordu. Tüm eksik keyword'ler eklendi.
|
||||
//
|
||||
// Keyword tablosu.
|
||||
inline constexpr std::string_view keywords[] = {
|
||||
// Control flow
|
||||
"if", "else", "for", "while", "do",
|
||||
"switch", "case", "default", "break", "continue",
|
||||
"return", "try", "catch", "finally", "throw",
|
||||
"throws", "assert",
|
||||
// Types
|
||||
"void", "int", "float", "double", "char",
|
||||
"string", "bool",
|
||||
// Literals
|
||||
"true", "false", "null",
|
||||
// OOP
|
||||
"class", "interface","enum", "extends", "implements",
|
||||
"class", "struct", "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"
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Tokenizer — Lexer Üzerinde Token Üretici
|
||||
// ============================================================================
|
||||
//
|
||||
// Tek sorumluluğu: karakter akışından token listesi üretmek.
|
||||
// Durum bilgisi: Lexer'ı içerir (hmx), kendi durumu yok.
|
||||
//
|
||||
// KULLANIM:
|
||||
// Tokenizer tokenizer;
|
||||
// auto tokens = tokenizer.scan(sourceCode);
|
||||
// // tokens artık kullanılabilir. İş bitince:
|
||||
// for (auto* t : tokens) delete t;
|
||||
//
|
||||
class Tokenizer {
|
||||
public:
|
||||
Lexer hmx; // İç Lexer. "hmx" adı tarihsel.
|
||||
Lexer hmx;
|
||||
|
||||
std::vector<Token*> scan(std::string input);
|
||||
|
||||
private:
|
||||
Token* scope(); // Bir sonraki token'ı döndür
|
||||
IdentifierToken* readIdentifier(); // Tanımlayıcı oku
|
||||
StringToken* readString(); // String literal oku
|
||||
void skipOneLineComment(); // // yorum satırını atla
|
||||
void skipMultiLineComment(); // /* */ yorum bloğunu atla
|
||||
Token* scope();
|
||||
IdentifierToken* readIdentifier();
|
||||
StringToken* readString();
|
||||
void skipOneLineComment();
|
||||
void skipMultiLineComment();
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// GERÇEKLEME (Implementation)
|
||||
// ============================================================================
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// scan: Kaynak kodu tara, token listesi üret.
|
||||
//
|
||||
// Akış:
|
||||
// 1. Lexer'a kaynak kodu yükle
|
||||
// 2. scope() ile tek tek token oku
|
||||
// 3. "EOL" (End Of Line) token'ı gelene kadar devam et
|
||||
// 4. Token listesini döndür
|
||||
//
|
||||
// "EOL" token'ı: scope() EOF'da üretilen özel bir Token. Parser'a "bitti" sinyali.
|
||||
// Neden nullptr değil? Çünkü scope() her zaman geçerli bir pointer döndürmeli,
|
||||
// aksi takdirde null kontrolü gerekir. "EOL" ile bu kontrol token tipine indirgenir.
|
||||
//
|
||||
// TODO: std::unique_ptr veya std::vector<std::unique_ptr<Token>> ile bellek yönetimi
|
||||
// --------------------------------------------------------------------------
|
||||
inline std::vector<Token*> Tokenizer::scan(std::string input) {
|
||||
std::vector<Token*> tokens;
|
||||
hmx.setText(input);
|
||||
while (true) {
|
||||
Token* token = scope();
|
||||
if (token->token == "EOL") break; // Dosya sonu sinyali
|
||||
tokens.push_back(token);
|
||||
if (hmx.isEnd()) break; // Güvenlik kontrolü
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// scope: Bir sonraki token'ı tanı ve döndür.
|
||||
//
|
||||
// Token tanıma sıralaması (önemli!):
|
||||
// 1. Boşlukları atla
|
||||
// 2. Yorum satırlarını atla (//, /* */)
|
||||
// 3. EOF kontrolü → "EOL" token'ı
|
||||
// 4. String literal ("...")
|
||||
// 5. Sayısal literal (0-9 ile başlayan)
|
||||
// 6. Keyword'ler (sınır kontrolü ile)
|
||||
// 7. Delimiter'lar
|
||||
// 8. Operatörler
|
||||
// 9. Identifier (fallback — yukarıdakilerden hiçbiri değilse)
|
||||
//
|
||||
// Keyword boundary check:
|
||||
// include(kw, false) ile önce eşleşme kontrolü yapılır (konum değişmez).
|
||||
// Sonra keyword'ün hemen sonrasındaki karaktere bakılır.
|
||||
// Eğer bu karakter harf/rakam/_/$ ise, bu bir keyword değil, daha uzun bir
|
||||
// identifier'ın parçasıdır. Örnek: "do" → "double"ın başlangıcı olabilir.
|
||||
//
|
||||
// BUG FIX (commit 438bc0e): Eskiden boundary check yoktu. "double" kelimesi
|
||||
// "do" + "uble" olarak iki token'a ayrılıyordu.
|
||||
// --------------------------------------------------------------------------
|
||||
inline Token* Tokenizer::scope() {
|
||||
hmx.skipWhiteSpace();
|
||||
|
||||
// Yorum satırları: sessizce atla, token üretme
|
||||
if (hmx.include("//", true)) skipOneLineComment();
|
||||
if (hmx.include("/*", true)) skipMultiLineComment();
|
||||
|
||||
// EOF kontrolü
|
||||
if (hmx.isEnd()) {
|
||||
Token* t = new Token();
|
||||
t->token = "EOL"; // Özel sinyal token'ı
|
||||
return t;
|
||||
}
|
||||
|
||||
// String literal: " ile başlar
|
||||
if (hmx.getchar() == '"')
|
||||
return readString();
|
||||
|
||||
// Sayısal literal: rakam ile başlar (isNumeric: 0-9)
|
||||
if (hmx.isNumeric()) {
|
||||
INumber lem = hmx.readNumeric();
|
||||
NumberToken* nt = new NumberToken();
|
||||
nt->base = lem.base;
|
||||
nt->start = lem.start;
|
||||
nt->end = lem.end;
|
||||
nt->hasEpsilon = lem.hasEpsilon;
|
||||
nt->isFloat = lem.isFloat;
|
||||
nt->token = lem.token;
|
||||
return nt;
|
||||
}
|
||||
|
||||
// Keyword'ler: sınır kontrolü ile tarama
|
||||
// include(kw, false) → eşleşme kontrolü yap ama konumu değiştirme
|
||||
// getchar(kw.size()) → keyword sonrası karaktere bak
|
||||
// Sonraki karakter harf/rakam/_/$ ise → bu bir keyword değil, devam et
|
||||
for (const auto& kw : keywords) {
|
||||
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; // Daha uzun bir identifier'ın parçası
|
||||
}
|
||||
KeywordToken* kt = new KeywordToken();
|
||||
kt->start = hmx.getOffset();
|
||||
hmx.toChar(static_cast<int>(kw.size()));
|
||||
kt->end = hmx.getOffset();
|
||||
kt->token = kw;
|
||||
return kt;
|
||||
}
|
||||
}
|
||||
|
||||
// Delimiter'lar
|
||||
for (const auto& del : delimiters) {
|
||||
if (hmx.include(std::string(del), false)) {
|
||||
DelimiterToken* dt = new DelimiterToken();
|
||||
dt->start = hmx.getOffset();
|
||||
hmx.toChar(static_cast<int>(del.size()));
|
||||
dt->end = hmx.getOffset();
|
||||
dt->token = del;
|
||||
return dt;
|
||||
}
|
||||
}
|
||||
|
||||
// Operatörler
|
||||
for (const auto& op : operators) {
|
||||
if (hmx.include(std::string(op), false)) {
|
||||
OperatorToken* ot = new OperatorToken();
|
||||
ot->start = hmx.getOffset();
|
||||
hmx.toChar(static_cast<int>(op.size()));
|
||||
ot->end = hmx.getOffset();
|
||||
ot->token = op;
|
||||
return ot;
|
||||
}
|
||||
}
|
||||
|
||||
// Identifier (fallback): hiçbir özel token tipine uymayan her şey
|
||||
return readIdentifier();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// readIdentifier: Bir tanımlayıcı (identifier) oku.
|
||||
//
|
||||
// Tanımlayıcı = harf ile başlayan, harf/rakam/_/$ ile devam eden karakter dizisi.
|
||||
// NOT: Rakam ile başlayamaz (o zaman sayı olurdu).
|
||||
//
|
||||
// Karakter seti:
|
||||
// a-z, A-Z: Latin harfleri
|
||||
// 0-9: Rakamlar (ilk karakter hariç)
|
||||
// _ (alt çizgi): Yaygın ayraç
|
||||
// $ (dolar): Java/C# uyumluluğu için
|
||||
//
|
||||
// TODO: Unicode harf desteği (Türkçe karakterler, Çince, Arapça, vb.)
|
||||
// --------------------------------------------------------------------------
|
||||
inline IdentifierToken* Tokenizer::readIdentifier() {
|
||||
hmx.beginPosition();
|
||||
IdentifierToken* it = new IdentifierToken();
|
||||
it->start = hmx.getOffset();
|
||||
|
||||
while (!hmx.isEnd()) {
|
||||
char c = hmx.getchar();
|
||||
bool read = false;
|
||||
|
||||
// Harf veya rakam kontrolü (ASCII)
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
|
||||
read = true;
|
||||
it->token.push_back(c);
|
||||
} else if (c == '_' || c == '$') {
|
||||
read = true;
|
||||
it->token.push_back(c);
|
||||
}
|
||||
|
||||
if (read) {
|
||||
hmx.nextChar();
|
||||
} else {
|
||||
break; // Tanımlayıcı karakteri değil → dur
|
||||
}
|
||||
}
|
||||
|
||||
it->end = hmx.getOffset();
|
||||
it->size = static_cast<int>(it->context.size());
|
||||
hmx.acceptPosition(); // Başarılı okuma → konumu kalıcı yap
|
||||
return it;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// readString: Bir string literal oku ("...")
|
||||
//
|
||||
// Desteklenen escape sequence'ler:
|
||||
// \" → çift tırnak
|
||||
// \\ → ters bölü
|
||||
// \n → satırsonu
|
||||
// \t → sekme
|
||||
// \r → satırbaşı
|
||||
//
|
||||
// Algoritma:
|
||||
// 1. Açılış tırnağını (" ) gör → started = true
|
||||
// 2. Karakterleri oku:
|
||||
// - \ ise → sonraki karakteri escape olarak işle, context'e ekle
|
||||
// - " ise → started zaten true, bu kapanış tırnağı → ended = true
|
||||
// - Diğer → context'e ekle
|
||||
// 3. Kapanış tırnağında dur
|
||||
//
|
||||
// token: Tüm karakterler (tırnaklar ve escape'ler dahil)
|
||||
// context: Sadece gerçek string içeriği (escape'ler çözülmüş)
|
||||
//
|
||||
// Örnek: "a\"b\\n" → token = "\"a\\\"b\\\\n\"", context = "a\"b\n"
|
||||
//
|
||||
// TODO: \xNN (hex escape), \uNNNN (Unicode), \UNNNNNNNN (geniş Unicode)
|
||||
// TODO: Çok satırlı string desteği ("""...""" veya backtick `...`)
|
||||
// --------------------------------------------------------------------------
|
||||
inline StringToken* Tokenizer::readString() {
|
||||
hmx.beginPosition();
|
||||
StringToken* st = new StringToken();
|
||||
bool started = false; // Açılış tırnağı görüldü mü?
|
||||
bool ended = false; // Kapanış tırnağı görüldü mü?
|
||||
st->start = hmx.getOffset();
|
||||
|
||||
while (!hmx.isEnd()) {
|
||||
char c = hmx.getchar();
|
||||
st->token.push_back(c);
|
||||
switch (c) {
|
||||
case '"':
|
||||
if (!started) {
|
||||
started = true; // Açılış tırnağı
|
||||
} else {
|
||||
ended = true; // Kapanış tırnağı
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
// Escape sequence: sonraki karakteri olduğu gibi al
|
||||
hmx.nextChar();
|
||||
c = hmx.getchar();
|
||||
st->token.push_back(c);
|
||||
st->context.push_back(c);
|
||||
break;
|
||||
default:
|
||||
st->context.push_back(c);
|
||||
break;
|
||||
}
|
||||
hmx.nextChar();
|
||||
if (ended) break;
|
||||
}
|
||||
|
||||
st->end = hmx.getOffset();
|
||||
st->size = static_cast<int>(st->context.size());
|
||||
hmx.acceptPosition();
|
||||
return st;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// skipOneLineComment: // ile başlayan yorum satırını satırsonuna kadar atla
|
||||
// --------------------------------------------------------------------------
|
||||
inline void Tokenizer::skipOneLineComment() {
|
||||
while (!hmx.isEnd()) {
|
||||
if (hmx.getchar() == '\n') {
|
||||
hmx.nextChar();
|
||||
hmx.skipWhiteSpace(); // Satırsonu sonrası boşlukları da temizle
|
||||
return;
|
||||
}
|
||||
hmx.nextChar();
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// skipMultiLineComment: /* */ bloğunu atla
|
||||
// NOT: İç içe yorum blokları desteklenmez (C standardı gibi).
|
||||
// --------------------------------------------------------------------------
|
||||
inline void Tokenizer::skipMultiLineComment() {
|
||||
while (!hmx.isEnd()) {
|
||||
if (hmx.include("*/", true)) {
|
||||
hmx.skipWhiteSpace();
|
||||
return;
|
||||
}
|
||||
hmx.nextChar();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SAQUT_TOKENIZER
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
//
|
||||
// AMAÇ:
|
||||
// Tüm derleyici modüllerinin ihtiyaç duyduğu ortak yardımcı fonksiyonlar.
|
||||
// Şu anda sadece padRight() içerir.
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
|
|
@ -19,15 +18,6 @@
|
|||
|
||||
// --------------------------------------------------------------------------
|
||||
// padRight: String'i sağdan boşluk ile belirtilen uzunluğa tamamla.
|
||||
//
|
||||
// KULLANIM: AST ağacını konsola yazdırırken girintileme (indent) için.
|
||||
// padRight("", indent) → indent adet boşluk döndürür.
|
||||
//
|
||||
// ÖRNEK:
|
||||
// padRight("", 4) → " "
|
||||
// padRight("abc", 6) → "abc "
|
||||
//
|
||||
// NOT: std::setw + std::left ile de yapılabilirdi, ancak bu daha basit.
|
||||
// --------------------------------------------------------------------------
|
||||
inline std::string padRight(std::string str, size_t totalLen) {
|
||||
if (str.size() < totalLen) {
|
||||
|
|
@ -36,4 +26,30 @@ inline std::string padRight(std::string str, size_t totalLen) {
|
|||
return str;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// jsonIndent: JSON çıktısı için girinti (her seviye 2 boşluk)
|
||||
// --------------------------------------------------------------------------
|
||||
inline std::string jsonIndent(int n) {
|
||||
return std::string(static_cast<size_t>(n) * 2, ' ');
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// jsonEscape: JSON string değerleri için kaçış karakterleri
|
||||
// --------------------------------------------------------------------------
|
||||
inline std::string jsonEscape(const std::string& s) {
|
||||
std::string out;
|
||||
out.reserve(s.size() + 4);
|
||||
for (char c : s) {
|
||||
switch (c) {
|
||||
case '"': out += "\\\""; break;
|
||||
case '\\': out += "\\\\"; break;
|
||||
case '\n': out += "\\n"; break;
|
||||
case '\r': out += "\\r"; break;
|
||||
case '\t': out += "\\t"; break;
|
||||
default: out += c;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif // SAQUT_TOOLS
|
||||
|
|
|
|||
Loading…
Reference in New Issue