Содержание

Как написать простейший компилятор

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

Стандартный компилятор осуществляет следующие шаги:

  • Парсинг: исходный текст конвертируется в абстрактное синтаксическое дерево (Abstract Syntax Tree, AST).
  • Разрешение зависимостей с другими модулями (С откладывает этот этап на шаг линковки).
  • Семантическая валидация: исключение синтаксически корректных, но бессмысленных выражений, например, повторного объявления переменных.
  • Эквивалентные преобразования и высокоуровневая оптимизация: AST преобразуется для осуществления более эффективных вычислений при той же семантике.
  • Генерация кода: AST трансформируется в линейный код низкого уровня с переходами, распределением регистров и тому подобным.
  • Локальная оптимизация: низкоуровневый код проверяется на простые локальные недостатки.

В большинстве современных компиляторов (вроде gcc и clang) последние два пункта повторяются еще раз. Для начальной генерации кода они используют не совсем низкоуровневый, но платформонезависимый язык. Потом этот промежуточный код переводится в зависящий от архитектуры (x86, ARM и так далее).

После этого объектный код готов к линковке. Большая часть нативных компиляторов автоматически вызывает линковщик, создающий исполняемый код, но это еще не компиляция. В языках вроде Java или C# линковка может быть полностью динамической и выполняться в виртуальной машине в момент загрузки.

Компилятор должен быть:

  • работающим
  • красивым
  • эффективным

Эта классическая последовательность применима ко всей сфере разработки ПО. Сконцентрируйтесь на первом пункте. Сделайте простейшую вещь и заставьте ее работать.

Прочтите книгу «Компиляторы: принципы, технологии и инструменты». Эта бессмертная классика до сегодняшнего дня не потеряла актуальности. «Дизайн современных компиляторов» — также стоящая вещь.

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

Убедитесь, что вам комфортно работать с графами, особенно с деревьями. Это основа построения программ на логическом уровне.

Хорошо определите свой язык

Вы можете использовать любую нотацию, но будьте уверены, что имеете полное и последовательное описание языка. Оно включает в себя как синтаксис, так и семантику.

Используйте свой любимый язык

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

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

Приготовьтесь к написанию множества тестов

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

Регулярно прогоняйте все тесты. Чините некорректные тесты. Будет очень обидно остаться у разбитого корыта с плохо определенным языком, который не способен принять валидный код.

Сделайте хороший парсер

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

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

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

Напишите семантический валидатор

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

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

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

Генерируйте код

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

Забудьте об эффективности и сосредоточьтесь только на правильности.

Настройте платформо-независимую низкоуровневую виртуальную машину

Вероятнее всего, вас не очень интересуют низкоуровневые аспекты, если только вы не страстный поклонник всего, что связано с архитектурой.

Варианты для вас:

  • LLVM: позволяет эффективно генерировать машинный код, чаще всего для х86 и ARM.
  • CLR: ориентирована на .NET.
  • JVM: нацелена на мир Java, мультиплатформенна.

Забудьте об оптимизации

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

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

6 книг по компиляторам

Книги по программированию: как читать и что именно

Пошаговое руководство. Компиляция собственной программы на языке C++ из командной строки

  • Чтение занимает 4 мин

В этой статье

Visual Studio включает в себя командную строку C и компилятор C++. Его можно использовать для создания всех элементов — от базовых консольных приложений до приложений универсальной платформы Windows, классических приложений, драйверов устройств и компонентов .NET.

В этом пошаговом руководстве приводятся инструкции по созданию программы на языке C++ в стиле «Hello, Wolrd» в текстовом редакторе с последующей компиляцией из командной строки. Если вы хотите попробовать интегрированную среду разработки Visual Studio вместо командной строки, см. статью Пошаговое руководство. Работа с проектами и решениями (C++) или Использование интегрированной среды разработки Visual Studio для разработки приложений для настольных систем на языке C++.

В этом пошаговом руководстве вместо ввода показанного кода можно использовать собственную программу на языке C++. Также можно использовать пример кода C++ из другой статьи справки.

Предварительные требования

Для выполнения этого пошагового руководства необходимо установить Visual Studio и дополнительную рабочую нагрузку Разработка настольных приложений на C++

или Build Tools командной строки для Visual Studio.

Visual Studio — интегрированная среда разработки (IDE). Она поддерживает полнофункциональный редактор, диспетчеры ресурсов, отладчики и компиляторы для многих языков и платформ. Доступные версии включают бесплатный выпуск Visual Studio Community Edition, и все они могут поддерживать разработку на C и C++. Сведения о скачивании и установке Visual Studio см. в статье Установка поддержки C++ в Visual Studio.

Build Tools для Visual Studio устанавливают только средства, библиотеки и компиляторы командной строки, необходимые для сборки программ C и C++. Это идеальный вариант для создания заданий и упражнений, а установка выполняется относительно быстро. Чтобы установить только средства командной строки, найдите Build Tools для Visual Studio на странице загрузки Visual Studio.

Прежде чем можно будет выполнить сборку программ C или C++ в командной строке, убедитесь, что эти средства установлены и к ним можно получить доступ из командной строки. Visual C++ имеет сложные требования к среде командной строки для поиска используемых средств, заголовков и библиотек. Visual C++ нельзя использовать в простом окне командной строки без предварительной подготовки. К счастью, Visual C++ устанавливает ярлыки для запуска командной строки разработчика, для которой настроена среда для сборок из командной строки. К сожалению, имена ярлыков командной строки разработчика и места их расположения отличаются практически во всех версиях Visual C++ и в различных версиях Windows. Первая задача пошагового руководства — найти нужную командную строку.

Примечание

Ярлык командной строки разработчика автоматически задает правильные пути для компилятора и средств, а также для всех необходимых заголовков и библиотек. Эти значения среды необходимо задавать самостоятельно, если используется обычное окно командной строки. Дополнительные сведения см. в статье Установка переменных пути и среды при построении из командной строки. Рекомендуется использовать ярлык командной строки разработчика вместо создания собственного.

Открытие командной строки разработчика

  1. Если вы установили Visual Studio 2017 или более поздней версии в Windows 10, откройте меню «Пуск» и выберите

    Все приложения. Прокрутите вниз и откройте папку Visual Studio (не приложение Visual Studio). Выберите элемент Командная строка разработчика для VS, чтобы открыть окно командной строки.

    Если вы установили Microsoft Visual C++ Build Tools 2015 в Windows 10, откройте меню Пуск и выберите Все приложения. Прокрутите вниз и откройте папку Microsoft Visual C++ Build Tools. Выберите элемент Командная строка Native Tools x86 Visual C++ 2015, чтобы открыть окно командной строки.

    Можно также ввести «командная строка разработчика» в строке поиска в Windows и выбрать командную строку, которая соответствует установленной версии Visual Studio. Откройте окно командной строки с помощью ярлыка.

  2. Затем убедитесь в том, что командная строка разработчика Visual C++ настроена правильно. В окне командной строки введите cl и убедитесь в том, что выходные данные выглядят примерно так:

    C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise>cl
    Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25017 for x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    usage: cl [ option... ] filename... [ /link linkoption... ]
    

    Возможно, существуют различия в текущем каталоге или номерах версий. Эти значения зависят от версии Visual C++ и установленных обновлений. Если приведенный выше результат похож на отображаемый, можно приступать к сборке программ C или C++ в командной строке.

    Примечание

    Если при выполнении команды cl появляется сообщение о том, что «cl не распознается как внутренняя или внешняя команда, исполняемая программа или пакетный файл», или возникают ошибки C1034 или LNK1104, дело в том, что вы не используете командную строку разработчика или что-то не так с установкой Visual C++. Для продолжения нужно будет исправить ошибку.

    Если вы не можете найти ярлык командной строки разработчика или при вводе cl появляется сообщение об ошибке, возможно, возникла проблема с установкой Visual C++. Попробуйте переустановить компонент Visual C++ в Visual Studio или Microsoft Visual C++ Build Tools. Не переходите к следующему разделу, пока команда cl не сработает. Дополнительные сведения об установке Visual C++ и устранении неполадок см. в статье Установка Visual Studio.

    Примечание

    В зависимости от версии Windows, установленной на компьютере, и конфигурации системы безопасности может потребоваться правой кнопкой мыши открыть контекстное меню для ярлыка командной строки разработчика и выбрать пункт Запуск от имени администратора, чтобы успешно выполнить сборку и запуск программы, созданной в этом пошаговом руководстве.

