На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (81) « Первая ... 77 78 [79] 80 81   ( Перейти к последнему сообщению )  
> Текущий Стандарт С++ и перспективы его развития
    Почему это? Не будет. \, предшествующий ", маскирует конец литерала и означает просто символ " как его элемент, тогда как если этому \ предшествует ещё один \, то это означает просто символ \ как элемент литерала, и если следом идёт ", то это его граница. Если мы видим \\\, то первые два означают просто \, а последний является модификатором следующего символа, и какой-то смысл они имеют только совместно. \\\" означает два обычных символа \ и ", не специальных, \\\t означает \ и TAB итп
      Проверю.
      ExpandedWrap disabled
        $ cat test.cpp
        #include <iostream>
         
        int main() {
                std::cout << "Test \\\";
                return 0;
        }
        $ g++ -o test test.cpp
        test.cpp:4:22: предупреждение: отсутствует завершающий символ "
            4 |         std::cout << "Test \\\";
              |                      ^
        test.cpp:4:22: ошибка: отсутствует терминирующий символ "
            4 |         std::cout << "Test \\\";
              |                      ^~~~~~~~~~~
        test.cpp: In function «int main()»:
        test.cpp:5:9: ошибка: expected primary-expression before «return»
            5 |         return 0;
              |         ^~~~~~
        $ cat test.cpp
        #include <iostream>
         
        int main() {
                std::cout << "Test \\\\";
                return 0;
        }
        $ g++ -o test test.cpp
        $ ./test
        Test \\$
        Цитата Славян @
        Может это и нормально, но совершенно выбесило меня!..

        Да, это проблемы раскрашивателя кода. Он не учитывает, что комментарии не могут начинаться, и не могут заканчиваться внутри строковых и символьных литералов.

        Цитата macomics @
        Судя по этому утверждению, тогда "\\\" должен быть завершенной строкой. Но не будет. Стоило сказать про нечетное количество \.

        Проще не заморачиваться количеством или чётностью. А просто определиться с обработкой.

        Символ \ может участвовать:
        • Просто как символ, если он внутри комментария
        • Может участвовать в литералах как экранирующий или экранируемый символ
        • Может участвовать в escape-преобразованиях
        • Может участвовать как склеиватель строк

        Экранирование
        Работает только внутри литералов. Символ \ может экранировать " или ' или \

        Escape-преобразования
        Работают только внутри литералов. Всем известные \n \r \t \a \0 \x42 ....

        Компилятор сканирует код посимвольно. Встречая символы, которые могут иметь и обычное, и управляющее значение. Компилятор, в процессе сканирования, "назначает" встретившему символу "роль" (обычный или управляющий) в зависимости от ряда состояний.
          Цитата Qraizer @
          Не всё, что внутри /**/ или //EOL, является комментом, а только то, что не в строковом литерале.

          Крайне подозрительное утверждение. Внутри комментариев - не может быть литералов. И если мы что-то решили закомментировать (я имею ввиду по правилам), то любые литералы автоматом превращаются в комментарии.
            Majestio, ну да. Это и означает, что
            ExpandedWrap disabled
              std::cout << "Тут я не должна заменяться, даже /* если я размещу её */ вот так"; // в отличие от "Тут я должна быть \
              заменена", потому что это всё ещё коммент
            нужно уметь правильно обработать
            Сообщение отредактировано: Qraizer -
              Qraizer, склейка строк не спасет от неправильности твоего этого утверждения.
              Хотя нет, не от неправильности. Твое это утверждение отчасти верно. Просто оно записано так, что несколько ломает причинно-следственные связи :lol: А именно ... как бы это правильно сказать: литералы все же первичны, конструкции комментариев вторичны. Т.е., выражаясь академическим языком, и чтобы не туманить мозги С++ неофитам, ты должен был выразить мысль в более "календарном" смысле, в обратном порядке:
              1. Сперва проход препроцессора (склейки строк, инклюды, дефайны &etc)
              2. Определение литералов
              3. Определение комментариев
              Заметь! 2 потом 3, а не 3 потом 2. Если я не ошибаюсь, это что-то типа раздел 5.2 "Phases of translation" текущего Стандарта.
                В чём именно ты видишь неправильность? Я не собирался описывать алгоритм, хошь сырцов?, они есть. Я описал спектр учитываемых аспектов. Естественно их не следует применять в описанном порядке. Давайте не будем душнить. Плз.
                  Цитата Qraizer @
                  В чём именно ты видишь неправильность?

                  Ну я же написал выше. Не неправильность, а скорее запутанность. Проще написать, что внутри литералов не может быть комментов, равно как и внутри комментов не может быть литералов.
                  Тогда всё ясно и понятно. И да, давай не будем душнить =)
                    Qraizer, тебе квест! ;)

                    Цитата Qraizer @
                    Как-то пришлось писать утилю, которая заменяла все я на Я, потому как используемая тулза (инструментатор исходного кода под: сбор структурного покрытия; сбор информации WCET, анализ потоков данных и/или управления; оценка наихудшего использования стека) на ASCII символах == 0xFF бажила. (Дело не в русской букве, а именно коде символа. В европейских кодировках на этом символе тот же баг.) Так это было целое приключение, т.к. заменять всё было нельзя, только в комментах. А то, что в строках, нужно оставить нетронутым, иначе меняется поведение кода, благо на таких тулза не бажила.
                    В итоге это вылилось в весьма нетривиальный алгоритм.

                    Я уверен, что сей велосипед ты навелосипедил на все 100% правильно. Но ты тогда, imho, пошел самым упоротым путем. Ибо для таких задач уже давно существовали специальные решения. А именно связка либ - bison (1985) + flex (1987). Генераторы парсеров и лексические анализаторы в лице вышеупомянутых либ.

                    Слабо изучить матчасть и перевести свой код под эти "платформы"?

                    Скрытый текст
                    заодно и мы поучимся ... а?
                      Зачем такое старьё, если есть boost::spirit :lol: ?
                        Bison + Flex однозначно лучше, когда требуется максимальная производительность парсинга для обработки больших входных данных, а грамматика подходит для LALR, так как они генерируют оптимизированный C-код, который работает быстрее, чем парсеры, созданные с помощью Boost::Spirit.
                          Ну я бы не был так категоричен на предмет производительности. Другое дело, компиляция... <_<
                          Та и в целом, меня напрягает любой автоматически непонятно кем непонятно как генерируемый код, вставляемый в мой проект. Никсоидам может быть и нормуль, когда make генерит makefile, по которому make генерит три makefile, по которым make генерит N makefiles для сборки N/3 пререквизитов, по которым рекурсивно процесс повторяется для сборки пререквизитов, ещё один собирает собственно проект с пререквизитами и ещё один конфигурит и инсталит, и поэтому им нормуль, когда половина пререквизитов автогенерится своими makefile, точнее, их половиной, тогда как вторая половина их собственно собирает... а, не, половина от N/3... :wacko: ойфсё. И да, в Cях метакодить иначе никак. Спасибо, я всё ж лучше метакодить по Стандарту буду
                            Цитата Qraizer @
                            Никсоидам может быть и нормуль, когда make генерит makefile, по которому make генерит три makefile, по которым make генерит N makefiles для сборки N/3 пререквизитов, по которым рекурсивно процесс повторяется для сборки пререквизитов, ещё один собирает собственно проект с пререквизитами и ещё один конфигурит и инсталит, и поэтому им нормуль, когда половина пререквизитов автогенерится своими makefile, точнее, их половиной, тогда как вторая половина их собственно собирает... а, не, половина от N/3... ойфсё.

                            Вот тут про никсоидов было очень обидно сказано! Нагнал жути :lol:

                            Спецом побеседовал с ChatGPT по этой теме, т.к. практического использования bison+flex не имел. Ради прикола собрал парсер С++ кода, который из кода извлекает все литералы и распечатывает их. Заметь, я не использовал make, а сделал это современными и отчасти удобными средствами сборки cmake+ninja.

                            А вот сам пример (да, понимаю, что сильно упрощенный - но наглядный)

                            lexer.l

                            ExpandedWrap disabled
                              %{
                              #include "parser.tab.h"  // Включаем заголовок от Bison для токенов
                              %}
                               
                              %option noyywrap
                               
                              /* Состояния */
                              %x SINGLE_COMMENT
                              %x MULTI_COMMENT
                               
                              %%
                               
                               /* Пропуск пробелов и другого кода (мы фокусируемся только на литералах и комментариях) */
                              [ \t\n]+    ;  // Игнорируем whitespace
                               
                               /* Начало однострочного комментария */
                              "//"        { BEGIN(SINGLE_COMMENT); }
                              <SINGLE_COMMENT>[^\n]*  ;  // Потребляем до конца строки
                              <SINGLE_COMMENT>\n      { BEGIN(INITIAL); }  // Возврат в нормальное состояние
                               
                               /* Начало многострочного комментария */
                              "/*"        { BEGIN(MULTI_COMMENT); }
                              <MULTI_COMMENT>[^*]*    ;  // Потребляем всё, кроме *
                              <MULTI_COMMENT>"*"+[^*/]*  ;  // Потребляем * не за которыми /
                              <MULTI_COMMENT>"*"+"/"  { BEGIN(INITIAL); }  // Конец комментария
                               
                               /* Строковый литерал (упрощённо, с escape) */
                              \"(\\.|[^\\"])*\"   { yylval.str = strdup(yytext); return STRING_LITERAL; }
                               
                               /* Символьный литерал (упрощённо) */
                              \'(\\.|[^\\'])*\'   { yylval.str = strdup(yytext); return CHAR_LITERAL; }
                               
                               /* Другие символы (пропускаем, так как не интересуют) */
                              .           ;  // Игнорируем остальной код
                               
                              %%

                            parser.y

                            ExpandedWrap disabled
                              %{
                              #include <iostream>
                              #include <vector>
                              #include <string>
                              #include <cstring>
                               
                              extern int yylex();
                              void yyerror(const char* s);
                               
                              std::vector<std::string> literals;  // Глобальный список для собранных литералов
                              %}
                               
                              %union {
                                  char* str;
                              }
                               
                              %token <str> STRING_LITERAL
                              %token <str> CHAR_LITERAL
                               
                              %start program
                               
                              %%
                               
                              program: statements
                                     ;
                               
                              statements: statement
                                        | statements statement
                                        ;
                               
                              statement: STRING_LITERAL   { literals.push_back($1); free($1); }
                                       | CHAR_LITERAL     { literals.push_back($1); free($1); }
                                       | /* пусто */      { /* Игнорируем другие токены или пустые */ }
                                       ;
                               
                              %%
                               
                              void yyerror(const char* s) {
                                  std::cerr << "Ошибка парсинга: " << s << std::endl;
                              }

                            main.cpp

                            ExpandedWrap disabled
                              #include <iostream>
                              #include <vector>
                              #include <string>
                              #include <windows.h> // Для Windows API
                              #include "parser.tab.h"
                               
                              extern std::vector<std::string> literals;
                              extern FILE* yyin;
                               
                              // Функция для вывода строки в CP1251 через WriteConsoleW
                              void print_in_cp1251(const std::string& str) {
                                  // Конвертируем UTF-8 или ASCII строку в UTF-16 (Windows использует UTF-16)
                                  int wstr_size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
                                  std::wstring wstr(wstr_size, 0);
                                  MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wstr[0], wstr_size);
                               
                                  // Выводим в консоль с CP1251 (Windows автоматически конвертирует UTF-16 в CP1251 для консоли)
                                  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
                                  WriteConsoleW(hConsole, wstr.c_str(), wstr.size() - 1, nullptr, nullptr);
                              }
                               
                              int main(int argc, char** argv) {
                                  // Устанавливаем кодировку консоли на CP1251
                                  SetConsoleOutputCP(1251);
                               
                                  if (argc > 1) {
                                      yyin = fopen(argv[1], "r");
                                      if (!yyin) {
                                          print_in_cp1251("Не удалось открыть файл: " + std::string(argv[1]) + "\n");
                                          return 1;
                                      }
                                  }
                               
                                  yyparse();  // Запускаем парсер
                               
                                  // Выводим собранные литералы
                                  print_in_cp1251("Собранные строковые и символьные литералы (вне комментариев):\n");
                                  for (const auto& lit : literals) {
                                      std::cout << lit << std::endl; // Литералы в ASCII, их можно выводить через std::cout
                                  }
                               
                                  if (yyin != stdin) fclose(yyin);
                                  return 0;
                              }

                            CMakeLists.txt

                            ExpandedWrap disabled
                              cmake_minimum_required(VERSION 3.10)
                              project(CPPScanner LANGUAGES C CXX)
                               
                              # Стандарт C++17
                              set(CMAKE_CXX_STANDARD 17)
                              set(CMAKE_CXX_STANDARD_REQUIRED ON)
                               
                              # Включаем статическую линковку
                              set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc")
                              set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
                               
                              # Найти Flex и Bison
                              find_package(BISON REQUIRED)
                              find_package(FLEX REQUIRED)
                               
                              # Генерация парсера из parser.y (с подавлением предупреждений Bison)
                              BISON_TARGET(MyParser parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser.tab.c
                                           DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/parser.tab.h
                                           COMPILE_FLAGS "-Wnone")
                               
                              # Генерация лексера из lexer.l
                              FLEX_TARGET(MyLexer lexer.l ${CMAKE_CURRENT_BINARY_DIR}/lex.yy.c)
                               
                              # Зависимость: парсер зависит от лексера
                              ADD_FLEX_BISON_DEPENDENCY(MyLexer MyParser)
                               
                              # Собрать исполняемый файл
                              add_executable(scanner
                                  main.cpp
                                  ${BISON_MyParser_OUTPUT_SOURCE}  # parser.tab.c
                                  ${FLEX_MyLexer_OUTPUTS}          # lex.yy.c
                              )
                               
                              # Указываем, что генерированные .c файлы — это C++
                              set_source_files_properties(${BISON_MyParser_OUTPUT_SOURCE} ${FLEX_MyLexer_OUTPUTS} PROPERTIES LANGUAGE CXX)
                               
                              # Подавляем предупреждение о deprecated для Clang
                              set_source_files_properties(${BISON_MyParser_OUTPUT_SOURCE} ${FLEX_MyLexer_OUTPUTS} PROPERTIES
                                  COMPILE_FLAGS "-Wno-deprecated")
                               
                              # Включить директории для заголовков
                              target_include_directories(scanner PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
                               
                              # Добавить путь к библиотекам clang64
                              target_link_directories(scanner PRIVATE E:/Tools/MSys64/clang64/lib)
                               
                              # Линковка со статическими библиотеками C++ и unwind
                              target_link_libraries(scanner PRIVATE -static libc++.a libunwind.a)

                            Процесс сборки:

                            ExpandedWrap disabled
                              mkdir build && cd build
                              cmake -G Ninja ..
                              ninja


                            ExpandedWrap disabled
                              -- The C compiler identification is Clang 20.1.8
                              -- The CXX compiler identification is Clang 20.1.8
                              -- Detecting C compiler ABI info
                              -- Detecting C compiler ABI info - done
                              -- Check for working C compiler: E:/Tools/MSys64/clang64/bin/cc.exe - skipped
                              -- Detecting C compile features
                              -- Detecting C compile features - done
                              -- Detecting CXX compiler ABI info
                              -- Detecting CXX compiler ABI info - done
                              -- Check for working CXX compiler: E:/Tools/MSys64/clang64/bin/c++.exe - skipped
                              -- Detecting CXX compile features
                              -- Detecting CXX compile features - done
                              -- Found BISON: E:/Tools/MSys64/usr/bin/bison.exe (found version "3.8.2")
                              -- Found FLEX: E:/Tools/MSys64/usr/bin/flex.exe (found version "2.6.4")
                              -- Configuring done (1.7s)
                              -- Generating done (0.0s)
                              -- Build files have been written to: E:/Tools/MSys64/home/Majestio/work/bison_flex/build
                              [6/6] Linking CXX executable scanner.exe

                            Проверяем на файле input.cpp следующего содержимого:

                            ExpandedWrap disabled
                              // Это комментарий с "строкой", которую нужно пропустить
                              int main() {
                                  std::string s = "hello world";  // Строка для сбора
                                  char c = 'a';  // Символ для сбора
                                  /* Многострочный комментарий с 'b' и "test", которые пропустить */
                                  return 0;
                              }


                            ExpandedWrap disabled
                              E:\Tools\MSys64\home\Majestio\work\bison_flex\build>scanner.exe input.cpp
                              Собранные строковые и символьные литералы (вне комментариев):
                              "hello world"
                              'a'

                            По-моему не всё так страшно-ужасно как ты расписал :victory:
                              Цитата Majestio @
                              Заметь! 2 потом 3, а не 3 потом 2
                              Тут ты ошибся.
                              Сперва из текста программы удаляются комментарии и производится склейка строк.
                              Потом производится разбор текста на лексемы, включая и строки с директивами препроцессора.
                              И уже после разбора на лексемы работает препроцессор, удаляя куски уже разобранного текста и заменяя макросы.

                              Причём всё это время лексемы остаются привязанными к исходному тексту программы, и результат препроцессирования может быть выдан в виде текста так, будто препроцессор работает непосредственно с текстом.

                              В ранних компиляторах C (K&R), состоящих из отдельных программ, каждая из которых выполняла свой проход, порядок был другой. Препроцессор выполнялся до разбора на лексемы, но после удаления комментариев (собственно он их и удалял).
                                amk, привет, дружище!!! :victory:

                                Вообще без проблем поговорить по этому вопросу и набраться современных знаний - тема есть, но наука то не стоит!
                                Будь добр, обеспечить свои утверждения линками-пруфами по этим темам, чисто для ознакомления.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (81) « Первая ... 77 78 [79] 80 81 


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,1151 ]   [ 16 queries used ]   [ Generated: 6.11.25, 07:14 GMT ]