Как я писал компилятор С++. Пересказ спустя 15 лет

    15 лет назад не было Хабрахабра, не было фейсбука, и что характерно, не было компилятора С++, с выводом диагностических сообщений на русском. С тех пор, вышло несколько новых стандартов С++, технологии разработки сделали гигантский скачок, а для написания своего языка программирования или анализатора кода может потребоваться в разы меньше времени, используя существующие фреймворки. Пост о том, как я начинал свою карьеру и путем самообразования и написания компилятора С++, пришел к экспертному уровню. Общие детали реализации, сколько времени это заняло, что получилось в итоге и смысл затеи — тоже внутри.

    image

    С чего все начиналось


    В далеком 2001-ом году, когда мне купили первый компьютер Duron 800mhz/128mb ram/40gb hdd, я стремительно взялся за изучение программирования. Хотя нет, сначала меня постоянно мучил вопрос, что же поставить Red Hat Linux, FreeBSD или Windows 98/Me? Ориентиром в этом бесконечном мире технологий для меня служил журнал Хакер.
    Старый такой, стебный журнал. К слову, с тех пор, стиль изложения в этом издании почти не поменялся.

    Виндузятники, ламеры, трояны, элита, линух — вот это все сносило крышу. Реально хотелось
    поскорей освоить этот весь стек, которые они там печатали и хакнуть Пентагон (без интернета).
    Внутренняя борьба за то, становиться ли Линуксоидом или рубится в игры на винде продолжалась до тех пор, пока в дом не провели интернет. Модемный, скрежечащий 56kb/s интернет, который занимал телефон на время подключения, и качал mp3-песню в районе получаса. При цене порядка 0.1$/mb, одна песня вытягивала на 40-50 центов. Это днем.

    А вот ночью, были совсем другие расценки. Можно было с 23.00 до 6.00 залипать во все сайты не отключая изображения в браузере! Поэтому все что можно было скачать из сети за ночь, качалось на винт, и далее прочитывалось уже днем.
    В первый день, когда мне домой провели и настроили сеть, админ передо мной открыл IE 5 и Яндекс. И быстро ретировался. Думая, что же первым делом искать в сети, я набрал что-то вроде «сайт для программистов». На что первой ссылкой в выдаче выпал совсем недавно открывшийся rsdn.ru. И на нем я стал зависать продолжительное время, испытывая чувство неудовлетворенности, от того, что мало что понимаю. На то время флагманом и самым популярным языком на форуме (да и вообще) был С++. Поэтому вызов был брошен, и ничего не оставалось, как догонять бородатых дядек в их знаниях по С++.
    А еще был не менее интересный сайт на то время — firststeps.ru. Я до сих пор считаю их метод подачи материала наилучшим. Маленькими порциями (шагами), с небольшими конечными результатами. Тем не менее все получалось!

    Активно скупая книги на барахолке, я стремился постичь все азы программирования. Одной из первых купленных книг было «Искусство программирования» — Д. Кнут. Не помню точную мотивацию купить именно эту книгу, а не какой-нибудь С++ для кофейников, наверное продавец порекомендовал, но я со всем своим усердием школьника взялся за изучение первого тома, с обязательным выполнением задач в конце каждой главы. Это была самая мякотка, и хотя с математикой у меня в школе не ладилось, но зато с мат.аном Кнута прогресс был, потому что было огромное желание и мотивация писать программы и делать это правильно. Осилив алгоритмы и структуры данных, я купил уже 3-ий том «Искусства программирования» Сортировка и поиск. Это была бомба. Пирамидальная сортировка, быстрая сортировка, бинарный поиск, деревья и списки, стеки и очереди. Все это я записывал на листочке, интерпретируя результат в своей голове. Читал дома, читал когда был на море, читал везде. Одна сплошная теория, без реализации. При этом я даже не догадывался, какую огромную пользу принесут эти базовые знания в будущем.
    Сейчас, проводя собеседования с разработчиками, мне еще не встретился человек, который смог бы написать реализацию бинарного поиска или быстрой сортировки на листочке. Жаль.

    Но вернемся к теме поста. Осилив Кнута, надо было двигаться дальше. Попутно я сходил на курсы Turbo Pascal, прочитал Кернигана и Ритчи, а за ними С++ за 21 день. Из С и С++, мне было не все понятно, я просто брал и переписывал тексты из книг. Загуглить или спросить было не у кого, но зато времени было вагон, так как школу я забросил и перешел в вечернюю, в которую можно было практически не ходить, или появляться на 3-4 урока в неделю.
    В итоге с утра до ночи, я фанатично развивался, познавая все новые и новые темы. Мог написать калькулятор, мог написать простое приложение на WinApi. На Delphi 6 тоже получалось что-то нашлепать. В итоге, получив диплом о среднем образовании, я уже был подготовлен на уровне 3-4 курса университета, и разумеется на какую специальность идти учится вопроса не стояло.

    Поступив на кафедру Компьютерных систем и сетей, я уже свободно писал на С и С++ задачи любого уровня сложности университета. Хотя, зайдя на тот же rsdn.ru, понимал, как много еще нужно изучить и насколько бывалые форумчане прокаченней меня в плюсах. Это задевало, непонимание и вместе с тем жгучее желание знать все, привело меня к книге «Компиляторы. Инструменты. Методы. Технологии» — А.Ахо, Рави Сети. В простонародье именуемой книгой Дракона. Вот тут и началось самое интересное. Перед этой книгой, был прочитан Герберт Шилдт, Теория и практика С++, в которой он раскрывал продвинутые темы разработки, такие как шифрование, сжатие данных, и самое интересное — написание собственного парсера.

    Начав скрупулезно изучать книгу дракона, двигаясь от лексического анализа, затем к синтаксическому и наконец к проверке семантики и генерации кода, ко мне пришло судьбоносное решение — написать свой компилятор С++.
    — А почему бы и нет, спросил себя?
    — А давай, ответила та часть мозга, которая с возрастом становится все скептичней ко всему
    новому. И разработка компилятора началась.

    Подготовка


    Модемный интернет к тому времени мне перекрыли, в силу смены телефонных линий на цифровые, поэтому для ориентира был скачан стандарт ISO C++ редакции 1998 года. Уже полюбившимся и привычным инструментом стала Visual C++ 6.0.
    И по сути задача свелась к тому, чтобы реализовать то, что написано в стандарте С++. Подспорьем в разработке компилятора была книга дракона. А отправной точкой, был парсер-калькулятор из книги Шилдта. Все части пазла собрались воедино и разработка началась.

    Препроцессор


    nrcpp\KPP_1.1\
    Во 2-ой главе в стандарте ISO C++ 98 идут требования к препроцессору и лексические конвенции (lexical conventions). Вот и славно, подумал я, ведь это наиболее простая часть и может реализоваться отдельно от самого компилятора. Другими словами, сначала запускается препроцессинг файла, на вход которому поступает С++ файл в том виде, котором вы привыкли его видеть. А после препроцессинга, на выходе мы имеем преобразованный С++ файл, но уже без комментариев, подставленными файлами из #include, подставленными макросами из #define, сохраненными #pragma и обработанной условной компиляцией #if/#ifdef/#endif.
    До препроцессинга:
    #define MAX(a, b) \
    	((a) > (b) ? a : b)
    #define STR(s)  #s
    
    /*
     This is the entry point of program
     */
    int main()
    {
    	printf("%s: %d", STR(This is a string), MAX(4, 5));
    }
    


    После препроцессинга:
    int main()
    {
              printf("%s: %d", "This is a string", ((4) > (5) ? 4 : 5));
    }
    


    В довесок, препроцессор делал еще много полезной работы, вроде вычисления константных выражений, конкатенации строковых литералов, вывода #warning и #error. Ах да, вы когда нибудь видели в С-коде Диграфы и триграфы? Если нет, знайте — они существуют!
    Пример триграфов и диграфов
    int a<:10:>; // эквивалент int a[10];
    if (x != 0) <% %> // эквивалент if (x != 0) { }

    // Пример триграфа
    ??=define arraycheck(a,b) a??(b??) ??!??! b??(a??)
    // проеобразуется в
    #define arraycheck(a,b) a[b] || b[a]

    Подробнее в вики.

    Разумеется, основной пользой от препроцессора С++, является подстановка макросов и вставка файлов обозначенных в #include.
    Чему я научился в процессе написания препроссора С++?
    • Как устроена лексика и синтаксис языка
    • Приоритеты операторов С++. И в целом как вычисляются выражения
    • Строки, символы, контанты, постфиксы констант
    • Структура кода

    В целом, на написание препроцессора ушло порядка месяца. Не слишком сложно, но и нетривиальная задача, тем не менее.
    В это время, мои одногруппники пытались написать первый «Hello, world!», да хотя бы собрать его. Далеко не у всех получалось. А меня ждали следующие разделы стандарта С++, с уже непосредственной реализацией компилятора языка.

    Лексический анализатор


    nrcpp/LexicalAnalyzer.cpp
    Тут все просто, основную часть анализа лексики я уже написал в препроцессоре. Задача лексического анализатора — разобрать код на лексемы или токены, которые уже будет анализироваться синтаксическим анализатором.
    Что было написано на этом этапе?
    • Конечный автомат для анализа целочисленных, вещественных и символьных констант. Думаете это просто? Впрочем просто, когда ты это прошел.
    • Конечный автомат для анализа строковых литеров
    • Разбор имен переменных и ключевых слов С++
    • Что-то еще, как пить дать. Вспомню допишу


    Синтаксический анализатор


    nrcpp/Parser.cpp
    Задача синтаксического анализатора — проверить правильность расстановки лексем, который были получены на этапы лексического анализа.
    За основу синтаксического анализатора были взяты опять же простенький парсер из Шилдта, прокаченный до уровня синтаксиса С++, с проверкой переполнения стека. Если мы например напишем:
    (((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))); // кол-во скобок может быть больше

    То мой рекурсивный анализатор съест стэк, и выдаст, что выражение слишком сложное.
    У внимательного читателя, может возникнуть вопрос. А зачем изобретать велосипед, ведь был же yacc и lex. Да, был. Но на том этапе, хотелся велосипед с полным контролем над кодом. Разумеется в производительности он уступал сгенерированному этими утилитами коду. Но не в этом была цель — техническое совершенство. Цель была — понять все.

    Семантика


    nrcpp/Checker.cpp
    nrcpp/Coordinator.cpp
    nrcpp/Overload.cpp

    Занимает соотвественно главы с 3-ей по 14-ую стандарта ISO C++ 98. Эта наиболее сложная часть, и я уверен, что >90% С++ разработчиков не знает всех правил описанных в этих разделах. Например:
    Знали ли вы, что функцию можно объявлять дважды, таким образом:
    void f(int x, int y = 7);
    void f(int x = 5, int y);
    


    Есть такие конструкции для указателей:
    const volatile int *const volatile *const p;


    А это указатель на функцию-член класса X:
    void (X::*mf)(int &)


    Это первое, что пришло в голову. Стоит ли говорить, что при тестировании кода из стандарта в Visual C++ 6, я не редко получал Internal Compiler Error.

    Разработка анализатора семантики языка заняла у меня 1.5 года, или полтора курса универа. За это время меня чуть не выгнали, по другим предметам кроме программирования, за счастье получалась тройка (ну, ок четверка), а компилятор тем временем разрабатывался и обрастал функционалом.

    Генератор кода


    nrcpp/Translator.cpp
    На этом этапе, когда энтузиазм немного начал угасать, уже имеем вполне рабочую версию фронт-енд компилятора. Что дальше делать с этим фронт-ендом, разработчик решает сам. Можно распространять его в таком виде, можно использовать для написания анализатора кода, можно использовать для создания своего конвертера вроде С++ -> C#, или C++ -> C. На этом этапе у нас есть провалидированное синтаксически и семантически AST (abstract syntax tree).
    И на этом этапе разработчик компилятора понимает, что он постиг дзен, достиг просветления, может неглядя понять почему код работает именно таким образом. Для добивания своей цели, создания компилятора С++, я решил закончить на генерации С-кода, который затем можно было бы конвертировать в любой существующий ассемблерный язык или подавать на вход существующим Сишным компиляторам (как делал Страуструп в первых версиях «С с классами»).

    Чего нет в nrcpp?


    • Шаблоны (templates). Шаблоны С++, эта такая хитровымудренная система с точки зрения реализации, что мне пришлось признать, без вмешательства в синтаксический анализатор и смешивания его с семантикой — шаблоны должным образом работать не будут.
    • namespace std. Стандартную библиотеку без шаблонов не напишешь. Да впрочем и заняло бы это еще много-много месяцев, так как занимает львиную долю стандарта.
    • Внутренние ошибки компилятора. Если вы будете играться с кодом, то сможете увидеть сообщения вроде:
      внутренняя ошибка компилятора: in.txt(20, 14): «theApp.IsDiagnostic()» --> (Translator.h, 484)
      Это либо не реализованный функционал, либо не учтенные семантические правила.


    Зачем писать свой велосипед?


    А в заключении хочу отметить то, ради чего писалась этот пост. Написание своего велосипеда, даже если на это потрачено 2 с лишним года, кормит меня до сих пор. Это бесценные знания, база, которая будет с Вами на протяжении всей карьеры разработчика. Будут меняться технологии, фреймворки, выходить новые языки — но фундамент в них будет заложен из прошлого. И на их понимание и освоение уйдет совсем немного времени.

    github.com/nrcpp/nrcpp — исходники компилятора. Можно играться правя файл in.txt и смотреть вывод в out.txt.
    github.com/nrcpp/nrcpp/tree/master/KPP_1.1 — исходники препроцессора. Собирается с помощью Visual C++ 6.
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 117
    • 0

      Респект и уважуха! Взял бы уроки (изучил бы авторские статьи) по написанию интерпретаторов, компиляторов, VM и т.д.

      • 0
        Увы. Автор прекратил работу там, где кончается общеизвестная теория и начинается know-how. То есть на кодогенерации под конкретный (или абстрактный, как в Java и С#) процессор.

        Как ни удивительно, но книг по кодогенерации очень мало. Одно из исключений — "Как паскаль и оберон попадает на Самсон". Как сделать архитектуру процессора — известно, а как сделать на неё кодогенерацию — know-how.

        Так что IMHO этот компилятор — хорошая дипломная работа. Но не более того.
        • 0
          Я как раз изучаю С++, так что, ваша статья оказалась очень полезной (в плане мотивации), но насчет Оберона не понял. Есть смысл прочитать Вирта по операционным системам? Буду очень благодарен за ответ.
      • НЛО прилетело и опубликовало эту надпись здесь
        • –2
          Известный факт — компилятор алгола-68 написан на алголе-68. В момент, когда компилятор правильно компилирует сам себя — это означает проверку всего лишь 30% языковых конструкций.

          Тесты — полезная штука, но вычитка кода дает больше. То есть при вычитке кода можно найти те ошибки, на которые не хватило фантазии придумать тесты. Это я как человек. бывший 20 лет начальником группы тестирования говорю.
          • +2

            Так никто не мешает после вычитки кода дописать набор тестов. А тесты сами по себе — это спецификация.


            Я как раз тоже пишу компилятор сейчас, пишу на хаскеле с тестами через hspec — одно удовольствие так разрабатывать, начиная от имеющегося инструментария вроде attoparsec для реализации парсеров и заканчивая сильной системой типов, а также сахарком вроде uniplate.

            • –2
              Собственноручно написанные тесты — это очень кривая спецификация. Для того, чтобы тесты действительно соответствовали тому, что есть в спецификации, а не тому, как прочел спецификацию разработчик, нужны ещё два человека: тестировщик, не знающий устройства кода, и гуру, который судит, кто вернее прочел спецификацию — автор кода или автор тестов.

              Типичный пример, чем плохи собственноручные тесты: =+ в ранних версиях Си. В итоге пришло поменять на +=, ибо вводить обязательность пробела в конструкции i = +5 было ещё хуже.

              У каждого тестера тут своих историй навалом. Ну в общем держали 20 корректур
              • +2
                Типичный пример, чем плохи собственноручные тесты: =+ в ранних версиях Си. В итоге пришло поменять на +=, ибо вводить обязательность пробела в конструкции i = +5 было ещё хуже.

                Не понял, причём тут тесты.

                • +1
                  Если тесты пишет не автор кода шансы обнаружить подобный ляп дизайна возрастают в разы.
              • 0

                А можно на него посмотреть? Пытался так писать, но не смог, видимо ещё недостаточно познал дзен TDD.

                • 0

                  К сожалению, нет, оно проприетарное и делаемое в рабочее время. Когда-нибудь, впрочем, я, возможно, напишу какую-нибудь статеечку о том, как подобные вещи можно делать.

              • НЛО прилетело и опубликовало эту надпись здесь
                • –2
                  Разработка через тестирование дает менее соответствующее спецификациям приложение, чем независимое написание тестов и кода.
                  • НЛО прилетело и опубликовало эту надпись здесь
                    • –2
                      Какой именно процесс? Если процесс написания компилятора под свой собственный диалект языка — тогда согласен. Если под стандарт — не согласен. Обоснование читайте в книге Евгения Зуева "редкая профессия".
                      • НЛО прилетело и опубликовало эту надпись здесь
                        • –3
                          И в момент, когда внутреннее тестирование закончено и надо сертифицировать продукт по внешний тестам — имеем скандал вплоть до мордобития и увольнения. Потому как по TDD — все работает. А по внешним тестам — не проходит.

                          И выясняется, что огромный массив тестов, написанный для TDD, годится только в мусорную корзину. :-)

                          TDD — хороший метод для написания собственного языка или собственного диалекта. Лет 25 назад я его использовал для электронных таблиц на УКНЦ. Но язык выражений там у меня был свой собственный.

                          • НЛО прилетело и опубликовало эту надпись здесь
                            • –3
                              На самом деле это и то и то. TDD провоцирует стиль разработки «снизу вверх», что довольно вредно для больших программ. Ну в общем когда обожжетесь на TDD — тогда и поговорим.

                              Как говорит мой коллега «при разработке по TDD обычно работают только тесты.» :-) Имеется ввиду, что если человек решил, что в году 365 дней — то он это же проверит тестами. Это я про DOS-11. Через десяток лет 1 марта високосного года не попало на выходные и мы сильно удивлялись невозможности поставить 1 марта 1984 года. :-)
                              • НЛО прилетело и опубликовало эту надпись здесь
                                • –3
                                  Не сложнее, а сильно проще. Подумайте внимательно, какого рода ошибки остабтся при TDD?
                                  • НЛО прилетело и опубликовало эту надпись здесь
                                    • –3
                                      TDD провоцирует писать качественно структурированный код.

                                      Каким это образом?! Докажите, плиз.

                                      Скорее уж наоборот, TDD (как и любые методы с ускоренным циклом разработки) приводят к плохо стуктурированной лапше. Ещё фича, ещё одна, быстрее, быстрее — а времени на то, чтобы проектировать структуру не остается.
                                      • НЛО прилетело и опубликовало эту надпись здесь
                                        • –3
                                          А выпячивание любого показателя на первое место — вредно.

                                          Платите за объем кода — получаете пухлый код в стиле Маяковского (ему тоже построчно платили). Платите за прохождение тестов — получаете код, который отлично работает на тестах, а на реальных данных — как получится.

                                          TDD хорошо сочетается с непрерывной интеграцией, всяким скрамом и так далее. То есть с работой, когда дальние цели (что надо через несколько лет) или не ясны или вообще лежат в плоскости «заработать N мешков денег». И как раз это и приводит к лапшевидному коду.

                                          А когда идет работа по плану, то какие-то этапы согласуются с идеологией TDD, а какие-то нет. Например, плохо согласуется с TDD разработка сверху вниз.

                                          Ну как пример. Делаем кубик рубика. Тестируем отдельные кубики, шарниры — вроде все хорошо. А при сборке — лажа.

                                          Опыт у меня специфический АСУТП — это ненастоящее программирование. Дай бог, цейтнот закончится — расскажу, как мы сделали систему размером 135кстрок при времени на отладку — 2 часа в месяц. В месяц, Карл! Два часа, Карл. Работает 365 на 24 уже почти 15 лет.

                                          Мы решили, что ошибки были есть и будут, поэтому система должна работать, несмотря на ошибки. И сделали это. Работает как Ванька-Встанька. Ушло на это примерно 30 процентов стоимости. Но ловля всех ошибок была бы дороже.

                                          Но при этом электронные таблицы 25 лет назад — делали в духе TDD. Потому что дял этого проекта, где язык дорабатывался в ходе написания парсера — TDD годится.
                                          • +2
                                            Тестируем отдельные кубики, шарниры — вроде все хорошо. А при сборке — лажа.

                                            Какое-то у вас странное TDD.
                                            • –1
                                              обычное. Если у ошибка не случайна, она повторяется и в тестах и в коде. если мы решили, что все шарниры одинаковые — мы так и сделаем. И тесты пройдут. А на сборке выяснится, что половина должна быть зеркальная.

                                              Ну как пример — один из разработчиков (кажется Сергей Тарасов) с его-то решил, что число часов в сутках может меняться. Так оно не только в код, оно ещё и в доку попало. Десяток людей это читало и никто не заметил маразма, пока все это не прочел тестер (@yole).
                                              • +1
                                                И тесты пройдут.

                                                Интеграционные — не пройдут.

                                                Задача тестов в TDD — чтобы желаемое сошлось с действительным. Если у вас кривое «желаемое», то это не в инструменте дело, а в руках, которые его держат.
                                                • –2
                                                  Кривое желаемое — это обычная ошибка. Ничем не отличается от ошибки в коде, которые каждый из нас делает каждый день.

                                                  Проблема TDD в том, что оно требует совмещать в одном человеке две разные роли. Не всякий хороший актер может быть хорошим критиком. И не каждый хороший критик может играть на сцене.

                                                  Для каких-то областей, где ошибка желаемого редка — TDD пригоден. А там, где такая ошибка типична — не пригоден.
                                                  • +1
                                                    Кривое желаемое — это обычная ошибка.

                                                    Это ошибка проектирования, архитектуры. TDD от этого не лечит и не должен.

                                                    Могу понять ваше негодование, что этот подход не всесилен, но почему же он вреден?
                                                    • –1
                                                      Мы о чем речь ведем? О написании компилятора стандартного С++. Стандарт С++ — огромен по сравнению со стандартом object pascal. Он не формализован, как стандарт алгола-68. Поэтому зачастую на двух программистов — 3 мнения, что имелось ввиду в стандарте.

                                                      Таким образом, в конкретной задаче ошибка желаемого становится типовой. Отсюда основная претензия к TDD — этот метод потворствует ошибкам желаемого. Разумеется в сравнении с написанием тестов по методу черного ящика.

                                                      Второй момент, очень похожий. Если я не вижу граничной ситуации при написании тестов — значит я не увижу её в коде. Таким образом TDD создает ложное впечатление о полноте тестирования. И опять-таки, спасает метод черного ящика.

                                                      И опять-таки, в конкретной ситуации написания стандартного компилятора — это важно. А вот в написании сайта — не важно. Как и в написании компилятора для своего языка или своего диалекта языка.

                                                      Дальнейшие претензии касается менеджмента. Как только мы вытащили TDD на первое место из большой обоймы методик экстремального программирования, сразу появляется опасность оценивать программистов по прохождению тестов. А это уже влечет за собой ситуацию «тесты работают, но работают только тесты», которую вживую наблюдал коллега. Вот это уже вредно.

                                                      Как и ранее, спасает организация отдельной группы тестирования с конкурирующими интересами. Или парное программирование, когда один пишет тесты, а другой код.

                                                      И последняя претензия — TDD не подходит для написания систем методом сверху вниз. Когда вначале придумывается архитектура, потом пишутся структуры данных и только на последних этапах появляется возможность использовать TDD.

                                                      Иными словами, повернутость менеджмента на TDD приводит к написанию систем методом снизу вверх. Лет 50 назад вполне доказали, что для написания отдельных классов систем (в том числе компиляторов) этот метод хуже.

                                                      С другой стороны, при развитии существующих систем этом метод вполне пригоден. Особенно он ценен для регрессионного тестирования.

                                                      Мне вообще очень странно, что приходится разжевывать правила, которым меня учили в 17 лет. Правило простое — тестирование собственного кода — это профанация. Самообман. Втирание очков начальству. Да, метод «сначала тесты» несколько уменьшает отрицательные стороны самотестирования. Но далеко не до нуля.

                                                      Таким образом приходим к выводам:

                                                      • TDD полезен там, где он не заменяет группу тестирования. Ну или где группа тестирования отсутствует как класс.
                                                      • TDD применим при итерационной разработке или при написании снизу верх
                                                      • TDD не должен использоваться как основной инструмент оценки качества работы программиста


                                                      Надеюсь, что моя позиция понятна.
                                                      • +1
                                                        Стандарт С++… Он не формализован

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

                                                        Разработка снизу вверх такого сложного ПО — это верх идиотизма. Если этим болеет менеджмент, то какой спрос с инструмента разработки?

                                                        TDD не запрещает разрабатывать сверху вниз.

                                                        Парное программирование реализуемо и для TDD. Без разницы кто пишет тесты, лишь бы они были раньше, чем имплементация.

                                                        TDD не отменяет QA. Равно как и юнит-тесты не отменяют его.
                                                        • –2
                                                          Жалко, что вы не читали формализованные стандарты языков.

                                                          Формализованный стандарт означает, что можно (хотя и сложно) написать программу, которая берет текст на язык и описание языка и проверяет, что программа соответствует стандарту.

                                                          Паскаль и алгол-60 использовали для описания синтаксиса БНФ, а алгол-68 — вВ-грамматику.

                                                          С формализаций семантики хуже, но тут, наверное, может просветить 0xd34df00d.

                                                          Увы, стандарт С++ не формализован ни на 0.001%.

                                                          Архитектор нашего проекта конкретной реализации подготовит свой документ, где все разночтения будут разрешены.

                                                          Не думаю, что вам удастся найти такого архитектора, который сможет назвать хотя бы 80% разночтений. Напоминаю историю про =+ в ранних версиях Си (а я на такой версии даже писал).

                                                          Без разницы кто пишет тесты, лишь бы они были раньше, чем имплементация.

                                                          Если программист имеет возможность прочитать тесты до реализации — это плохо (см. предыдущий пост), а если он видит тесты лишь после реализации — какая разница, когда они написаны?

                                                          TDD не отменяет QA.

                                                          Хорошо. Оставляем QA, отменяем TDD как критерий качества работы программиста. Разрешаем не писать ни единого теста на время разработки архитектуры и данных, то есть отходим от коротких циклов, принятых в TDD.

                                                          Какая тогда польза от TDD? Чем оправдываются его накладные расходы?
                                                          • 0
                                                            Жалко, что вы не читали формализованные стандарты языков.

                                                            Не экспериментируйте с этим, говорят, затягивает.
                                                            Не думаю, что вам удастся найти такого архитектора, который сможет назвать хотя бы 80% разночтений.

                                                            Без комментариев.
                                                            Если программист имеет возможность прочитать тесты до реализации — это плохо

                                                            TDD-тесты должны отражать AC. Программист же может читать ТЗ до реализации, верно? =)
                                                            Какая тогда польза от TDD?

                                                            Очевидно, что это принудительная модуляризация, покрытие юнит-тестами стремится к 100%, что само по себе обеспечивает некоторый уровень качества (еще до QA и DevQA) и документирует код.
                                                            Чем оправдываются его накладные расходы?

                                                            Сокращением времени на дебаг и большим количеством тестов «из коробки».
                                                            • –2
                                                              Программист же может читать ТЗ до реализации, верно? =)

                                                              ТЗ — можно читать, но не изменять. Знание тестов до реализации провоцирует удовлетворять требованиям тестов, а не ТЗ. В итоге получается, что написанием тестов мы изменяем ТЗ.

                                                              Хорошо это или плохо — зависит от качества ТЗ. Если ТЗ писали мы сами — нормально, а вот если ТЗ — это стандарт, то изменения его тихой сапой недопустимы.

                                                              принудительная модуляризация, покрытие юнит-тестами стремится к 100%, что само по себе обеспечивает некоторый уровень качества (еще до QA и DevQA) и документирует код.


                                                              Структуру модулей должен задавать архитектор системы. Как и межмодульные интерфейсы. Отдача её на самотек — допустима лишь в некоторых классах проектов, где не предусматривается время жизни (развития) системы в 20 и более лет.

                                                              Покрытие юнит-тестами — наоборот, полезнее всего для долго развивающихся систем. Оно обеспечивает регрессионное тестирование.

                                                              Документирование кода тестами вместо самодокументирующего кода — тоже имеет свою применимость. Если вместо одного темного места мы имеем два — понятнее не станет.

                                                              Сокращением времени на дебаг и большим количеством тестов «из коробки».

                                                              «Большое» количество не означает качества тестирования. Можно написать 100 тестов на положительных числах и забыть про отрицательные. :-)

                                                              Думаю, что и вам понятно, что тестирование и отладка в отладчике происходит раз в 5-10 быстрее, чем тестирование и отладка в пакетном режиме, то есть вставкой операторов печати и задания условий.

                                                              Так что или в сумме (написание тестов + сидение в отладчике) сокращения нет или оно достигается тем, что тесты покрывают далеко не все ситуации.

                                                              Остается один серьезный плюс — большое количество тестов для регрессионного тестирования. Если в системе частые рефакторинги реализации без рефакторинга архитектуры (частый случай) — это плюс. Если идет рефакторинг архитектуры — то модульные тесты все равно придется переписывать. Если рефакторинга вообще нет — это лишние трудозатраты.

                                                              А вот теперь о вреде. Подумайте, почему у вас программисты такого уровня, что вам приходится вводить меры для принудительной модульности? И насколько это связано с принудительным TDD?

                                                              Хорошие программисты — это коты, они работают там, где им нравится.И в той методологии, которая им нравится. Потому что куда уйти — у них всегда есть.

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

                                                              Напоследок - цитата из Alex Young (кто был в FIDO, тот знает)
                                                              А не задумывались ли уважаемые джентльмены о том, что вот у католиков есть папа pимский, а у вегетаpианцев своего вожака нет, что у военных есть генеpалы, а холостяки каждый сам за себя? И все они не слишком-то стpемятся добиться обpатного. Или может pешим все за всех на пользу них же, глупых?

                                                              • +1
                                                                ТЗ — можно читать, но не изменять.

                                                                TDD не предполагает изменения. Более того, даже итеративные методики разработки, вроде Scrum, не позволяют менять ТЗ посреди спринта. А после каждого спринта должно быть «готовое к употреблению» ПО (или его функционирующая часть).

                                                                Знание тестов до реализации провоцирует удовлетворять требованиям тестов, а не ТЗ

                                                                Тесты пишутся по ТЗ, здесь не должно быть противоречий.

                                                                Структуру модулей должен задавать архитектор системы.

                                                                Согласен.

                                                                «Большое» количество не означает качества тестирования. Можно написать 100 тестов на положительных числах и забыть про отрицательные

                                                                Голову отключать — вообще порочная практика.

                                                                Думаю, что и вам понятно, что тестирование и отладка в отладчике происходит раз в 5-10 быстрее, чем тестирование и отладка в пакетном режиме, то есть вставкой операторов печати и задания условий.

                                                                Мне понятно, что тест найдет очевидную для него ошибку на порядки быстрее, чем отладчик подключится к процессу. Не говоря уже о процессе занимательного поиска места происшествия. Тесты же все равно придется писать, хоть перед кодом, хоть после… И, да, опережая ваши аргументы, логгирование и грамотную систему детектирования ошибок это не отменяет =)

                                                                почему у вас программисты такого уровня, что вам приходится вводить меры для принудительной модульности?

                                                                Бывают новички, бывает легаси. А юнит-тесты стимулируют рефакторинг, это тоже хорошо.

                                                                Хорошие программисты — это коты, они работают там, где им нравится.

                                                                Хорошие программисты дают хороший и поддерживаемый результат. Гениев-джедаев любят стартапы, а ненавистный вами интерпрайз или просто крупные продуктовые компании скорее сделают выбор в пользу нескольких «рабочих лошадок». Пусть это дороже, но bus factor никто не отменял.

                                                                Документирование кода тестами вместо самодокументирующего кода...

                                                                Возможно. Но что мешает писать тесты в поддержку самодокументирующемуся коду?

                                                                ЗЫ Цитата, кстати, интересная, но холиварить о ней я конечно же не буду =)
                                                                • –1
                                                                  Тесты пишутся по ТЗ, здесь не должно быть противоречий.

                                                                  Пожалуйста, перечитайте весь тред. Мы уже говорили про разные интерпретации при чтении ТЗ и про ошибки при интерпретации. Не вижу смысла обсуждать по второму кругу.

                                                                  (foo *)(*bar)(); — это что? Опережающее определение функции или вызов с приведением типа результата? Ответ по ссылкой на стандарт, пожалуйста.

                                                                  TDD не предполагает изменения.

                                                                  Тесты вносят свое понимание в ТЗ. И не факт, что это понимание будет верным. Умышленного изменения ТЗ тесты не вносят, но запросто добавляют вагон неумышленных изменений.

                                                                  20 лет назад, будучи начальником группы тестирования и я такое сотворил. Могу рассказать, если интересно.

                                                                  Сокращением времени на дебаг
                                                                  Мне понятно, что тест найдет очевидную для него ошибку на порядки быстрее, чем отладчик подключится к процессу.

                                                                  Вы про что, простите? Про тестирование или про отладку? Разницу между двумя этим процессами понимаете? А разницу между обнаружением ошибки по тесту и нахождением ошибочного места в коде?

                                                                  Гениев-джедаев любят стартапы, а ненавистный вами интерпрайз или просто крупные продуктовые компании скорее сделают выбор в пользу нескольких «рабочих лошадок».

                                                                  А если чуть грамотней — начальное написание крупного ПО и его сопровождение и развитие — это разные процессы. И как раз на стадии сопровождения и развития — TDD вполне годится, о чем я уже много раз писал.

                                                                  А на стадии написания стандартного компилятора одним человеком — лучше ориентироваться на уже готовый набор тестов.

                                                                  А обсуждали мы именно это — написание компилятора С++ одним человеком. См. тему статьи и начало треда.

                                                                  И да, компиляторы в одиночку пишут в основном джедаи.
                                                                • –1
                                                                  Хорошие программисты — это коты
                                                                  Я никогда не видел, как программисты работают вместе с архитектором, но скорее они должны походить на обезьянок, а не на котов — им сверху спускают «архитектуру», т.е. набор интерфейсов и спецификаций, а они тупо кодят тела функций, никакого творчества.
                                                                  • –1
                                                                    Где бы мне найти таких кодировщиков, которые тупо кодили? Может и есть такие, да вот по квалификацию не подойдут. А те, кто по квалификации годится — тот не кодировщик, а программист. И разбирается в архитектуре не хуже меня.
                                                                    • 0
                                                                      И всё же, разве программисты в вашем мире разработки похожи на независимых котов, которые делают то, что им нравится и так, как им нравится, если всё самое вкусное за них решил архитектор, а им остаётся лишь догрызть несущественные детали.
                                                                      • –1
                                                                        Да даже не просто коты… ну не саблезубые тигры. но хотя бы оцелоты , сервалы, манулы

                                                                        Ну как пример. Взяли специалиста по linux. А он решил, что ему интереснее делать платы. И теперь у нас свое железо. А потом решил, что linux — это лишние ОЗУ и ПЗУ. И итоге от операционки отказались. Ну почти отказались. FREERTOS — это 1% нормальной ОС. Зато — вес платы грамм 150. И она для беспилотников годится.

                                                                        МЯУ!!!
                                                                        • 0
                                                                          И как это соотносится с вашим
                                                                          Структуру модулей должен задавать архитектор системы.
                                                                          Или человек — сам себе архитектор?
                                                                          • 0
                                                                            Мы о чем говорим? О том, как в тех командах, где я работал или о том, как писать компилятор стандартного С++?

                                                                            Там, где я работал и работаю- архитектором может быть каждый. Плюс-минус, но все достаточно грамотные и опытные. И главное — не придумать архитектуру, а убедить коллег в том, что эта архитектура лучше того, что придумали они. Ну или признать, что коллега придумал лучше (а это ещё сложнее).

                                                                            Если речь идет о компиляторе С++, то его надо разрабатывать по модели водопада. ну представьте себе, что вы сделали MVP, который из всех целочисленных типов умеет только int. На очередном спринте добавляете long, потом const, потом unsigned, потом volatile, потом short. Добавляете сразу на всех уровнях — лексический анализатор, синтаксический, кодогенерация. Будет у вас хорошая структура?

                                                                            Вот вам цитата от Евгения Зуева, чья команда писала этот самый компилятор С++
                                                                            У нас в свое время просто не хватило духу проговорить все до конца и определиться полностью по всем принципиальным вопросам. В результате некоторые существенные решения принимались "по умолчанию" тем или иным участником проекта без согласования с другими. Винить в этом, естественно, следует прежде всего старшего участника - автора этой статьи (как самого опытного, а не самого умного!).

                                                                            Так, компилятор сначала выполняет полный семантический анализ всего исходного текста, и только потом генерирует для всей программы результирующий код. Почему такая организация компилятора была выбрана третьим участником, до сих пор непонятно. Какое-то объяснение было тогда дано, но оно тут же выпало из нашей памяти, и вспомнить сейчас невозможно, а попытаться самим объяснить - не получается.

                                                                            Такое решение (удивительно, но принятое без всякого обсуждения) приводит к тому, что компилятор сохраняет полное дерево программы (и, следовательно, вынужден сохранять и семантические таблицы, так как они друг с другом сильно связаны) вплоть до завершения обработки всего исходного текста. Более логичным и экономичным был бы подход, согласно которому для каждой функции выполняется вся обработка, вплоть до генерации кода, после чего в структурах компилятора сохраняется только информация из ее заголовка, необходимая для компиляции вызовов. Исключение достаточно сделать для встраиваемых (inline) функций, да и то не всегда




                                                                            С другой стороны, вы несколько неверно понимаете, что относится к архитектуре (проектированию), а что к реализации (программированию). Поверьте, что в реальности архитектура не мешает творчеству программиста.

                                                                            Если интересно — могу подробней рассказать о делении между архитектурой и программированием.
                                                                            • +1
                                                                              Там, где я работал и работаю- архитектором может быть каждый.
                                                                              Это к замечанию «Структуру модулей должен задавать архитектор системы», которое якобы убережёт программиста от плохого дизайна. Это — не убережёт, если он сам себе архитектор (может, что-то другое убережёт).
                                                                              Если интересно — могу подробней рассказать о делении между архитектурой и программированием
                                                                              Если есть ссылки на материалы, было бы интересно. Сейчас мне не ясно, что мы получаем на выходе от «архитектора». Какой-то документ? Хотелось бы пример реального документа.

                                                                              Интересен вопрос с практической стороны, как часто архитектор вносит правки в разработанную структуру после того, как закончил с архитектурой. Я, например, могу при разработке понять, что мне пришла классная идея, как организовать код, и нафиг всё капитально переписать. Чтобы это понять, нужно некоторое время работать над реализацией. Сразу так не придумаешь, потому что заранее не знаешь, что вылезет.
                                                                              • +1
                                                                                То, что с архитектором может быть каждый, не означает, что на одном проекте несколько архитекторов. Это означает, что качество проекта архитектуры придется доказывать коллегам, которые понимают в архитектуре столько же или больше.

                                                                                Переделка архитектуры — это признак очень низкой квалификации. Или аналитика (не предусмотрели изменения ТЗ) или самого архитектора. Переделка архитектуры — это очень дорогостоящее решение. Обычно проще написать заново, чем кардинально переделывать архитектуру всей системы. Так что ваши системы или малы (до 10 тысяч строк) или ваша цель сделать шедевр, а не выпустить систему в установленный срок.

                                                                                Собственно что такое архитектура системы и архитектура ПО рассказано в вики.

                                                                                Определений много, я дам ещё одно. Представьте, что у вас 100500 программистов и вам нужно как можно быстрее создать систему. Какие части вы им выделите? Какие будут интерфейсы между этими частями? Вот сумма ТЗ для исполнителей — это и есть архитектура.

                                                                                Выделять много частей — нельзя, будут слишком много связей. Как писал Брукс "если 9 беременных женщин собрать вместе, ребенок через месяц не родится". Поэтому делим проект на подсистемы и описываем интерфейсы между подсистемами.

                                                                                Если брать компилятор С++, то я бы выделил такие поддсистемы:
                                                                                • Препроцессор
                                                                                • Таблица символов препроцессора
                                                                                • Подсистема ввода-вывода и кэширования
                                                                                • Лексический анализатор
                                                                                • Синтаксический анализатор
                                                                                • Таблица идентификаторов
                                                                                • Кодогенератор в p-code
                                                                                • Машино-независимый оптимизатор
                                                                                • Машино-зависимый оптимизатор
                                                                                • Вывод в .obj
                                                                                • runtime-библиотека компилятораj


                                                                                Ну это так, на первый взгляд. Ту же работу с темплейтами — возможно стоит выделить в отдельную подсистему.

                                                                                Какие-то документы по архитектуре может и можно показать, но только на мыло. Я всегда работал в небольших командах, а в команде до 10 человек нету особого смысла излишне все документировать.
                                                                                • 0
                                                                                  Переделка архитектуры — это признак очень низкой квалификации
                                                                                  Ага, лучше есть кактус самому и других заставлять, но не признаться, что сделал неправильно и оказался низкоквалифицированным.
                                                                                  Переделка архитектуры — это очень дорогостоящее решение.
                                                                                  Иногда да. Но часто бывают мелкие правки. Как то, объединить две подсистемы в одну (если они используют много общих данных) или наоборот, одну растолстевшую разбить на несколько.
                                                                                  • +1
                                                                                    Переделка архитектуры — всегда дорогостоящее решение. В отличие от мелких изменений (тем более предусмотренных в ходе разработки архитектуры) или переделки того, что архитектурой не является.

                                                                                    Разбиение подсистемы на две части с сохранением интерфейсов с остальными частями — это не переделка. Как и мелкое изменение интерфейсов. Переделка — это изменение принципов работы интерфейсов. ну например, у вас было понятие сессии и контекст влиента хранился на сервере. А тут вы решили перейти на stateless. Вот это — коренная переделка.

                                                                                    Признать в ошибках — можно. В этом нет ничего страшного. Почитайте, какая горка костылей в unix. Но переделывать — себе дороже.

                                                                                    Этим и отличается профессиональный подход от любительского. Любители делают шедевры, а профессионалу нужно хотя бы попытаться уложиться в бюджет.

                                                                                    Как говорится — Ноев ковчег строили любители, профессионалы строили Титаник.
                                                              • 0
                                                                С формализаций семантики хуже

                                                                Ну, там всё плохо, да. Всякие традиционные сиплюсплюсы и прочие питоны описываются через некоторую пародию некоторое подобие операционной семантики, и то на неформальном языке (пусть и достаточно формально звучащем для человеческого уха). Доказывать что-то про свойства программ тяжело.


                                                                Для моего любимого хаскеля есть описание в денотационной семантике, где можно строить всякие теоремки, но там тоже есть свои тонкости (едва ли важные в рамках данной дискуссии, правда).


                                                                Более того, в случае C++ на ум сходу приходит такой вот курьёз: в нём не должно быть вечных циклов, которые ничего не делают, если упрощать, иначе это UB, а в семантически корректной программе UB быть не должно. Поэтому полноценный чекер, если бы он и существовал, должен был бы как минимум доказывать великую теорему Ферма (ну, три-четыре вложенных цикла, чего там) как минимум, как максимум — решать проблему останова.

                                                                • 0

                                                                  А как насчет Idris? В нем есть встроенный доказатель теорем и зависимые типы.

                                                                  • 0

                                                                    Я его пока близко не тыкал, ничего умного по существу сказать не могу.

                                                            • +4
                                                              Таким образом TDD создает ложное впечатление о полноте тестирования.

                                                              TDD никому никогда не обещает полноту тестирования.


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

                                                              Вполне подходит.


                                                              Я писал синтаксический анализатор снизу вверх (потому что формальной спеки на язык нет, начальный набор тестов для TDD — примеры использования), а вот интерпретатор для отладки, кодогенератор и прочие подобные вещи — сверху вниз. И тоже с тестами и всем таким.


                                                              Тут, кстати, для тестирования архитектуры помогает строгая типизация, когда можно сначала просто написать все типы данных и сигнатуры функций, поставив в определения undefined, а потом начать реализовывать сами функции, параллельно описывая для них тесты.

                                                              • –1
                                                                потому что формальной спеки на язык нет

                                                                А вот тут как раз TDD на своем месте. И уже на основании реализации и тестов — пишется стандарт. Как пример — правила расширенной области цикла в фортране, описывающие поведение первых компиляторов.

                                                                Все-таки дискуссия развернулась насчет использования TDD для написания компилятора стандартного С++. А это иная ситуация.
                                                • –2
                                                  Все, вижу у вас в профиле энтерппрайс и веб-дизайн. Ну совсем иная область, в которой я не разбираюсь. Скорее всего там действительно вечные изменения ТЗ и TDD вполне в тему.

                                                  Но мы тут говорили про компилятор стандартного С++. А не про сайты.
                                                • +1
                                                  TDD провоцирует писать качественно структурированный код.
                                                  Каким это образом?! Докажите, плиз.
                                                  Некачественный код — это когда разработчик пишет большие блоки новой логики где проще, например прямо в функции main(). Или пишет большую колбасу кода прямо в событии OnClick какой-то кнопки.

                                                  Если есть требование, что код нужно тестировать, внутри main() как правило уже не попишешь, нужно выносить выносить логику в отдельные классы, чтобы тестовая среда создала эти классы и выполнила в них некоторые методы. Хочешь-не хочешь, придётся тестируемое поведение выносить в отдельный слой, отделять от графического интерфейса и скелета программы, вынося за абстракции.
                                                  • –3
                                                    А! Вы про школьников? Ну для школьников все верно, TDD отличный подход.

                                                    Если вы не пишите логику прямо внутри main только из-за тестов — то да, тогда TDD для вас незаменим.

                                                    я-то не про школьников говорил…
                                                    • 0
                                                      Может вы живёте в идеальном мире, где розовые пони какают бабочками, но мне приходится работать с совершенно разными людьми. Есть и 30-летние «школьники», сваливающие в main тонну кода, и убедить их непросто. Аргумент «потому что тестировать надо» вполне помогает.
                                                      • –2
                                                        я живут в мире реальных вещей. Атомные ледоколы, локомотивы, беспилотники размером с домик, трактора, гендиректора, фирмы с двухбуквенными именами…

                                                        Если вам нравится держать в команде тридцатилетнего школьника… ну значит вам так нравится.

                                                        Для забивания гвоздей, микроскоп, конечно, подходит. А вот для того, чтобы смотреть на луну — совсем не годится. у TDD есть свои области применения, и не надо считать его панацеей.
                                                        • 0
                                                          Значит, везёт вам (пока что). У меня же выбор — или мириться, или учить, или увольняться.
                                                          • +1
                                                            У меня выбора нет — я не умею работать там, где мне неинтересно. Потому уже почти 35 лет работаю в разных очень интересных мне командах.
                                                            • 0

                                                              Интересно, что в нашей прошлой дискуссии о людях, которые уходят туда, где им интересно, вы отзывались скорее с негативными коннотациями. Либо я вас просто неправильно понял.

                                                              • –1
                                                                Вы не путайте уход успешного программиста туда, где ему интересно и уход человека, который за год не довел ни одного проекта до работы с реальными данными. На тестах у него все летало, а как реальные данные — падало.

                                                                Кстати, там, куда он ушел, ситуация повторилась. Теперь опять попросился к нам, но внештатником. И с договором, что основные деньги по результату на реальных данных.

                                                                Может он и вырос, не знаю.

                                                                Год назад у него весь пар в свисток уходил. То есть в написание структуры классов на все случаи жизни. Шик, блеск, гам — а включаешь, не работает.
                      • +2

                        Отличная история, даже очень мотивирует делать свои велосипеды! Спасибо.

                        • +1
                          А что скажите на счёт rust, насколько он перспективен в качестве замены C++ в будущем?
                          Если почитать Страуструпа, то он сам пишет что если бы не требование обратной совместимости, то он бы разработал С++ другим.
                          • 0
                            Жалко, что не получилось совместимости=(
                            Да-да, я встречал живой C-код «int class;»
                            А диграфы «or», «and» меня совсем огорчают.
                        • 0
                          Круто.

                          Замечание: нигде в коде не указана лицензия (может, я просто не нашёл).
                          • 0
                            обычно такое под wtfpl подпадает
                            • +1
                              По обычным законам, всё с точностью до наоборот — если автор кода (и владелец всех прав на него) прямым текстом не дал другим людям какие-либо права на него (т.е. не приложил лицензию), то считается, что у других людей на этот код нет никаких прав (т.е. такой код считается закрытым).
                          • +1
                            Да, трехтомник Кнута должен иметь в своем арсенале любой уважающий себя разработчик, не зависимо от языка программирования, который он использует. Соглашусь с автором, разработка своего велосипеда, хоть и не такого масштабного (например, написание своего std::map) повышает грамотность разработчика и способствует лучшему пониманию мироустройства.
                            • +1
                              Узнаю брата Колю. Я писал в молодости компилятор на Форте для языка собственного сочинения, сделав в начале что-то типа собственного yacc.
                              • –2
                                Компилятор какого языка? Насколько я помню, компилятор форта написан на самом форте и занимал (как это и принято на форте) одну строку кода.
                              • 0
                                Круто! Помню в детстве программировал на тетрадном листочке на бейсике :D Прошли годы — сейчас допиливаю (just for fun) свой компилятор для C-подобного языка — заинтересовался этой темой лет пять назад с тех пор в свободное время занимаюсь этой безумно интересной темой. Куча времени потрачено, но опыт — бесценен :)
                                • –1
                                  > Это бесценные знания, база, которая будет с Вами на протяжении всей карьеры разработчика.

                                  Согласен, профессиональная база знаний у вас серьезная. Интересно, что вы думаете по поводу структуры индексных cdx-файлов для dbf-файлов базы данных Visual FoxPro? Это, судя по всему, B+-деревья с двойными списками на всех уровнях (судя по хилой картинке от M$). Смогли бы реализовать API для работы с ними? Похоже на то, что там реализованы, в т.ч., динамические массивы со свойствами связных списков. Что бы вы предложили для реализация этих самых «динамических массивов со свойствами связных списков»?

                                  Еще вопрос, который меня интересует чисто с теоретической точки зрения. Известно, что байт-код 1С8х относительно легко декомпилировать. А вы сможете, хотя бы для себя (это не надо публиковать) декомпилировать обфусцированный, скажем от Wise Advise или от Авы (Awa) байт-код? Здесь уже нужно изобретать нетривиальные алгоритмы по топологической сортировке циклических(!) плоских графов. Еще точнее, там нужны немножко другие малоизвестные алгоритмы, которые без хорошего знания математики вряд ли придут в голову.

                                  Никаких ответов, содержащих программный код, не требуется, только общие идеи.
                                  • 0
                                    >> декомпилировать обфусцированный, скажем от Wise Advise или от Авы (Awa) байт-код? Здесь уже нужно изобретать нетривиальные алгоритмы [...]

                                    Все уже изобретено. Проблема декомпиляции байткода (в более общем виде: архитектур с разделением кода и данных) решена около 10 лет назад, алгоритмы свободно доступны.
                                    • 0
                                      У меня тоже было написано выше: «Известно, что байт-код 1С8х относительно легко декомпилировать». Т.е., по смыслу, речь шла не о декомпиляции, а о деобфускации байт кода. Другими словами, если байт код 1С8х не обфусцирован, то легко декомпилируется (имеются обработки в открытом доступе от Awa), но если обфусцирован, то декомпилировать его весьма нетривиально. Тот же декомпилятор Авы обфусцирован, поэтому сам себя не компилирует, да и код 1С8х обфусцированный Wise Adbvise тоже не берет.
                                      • 0
                                        Обфускация здесь не имеет значения, проблема решена в том числе и для irreducible CFG.
                                        • 0
                                          > Обфускация здесь не имеет значения, проблема решена в том числе и для irreducible CFG.

                                          Ну, что ж, это ключевое замечание. Как там, у Мольера, Журден очень удивился, когда узнал, что всю жизнь говорил прозой. У меня этот «irreducible CFG» назывался «ориентированный циклический плоский граф (DCG), с минимальным количеством левосторонних связей». Соответственно свой алгоритм по его получению я назвал «Алгоритм минимизации левосторонних связей ориентированного плоского графа (циклического, в общем случае)». Может быть, вы подскажите, как называется соответствующий алгоритм в вашем случае?
                                          • 0
                                            >> У меня этот «irreducible CFG» назывался «ориентированный циклический плоский граф (DCG), с минимальным количеством левосторонних связей».

                                            Хм… сомневаюсь, что мы говорим об одном и том же, но все может быть.

                                            Где-то году в 2008 я написал решение на примере Явы — декомпилятор Fernflower, теперь он часть IntelliJ IDEA:
                                            https://github.com/JetBrains/intellij-community/tree/5a805eea3e550c3605f2f01b6857858e170b7954/plugins/java-decompiler
                                            Оформить в статью руки так и не дошли, а поскольку код никто не читает :) то в 2015 его благополучно переоткрыли товарищи из Бонна:
                                            https://net.cs.uni-bonn.de/fileadmin/ag/martini/Staff/yakdan/dream_ndss2015.pdf
                                            • 0
                                              > Где-то году в 2008 я написал решение на примере Явы — декомпилятор Fernflower

                                              Круто! Я знаком с вашим декомилятором, скачал где-то из просторов Интернет и даже немного пользовался им. Впечатления очень хорошие, правда, к самой Яве я быстро охладел, все-таки с++ с фреймворками мне импонирует немного больше.

                                              > Хм… сомневаюсь, что мы говорим об одном и том же, но все может быть.

                                              Речь идет об абстрактных структурах. Любой программный код с точки зрения связей это ориентированный граф, узлы которого (линейные операторы либо даже произвольные блоки кода) можно упорядочить на прямой (тот же ассемблерный код), со связями перехода. Если представить себе эти связи резиновыми, а узлы фиксированными (по принципу «чёрного ящика»), то, очевидно, код, без нарушения логики работы можно упорядочивать на прямой многими разными способами. При этом обфусцированный, а частности, сильно запутанный или перепутанный относительно связей код, будет содержать огромное количество как левых, так и правых связей (переходов). В том числе он может быть сильно перемешан с кодом, который никогда не получит управления. В таком случае мы имеем несвязный граф из которых нас интересует только та связная часть графа, которая получает управление.

                                              Исходя из сказанного, все же есть разница между декомпиляцией и деобфускацией. Например, байт код может быть не декомпилирован, но и не обфусцирован. А может быть не декомпилирован, но обфусцирован. Пока нас даже не интересует декомпиляция (скажем ассемблера в Си, как в IdaPro). Независимо от того, можем мы декомпилировать ассемблер в Си или не можем, важно сначала деобфусцировать запутанный код, а декомпилировать имеет смысл только регулярный, неулучшаемый уже в смысле распутывания либо максимального упорядочивания код.

                                              Так вот это неулучшаемое представление кода в виде максимально упорядоченного линейного связного графа с левыми и правыми связями и есть тот самый «irreducible CFG», судя по Википедии. Понятно, что при этом количество левых связей будет минимально возможным (при направлении потока управления слева направо). Например, бесконечный оператор, ссылающийся сам на себя, может быть представлен в виде двух линейных операторов, с одной левой и одной правой связью и одной входящей связью на любой из этих операторов. Очевидно, что это уже не улучшаемое представление, в данном случае, типичный пример «irreducible CFG».

                                              В случае обфусцированного кода, наша задача получить его сначала в виде «irreducible CFG». Как это сделать это уже другой вопрос. Еще раз подчеркну, пока о декомпиляции речь вообще не идет.

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

                                              У меня есть работающий алгоритм по получению неулучшаемого представления кода в виде упорядоченного связного линейного графа. В частности, байт-код 1С8х, обфусцированный Wise Advise он неплохо деобфусцировывает, а, следовательно, и позволят декомпилировать. С деобфускацией байт-кода от Awa сложностей немного больше. Но это не алгоритмические сложности, а технические, Уж очень изощренные методы применяет этот Ава. Но чисто декомпиляция Awa кода меня интересует абстрактно, просто как хорошее тестирование моего алгоритма. Главное, для меня найти математическое доказательство, что алгоритм работает всегда, а не только в данных частных случаях. Тогда можно будет и статью наваять, аналогичную как у «боннских товарищей» :).
                                              • 0
                                                Кстати, если вы попытаетесь декомпилировать несвязный граф, то может выйти облом, поскольку байт код не получающий управление может быть, просто абсолютно бессмысленным, но при этом выполняющимся (дефектные ветви ведь не получают управления, а на уровне исполнения байт-кода его синтаксических проверок уже может не предусматриваться, как в том же 1С8х).
                                                • 0
                                                  >> Так вот это неулучшаемое представление кода в виде максимально упорядоченного линейного связного графа с левыми и правыми связями и есть тот самый «irreducible CFG»

                                                  Нет, это совершенно другой термин. Irreducible (неприводимый?) граф — это такой граф выполнения, который не может быть выражен средствами структурного программирования, т.е. без использования goto. Посмотрите статьи на эту тему.

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

                                                  Это не обфускация, а обыкновенное перемешивание блоков. Оно не имеет никакого значения, так как мы работаем с графом, а не с линейным порядком. Хотя примитивные декомпиляторы (типа jad'a) могут и споткнуться.
                                                  • 0
                                                    >> Так вот это неулучшаемое представление кода в виде максимально упорядоченного линейного связного графа с левыми и правыми связями и есть тот самый «irreducible CFG»

                                                    > Нет, это совершенно другой термин. Irreducible (неприводимый?) граф — это такой граф выполнения, который не может быть выражен средствами структурного программирования, т.е. без использования goto. Посмотрите статьи на эту тему.

                                                    Не понимаю, где вы здесь увидели противоречие? Смотрим перевод «irreducible» в Multilex-2.0:

                                                    1. Не поддающийся упрощению, улучшению;
                                                    2. (мат.) Несократимый; неприводимый, несводимый;
                                                    3. Минимальный;
                                                    4. Непревратимый (в иное состояние);
                                                    5. (мед.) Невправимый;
                                                    6. (книжн.) Непреодолимый.

                                                    Это все варианты. В общем, то они говорят об одном и том же. Далее, смотрим определение cfg в википедии: https://en.wikipedia.org/wiki/Control_flow_graph

                                                    «A control flow graph (CFG) in computer science is a representation, using graph notation, of all paths that might be traversed through a program during its execution.» В переводе с буржуйского: «Граф потока управления (ГПУ) в информатике – это представление, с помощью графической нотации, всех путей, которые могут быть пройдены в рамках программы в ходе ее выполнения.»

                                                    Отсюда следует, что «Irreducible CFG» это неупрощаемый (неулучшаемый, неприводимый, несводимый, минимальный, канонический и т.п.) граф потока управления программного кода.

                                                    Кстати, причем здесь оператор GoTo? Направленный граф это просто кружочки со стрелочками. Ему может соответствовать код как с goto, так без него. Для графа это безразлично. В указанной вами статье «боннских товарищей» есть хороший пример cfg на рис. 3 (по сути это «Irreducible CFG», поскольку упростить этот граф уже нельзя). Смысл статьи в том, что авторы декомпилируют этот низкоуровневый «код» в псевдокод высокого уровня с помощью программы DREAM (рис. 5.1), а рядом (рис. 5.2) приводят псевдокод получаемый хорошо известным Hex-Rays от IdaPro Ильфака Гильфанова. Мол, вся фишка в том, что у Hex-Rays псевдокод использует оператор goto, а псевдокод от DREAM, goto не использует.

                                                    Но все дело в том, что я веду речь на уровне самого «графа потока управления». Да, когда этот cfg стал «иридьюсаблым», то тогда уже совершенно ультрафиолетово какой декомпилятор к нему применить. Если речь идет о Яве, то можно использовать ваш Fernflower, если о байт-коде 1С8х, то декомпилятор Авы, например, если об ассемблере, то Hex-Rays, либо DREAM (но когда он станет доступным, хотя бы в триале, это большой вопрос).

                                                    У меня задача другая, дан обфусцированный «неиридьюсаблый» граф потока управления. Мне нужно из него получить «иридьюсаблый» граф. Говоря по-русски, обфусцированный (перепутанный, намеренно усложненный) граф превратить в граф наиболее простого вида, далее уже неулучшаемого, несводимого, неприводимого и т.п. И только затем я буду применять к нему декомпилятор. В случае байткода 1С можно использовать даже собственный декомпилятор, построенный на базе идей Форт-машины.

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

                                                    > Это не обфускация, а обыкновенное перемешивание блоков. Оно не имеет никакого значения, так как мы работаем с графом, а не с линейным порядком. Хотя примитивные декомпиляторы (типа jad'a) могут и споткнуться.

                                                    Судя по всему, вы здесь проблемы не видите. Я не знаю, что из себя представляет байт код Явы, но в 1С нормально декомпилировать обфусцированный байт код пока никому явно не удается (по крайней мере, на эту тему нет публикаций), хотя с декомпиляцией необфусцированного байт кода проблем не возникает. Причем, неважно даже будет декомпилированный код с операторами гото или без них. Иногда гото позволяет упростить ситуацию, скажем, прямой выход из глубокого многомерного цикла, изредка я им даже сам пользуюсь, если только при этом логика программирования не усложняется.

                                                    Тот же байт-код 1С, даже необфусцированный, оператор гото (условный и безусловный) использует сплошь и рядом. Удобно, при собственной декомпиляции, на эту тему сильно не заморачиваться. Главное, чтобы декомпилированный код был работоспособным. Если цель состоит в том, чтобы подрегулировать что-либо только в одном месте, то полученного результата вполне достаточно. Если же нужно полное восстановление прикладного кода 1С, то просто внимательно читаем, осмысливаем, вычищаем и документируем полученный код. Эта задача относительно трудоемкая, но обозримая. За день можно таким образом обработать до 100 КБ кода высокого уровня.

                                                    Насчет линейного порядка. Узлы любого плоского графа (а может быть даже и многомерного, не проверял) можно уложить на прямую линию, полностью сохраняя топологию направленных связей. Относительно узлов любой граф линеен. Он нелинеен (плоский) относительно своих связей. Это достаточно очевидно для cfg (гпу). Тот же ассемблерный листинг бинарного кода, это линейно упорядоченный набор операторов. Другое дело, что множество переходов (связей) уже будет нелинейным. И то же самое для любого программного кода.

                                                    А обфускация это не только перепутывание и перемешивание. Просто распутывание кода, одна из самых сложных операций. Такого же уровня сложности может быть шифрование данных и кода, полиморфизм (динамическое изменение кода) и любые другие методы усложняющий анализ кода человеком, коих может быть не так уж и мало. А вот обфусцирование на уровне ЯВУ (языков высокого уровня) более простая задача, не требующая привлечения особо сложных алгоритмов.
                                      • +4
                                        А я, кажется, как-то с тобой бухал :) (в славном городе на букву «Л»).
                                        Пожалуй это все, что я знаю про компиляторы :)
                                        • 0

                                          А нельзя заменять все символы директив препроцессора на пробелы для сохранения корректных номеров строчек обычного C++ кода? Это может пригодиться для правильного репорта ошибок. Также смотри пример из моей статьи: препроцессор в Objective-C.

                                          • +3
                                            Это не поможет, потому что в си макрос может раскрыться в несколько строк, да ещё можно подключать код из других файлов через #include
                                            Например, в исходнике было 100 строк, а в выходе препроцессора — 250. Как ни пытайся, номера не сохранятся.

                                            Стандартное решение этой проблемы — директива #line

                                            Было на входе:
                                            #include "test.h"
                                            int x;


                                            стало на выходе
                                            #line 1 "test.h"
                                            int test_function1();
                                            int test_function2();
                                            #line 2 "main.cpp"
                                            int x;

                                            Компилятор смотрит на эти директивы #line и в отладочной информации связывает код с исходными строками и исходными файлами до препроцессинга.
                                            • 0

                                              Хм, спасибо за разъяснение, не задумывался о таком. Однако же можно составить ассоциативную коллекцию "символ-позиция" до этапа препроцессинга, чтобы корректно потом сопоставлять позиции хотя бы для обычного кода без макросов? Хотя, возможно, такая сложность избыточна.

                                          • +1
                                            Для меня Кнут был недостижимой мечтой, отсутствовал в библиотеке, а один том в магазине стоил полторы зарплаты! Стоит ли говорить, о степени велосипедостроения в то время? Ну скажем так, я был удивлен, когда узнал, что бинароное дерево поиска это классика, а не мое личное изобретение :)
                                            • 0
                                              Крутая статья, читается на одном дыхании!

                                              Мне сразу же вспомнился другой рассказ о создании компилятора C++. До сих пор перечитываю эту статью, очень мотивирует.
                                              • 0
                                                При много благодарен за рассказ. 32 листа поглотил на одном дыхании.
                                              • +1
                                                Спасибо за мотивирующую историю. К сожалению, сейчас все чаще встречаю «программистов», знающих 1-2 фреймворка, но не имеющих базовых знаний. Теперь будет, что рекомендовать таким к прочтению.
                                                • 0
                                                  Браво!
                                                  После вашего прочтения захотел перечитать читать книги, которые отложил на полку до лучших времен.

                                                  • 0
                                                    Желание программировать у меня появилась сравнительно недавно. Как всегда стал вопрос, с чего начать. Первое, что посоветовали — С++. В 2007 году, мною была приобретена книга П. Франка «С++ учебный курс», издательства Питер, 2006 года. Но, книга была переводом, сделанным в 2004 году, а в самом материале велась полемика, что лучше Windows 3.1 или Windows 95. Я понял, что я влип. Но посчитав, что на само изучения основ языка это особо повлиять не должно, и как говориться, деньги потрачены, решил начать изучение. Большего издевательства над своим мозгом я не производил, с момента использования справочников, по проектированию тяговых (силовых) подстанций, при написании дипломной работы. В общем все усилия сошли на нет благодаря нудности изложения, лени (хотя ее скорее надо было поставить в начало списка) и нежелании трать время на данный. как мне казалось, треш.Потом в нашу жизнь ворвался андроид. Желание попытать счастья в кодинге проснулось с новой силой. В виду отсутствия по ряду причин, возможности приобретения книги, была сделана распечатка ее копии и прошивка в два тома. Сам процесс был не простым но затягивающим. В книге оговаривалась, при чем безоговорочно, среда разработки Eclipse. Установил я сие чудо, начинаю копать глубже по страницам книги, и натыкаюсь на искрометную фразу: «Для нормального программирования в Eclipse вам необходимо хорошие знания языка программирования Java...» И это в книге о программировании приложений для андроид с нуля. В общем опять шляпа. Немного отлегло от сердца, думаю вернусь к начатому с андроидом. Каково мое было удивление, когда я узнаю, что Eclipse не поддерживает разработку выше 5-й версии андроида и то при условии установки новой версии Mars. С Майкрософтофским софтом таже печалька. Их визуальный экспресс уже поменял бесчисленное количество переизданий, как минимум одно переиздание в два года. «Hello World» в них занимает хороших пол листа кода-мусора, который компилятор впихивает его туда, как принято у мягкотелых, на всякий случай. К чему все это я, а стоит принять еще одну попытку, и с чего стоит начать...?
                                                    • 0
                                                      Потом в нашу жизнь ворвался андроид… В книге оговаривалась, при чем безоговорочно, среда разработки Eclipse… «Для нормального программирования в Eclipse вам необходимо хорошие знания языка программирования Java...» И это в книге о программировании приложений для андроид с нуля.

                                                      Лул, это случайно не "Android. Разработка приложений для чайников"? У меня начиналось все точно также, правда под Android я так до сих пор ничего и не написал...

                                                    • 0
                                                      Если вы сделали выводы из статьи выше, то вам надо изучать «базу», т.е. ту самую яву. Eclipse там или нет — никакой роли для начала не играет. Литература и куча всяких курсов на любом языке (хотя я бы сильно рекомендовал английский) легко гуглится. Через месяца три занятий по базовой яве можно пробовать андроид.
                                                      • 0
                                                        Я, на подсознательном уровне, если так можно выразиться, тяготею к С++. К Java, я пока не дорос. Объясню почему. У меня в голове есть идеи создания небольших утилит, которые в теории, помогут очень облегчить мою рутинную жизнь на работе. При чем, это такие аспекты, где виртуальные машины и прочие прелести жабы не уместны. Рискнул опять начать все сначала. Восстановил учетку на мелкософтовском комьюнити, заново переустановил экспрес визуалку и радую по вечерам себя лицезрением уроков Маркова по С++. Как то так. :)
                                                        • +1
                                                          для небольших утилит уместнее что-то типа питона использовать, а есть у явы ВМ или нет — не столь важно, главное, что новичку там сложнее прострелить себе ногу :) плюс много готовых библиотек на все случаи жизни и хорошая помощь от IDE
                                                          • 0
                                                            Пример из реальной ситуации. На win-машине нужно было менять сетевые настройки, выкрутился скриптами и батниками (netsh). Для удаленного управления настройками shdsl Zyxel Prestige по телнету(минимум загрузки канала) никакие скрипты и прочие костыли не помогали. Начал копать глубже и нашел решение — TSE (Telnet Script Executor) Любимкова Дмитрия. Чистокровная С++ программа, которая намного облегчила мне жизнь. И вот с того времени меня не покидает идея двигаться в этом направлении(кодинга) дальше по принципу от малого к великому. Может мне просто не попадались в живую хорошие легковесные решения на джаве, которыми я бы смог воспользоваться на практике. Просто из того что мне приходилось видеть вызывало только отвращение не только к жабе, но и к кодингу в принципе. :)
                                                            • 0
                                                              Если сидите чисто на форточках, то почему бы вам не попробовать C#. [dot]Net из коробки умеет достаточно много. Для быстрых набросков самое оно.
                                                              Если уж хочется именно C++, то можно попробовать С++/CLI (с моей точки зрения это изврат).
                                                              А еще лучше (IMHO) начать со связки Qt + QtCreator.

                                                              Но самое важное понимать, как достичь цели при помощи правильно подобранного инструмента.
                                                              • 0
                                                                Форточки — 90% моего соприкосновения с ПК и 10% — это mikrotik.Все таки, пока хочу базу начать с визуального, а там, если «полетит — не полетит» то можно будет найти какой то фреймворк под себя. Связку Qt + QtCreator взял на заметку.
                                                      • –1
                                                        Пробуйте Delphi, как бы не хаяли, но это одна из немногих сред, не обросших говном в виде длинных мастеров создания проекта, кучи сгенерированного имиже кода и т.д.
                                                        Запустил, нажал пару кнопок, и можешь творить :)
                                                        • 0
                                                          Вы не поверите, но к Delphi я пытался приобщится с 2005 года. Приобрел «Delphi 7 учебный курс» С. Бобровского, издательства Питер и в 2006 «Delphi базы данных и приложения» С.П. Кандзюба+ В.Н. Громов, издательства Диасофт. Сначала, нашел ломаную дельфийскую 7-ку, сделал даже слепок. Запустил, чего то там даже начал «Приветы миру» передавать. Потом смена профессии и приоритетов. Ушло все на второй план. Через время «вернувшись в тему», увидел, что дружба Delphi с майкрософтом осталась на уровне ХР. Тема осталась без продвижения, из-за очевидности тупика. Выход борландовских Х-ов уже ничего кардинально не менял. Может я и не прав, в своих суждениях. =)
                                                          • –1
                                                            Современные версии отлично дружат с Windows 10, и имеют поддержку многих новомодных «фишек».
                                                            В целом вам необходимо научиться программировать, и не важно есть что-то модное или нет в Delphi, главное тут то, что порог вхождения в разы ниже.
                                                            • 0
                                                              Волей случая, пришлось кое что делать из тестовых заданий, для дочери знакомых, в Lazarus. Не скажу, что меня все устроило, но до воли сносно. А главное, что софт условно бесплатный, ничего крякать/хакать/мутить не нужно. Я так думаю, что мои книги, по крайней мере, в теории должны подойти.
                                                      • 0
                                                        В программировании я недавно. Почувствовал, как мои «знания» утонули в океане уважаемого автора.
                                                        Респект.
                                                        • +1
                                                          Автор — фанатик в хорошем смысле этого слова. Мне вот тоже высшая математика плохо давалась. Пока на пришлось писать программу для расчета ректификационной колонны в маткаде (там была возможность делать встроенные программки). Потом уже дело и до С++ дошло…
                                                          З.Ы. Мне одному кажется, что код на титульной иллюстрации не скомпилируется?
                                                        • +2
                                                          А главный вопрос готовности компилятора, может ли он сам себя собрать?
                                                          • –1
                                                            Сейчас, проводя собеседования с разработчиками, мне еще не встретился человек, который смог бы написать реализацию бинарного поиска или быстрой сортировки на листочке. Жаль.


                                                            Даже странно, я таких немало повидал. Тем более, что вопросы довольно штатные для компаний вроде Yandex/Google/Facebook.
                                                            • 0
                                                              Написание своего велосипеда, даже если на это потрачено 2 с лишним года, кормит меня до сих пор. Это бесценные знания, база, которая будет с Вами на протяжении всей карьеры разработчика. Будут меняться технологии, фреймворки, выходить новые языки — но фундамент в них будет заложен из прошлого. И на их понимание и освоение уйдет совсем немного времени.


                                                              О том как писались ОС 30 лет назад:

                                                              30-летие учебного пособия ОС Minix
                                                              • +1

                                                                Круто — что еще сказать, может на пенсию выйду и попробую повторить сей подвиг ;), автору репспект !

                                                                • –2
                                                                  А чем ваш компилятор лучше остальных? Какие преимущества у него есть?
                                                                  • 0

                                                                    Круто. Спасибо за статью.
                                                                    Для тех, кто хочет посмотреть/подебажить всё вместе — держите CMakeLists.txt:


                                                                    CMakeLists.txt
                                                                    cmake_minimum_required(VERSION 3.4)
                                                                    
                                                                    project(nrcpp)
                                                                    
                                                                    # Remove unneeded configurations
                                                                    set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
                                                                    
                                                                    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267 /wd4244 /wd4996 /wd4018 /wd4477")
                                                                    
                                                                    file(GLOB kpp_src "KPP_1.1/*.cpp")
                                                                    file(GLOB kpp_hds "KPP_1.1/*.h")
                                                                    set(kpp_other_files 
                                                                        KPP_1.1/in.txt
                                                                        KPP_1.1/in.txt.comment
                                                                        KPP_1.1/in.txt.slash
                                                                        KPP_1.1/in.txt.trig
                                                                        KPP_1.1/limits.h.comment
                                                                        KPP_1.1/limits.h.slash
                                                                        KPP_1.1/limits.h.trig
                                                                        KPP_1.1/out.txt)
                                                                    
                                                                    add_library(nrcpp_lib nrcpp/LIB/NRC.H nrcpp/LIB/NRC.CPP)
                                                                    target_include_directories(nrcpp_lib PUBLIC nrcpp/LIB)
                                                                    
                                                                    add_executable(KPP ${kpp_src} ${kpp_hds} ${kpp_other_files})
                                                                    
                                                                    file(GLOB nrcpp_src "nrcpp/*.cpp")
                                                                    file(GLOB nrcpp_hds "nrcpp/*.h")
                                                                    set(nrcpp_other_files 
                                                                        nrcpp/in.txt
                                                                        nrcpp/out.txt)
                                                                    
                                                                    add_executable(nrcpp ${nrcpp_src} ${nrcpp_hds} ${nrcpp_other_files})
                                                                    target_link_libraries(nrcpp nrcpp_lib)

                                                                    я собирал как-то так:


                                                                    mkdir build && ^
                                                                    cd build && ^
                                                                    cmake -G "Visual Studio 15 2017 Win64" .. && ^
                                                                    cmake --build . --config Debug
                                                                    • +1
                                                                      А я писал только компилятор слегка кострированного С, но действительно, фундамент заложился очень сильный :) Написать же компилятор С++, это просто респект!
                                                                      • +1
                                                                        Интересная статья, спасибо за возможность посмотреть исходники. Жалко, что много комментариев не читаемы в Гите.
                                                                        Местами в вашей статье вижу снобизм, но вы в своём коде смешиваете string-классы и «строки». Я удивлён, что не встретил хэш-функций в таком проекте. Ваша реализация isdigit8 и ей подобных неэффективна, использование register тоже удивило Хотя это мелочи, так же как и BSP. :-)
                                                                        В 2000 году я закончил свой транслятор, защитился и убрал свой «велосипед» в долгий ящик. У меня не было полноценного генератора ассемблерного/машинного кода. Я думал, что увижу у вас… Но не нашёл.
                                                                        Меня в 1993 году вдохновила книга Дьюхарст С., Старк К. «Программирование на С++». В формате ДежаВю http://www.nehudlit.ru/books/detail1193725.html можно полистать. На 139 и 141 страницах идеальная (на мой взгляд) реализация для парсера в виде дерева, к которой можно прикрутить генератор кода.
                                                                        • 0

                                                                          За 15 лет можно уже написать ещё один или три велосипеда на на свой велосипед...

                                                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.