Создание файла исходного кода на языке Visual C++ и его компиляция из командной строки

  1. В окне командной строки разработчика введите md c:\hello, чтобы создать каталог, а затем введите cd c:\hello, чтобы перейти к этому каталогу. В этом каталоге создаются файл исходного кода и скомпилированная программа.

  2. В окне командной строки введите notepad hello.cpp.

    Когда Блокнот предложит создать файл, выберите Да. Откроется пустое окно Блокнота, в котором можно ввести код для файла hello.cpp.

  3. В окне блокнота введите следующие строки кода:

    #include <iostream>
    using namespace std;
    int main()
    {
        cout << "Hello, world, from Visual C++!" << endl;
    }
    

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

  4. Сохраните файл. В Блокноте, в меню Файл выберите Сохранить.

    Поздравляем, вы создали исходный файл C++ hello.cpp, который готов к компиляции.

  5. Вернитесь к окну командной строки разработчика. Введите dir в командной строке, чтобы получить список содержимого каталога c:\hello. Вы увидите исходный файл hello.cpp в списке каталогов, который выглядит примерно так:

    c:\hello>dir
     Volume in drive C has no label.
     Volume Serial Number is CC62-6545
    
     Directory of c:\hello
    
    05/24/2016  05:36 PM    <DIR>          .
    05/24/2016  05:36 PM    <DIR>          ..
    05/24/2016  05:37 PM               115 hello.cpp
                   1 File(s)            115 bytes
                   2 Dir(s)  571,343,446,016 bytes free
    
    

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

    Примечание

    Если файл исходного кода hello.cpp не отображается, убедитесь, что текущий рабочий каталог в командной строке — это созданный вами каталог C:\hello . Это должен быть каталог, в который вы сохранили файл исходного кода. Также убедитесь, что файл исходного кода был сохранен с расширением имени файла .cpp , а не .txt . Если открыть Блокнот из командной строки с помощью команды notepad hello.cpp , файл исходного кода автоматически сохраняется в текущем каталоге в виде файла .cpp . Если Блокнот открыть другим способом, его поведение также будет другим. По умолчанию Блокнот добавляет расширение .txt в новые файлы при их сохранении. Кроме того, файлы по умолчанию сохраняются в каталоге Документы. Чтобы сохранить файл с расширением .cpp в Блокноте, выберите Файл > Сохранить как. В диалоговом окне Сохранение файла перейдите к папке C:\hello в элементе управления иерархического представления каталогов. Затем в раскрывающемся списке Сохранить как выберите вариант Все файлы (*.*) . Введите hello.cpp в элемент управления «Поле ввода» Имя файла и нажмите кнопку Сохранить, чтобы сохранить файл.

  6. В командной строке разработчика введите cl /EHsc hello.cpp, чтобы скомпилировать свою программу.

    Компилятор cl.exe создаст OBJ-файл, содержащий скомпилированный код, а затем запустит компоновщик для создания исполняемой программы с именем hello.exe. Это имя отображается в строках информации, выводимой компилятором. Выходные данные компилятора должны выглядеть следующим образом:

    c:\hello>cl /EHsc hello.cpp
    Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25017 for x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    hello.cpp
    Microsoft (R) Incremental Linker Version 14.10.25017.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /out:hello.exe
    hello.obj
    

    Примечание

    Если вы получаете сообщение об ошибке, например «cl не распознается как внутренняя или внешняя команда, исполняемая программа или пакетный файл», ошибке C1034 или LNK1104, командная строка разработчика настроена неправильно. Чтобы получить сведения о том, как устранить эту проблему, вернитесь к разделу Открыть командную строку разработчика.

    Примечание

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

  7. Чтобы запустить программу hello.exe, в командной строке введите hello.

    Программа выводит следующий текст и закрывается:

    Hello, world, from Visual C++!
    

    Поздравляем, вы скомпилировали и запустили программу C++ с помощью средств командной строки.

Следующие шаги

Этот пример «Hello, World» является самой простой программой C++. Реальные программы обычно имеют файлы заголовков, дополнительные исходные файлы и ссылки на библиотеки.

Вы можете использовать шаги, описанные в этом пошаговом руководстве по C++, для создания собственного кода, чтобы не вводить приведенный пример. Эти шаги также позволяют собрать множество примеров кода C++, которые можно найти в других местах. Вы можете разместить исходный код и собрать приложения в любом доступном для записи каталоге. По умолчанию интегрированная среда разработки Visual Studio создает проекты в папке пользователя во вложенной папке source\repos. Более старые версии могут помещать проекты в папку Документы\Visual Studio <version>\ Проекты*.

Чтобы скомпилировать программу с дополнительными файлами исходного кода, введите их все в командной строке, например:

cl /EHsc file1.cpp file2.cpp file3.cpp

Параметр командной строки /EHsc указывает компилятору на необходимость стандартной обработки исключений C++. В противном случае созданные исключения могут привести к неуничтоженным объектам и утечкам ресурсов. Дополнительные сведения см. в статье /EH (модель обработки исключений).

При указании дополнительных исходных файлов компилятор использует первый входной файл для создания имени программы. В этом случае выводится программа с именем file1.exe. Чтобы изменить имя на program1.exe, добавьте параметр компоновщика /out:

cl /EHsc file1.cpp file2.cpp file3.cpp /link /out:program1.exe

Чтобы автоматически перехватывать другие ошибки программирования, рекомендуется выполнить компиляцию с помощью порога предупреждений /W3 или /W4:

cl /W4 /EHsc file1.cpp file2.cpp file3.cpp /link /out:program1.exe

В компиляторе cl.exe есть множество дополнительных параметров. Их можно применять для создания, оптимизации, отладки и анализа кода. Чтобы просмотреть краткий список, введите cl /? в командной строке разработчика. Можно также выполнять компиляцию и компоновку отдельно и применять параметры компоновщика в более сложных сценариях сборки. Дополнительные сведения о параметрах и использовании компилятора и компоновщика см. в справочнике по сборке для C/C++.

Для настройки и создания более сложных проектов в командной строке можно использовать NMAKE и файлы makefile, MSBuild и файл проекта или CMake. Дополнительные сведения об использовании этих средств см. в разделах Справочник по NMAKE, MSBuild и Проекты CMake в Visual Studio.

Языки C и C++ похожи, но имеют различия. Компилятор MSVC использует простое правило для определения языка, используемого при компиляции кода. По умолчанию компилятор MSVC рассматривает файлы с расширением .c как исходные файлы на языке С, а файлы с расширением .cpp  — как исходные файлы на языке С++. Если указан параметр компилятора /TP, компилятор будет рассматривать все файлы как исходные файлы на языке С++ вне зависимости от расширения.

Компилятор MSVC содержит библиотеку времени выполнения C (CRT), которая соответствует стандарту ISO C99 с небольшими исключениями. Переносимый код обычно компилируется и выполняется, как ожидалось. Некоторые устаревшие функции библиотеки и несколько имен функций POSIX не рекомендуется использовать в компиляторе MSVC. Функции поддерживаются, но предпочтительные имена изменились. Дополнительные сведения см. в статьях Функции безопасности в CRT и Предупреждение компилятора (уровень 3) C4996.

См. также

Справочник по языку C++
Проекты и системы сборки
Параметры компилятора MSVC

Компиляция кода C/C++ в WebAssembly — WebAssembly

После того как вы написали код на C / C ++, вы можете скомпилировать его в WebAssembly, например, с помощью инструмента Emscripten. Давайте посмотрим, как это работает.

Первым делом установим компоненты для дальнейшей работы.

Необходимые компоненты

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

  • Компиляция в wasm и создание HTML-страницы для запуска вашего кода, а также JavaScript кода, необходимого для работы wasm модуля в веб-среде.
  • Просто компиляция в wasm и создание JavaScript кода.

Мы рассмотрим оба способа ниже.

Создание HTML и JavaScript

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

  1. Нам понадобится простой пример для компиляции. Скопируйте следующий код программы на Си и сохраните его в файле  hello.c в новой папке на вашем локальном диске:
    #include <stdio.h>
    
    int main(int argc, char ** argv) {
      printf("Hello World\n");
    }
  2. Теперь, используя терминал, перейдите в каталог, в котором находится ваш файл hello.c, и выполните следующую команду:
    emcc hello.c -s WASM=1 -o hello.html

Рассмотрим параметры, которые мы передали компилятору:

  • -s WASM=1 — Указывает, что мы хотим получить wasm модуль. Если не использовать этот параметр, по умолчанию Emscripten просто создаёт  asm.js;
  • -o hello.html — Указывает, что мы хотим, чтобы Emscripten сгенерировал HTML-страницу hello.html запускающую наш код, а также сам модуль wasm и код JavaScript который позволит использовать модуль в веб-среде.

На этом этапе в вашем каталоге должны находится:

  • Бинарный код модуля wasm (hello.wasm)
  • Файл JavaScript, содержащий код связывающий нативные функции Си и JavaScript/wasm (hello.js)
  • HTML-страница для загрузки, компиляции и инициализации wasm модуля, и отображающий его вывод в браузере (hello.html)

Запуск вашего примера

