Как написать простейший компилятор
Лучший способ понять как работает компилятор — написать свой собственный. В этом поможет этот краткий, но исчерпывающий гайд.
Стандартный компилятор осуществляет следующие шаги:
- Парсинг: исходный текст конвертируется в абстрактное синтаксическое дерево (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++
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. Первая задача пошагового руководства — найти нужную командную строку.
Примечание
Ярлык командной строки разработчика автоматически задает правильные пути для компилятора и средств, а также для всех необходимых заголовков и библиотек. Эти значения среды необходимо задавать самостоятельно, если используется обычное окно командной строки. Дополнительные сведения см. в статье Установка переменных пути и среды при построении из командной строки. Рекомендуется использовать ярлык командной строки разработчика вместо создания собственного.
Открытие командной строки разработчика
Если вы установили Visual Studio 2017 или более поздней версии в Windows 10, откройте меню «Пуск» и выберите
Если вы установили Microsoft Visual C++ Build Tools 2015 в Windows 10, откройте меню Пуск и выберите Все приложения. Прокрутите вниз и откройте папку Microsoft Visual C++ Build Tools. Выберите элемент Командная строка Native Tools x86 Visual C++ 2015, чтобы открыть окно командной строки.
Можно также ввести «командная строка разработчика» в строке поиска в Windows и выбрать командную строку, которая соответствует установленной версии Visual Studio. Откройте окно командной строки с помощью ярлыка.
Затем убедитесь в том, что командная строка разработчика 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++ и его компиляция из командной строки
В окне командной строки разработчика введите
md c:\hello
, чтобы создать каталог, а затем введитеcd c:\hello
, чтобы перейти к этому каталогу. В этом каталоге создаются файл исходного кода и скомпилированная программа.В окне командной строки введите
notepad hello.cpp
.Когда Блокнот предложит создать файл, выберите Да. Откроется пустое окно Блокнота, в котором можно ввести код для файла hello.cpp.
В окне блокнота введите следующие строки кода:
#include <iostream> using namespace std; int main() { cout << "Hello, world, from Visual C++!" << endl; }
Это простая программа, которая выведет одну строку текста на экран, а затем завершит работу. Для сведения числа ошибок к минимуму скопируйте этот код и вставьте его в Блокнот.
Сохраните файл. В Блокноте, в меню Файл выберите Сохранить.
Поздравляем, вы создали исходный файл C++ hello.cpp, который готов к компиляции.
Вернитесь к окну командной строки разработчика. Введите
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
в элемент управления «Поле ввода» Имя файла и нажмите кнопку Сохранить, чтобы сохранить файл.В командной строке разработчика введите
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, командная строка разработчика настроена неправильно. Чтобы получить сведения о том, как устранить эту проблему, вернитесь к разделу Открыть командную строку разработчика.
Примечание
Если вы получаете другое сообщение об ошибке или предупреждение компилятора или компоновщика, проверьте исходный код, исправьте ошибки, сохраните его и снова запустите компилятор. Для получения сведений о конкретных ошибках введите номер ошибки в поле поиска.
Чтобы запустить программу 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.
- Нам понадобится простой пример для компиляции. Скопируйте следующий код программы на Си и сохраните его в файле
hello.c
в новой папке на вашем локальном диске:#include <stdio.h> int main(int argc, char ** argv) { printf("Hello World\n"); }
- Теперь, используя терминал, перейдите в каталог, в котором находится ваш файл 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. Давайте посмотрим, как это сделать:
Прежде всего, сохраните следующий код в файле hello2.c в новом каталоге:
#include <stdio.h> int main(int argc, char ** argv) { printf("Hello World\n"); }
Найдите файл
shell_minimal.html
в вашем репозитории emsdk. Скопируйте его в подкаталогhtml_template
внутри вашего нового каталога.Теперь, используя терминал, перейдите в ваш новый каталог и выполните следующую команду:
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 страницы запускающей этот пример.
- Мы указали
Теперь давайте запустим этот пример. Команда, указанная выше, сгенерирует файл
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
которое добавит вашу функцию в список экспортируемых функций (см. Почему функции в моем коде исчезают после компиляции и/или я получаю сообщение «Нет функций для обработки»). Давайте посмотрим, как это работает.
Для начала сохраните следующий код в файле
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++ код. Из за различия в правилах преобразования имён между Си и Си++, этот код может сломаться, но мы написали его так, что функция будет рассматриваться как функция Си даже если вы будете использовать Си++.Теперь добавьте
html_template/shell_minimal.html
в ваш новый каталог, просто для удобства. В настоящем проекте стоит размещать его в специально определённый каталог.Теперь снова займёмся этапом компиляции. Внутри вашего последнего каталога, используя терминал, скомпилируйте ваш Си код следующей командой. (Обратите внимание что при компиляции обязательно нужно использовать опцию 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"]'
Если вы снова запустите пример в своём браузере, вы увидите тоже самое что и до этого!
Теперь нам нужно вызвать нашу новую функцию
myFunction()
из JavaScript. Прежде всего, добавьте<button>
как показано ниже, чуть выше первого открывающего тега<script type='text/javascript'>
.<button>Run myFunction</button>
Теперь добавьте следующий код в конце первого элемента
<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 | ||||||
---|---|---|---|---|---|---|---|---|
C | C++ | Fortran | Fortran 90 | Fortran 95 | Fortran 2003 | Fortran 2008 | ||
GNU | gcc | g++ | gfortran | — | — | — | — | |
IBM XL | xlc | xlC/xlc++ | xlf | xlf90 | xlf95 | xlf2003 | xlf2008 | |
потокобезопасная версия | xlc_r | xlC_r/xlc++_r | xlf_r | xlf90_r | xlf95_r | xlf2003_r | xlf2008_r |
Для создания и запуска программ написанных с использованием MPI стандарта необходимо загрузить модуль SpectrumMPI. Для этого необходимо выполнить следующую коменду
module load SpectrumMPI
Возможно также использование OpenMPI, для его загрузки нужно выполнить
module load OpenMPI
Однако использование SpectrumMPI предпочтительней. Всё дальнейшее описание будет приводится для SpectrumMPI.
Компиляторы программ, использующих стандарт MPI перечислены в таблице ниже.
Компилятор | C/C++ | Fortran | |||
---|---|---|---|---|---|
C | C++ | Fortran | Fortran 77 | Fortran 90 | |
mpicc | mpicxx | mpifort | mpif77 | mpif90 | |
IBM XL | mpixlc | mpixlC | mpixlf | — | — |
Ключ | Значение |
---|---|
-I include_dir | опция, которая задаёт пути для поиска include файлов |
-L library_dir | задаёт пути для поиска подключаемых библиотек. |
-l library_name | линкует с указанной библиотекой |
-o file_name | задаёт имя бинарного файла, который будет получен как результат деятельности компилятора |
-g | добавление отладочной информации в бинарный код, после этого при запуске программы через отладчик будет доступна информация об исходном коде программы. |
-ON | Опции начального уровня оптимизации (N число от 1 до 5) |
Ключ | Значение |
---|---|
-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++
Откройте мастер «Новый проект» с помощью команды FIle («Файл») > New Project («Новый проект»).
Выберите категорию «C/C++» в мастере.
Мастер предлагает возможность выбора типа нового проекта. Выберите параметр «Приложение на C/C++» и нажмите кнопку «Далее».
Создайте в мастере новый проект «Приложение C/C++», используя параметры по умолчанию. Название проекта и его расположение выбираются разработчиком.
Для выхода из мастера нажмите кнопку «Готово».
Проект создается с логическими папками. Логическая папка не является каталогом. Она представляет собой способ упорядочения файлов и не отражает физическое расположение файлов на диске. Добавленные в логические папки файлы автоматически становятся частью проекта и компилируются при сборке проекта.
Файлы, добавленные в папку ‘Важные файлы’, не являются частью проекта и не компилируются при сборке проекта. Эти файлы являются справочной информацией и могут быть полезны разработчику, когда имеется проект с существующим файлом make.
Переключение логического и физического представления проекта
Проект имеет логическое и физическое представление. Разработчик может переключаться между логическим и физическим представлением проекта.
Перейдите на вкладку «Файлы». В этом окне содержится физическое представление проекта. В нем отображается расположение файлов и папок на диске.
Перейдите на вкладку «Проекты». В этом окне содержится логическое представление проекта.
Большинство команд, выполняемых в проекте, доступны на вкладке «Проекты», а не на вкладке «Файлы».
Добавление файлов и папок в проект
Разработчик может добавлять в проект логические папки.
Щелкните узел проекта «Приложение» правой кнопкой мыши и выберите команду «Новая логическая папка». В проект будет добавлена новая логическая папка.
Щелкните добавленную логическую папку правой кнопкой мыши и выберите команду «Переименовать». Введите имя для новой папки.
В существующую папку можно добавлять файлы и папки. Логические папки могут быть вложенными.
Добавление новых файлов в проект
В проект также можно добавить новые файлы.
Щелкните правой кнопкой мыши папку «Исходные файлы» и выберите команду «Создать» > «Исходный файл на языке C++».
Перейдите на страницу «Имя и местоположение» диалогового окна «Новый файл» и введите
newfile
в поле «Имя файла».
Нажмите кнопку ‘Готово’.
Файл newfile.cpp
создается на диске в каталоге, указанном в мастере, а также добавляется в папку «Исходные файлы». В эту папку можно добавлять любые файлы, а не только файлы исходного кода.
Добавление дополнительных новых файлов в проект
Щелкните папку «Файлы заголовка» правой кнопкой мыши и выберите команду «Создать» > «Файл заголовка на языке C++».
Перейдите на страницу «Имя и местоположение» диалогового окна «Новый файл» и введите
newfile
в поле «Имя файла».Нажмите кнопку ‘Готово’.
Файл newfile.h
создается на диске в каталоге, указанном в мастере, а также добавляется в папку «Исходные файлы».
Добавление существующих файлов в проект
Существующие файлы добавляются в проект двумя способами:
Правой кнопкой щелкните папку «Исходные файлы» и выберите команду «Добавить существующий элемент». Чтобы установить ссылку на существующий файл на диске, откройте диалоговое окно «Выбрать элемент» и добавьте файл в проект.
Правой кнопкой щелкните папку «Исходные файлы» и выберите команду «Добавить существующие элементы из папок». Используйте диалоговое окно «Добавить файлы» для добавления папок, содержащих существующие файлы.
Не используйте команду меню «Новый» для добавления существующих элементов. На панели «Имя и местоположение» отображаются сведения о существовании файла.
Установка свойств проекта
После создания проекта у него появляются две конфигурации: ‘Отладка’ и ‘Выпуск’. Настройка – это набор параметров, использованных для проекта. При выборе настройки несколько параметров переключаются одновременно. При выборе настройки «Отладка» создается версия приложения, которая содержит сведения об отладке. При выборе настройки «Выпуск» создается оптимизированная версия.
В диалоговом окне «Свойства проекта» содержатся сведения о сборке и настройке проекта. Открытие диалогового окна «Свойства проекта»
Разработчик может изменять параметры средства сборки по умолчанию, параметры компилятора и другие параметры настройки в диалоговом окне «Свойства проекта». Для этого следует выбрать узел на левой панели и изменить свойства на правой панели. Выберите несколько узлов и значений свойств и обратите внимание на то, какие свойства можно установить. При установке свойств категории «Общие» они устанавливаются для всех настроек проекта. Свойства ‘Сборка’, ‘Запуск’ или ‘Отладка’ устанавливаются для текущей выбранной конфигурации.
Управление настройками
Свойства, измененные в окне «Свойства проекта», хранятся в файле make для текущей настройки. Разработчик может изменять настройки по умолчанию или создавать новые. Для создания новой настройки выполните следующие действия.
Нажмите кнопку «Управление настройками» в диалоговом окне «Свойства проекта».
В диалоговом окне «Настройки» выберите настройку, которая больше всего совпадает с требуемой настройкой. В этом случае выберите настройку «Выпуск» и нажмите кнопку «Дублировать». Затем нажмите кнопку «Переименовать».
В диалоговом окне «Переименовать» переименуйте настройку в PerformanceRelease. Нажмите кнопку «ОК».
Нажмите кнопку «ОК» в диалоговом окне «Настройки».
На левой панели выберите узел «Компилятор C».
В диалоговом окне «Свойства проекта» обратите внимание на то, что в раскрывающемся списке «Настройка» выбрана настройка «PerformanceRelease».
В окне свойств на правой панели измените значение «Режим разработки» со значения «Release» на значение «PerformanceRelease». Нажмите кнопку «ОК».
Создана новая настройка, которая служит для компиляции приложения с другим набором параметров.
Установка свойств файлов исходного кода
При установке свойств проекта C или C++ они действуют для всех файлов проекта. Также можно установить некоторые свойства для отдельных файлов.
Щелкните правой кнопкой мыши исходный файл
newfile.cpp
на вкладке «Проекты» и выберите команду «Свойства».Щелкните категорию «Общее» и обратите внимание, что существует возможность указания различных компиляторов или других средств для сборки этого файла. Также можно использовать флажок для исключения файла из сборки выбранной в настоящий момент настройки проекта.
Щелкните категорию «Компилятор C++» и убедитесь, что существует возможность переопределения параметров компилятора и других свойств этого файла.
Отмените работу с диалоговым окном «Свойства файла».
Сборка и пересборка проекта
Для сборки проекта выполните следующие действия.
Правой кнопкой мыши щелкните узел проекта и выберите команду «Сборка», после чего будет выполнена сборка проекта. Результат сборки выводится в окне «Вывод»
Замените настройку «Отладка» на настройку «PerformanceRelease» в раскрывающемся списке настроек на главной панели инструментов. Проект будет собран с использованием настройки PerformanceRelease.
Правой кнопкой мыши щелкните узел проекта и выберите команду «Сборка», после чего будет выполнена сборка проекта. Результат сборки выводится в окне «Вывод»
Для повторной сборки проекта:
Щелкните узел проекта правой кнопкой мыши и выберите ‘Очистить и собрать’, чтобы выполнить полную повторную сборку проекта после удаления результатов предыдущей сборки.
Щелкните узел проекта правой кнопкой мыши и выберите ‘Собрать’, чтобы выполнить инкрементную сборку. Результаты предыдущей сборки сохраняются, если их исходные файлы не изменены.
Сборку, очистку или очистку со сборкой проекта можно выполнять с помощью команд в меню ‘Выполнить’ или с помощью кнопок на панели инструментов. Файлы объектов и исполняемые файлы разных настроек также хранятся отдельно, что предотвращает смешение файлов различных настроек.
Компиляция отдельного файла
Для компиляции отдельного файла исходного кода выполните следующие действия.
Компиляция отдельных файлов не поддерживается для типа проекта «Проект на C/C++ с существующими исходными файлами».
КОМПИЛЯТОР • Большая российская энциклопедия
В книжной версии
Том 14. Москва, 2009, стр. 688
Скопировать библиографическую ссылку:
Авторы: А. Ю. Дроздов, А. В. Ильин
КОМПИЛЯ́ТОР (от лат. compilo – собирать, составлять, компилировать) в информатике, компьютерная программа, которая преобразует исходный код программы на языке программирования высокого уровня (ЯПВУ) в функционально эквивалентный набор инструкций на языке низкого уровня (т. н. объектный код). К. является одним из видов транслятора и, как правило, входит в состав системного программного обеспечения компьютера. Каждый К. соответствует определённому ЯПВУ (паскалю, си, фортрану и др.) и одной или нескольким вычислит. платформам. Вычислит. платформа определяется архитектурой семейства центр. процессоров; напр., x86 – Intel 8086 Family Architecture (архитектура семейства Intel 8086), операционной системой и, в ряде случаев, дополнит. программным обеспечением (напр., виртуальной машиной), необходимым для работы исполняемого кода на процессорах данной архитектуры.
К. выполняет лексич., синтаксич., семантич. анализ исходного кода программы и генерацию объектного кода. На этапе лексич. анализа исходный код преобразуется в последовательность лексич. единиц – лексем (ключевые слова языка программирования, идентификаторы переменных, константы и др.). Во время синтаксич. анализа осуществляется проверка последовательности лексем на наличие синтаксич. ошибок (в соответствии с синтаксич. правилами языка программирования) и затем – преобразование этой последовательности в т. н. дерево разбора. Семантич. анализ предназначен для выявления логич. ошибок в исходной программе и определения значения языковых конструкций дерева разбора. После этого К. либо переходит к генерации объектного кода, либо завершает работу выводом сообщения об ошибках. Исходный код программы, как правило, содержится в нескольких файлах. К. преобразует каждый из них в отд. объектный модуль (файл, содержащий объектный код), а затем спец. программа-компоновщик собирает исполняемый код программы из объектных модулей, стандартных библиотечных модулей и др. До создания компоновщиков сборка производилась компилятором.
Для эффективного использования особенностей архитектуры процессоров и улучшения характеристик исполняемого кода (производительности, компактности и др.) в К. встроены компоненты, оптимизирующие код. Различают оптимизацию машинно независимую (т. е. не зависящую от архитектуры процессора) и машинно зависимую. Машинно независимая оптимизация часто выполняется на этапе генерации объектного кода. Машинно зависимые преобразования направлены на эффективное использование особенностей архитектур процессоров, оптимальное распараллеливание программ (для многоядерных процессоров) и др.
Альтернативой К. и компоновщикам служат интерпретаторы. Для ряда ЯПВУ существуют как К., так и интерпретаторы. Для некоторых ЯПВУ (напр., Java) применяется двухэтапная компиляция или сочетание компиляции и интерпретации. На первом этапе на спец. промежуточном языке генерируется т. н. байт-код, который не привязан к конкретной операционной системе и архитектуре семейства процессоров. Байт-код предназначен для последующей интерпретации или т. н. JIT-компиляции (Just-In-Time compilation – компиляция «на лету») во время выполнения программы. На втором этапе преобразование байт- кода в исполняемый код (для конкретных операционной системы и процессора) осуществляется спец. программным обеспечением (напр., Java Virtual Machine – виртуальной машиной 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 авг.
jub0bsjub0bs46.7k2222 золотых знака147147 серебряных знаков157157 бронзовых знаков
2Это называется Bootstrapping, цитата из Википедии:
Если нужен компилятор для языка X, чтобы получить компилятор для языка X (который написан на языке X), как был написан первый компилятор? Возможные методы решения этой проблемы с курицей или яйцом включают:
- Реализация интерпретатора или компилятора для языка X на языке Ю.Никлаус Вирт сообщил, что он написал первый компилятор Паскаля в Фортран.
- Другой интерпретатор или компилятор для X уже был написан на другой язык Y; так часто загружается Scheme.
- Ранние версии компилятора были написаны в подмножестве X для который существовал какой-то другой компилятор; вот как некоторые суперсеты Java, Haskell и исходный компилятор Free Pascal загрузился.
- Компилятор для X скомпилирован из другой архитектуры, где существует компилятор для X; вот как компиляторы для C обычно портируется на другие платформы.Также этот метод используется для Освободите Паскаль после начальной загрузки.
- Написание компилятора на X; затем вручную скомпилировать его из исходного кода (большинство вероятно, не оптимизированным способом) и запустив это в коде, чтобы получить оптимизированный компилятор. Дональд Кнут использовал это для своего веб-грамотного система программирования.
И если вам интересно, вот первый исходный код компилятора C Денниса Ричи.
Создан 15 авг.
Ю Хао Ю Хао111k4040 золотых знаков210210 серебряных знаков267267 бронзовых знаков
См. Раздел «Курица и яйцо» на странице Википедии:
Если нужен компилятор для языка X, чтобы получить компилятор для языка X (который написан на языке X), как был написан первый компилятор? Возможные методы решения этой проблемы с курицей или яйцом включают:
- Реализация интерпретатора или компилятора для языка X на языке Y.Никлаус Вирт сообщил, что он написал первый компилятор Паскаля на Фортране.
- Другой интерпретатор или компилятор для X уже был написан на другом языке Y; так часто загружается Scheme.
- Ранние версии компилятора были написаны в подмножестве X, для которого существовал какой-то другой компилятор; так загружаются некоторые надмножества Java, Haskell и исходного компилятора Free Pascal.
- Компилятор для X скомпилирован из другой архитектуры, где существует компилятор для X; именно так компиляторы для C обычно переносятся на другие платформы.Также этот метод используется для Free Pascal после начальной загрузки.
- Написание компилятора на X; затем вручную скомпилируйте его из источника (скорее всего, не оптимизированным способом) и запустите его в коде, чтобы получить оптимизированный компилятор. Дональд Кнут использовал это для своей системы грамотного программирования WEB.
Создан 15 авг.
Паскаль КуокПаскаль Куок75.4k66 золотых знаков144144 серебряных знака260260 бронзовых знаков
1Обычно первый компилятор пишется на другом языке (в данном случае непосредственно на ассемблере PDP11 или на C для большинства «современных» языков). Затем этот первый компилятор используется для программирования компилятора, написанного на самом языке.
Вы можете прочитать эту страницу об истории языка Си.Вы увидите, что он также сильно связан с системой UNIX.
Создан 15 авг.
perrorperror6,3291616 золотых знаков5656 серебряных знаков7272 бронзовых знака
3Для компилятора совершенно нормально быть написанным на языке, который он компилирует.Один из способов добиться этого — написать полный компилятор для языка L на каком-то другом языке, а затем написать новый компилятор для L в L. Более интересным подходом было бы написать минимальный компилятор для подмножества L в некотором другой язык, а затем используйте это минимальное подмножество для улучшения компилятора, сделав его менее минимальным, увеличив доступное подмножество L. Таким образом, можно построить полный компилятор.
Создан 15 авг.
Джон КипарскиДжон Кипарски6,84422 золотых знака2020 серебряных знаков3737 бронзовых знаков
язык-с
Stack Overflow лучше всего работает с включенным JavaScriptВаша конфиденциальность
Нажимая «Принять все файлы cookie», вы соглашаетесь с тем, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.