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

Что такое компилятор?
Ответ звучит так: «Компилятор — это один из компонентов цепочки инструментов программ, используемых для создания исполняемых файлов из исходного кода».

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

Спасибо за чтение ПОКА !!
Подождите, вы ничего не поняли :[

Давайте начнем с некоторой базовой истории -:

  • Первый компилятор был разработан Грейс Хоппер, известной как мать COBOL. в 1952 г. Ее компилятор, названный А-0, был разработан для перевода математических уравнений в машинный код для компьютера UNIVAC I
    Говоря простым языком. А -> 01000001
  • Развитие языков программирования, таких как Fortran, Algol и COBOL, в конце 1960-х годов привело к созданию более совершенных компиляторов. Эти компиляторы были разработаны для обработки более сложных конструкций программирования и создания более эффективного машинного кода.
  • Затем разработка языков высокого уровня, таких как C и Pascal, привела к созданию компиляторов, которые могли генерировать код для нескольких платформ. Это означало, что разработчики могли один раз написать код, а затем скомпилировать его для разных операционных систем и аппаратных архитектур.

Хорошо, все еще очень запутанно и неясно, но, по крайней мере, теперь вы знаете крутые вещи 60-х.

Компиляция (эпическая история компилятора)

Давайте разберемся на примере, подумайте о программе набора текста, такой как TEX, которая переводит рукопись в документ Postscript.
Более простой вариант → Веб-браузер переводит HTML-документ в интерактивный графический дисплей.

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

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

  • Сканер использует обычный текст программы и группирует отдельные символы, чтобы сформировать полные токены. Это очень похоже на группировку символов в слова на естественном языке.
  • Синтаксический анализатор использует токены и группирует их вместе в полные операторы и выражения, подобно тому, как слова группируются в предложения на естественном языке. Синтаксический анализатор руководствуется грамматикой,которая устанавливает формальные правила композиции на данном языке. Результат работы синтаксического анализатора — абстрактное синтаксическое дерево (AST), которое фиксирует грамматические структуры программы. AST также запоминает, где в исходном файле появилась каждая конструкция, поэтому он может генерировать целевые сообщения об ошибках.
  • Семантические подпрограммы проходят через AST и извлекают дополнительное значение (семантику) о программе из правил языка и отношений между элементами программы. Например, мы можем определить, что m+ 23 является выражением с плавающей запятой, наблюдая за типом m из более раннего объявления, а затем применяя правило языка, согласно которому сложение между значениями int и float дает число с плавающей запятой. После семантических подпрограмм AST часто преобразуется в промежуточноепредставление (IR), которое представляет собой упрощенную форму ассемблерного кода, подходящую для детального анализа.
  • Генератор кода использует оптимизированный IR и преобразует его в конкретную программу на языке ассемблера. Как правило, генератор кода должен выполнять распределение регистровдля эффективного управления ограниченным числом аппаратных регистров, а также выбор инструкцийи упорядочиваниедля упорядочения инструкций сборки в самая эффективная форма.

Компиляция с примером:
высота = (длина+23) * фактор(сумма)

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

id:height = (id:length + int:23) * id:factor (id:sum);

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

Это просто проверить :)
1. zexy→ zexy+ zexy
2. zexy→ zexy* zexy
3. zexy→ zexy = zexy
4. zexy→ id(zexy)
5. zexy→(zexy)
6. zexy→ id
7. zexy→ int

См. его ZEXY

Каждая строка грамматики называется правилом и объясняет, как построены различные части языка.

Анализатор ищет последовательности токенов, которые можно заменить левой частью правила в нашей грамматике. Каждый раз, когда применяется правило, синтаксический анализатор создает узел в дереве и соединяет подвыражения в абстрактное синтаксическое дерево (AST). AST показывает структурные отношения между каждым символом: сложение выполняется по длине и 23, а вызов функции применяется к множителю и сумме.

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

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

Промежуточное представление
LOAD $23 -> r1
Длина LOAD -> r2
IADD r1, r2 -> r3
Сумма ARG
Коэффициент CALL -> r4< br /> IMUL r3, r4 -> r5
STOR r5 -> высота

В промежуточном представлении происходит большинство форм оптимизации. Мертвый код удаляется, общие операции объединяются, а код в целом упрощается, чтобы потреблять меньше ресурсов и выполняться быстрее.
И :)
Наконец, промежуточный код необходимо преобразовать в желаемый ассемблерный код. Ассемблерный код X86, который является одним из возможных переводов IR, приведенного выше. Обратите внимание, что инструкции по сборке не обязательно полностью совпадают с инструкциями IR.

Длина MOVQ, %rax
ADDQ $23, %rax
MOVQ %rax, -8(%rbp) # сохранить сумму во временном
MOVQ sum, %edi # загрузить забавную сумму в arg 0 register
CALL factor # вызвать фактор, результат rax
MOVQ -8(%rbp), %rbx # загрузить сумму в rbx
IMULQ %rbx # умножить rbx на rax
MOVQ %rax, height # сохранить результат в height
(:| эта уродливая штука — ассемблерный код)

Затем это преобразуется в двоичный код (0 и 1), который может быть понят процессором.

КАК СОЗДАТЬ КОМПИЛЯТОР

Предупреждение

Это немного сложно, но выполнимо. Хорошо, давайте начнем с того, какую платформу и язык программирования использовать, это зависит от ваших предпочтений. Я выбираю dotnet с С#.

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

Начните с лексера, синтаксического анализатора и оценщика
, который может обрабатывать +, -, *, / и выражения в скобках и печатать синтаксические деревья. Вы можете написать парсер рекурсивного спуска
Обобщенный анализ с использованием приоритетов поддерживать унарные операторы, такие как +2 и -3 логические литералы (false, true), и условия, такие как 1 == 3 && 2 != 3 || true

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

Нужна помощь с кодом визит :) -› SourCompiler

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

перейдите по ссылке ниже, чтобы начать

Некоторые книги и ссылки для продолжения

~ by Aryan
Polymath 147