Теперь, всё что нужно чтобы запустить полученный  hello.html в браузере, это поддержка WebAssembly. Он включён по умолчанию в Firefox 52+, Chrome 57+ и последних версиях Opera. Также вы можете использовать модули WebAssembly в Firefox 47+, включив флаг javascript.options.wasm в about:config, или в Chrome 51+ и Opera 38+ перейдя в chrome://flags и включив флаг Experimental WebAssembly.

Если все работает как планировалось, вы должны увидеть надпись «Hello world» на открывшейся веб-странице и в JavaScript консоли вашего браузера. Поздравляем, вы только что скомпилировали программу на Си в WebAssembly и запустили её в своём браузере!

Примечание: На самом деле, если просто открыть полученный hello.html, то ничего работать не будет. Подразумевается что все файлы находятся на веб-сервере и вы запускаете страницу через localhost/hello.html. Для этих целей можно использовать отладочный веб-сервер Emscripten. Чтобы его запустить, откройте терминал, перейдите в каталог, в котором находятся ваши файлы и выполните команду emrun hello.html

Использование собственного HTML шаблона

Вы можете использовать собственный шаблон HTML. Давайте посмотрим, как это сделать:

  1. Прежде всего, сохраните следующий код в файле hello2.c в новом каталоге:

    #include <stdio.h>
    
    int main(int argc, char ** argv) {
        printf("Hello World\n");
    
    }
  2. Найдите файл  shell_minimal.html в вашем репозитории emsdk. Скопируйте его в подкаталог html_template внутри вашего нового каталога.

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

    emcc -o hello2.html hello2.c -O3 -s WASM=1 --shell-file html_template/shell_minimal.html

    В этот раз мы использовали немного другие параметры компиляции:

    • Мы указали -o hello2.html, чтобы компилятор по прежнему генерировал необходимый JavaScript код и .html файл.
    • Также, мы указали  --shell-file html_template/shell_minimal.html чтобы компилятор использовал ваш шаблон для создания HTML страницы запускающей этот пример.
  4. Теперь давайте запустим этот пример. Команда, указанная выше, сгенерирует файл hello2.html, который будет иметь тоже содержание что и шаблон, но с некоторым кодом, добавленным в процесс загрузки сгенерированного wasm, запускающим его и т.д. Откройте его в своём браузере, и вы увидите тот же результат, что и прошлом примере.

Примечание: вы можете указать компилятору создавать только JavaScript кода, без HTML, используя внутри флага -o, .js вместо .html для формата выходного файла, например emcc -o hello2.js hello2.c -O3 -s WASM=1. После этого вы должны создать свой собственный HTML файл с нуля. Однако так делать не рекомендуется — Emscripten требуется большое количество связывающего кода для обработки операций выделения памяти, утечек памяти и других проблем, которые уже включены в предоставляемый шаблон. Намного легче использовать уже готовое решение, чем создавать свои собственные версии самому.

Вызов пользовательской функции, определённой в Си

Если у вас есть функция определённая в коде на Си, которую вы хотите по необходимости вызывать из JavaScript, то вы можете использовать для этого функцию  ccall() из Emscripten, и объявление EMSCRIPTEN_KEEPALIVE которое добавит вашу функцию в список экспортируемых функций (см. Почему функции в моем коде исчезают после компиляции и/или я получаю сообщение «Нет функций для обработки»). Давайте посмотрим, как это работает.

  1. Для начала сохраните следующий код в файле  hello3.c в новом каталоге:

    #include <stdio.h>
    #include <emscripten/emscripten.h>
    
    int main(int argc, char ** argv) {
        printf("Hello World\n");
    }
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    void EMSCRIPTEN_KEEPALIVE myFunction(int argc, char ** argv) {
      printf("MyFunction Called\n");
    }
    
    #ifdef __cplusplus
    }
    #endif

    По умолчанию, код созданный Emscripten, всегда просто вызывает функцию  main() , а остальные неиспользуемые функции удаляются. Добавьте определение EMSCRIPTEN_KEEPALIVE перед именем функции чтобы этого не происходило. Также вы должны подключить библиотеку emscripten.h для использования EMSCRIPTEN_KEEPALIVE.

    Примечание: Мы используем блоки #ifdef чтобы, пример оставался рабочим если вы попытаетесь использовать C++ код. Из за различия в правилах преобразования имён между Си и Си++, этот код может сломаться, но мы написали его так, что функция будет рассматриваться как функция Си даже если вы будете использовать Си++.

  2. Теперь добавьте html_template/shell_minimal.html в ваш новый каталог, просто для удобства. В настоящем проекте стоит размещать его в специально определённый каталог.

  3. Теперь снова займёмся этапом компиляции. Внутри вашего последнего каталога, используя терминал, скомпилируйте ваш Си код следующей командой. (Обратите внимание что при компиляции обязательно нужно использовать опцию NO_EXIT_RUNTIME, иначе после выполнения функции main(), рабочий цикл будет завершён. Это приведёт, например, к вызову функции atexits и дальше будет невозможно использовать наш скомпилированный код. Другими словами это необходимо для правильной эмуляции Си.)

    emcc -o hello3.html hello3.c -O3 -s WASM=1 --shell-file html_template/shell_minimal.html -s NO_EXIT_RUNTIME=1  -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall"]'
  4. Если вы снова запустите пример в своём браузере, вы увидите тоже самое что и до этого!

  5. Теперь нам нужно вызвать нашу новую функцию myFunction() из JavaScript. Прежде всего, добавьте <button> как показано ниже, чуть выше первого открывающего тега <script type='text/javascript'>.

    <button>Run myFunction</button>
  6. Теперь добавьте следующий код в конце первого элемента <script> (чуть выше закрывающего тега </script>):

    document.querySelector('.mybutton').addEventListener('click', function(){
      alert('check console');
      var result = Module.ccall('myFunction', 
                                 null, 
                                 null, 
                                 null); 
    });

Это показывает как использовать ccall() для вызова экспортируемой функции.

Разработчик оптимизирующего компилятора/МЦСТ

Компания МЦСТ приглашает системного программиста в команду разработки оптимизирующего компилятора. Наша компания занимается разработкой микропроцессоров с архитектурами Эльбрус и Sparc. Залогом их высокой производительности является мощный оптимизирующий компилятор, к качеству работы которого предъявляются высокие требования. Ряд уникальных разработок позволяет использовать аппаратные возможности микропроцессора Эльбрус без низкоуровневого программирования. Поддерживается совместимость с gcc и современными стандартами языков C/C++/Fortran, что позволяет успешно собирать дистрибутив и ядро Linux в режиме с оптимизациями.

Обязанности:

  • Разработка и поддержка оптимизирующих фаз компилятора.
  • Профилирование и анализ производительности.
  • Написание направленных тестов для оптимизаций.

Требования:

  • Отличное знание языка C.
  • Базовое знание C++, принципов ООП.
  • Опыт системного программирования на C/C++ под Linux от 3 лет.
  • Знание базовых алгоритмов и структур данных.
  • Высшее образование.

Приветствуется:

  • Понимание внутреннего устройства современных компиляторов, таких как gcc и llvm.
  • Понимание базовых принципов работы современных микропроцессоров.
  • Владение техническим английским языком.
  • Базовое знание Fortran, bash, perl, python, awk.

Условия:

  • Работу в одной из самых интересных областей программирования.
  • Дружный коллектив профессионалов.
  • Гибкий график работы.
  • Белая зарплата.
  • Медицинская страховка.
  • Возможность заниматься научной деятельностью.
  • Удобное расположение офиса — 10 минут пешком от метро Ленинский проспект.

Заработная плата:

Резюме просьба направлять на адрес: [email protected]

Компиляция программ | hpc@cmc

На суперкомпьютере Polus установлены как компиляторы семейства GNU, так и фирменные компиляторы компании IBM поддерживающие языки программирования C/C++, Fortran. Ниже они перечислены в таблице, постфикс _r в названии скрипта, применяемого для компиляции, говорит о том, что будут использованы потокобезопасные компоненты. Такие скрипты необходимо вызывать при сборке OpenMP- и pthreads-программ.

КомпиляторC/C++Fortran
CC++FortranFortran 90Fortran 95Fortran 2003Fortran 2008
GNUgccg++gfortran
IBM XLxlcxlC/xlc++xlfxlf90xlf95xlf2003xlf2008
потокобезопасная версияxlc_rxlC_r/xlc++_rxlf_rxlf90_rxlf95_rxlf2003_rxlf2008_r

Для создания и запуска программ написанных с использованием MPI стандарта необходимо загрузить модуль SpectrumMPI. Для этого необходимо выполнить следующую коменду

module load SpectrumMPI

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

module load OpenMPI

Однако использование SpectrumMPI предпочтительней. Всё дальнейшее описание будет приводится для SpectrumMPI.

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

КомпиляторC/C++Fortran
CC++FortranFortran 77Fortran 90
mpiccmpicxxmpifortmpif77mpif90
IBM XLmpixlcmpixlCmpixlf
Для всех компиляторов, не только компиляторов IBM, но и gcc, а так же большинства других компиляторов, имеются следующие ключи:
КлючЗначение
-I include_dir опция, которая задаёт пути для поиска include файлов
-L library_dir задаёт пути для поиска подключаемых библиотек.
-l library_name линкует с указанной библиотекой
-o file_name задаёт имя бинарного файла, который будет получен как результат деятельности компилятора
-g добавление отладочной информации в бинарный код, после этого при запуске программы через отладчик будет доступна информация об исходном коде программы.
-ON Опции начального уровня оптимизации (N число от 1 до 5)
Нестандартные ключи компиляторов IBM
КлючЗначение
-qsmp=omp обеспечивает возможность писать программы с использованием OpenMP стандарта.
-qsmp=auto для автоматического распараллеливания программ
-qarch=pwr8 компилятор генерирует оптимальный код для Power8 процессоров.
-qhot оптимизация кода с принудительным внедрением параллелизма внутрь кода, здесь начинает использоваться параллелизм функциональных устройств архитектуры Power8. При этом может нарушаться последовательность команд, которая была задана в исходном файле. В результате, программа становится недетерминированной.

Краткий учебный курс по началу работы с проектами C/C++

Поддержка C/C в среде NetBeans позволяет создавать проекты приложений и библиотек C и C с автоматически созданными файлами make, а также проекты C и C++ с существующими исходными кодами.

Разработчик может осуществлять сборку, выполнение и отладку проекта на локальном узле (в системе, из которой запускается среда IDE) или на удаленном узле под управлением операционной системы UNIX®. Информацию о выборе набора средств для проекта, а также об определении и использовании удаленных узлов см. в разделе Определение среды разработки проекта C/C+ +.

Среда IDE управляет всеми параметрами сборки, запуска и отладки приложения проектов «Приложение на C/C++», «Динамическая библиотека» или «Статическая библиотека». Параметры проекта указываются при создании проекта, а также в диалоговом окне «Свойства проекта». Среда IDE создает файл make, в котором хранятся все настройки.

Создание проекта приложения на языке C/C++

  1. Откройте мастер «Новый проект» с помощью команды FIle («Файл») > New Project («Новый проект»).

  2. Выберите категорию «C/C++» в мастере.

  3. Мастер предлагает возможность выбора типа нового проекта. Выберите параметр «Приложение на C/C++» и нажмите кнопку «Далее».

  1. Создайте в мастере новый проект «Приложение C/C++», используя параметры по умолчанию. Название проекта и его расположение выбираются разработчиком.

  1. Для выхода из мастера нажмите кнопку «Готово».

Проект создается с логическими папками. Логическая папка не является каталогом. Она представляет собой способ упорядочения файлов и не отражает физическое расположение файлов на диске. Добавленные в логические папки файлы автоматически становятся частью проекта и компилируются при сборке проекта.

Файлы, добавленные в папку ‘Важные файлы’, не являются частью проекта и не компилируются при сборке проекта. Эти файлы являются справочной информацией и могут быть полезны разработчику, когда имеется проект с существующим файлом make.

Переключение логического и физического представления проекта

Проект имеет логическое и физическое представление. Разработчик может переключаться между логическим и физическим представлением проекта.

  1. Перейдите на вкладку «Файлы». В этом окне содержится физическое представление проекта. В нем отображается расположение файлов и папок на диске.

  1. Перейдите на вкладку «Проекты». В этом окне содержится логическое представление проекта.

Большинство команд, выполняемых в проекте, доступны на вкладке «Проекты», а не на вкладке «Файлы».

Добавление файлов и папок в проект

Разработчик может добавлять в проект логические папки.

  1. Щелкните узел проекта «Приложение» правой кнопкой мыши и выберите команду «Новая логическая папка». В проект будет добавлена новая логическая папка.

  2. Щелкните добавленную логическую папку правой кнопкой мыши и выберите команду «Переименовать». Введите имя для новой папки.

В существующую папку можно добавлять файлы и папки. Логические папки могут быть вложенными.

Добавление новых файлов в проект

В проект также можно добавить новые файлы.

  1. Щелкните правой кнопкой мыши папку «Исходные файлы» и выберите команду «Создать» > «Исходный файл на языке C++».

  2. Перейдите на страницу «Имя и местоположение» диалогового окна «Новый файл» и введите newfile в поле «Имя файла».

  1. Нажмите кнопку ‘Готово’.

Файл newfile.cpp создается на диске в каталоге, указанном в мастере, а также добавляется в папку «Исходные файлы». В эту папку можно добавлять любые файлы, а не только файлы исходного кода.

Добавление дополнительных новых файлов в проект

  1. Щелкните папку «Файлы заголовка» правой кнопкой мыши и выберите команду «Создать» > «Файл заголовка на языке C++».

  2. Перейдите на страницу «Имя и местоположение» диалогового окна «Новый файл» и введите newfile в поле «Имя файла».

  3. Нажмите кнопку ‘Готово’.

Файл newfile.h создается на диске в каталоге, указанном в мастере, а также добавляется в папку «Исходные файлы».

Добавление существующих файлов в проект

Существующие файлы добавляются в проект двумя способами:

  • Правой кнопкой щелкните папку «Исходные файлы» и выберите команду «Добавить существующий элемент». Чтобы установить ссылку на существующий файл на диске, откройте диалоговое окно «Выбрать элемент» и добавьте файл в проект.

  • Правой кнопкой щелкните папку «Исходные файлы» и выберите команду «Добавить существующие элементы из папок». Используйте диалоговое окно «Добавить файлы» для добавления папок, содержащих существующие файлы.

Не используйте команду меню «Новый» для добавления существующих элементов. На панели «Имя и местоположение» отображаются сведения о существовании файла.

Установка свойств проекта

После создания проекта у него появляются две конфигурации: ‘Отладка’ и ‘Выпуск’. Настройка – это набор параметров, использованных для проекта. При выборе настройки несколько параметров переключаются одновременно. При выборе настройки «Отладка» создается версия приложения, которая содержит сведения об отладке. При выборе настройки «Выпуск» создается оптимизированная версия.

В диалоговом окне «Свойства проекта» содержатся сведения о сборке и настройке проекта. Открытие диалогового окна «Свойства проекта»

Разработчик может изменять параметры средства сборки по умолчанию, параметры компилятора и другие параметры настройки в диалоговом окне «Свойства проекта». Для этого следует выбрать узел на левой панели и изменить свойства на правой панели. Выберите несколько узлов и значений свойств и обратите внимание на то, какие свойства можно установить. При установке свойств категории «Общие» они устанавливаются для всех настроек проекта. Свойства ‘Сборка’, ‘Запуск’ или ‘Отладка’ устанавливаются для текущей выбранной конфигурации.

Управление настройками

Свойства, измененные в окне «Свойства проекта», хранятся в файле make для текущей настройки. Разработчик может изменять настройки по умолчанию или создавать новые. Для создания новой настройки выполните следующие действия.

  1. Нажмите кнопку «Управление настройками» в диалоговом окне «Свойства проекта».

  2. В диалоговом окне «Настройки» выберите настройку, которая больше всего совпадает с требуемой настройкой. В этом случае выберите настройку «Выпуск» и нажмите кнопку «Дублировать». Затем нажмите кнопку «Переименовать».

  3. В диалоговом окне «Переименовать» переименуйте настройку в PerformanceRelease. Нажмите кнопку «ОК».

  4. Нажмите кнопку «ОК» в диалоговом окне «Настройки».

  5. На левой панели выберите узел «Компилятор C».

  6. В диалоговом окне «Свойства проекта» обратите внимание на то, что в раскрывающемся списке «Настройка» выбрана настройка «PerformanceRelease».

  7. В окне свойств на правой панели измените значение «Режим разработки» со значения «Release» на значение «PerformanceRelease». Нажмите кнопку «ОК».

Создана новая настройка, которая служит для компиляции приложения с другим набором параметров.

Установка свойств файлов исходного кода

При установке свойств проекта C или C++ они действуют для всех файлов проекта. Также можно установить некоторые свойства для отдельных файлов.

  1. Щелкните правой кнопкой мыши исходный файл newfile.cpp на вкладке «Проекты» и выберите команду «Свойства».

  2. Щелкните категорию «Общее» и обратите внимание, что существует возможность указания различных компиляторов или других средств для сборки этого файла. Также можно использовать флажок для исключения файла из сборки выбранной в настоящий момент настройки проекта.

  3. Щелкните категорию «Компилятор C++» и убедитесь, что существует возможность переопределения параметров компилятора и других свойств этого файла.

  4. Отмените работу с диалоговым окном «Свойства файла».

Сборка и пересборка проекта

Для сборки проекта выполните следующие действия.

  1. Правой кнопкой мыши щелкните узел проекта и выберите команду «Сборка», после чего будет выполнена сборка проекта. Результат сборки выводится в окне «Вывод»

  2. Замените настройку «Отладка» на настройку «PerformanceRelease» в раскрывающемся списке настроек на главной панели инструментов. Проект будет собран с использованием настройки PerformanceRelease.

  3. Правой кнопкой мыши щелкните узел проекта и выберите команду «Сборка», после чего будет выполнена сборка проекта. Результат сборки выводится в окне «Вывод»

Для повторной сборки проекта:

  • Щелкните узел проекта правой кнопкой мыши и выберите ‘Очистить и собрать’, чтобы выполнить полную повторную сборку проекта после удаления результатов предыдущей сборки.

  • Щелкните узел проекта правой кнопкой мыши и выберите ‘Собрать’, чтобы выполнить инкрементную сборку. Результаты предыдущей сборки сохраняются, если их исходные файлы не изменены.

Сборку, очистку или очистку со сборкой проекта можно выполнять с помощью команд в меню ‘Выполнить’ или с помощью кнопок на панели инструментов. Файлы объектов и исполняемые файлы разных настроек также хранятся отдельно, что предотвращает смешение файлов различных настроек.

Компиляция отдельного файла

Для компиляции отдельного файла исходного кода выполните следующие действия.

Компиляция отдельных файлов не поддерживается для типа проекта «Проект на C/C++ с существующими исходными файлами».

КОМПИЛЯТОР • Большая российская энциклопедия

  • В книжной версии

    Том 14. Москва, 2009, стр. 688

  • Скопировать библиографическую ссылку:


Авторы: А. Ю. Дроздов, А. В. Ильин

КОМПИЛЯ́ТОР (от лат. compilo – со­би­рать, со­став­лять, ком­пи­ли­ро­вать) в ин­фор­ма­ти­ке, ком­пь­ю­тер­ная про­грам­ма, ко­то­рая пре­об­ра­зу­ет ис­ход­ный код про­грам­мы на язы­ке про­грам­ми­ро­ва­ния вы­со­ко­го уров­ня (ЯПВУ) в функ­цио­наль­но эк­ви­ва­лент­ный на­бор ин­ст­рук­ций на язы­ке низ­ко­го уров­ня (т. н. объ­ект­ный код). К. яв­ля­ет­ся од­ним из ви­дов транс­ля­то­ра и, как пра­ви­ло, вхо­дит в со­став сис­тем­но­го про­грамм­но­го обес­пе­че­ния ком­пь­ю­те­ра. Ка­ж­дый К. со­от­вет­ст­ву­ет оп­ре­де­лён­но­му ЯПВУ (пас­ка­лю, си, фор­тра­ну и др.) и од­ной или не­сколь­ким вы­чис­лит. плат­фор­мам. Вы­чис­лит. плат­фор­ма оп­ре­де­ля­ет­ся ар­хи­тек­ту­рой се­мей­ст­ва центр. про­цес­со­ров; напр., x86 – Intel 8086 Family Architecture (ар­хи­тек­ту­ра се­мей­ст­ва Intel 8086), опе­ра­ци­он­ной сис­те­мой и, в ря­де слу­ча­ев, до­пол­нит. про­грамм­ным обес­пе­че­ни­ем (напр., вир­ту­аль­ной ма­ши­ной), не­об­хо­ди­мым для ра­бо­ты ис­пол­няе­мо­го ко­да на про­цес­со­рах дан­ной ар­хи­тек­ту­ры.

К. вы­пол­ня­ет лек­сич., син­так­сич., се­ман­тич. ана­лиз ис­ход­но­го ко­да про­грам­мы и ге­не­ра­цию объ­ект­но­го ко­да. На эта­пе лек­сич. ана­ли­за ис­ход­ный код пре­об­ра­зу­ет­ся в по­сле­до­ва­тель­ность лек­сич. еди­ниц – лек­сем (клю­че­вые сло­ва язы­ка про­грам­ми­ро­ва­ния, иден­ти­фи­ка­то­ры пе­ре­мен­ных, кон­стан­ты и др.). Во вре­мя син­так­сич. ана­ли­за осу­ще­ст­в­ля­ет­ся про­вер­ка по­сле­до­ва­тель­но­сти лек­сем на на­ли­чие син­так­сич. оши­бок (в со­от­вет­ст­вии с син­так­сич. пра­ви­ла­ми язы­ка про­грам­ми­ро­ва­ния) и за­тем – пре­об­ра­зо­ва­ние этой по­сле­до­ва­тель­но­сти в т. н. де­ре­во раз­бо­ра. Се­ман­тич. ана­лиз пред­на­зна­чен для вы­яв­ле­ния ло­гич. оши­бок в ис­ход­ной про­грам­ме и оп­ре­де­ле­ния зна­че­ния язы­ко­вых кон­ст­рук­ций де­ре­ва раз­бо­ра. По­сле это­го К. ли­бо пе­ре­хо­дит к ге­не­ра­ции объ­ект­но­го ко­да, ли­бо за­вер­ша­ет ра­бо­ту вы­во­дом со­об­ще­ния об ошиб­ках. Ис­ход­ный код про­грам­мы, как пра­ви­ло, со­дер­жит­ся в не­сколь­ких фай­лах. К. пре­об­ра­зу­ет ка­ж­дый из них в отд. объ­ект­ный мо­дуль (файл, со­дер­жа­щий объ­ект­ный код), а за­тем спец. про­грам­ма-ком­по­нов­щик со­би­ра­ет ис­пол­няе­мый код про­грам­мы из объ­ект­ных мо­ду­лей, стан­дарт­ных биб­лио­теч­ных мо­ду­лей и др. До соз­да­ния ком­по­нов­щи­ков сбор­ка про­из­во­ди­лась ком­пи­ля­то­ром.

Для эф­фек­тив­но­го ис­поль­зо­ва­ния осо­бен­но­стей ар­хи­тек­ту­ры про­цес­со­ров и улуч­ше­ния ха­рак­те­ри­стик ис­пол­няе­мо­го ко­да (про­из­во­ди­тель­но­сти, ком­пакт­но­сти и др.) в К. встрое­ны ком­по­нен­ты, оп­ти­ми­зи­рую­щие код. Раз­ли­ча­ют оп­ти­ми­за­цию ма­шин­но не­за­ви­си­мую (т. е. не за­ви­ся­щую от ар­хи­тек­ту­ры про­цес­со­ра) и ма­шин­но за­ви­си­мую. Ма­шин­но не­за­ви­си­мая оп­ти­ми­за­ция час­то вы­пол­ня­ет­ся на эта­пе ге­не­ра­ции объ­ект­но­го ко­да. Ма­шин­но за­ви­си­мые пре­об­ра­зо­ва­ния на­прав­ле­ны на эф­фек­тив­ное ис­поль­зо­ва­ние осо­бен­но­стей ар­хи­тек­тур про­цес­со­ров, оп­ти­маль­ное рас­па­рал­ле­ли­ва­ние про­грамм (для мно­го­ядер­ных про­цес­со­ров) и др.

Аль­тер­на­ти­вой К. и ком­по­нов­щи­кам слу­жат ин­тер­пре­та­то­ры. Для ря­да ЯПВУ су­ще­ст­ву­ют как К., так и ин­тер­пре­та­то­ры. Для не­ко­то­рых ЯПВУ (напр., Java) при­ме­ня­ет­ся двух­этап­ная ком­пи­ля­ция или со­че­та­ние ком­пи­ля­ции и ин­тер­пре­та­ции. На пер­вом эта­пе на спец. про­ме­жу­точ­ном язы­ке ге­не­ри­ру­ет­ся т. н. байт-код, ко­то­рый не при­вя­зан к кон­крет­ной опе­ра­ци­он­ной сис­те­ме и ар­хи­тек­ту­ре се­мей­ст­ва про­цес­со­ров. Байт-код пред­на­зна­чен для по­сле­дую­щей ин­тер­пре­та­ции или т. н. JIT-ком­пи­ля­ции (Just-In-Time compilation – ком­пи­ля­ция «на ле­ту») во вре­мя вы­пол­не­ния про­грам­мы. На вто­ром эта­пе пре­об­ра­зо­ва­ние байт- ко­да в ис­пол­няе­мый код (для кон­крет­ных опе­ра­ци­он­ной сис­те­мы и про­цес­со­ра) осу­ще­ст­в­ля­ет­ся спец. про­грамм­ным обес­пе­че­ни­ем (напр., Java Virtual Ma­chine – вир­ту­аль­ной ма­ши­ной Java). Та­кой под­ход по­зво­ля­ет су­ще­ст­вен­но умень­шить тру­до­за­тра­ты про­грам­ми­стов, по­сколь­ку один и тот же байт-код мо­жет быть ис­поль­зо­ван на разл. вы­чис­лит. плат­фор­мах, вклю­чаю­щих не­об­хо­ди­мую вир­ту­аль­ную ма­ши­ну. С кон. 20 в. боль­шое вни­ма­ние уде­ля­ет­ся соз­да­нию т. н. дво­ич­ных К., ко­то­рые по­зво­ля­ют пе­ре­во­дить ском­по­но­ван­ный ис­пол­няе­мый код од­ной плат­фор­мы в код аль­тер­на­тив­ной ар­хи­тек­тур­ной плат­фор­мы, обес­пе­чи­вая пе­ре­нос го­то­во­го про­грамм­но­го обес­пе­че­ния.

К. клас­си­фи­ци­ру­ют по разл. при­зна­кам: по вре­ме­ни за­пус­ка про­цес­са ком­пи­ля­ции, по клас­су це­ле­вых ар­хи­тек­тур, по на­ли­чию оп­ти­ми­зи­рую­щих пре­об­ра­зо­ва­ний и др. По вре­ме­ни за­пус­ка раз­ли­ча­ют К. ста­ти­че­ские и ди­на­ми­че­ские. Ста­тич. К. за­пус­ка­ет­ся про­грам­ми­стом из ин­ст­ру­мен­таль­ной сис­те­мы про­грам­ми­ро­ва­ния или ко­манд­ной стро­ки. Ди­на­мич. К. за­пус­ка­ет­ся во вре­мя ра­бо­ты про­грам­мы, т. е. па­рал­лель­но с ис­пол­не­ни­ем про­грам­мы идёт пе­ре­ком­пи­ля­ция не­ко­то­рых её час­тей. На ос­но­ве клас­сов це­ле­вых ар­хи­тек­тур про­цес­со­ров раз­ли­ча­ют К. для ар­хи­тек­тур с яв­но вы­ра­жен­ным па­рал­ле­лиз­мом (EPIC – Explicitly Parallel Instruction Computing), для встро­ен­ных (embedded) ар­хи­тек­тур, ав­то­ма­тич. рас­па­рал­ле­ли­ва­те­ли для мно­го­ядер­ных (multicore) ар­хи­тек­тур и др.

Один из пер­вых К. раз­ра­бо­тан в 1952 Г. Хоп­пер (США) для соз­дан­но­го ею язы­ка про­грам­ми­ро­ва­ния A-0 ЭВМ UNIVAC I. До по­яв­ле­ния ЯПВУ и их К. про­грам­мы пи­са­ли на язы­ках низ­ко­го уров­ня (сна­ча­ла в ко­дах ма­шин­ных ко­манд, позд­нее – на язы­ках ас­семб­ле­ра). Зна­чит. дос­ти­же­ни­ем ав­то­ма­ти­за­ции про­грам­ми­ро­ва­ния ста­ла раз­ра­бот­ка в 1957 К. для язы­ка фор­тран ЭВМ IBM 704, вы­пол­нен­ная под рук. Дж. Бэ­ку­са (США). В 1950–60-х гг. ис­ход­ные ко­ды К. пи­са­лись толь­ко на язы­ках ас­семб­ле­ра. Од­ной из пер­вых рос. ра­бот в об­лас­ти оп­ти­ми­зи­рую­щей ком­пи­ля­ции стал Аль­фа-транс­ля­тор с язы­ка ал­гол-60 для ЭВМ М-20, соз­дан­ный под рук. А. П. Ер­шо­ва (1959–64). В 1970-х гг. реа­ли­зо­ва­ны ЯПВУ пас­каль, си и др., ко­то­рые ста­ли при­ме­нять­ся как для соз­да­ния при­клад­ных про­грамм, так и для раз­ра­бот­ки К., что при­ве­ло к зна­чит. со­кра­ще­нию тру­до­ём­ко­сти соз­да­ния К. Рост чис­ла ЯПВУ и раз­лич­ных вы­чис­лит. плат­форм обу­слов­ли­ва­ет по­яв­ле­ние но­вых К. и со­вер­шен­ст­во­ва­ние ме­то­дов их кон­ст­руи­ро­ва­ния.

c — Как написать очень простой компилятор

Введение

Типичный компилятор выполняет следующие шаги:

  • Анализ: исходный текст преобразуется в абстрактное синтаксическое дерево (AST).
  • Разрешение ссылок на другие модули (C откладывает этот шаг до линковки).
  • Семантическая проверка: отсеивание синтаксически правильных утверждений, которые не имеют смысла, например недостижимый код или повторяющиеся объявления.
  • Эквивалентные преобразования и высокоуровневая оптимизация: AST преобразуется для представления более эффективных вычислений с той же семантикой.Это включает, например, раннее вычисление общих подвыражений и константных выражений, устранение чрезмерных локальных присваиваний (см. также SSA) и т. д.
  • Генерация кода: AST преобразуется в линейный низкоуровневый код с переходами, распределением регистров и т.п. Некоторые вызовы функций могут быть встроены на этом этапе, некоторые циклы развернуты и т. Д.
  • Оптимизация с помощью глазка: код нижнего уровня сканируется для выявления простых локальных недостатков, которые устраняются.

Большинство современных компиляторов (например, gcc и clang) повторяют два последних шага еще раз.Они используют промежуточный низкоуровневый, но независимый от платформы язык для первоначальной генерации кода. Затем этот язык преобразуется в код, зависящий от платформы (x86, ARM и т. Д.), Делая примерно то же самое оптимизированным для платформы способом. Это включает, например, использование векторных инструкций, когда это возможно, переупорядочение инструкций для повышения эффективности предсказания ветвлений и т. д.

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

Помните основы

  • Заставить работать
  • Сделай это красиво
  • Сделайте это эффективным

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

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

Прочтите книги!

Прочтите «Книгу дракона» Ахо и Ульмана.Это классика, которая актуальна и сегодня.

Modern Compiler Design также получил высокую оценку.

Если это слишком сложно для вас прямо сейчас, прочтите сначала несколько вступлений по синтаксическому анализу; обычно библиотеки синтаксического анализа включают вступления и примеры.

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

Четко определите свой язык

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

Пришло время написать фрагменты кода на вашем новом языке в качестве тестовых примеров для будущего компилятора.

Используйте свой любимый язык

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

Также можно писать разные этапы компилятора на разных языках, если это необходимо.

Приготовьтесь написать много тестов

Весь ваш язык должен быть покрыт тестовыми примерами; фактически это будет , определенный ими . Ознакомьтесь с предпочтительной средой тестирования. Пишите тесты с первого дня. Сконцентрируйтесь на «положительных» тестах, которые принимают правильный код, а не на обнаружении неправильного кода.

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

Создайте хороший парсер

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

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

Вывод вашего синтаксического анализатора представляет собой абстрактное синтаксическое дерево.

Если в вашем языке есть модули, выходные данные анализатора могут быть простейшим представлением генерируемого вами «объектного кода».Существует множество простых способов сбросить дерево в файл и быстро загрузить его обратно.

Создать семантический валидатор

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

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

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

Сгенерировать код

Используйте самые простые методы, которые вы знаете. Часто нормально переводить языковую конструкцию (например, оператор if ) в слегка параметризованный шаблон кода, мало чем отличающийся от шаблона HTML.

Опять же, игнорируйте эффективность и сосредоточьтесь на правильности.

Целевой объект для низкоуровневой виртуальной машины, не зависящей от платформы.

Я полагаю, что вы игнорируете низкоуровневые вещи, если вас не интересуют детали, специфичные для оборудования. Эти детали кровавые и сложные.

Ваши варианты:

  • LLVM: позволяет эффективно генерировать машинный код, обычно для x86 и ARM.
  • CLR: ориентирована на .NET, мультиплатформенность; имеет хороший JIT.
  • JVM: ориентирована на мир Java, достаточно многоплатформенная, имеет хорошую JIT.

Игнорировать оптимизацию

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

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

И что?

Если все это вас не пугает, продолжайте! Для простого языка каждый из шагов может быть проще, чем вы думаете.

Вид «Hello world» из программы, созданной вашим компилятором, может стоить затраченных усилий.

Зачем C ++ писать компилятор?

Проект CompCert — это исследовательский компилятор C, который написан не на C или C ++, а больше на Ocaml и Coq.

Обратите внимание, что C ++ раньше переводился на C (в Cfront). Теперь вы можете использовать интерфейс GCC для Gimple, затем сбросить Gimple в некоторую базу данных, а затем написать GIMPLE в свой переводчик ассемблера. Но по юридическим причинам (исключение из библиотеки времени выполнения GCC) такой компилятор должен иметь открытый исходный код. Подробности уточняйте у своего юриста, я не юрист.Старые варианты GCC были написаны на C (+ несколько языков, специфичных для предметной области) с интерфейсом для некоторого варианта C ++. OpenWatcom может быть компилятором C ++, написанным на C (оставляю вас проверить).

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

См. Также ответы на два связанных вопроса.

Если бы в 2020 году мне было поручено написать компилятор C (или C ++) с нуля (работающий в Linux, возможно, какой-нибудь кросс-компилятор), я, вероятно, не стал бы писать его на C ++.Я бы подумал написать его с помощью Ocaml, Go или Rust. И я мог бы основывать это на Frama-C, если бы мне было позволено. Если требуется кодировать на C или C ++, я бы сначала написал для него библиотеку сборщика мусора, возможно, какой-то уровень сохраняемости — очень полезный для оптимизации всей программы — а затем я бы рассмотрел подход метапрограммирования (генерирующий большую часть кода C или C ++ для компилятор с моими специальными инструментами, возможно, Bismon или RefPerSys, если это разрешено).

Вы можете найти некоторые (более или менее открытые) компиляторы C, написанные на Common Lisp или Python (e.грамм. ShivyC или nqcc). Посмотрите также на сайт ZetaC.

Обратите внимание, что последние версии GCC технически не написаны на чистом C ++, они представляют собой дюжину предметно-ориентированных языков, задействованных в GCC (некоторые из них являются полными по Тьюрингу). См. Также мой старый проект GCC MELT.

Я не удивлюсь, если в будущих версиях GCC какой-нибудь интерпретатор Python или Guile будет встроен в них (например, в качестве замены диспетчеру проходов GCC).

Посмотрите также проект MILEPOST GCC.

шагов к компилятору C — Уилфред Хьюз :: Блог

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

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

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

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

Для моего компилятора, babyc, я выбрал это моя первая базовая программа:

  int main () {
    возврат 2;
}  

Нет переменных, вызовов функций, внешних зависимостей, операторов if или петли. Не все так страшно.

Нам нужно сначала это разобрать. Мы будем использовать Flex и Bison. Есть пример грамматик C99 что мы можем смотреть, но вся наша грамматика крошечная. Вот лексер:

  "{" {return '{'; }
"}" { возвращаться '}'; }
"(" { возвращаться '('; }
")" { возвращаться ')'; }
";" { возвращаться ';'; }
[0-9] + {return NUMBER; }
"возврат" {возврат ВОЗВРАТ; }
"int" {ТИП возврата; }
"main" {return IDENTIFIER; }
  

Вот грамматика:

  функция:
ИДЕНТИФИКАТОР ТИПА '(' ')' '{' выражение '}'
;
    
выражение:
НОМЕР ВОЗВРАТА ';'
;
  

Наконец, нам нужно сгенерировать некоторую сборку.Мы будем использовать 32-битный x86. сборка, потому что она очень распространена и, вероятно, работает изначально на вашей текущей машине. Есть отличный справочный сайт x86 здесь.

Вот файл сборки, который нам нужно сгенерировать:

 . Текст
        .global _start # Сообщаем загрузчику, что мы хотим начать с _start.

_Начало:
        movl $ 2,% ebx # Аргумент нашего системного вызова.
        movl $ 1,% eax # Номер системного вызова sys_exit равен 1.
        int $ 0x80 # Отправить прерывание  

Подключить лексер и парсер (источник), записать эту сборку в файл (источник), и поздравляю! Вы писатель-компилятор!

Babyc начинался таким образом, и вы можете увидеть эту минимальную версию здесь.

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

  # Вот файл, который мы хотим скомпилировать.
$ cat return_two.c
#include 

int main () {
    возврат 2;
}

# Запустить компилятор с этим файлом.
$ ./babyc return_two.c
Записано.

# Убедитесь, что вывод выглядит разумным.
$ cat out.s
.текст
    .global _start

_Начало:
    movl $ 2,% ebx
    movl $ 1,% eax
    int $ 0x80  

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

  # Соберите файл. Мы явно ассемблируем как 32-битные
# чтобы избежать путаницы на машинах x86_64.
$ как out.s -o out.o --32

# Свяжите файл, снова указав 32-битный.
$ ld -m elf_i386 -s -o out out.o

# Запустить его!
$ ./out

# Какой был код возврата?
$ echo $?
2 # Woohoo!  

Отсюда небо — предел. Вы можете работать с инкрементальным Подойдите к бумаге и постепенно сделайте свой компилятор более сложный. Вам нужно будет построить более сложное дерево синтаксического анализа, тогда пройтись по нему, чтобы сгенерировать сборку.Следующие шаги (1) позволяют произвольные возвращаемые значения (например, return 3; пример кода здесь) а затем (2) добавление поддержки для отрицания значений (например, return ~ 1; пример кода здесь). Каждый дополнительный шаг научит вас больше о C, больше о том, как ваш компьютер действительно работает, и еще мир компиляторов.

Это подход, который использует babyc. В Babyc теперь есть операторы if, циклы, переменные и основная арифметика. Добро пожаловать в проверьте код, но я надеюсь Я искушал попробовать самому.

Не бойтесь мелочей. Здесь очаровательный мир.

rui314 / 8cc: небольшой компилятор C

Примечание. 8cc больше не является активным проектом. Преемник чибич.

8cc — компилятор для языка программирования C. Он предназначен для поддержки всех функций языка C11 сохраняя при этом код как можно меньше и проще.

Компилятор может компилироваться сам. Вы можете видеть его код как реализацию языка C и как пример того, что умеет компилировать этот компилятор.

Исходный код

8cc тщательно написан, чтобы он был кратким и легким для чтения. по возможности, чтобы исходный код стал хорошим учебным материалом чтобы узнать о различных методах, используемых в компиляторах. Вы можете обнаружить, что лексер, препроцессор и парсер Уже полезно узнать, как исходный код C обрабатывается на каждом этапе.

Это не оптимизирующий компилятор. Сгенерированный код обычно в 2 раза или более медленнее, чем GCC. Я планирую реализовать разумный уровень оптимизации в будущем.

8cc поддерживает только x86-64 Linux. Я не планирую делать его портативным, пока Я исправляю все известные ошибки компиляции и реализую этап оптимизации. С 2015 года я использую Ubuntu 14 в качестве платформы для разработки. Однако он должен работать с другими дистрибутивами Linux x86-64.

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

сборка

Запустите make, чтобы построить:

8cc поставляется с модульными тестами. Для запуска тестов укажите «test» в качестве аргумента:

Следующая цель трижды строит 8cc, чтобы убедиться, что Компилятор stage1 может построить stage2, а stage2 может построить stage3. Затем он побайтово сравнивает двоичные файлы stage2 и stage3 для проверки что мы достигаем фиксированной точки.

Автор

Руи Уэяма [email protected]

Ссылки для разработки компилятора C

Помимо популярных книг о компиляторах, таких как «Книга дракона», Я обнаружил, что следующие книги / документы очень полезны разработать компилятор C.Обратите внимание, что стандартные черновые версии очень близки к ратифицированным версиям. Вы можете практически использовать их как стандартные документы.

DoctorWkt / acwj: Путешествие по написанию компиляторов

В этом репозитории Github я документирую свой путь к написанию самокомпилирующийся компилятор для подмножества языка C. Я также выписываю детали, чтобы, если вы хотите продолжить, вам будет объяснено, что Я сделал, почему, и с некоторыми ссылками на теорию компиляторов.

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

Вот шаги, которые я сделал на данный момент:

  • Часть 0: Введение в путешествие
  • Часть 1: Введение в лексическое сканирование
  • Часть 2: Введение в синтаксический анализ
  • Часть 3: Приоритет оператора
  • Часть 4: Фактический компилятор
  • Часть 5: Заявления
  • Часть 6: Переменные
  • Часть 7: Операторы сравнения
  • Часть 8: Утверждения If
  • Часть 9: Циклы в то время как
  • Часть 10: Для циклов
  • Часть 11: Функции, часть 1
  • Часть 12: Типы, часть 1
  • Часть 13: Функции, часть 2
  • Часть 14: Создание кода сборки ARM
  • Часть 15: Указатели, часть 1
  • Часть 16: Правильное объявление глобальных переменных
  • Часть 17: Улучшенная проверка типа и смещения указателя
  • Часть 18: Новый взгляд на L-значения и R-значения
  • Часть 19: Массивы, часть 1
  • Часть 20: Символьные и строковые литералы
  • Часть 21: Больше операторов
  • Часть 22: Идеи проектирования для локальных переменных и вызовов функций
  • Часть 23: Локальные переменные
  • Часть 24: Функциональные параметры
  • Часть 25: Вызовы функций и аргументы
  • Часть 26: Прототипы функций
  • Часть 27: Регрессионное тестирование и приятный сюрприз
  • Часть 28: Добавление дополнительных флагов времени выполнения
  • Часть 29: Немного рефакторинга
  • Часть 30: Проектирование структур, объединений и перечислений
  • Часть 31: Реализация структур, часть 1
  • Часть 32: Доступ к элементам конструкции
  • Часть 33: Внедрение профсоюзов и доступа к членам
  • Часть 34: Перечисления и определения типов
  • Часть 35: Препроцессор C
  • Часть 36: перерыв и продолжение
  • Часть 37: Операторы переключения
  • Часть 38: Висячие другие и многое другое
  • Часть 39: Инициализация переменной, часть 1
  • Часть 40: Инициализация глобальной переменной
  • Часть 41: Инициализация локальной переменной
  • , часть 42: приведение типов и NULL
  • Часть 43: Исправления и другие операторы
  • Часть 44: Постоянное складывание
  • Часть 45: Объявления глобальных переменных, повторное рассмотрение
  • Часть 46: Параметры функции аннулирования и изменения сканирования
  • Часть 47: Подмножество размером
  • Часть 48: Подмножество статических
  • Часть 49: Тернарный оператор
  • Часть 50: Зачистка, часть 1
  • Часть 51: Массивы, часть 2
  • Часть 52: Указатели, часть 2
  • Часть 53: Зачистка, часть 2
  • Часть 54: Регистры разлива
  • , часть 55: Ленивая оценка
  • Часть 56: Локальные массивы
  • Часть 57: Зачистка, часть 3
  • Часть 58: Исправление увеличения / уменьшения указателя
  • Часть 59: Почему не работает, часть 1
  • Часть 60: Прохождение тройного теста
  • Часть 61: Что дальше?
  • Часть 62: Очистка кода

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

Авторские права

Я позаимствовал часть кода и множество идей из Компилятор SubC, написанный Нильсом М. Холмом. Его код находится в открытом доступе. Я думаю, что мой код существенно достаточно разные, чтобы я мог применить к своему коду другую лицензию.

Если не указано иное,

  • весь исходный код и скрипты (c) Warren Toomey под лицензия GPL3.
  • все документы, не являющиеся исходными кодами (например, документы на английском языке, файлы изображений) (c) Уоррен Туми в разделе Creative Общество BY-NC-SA 4.0 лицензия.

Начиная с простого (возможно, самого простого) компилятора C?

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

Вместо того, чтобы писать полный компилятор языка C или соответствующий стандартам (по крайней мере, в начале), я бы предложил ограничиться базовым подмножеством языка, таким как общие операторы, поддержка только целых чисел, а также базовые функции и указатели.Классическим примером этого является Small-C Рона Кейна, ставший популярным благодаря серии статей, написанных в журнале Dr. Dobbs Journal в 1980-х годах. Они издают компакт-диск с вышедшей из печати книгой Джеймса Хендрикса «Компилятор Small-C».

Я бы посоветовал следовать руководству Креншоу, но писать его для компилятора языка C и любого целевого процессора (Креншоу нацелен на ЦП Motorola 68000), на который вы хотите ориентироваться. Для этого вам нужно знать базовую сборку, на какой целевой машине вы хотите запускать скомпилированные программы.Сюда может входить эмулятор для 68000 или MIPS, которые, возможно, на лучше наборов инструкций сборки , чем почтенный набор инструкций CISC для Intel x86 (16/32-бит).

Существует множество потенциальных книг, которые можно использовать в качестве отправных точек для изучения теории (и практики) компиляторов / переводчиков. Прочтите часто задаваемые вопросы о comp.compilers и обзоры у различных онлайн-продавцов книг. Большинство вводных книг написаны как учебники для второкурсников и старших курсов бакалавриата по информатике, поэтому они могут быть медленными при чтении без опыта в области компьютерных наук.Одна из более старых книг, которая может быть более вводной, но более легкой для чтения, чем «Книга дракона» — это Введение в конструкцию компилятора Томаса Парсонса. Он старше, поэтому вы сможете найти подержанный экземпляр у онлайн-продавцов книг по разумной цене.

Итак, я бы сказал, попробуйте начать с учебника Джека Креншоу Let’s Build a Compiler, напишите свой собственный, следуя его примерам в качестве руководства, и создайте основы простого компилятора . После того, как у вас будет эта работа, вы можете лучше решить, где вы хотите ее продолжить с этого момента.

Добавлен:

Что касается процесса начальной загрузки. Поскольку существующие компиляторы C доступны в свободном доступе, вам не нужно беспокоиться о начальной загрузке. Напишите свой компилятор с помощью отдельных существующих инструментов (GCC, Visual C ++ Express, Mingw / djgpp, tcc), и вы можете беспокоиться о самокомпилировании проекта на более позднем этапе. Я был удивлен этой частью вопроса, пока не понял, что вы пришли к идее написания собственного компилятора, прочитав награду ACM Turing Кена Томаса «Размышления о доверии», которая действительно входит в процесс начальной загрузки компилятора.Это модерируемая продвинутая тема, которая также доставляет массу хлопот. Я считаю, что даже загрузка компилятора GCC C в старых системах Unix (Digital OSF / 1 на 64-битной Alpha), которые включали компилятор C, является медленным и трудоемким, подверженным ошибкам процессом.

Другой вопрос, что на самом деле делает такой компилятор, как Yacc. Yacc (еще один компилятор компилятора или Bison от GNU) — это инструмент, предназначенный для упрощения написания синтаксического анализатора компилятора (или переводчика). На основе формальной грамматики для вашего целевого языка, которую вы вводите в yacc, он генерирует синтаксический анализатор , который является частью общей конструкции компилятора.Далее идет Lex (или flex из GNU), который используется для создания лексического анализатора или сканера, который часто используется в сочетании с синтаксическим анализатором, сгенерированным yacc, для формирования скелета внешнего интерфейса компилятора. Эти инструменты делают писателя проще, чем писать лексический анализатор и парсер самостоятельно. В учебнике Креншоу эти инструменты не используются, да и вам это не нужно, многие разработчики компиляторов не всегда их используют. Конечно, Креншоу признает, что синтаксический анализатор учебника довольно прост.

В учебном пособии

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

Как можно написать компилятор C на C?

Как можно написать компилятор C на C? — Переполнение стека

Присоединяйтесь к Stack Overflow , чтобы учиться, делиться знаниями и строить свою карьеру.

Спросил

Просмотрено 47k раз

На этот вопрос уже есть ответы :

Закрыт 7 лет назад.

Этот вопрос может возникнуть из-за неправильного понимания компиляторов с моей стороны, но вот …

В предисловии к первому изданию K&R (стр. Xi) можно найти следующее утверждение:

Операционная система, компилятор C и практически все приложения UNIX (включая все программное обеспечение, использованное для подготовки этой книги) написаны на C.

(курсив мой)

Вот чего я не понимаю: разве этот компилятор C не должен компилироваться, прежде чем он сможет скомпилировать любой код C? И если этот компилятор C написан на C, разве для его компиляции не потребуется уже существующий компилятор C ?!

Единственный выход из этой головоломки бесконечной регрессии (или проблемы курицы и яйца) состоит в том, что компилятор C, написанный на C, на который ссылается K&R, был фактически скомпилирован с уже существующим компилятором C, который был написан на языке, отличном от С.Компилятор C, написанный на C, затем заменил последний.

Или я совсем выключен?

Создан 15 авг.

jub0bsjub0bs

46.7k2222 золотых знака147147 серебряных знаков157157 бронзовых знаков

2

Это называется Bootstrapping, цитата из Википедии:

Если нужен компилятор для языка X, чтобы получить компилятор для языка X (который написан на языке X), как был написан первый компилятор? Возможные методы решения этой проблемы с курицей или яйцом включают:

  1. Реализация интерпретатора или компилятора для языка X на языке Ю.Никлаус Вирт сообщил, что он написал первый компилятор Паскаля в Фортран.
  2. Другой интерпретатор или компилятор для X уже был написан на другой язык Y; так часто загружается Scheme.
  3. Ранние версии компилятора были написаны в подмножестве X для который существовал какой-то другой компилятор; вот как некоторые суперсеты Java, Haskell и исходный компилятор Free Pascal загрузился.
  4. Компилятор для X скомпилирован из другой архитектуры, где существует компилятор для X; вот как компиляторы для C обычно портируется на другие платформы.Также этот метод используется для Освободите Паскаль после начальной загрузки.
  5. Написание компилятора на X; затем вручную скомпилировать его из исходного кода (большинство вероятно, не оптимизированным способом) и запустив это в коде, чтобы получить оптимизированный компилятор. Дональд Кнут использовал это для своего веб-грамотного система программирования.

И если вам интересно, вот первый исходный код компилятора C Денниса Ричи.