Как мне создать свой собственный язык программирования и компилятор для него [закрыто]


427

Я разбираюсь в программировании и сталкивался с такими языками, как BASIC, FORTRAN, COBOL, LISP, LOGO, Java, C ++, C, MATLAB, Mathematica, Python, Ruby, Perl, JavaScript, Assembly и так далее. Я не могу понять, как люди создают языки программирования и разрабатывают компиляторы для этого. Я также не мог понять, как люди создают такие ОС, как Windows, Mac, UNIX, DOS и так далее. Другая загадочная вещь для меня - это то, как люди создают библиотеки, такие как OpenGL, OpenCL, OpenCV, Cocoa, MFC и так далее. Последнее, что я не могу понять, - как ученые разрабатывают язык ассемблера и ассемблер для микропроцессора. Я действительно хотел бы изучить все эти вещи, и мне 15 лет. Я всегда хотел быть ученым-информатором, таким как Бэббидж, Тьюринг, Шеннон или Деннис Ричи.


Я уже читал книгу Aho's Compiler Design и Tanenbaum's OS, и все они обсуждают концепции и код только на высоком уровне. Они не вдавались в детали и нюансы, а также в то, как разработать компилятор или операционную систему. Мне нужно конкретное понимание, чтобы я мог создать его сам, а не просто понять, что такое поток, семафор, процесс или анализ. Я спросил моего брата обо всем этом. Он студент SB в EECS в Массачусетском технологическом институте и не имеет ни малейшего представления о том, как на самом деле создать все эти вещи в реальном мире. Все, что он знает, - это просто понимание дизайна компилятора и концепций ОС, подобных тем, которые вы, ребята, упомянули (например, такие как поток, синхронизация, параллелизм, управление памятью, лексический анализ, генерация промежуточного кода и так далее)


Если вы на Unix / Linux, вы можете получить информацию о специальных инструментах: lex, yaccи bison.
Mouviciel

Моим первым предложением было бы прочитать книгу дракона Ахо. amazon.com/Compilers-Principles-Techniques-Alfred-Aho/dp/…
Джулиан,

1
Возможно, это не слишком полезно, но я рекомендую просмотреть sites.google.com/site/steveyegge2/blog-rants (блог Стива Йегге) и steve-yegge.blogspot.com/ (другой блог Стива Йегге).
К.К.

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

1
подсказка: интерпретатор легче компилятора; это просто класс, который «делает что-то» на основе входного текста, который он читает построчно. Еще один совет: свяжите это с отражением, и вы можете управлять произвольными объектами с помощью вашего скрипта.
Дэйв Кузино

Ответы:


407

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

Тем не менее, я могу взять трещину в:

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

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

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

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

В качестве альтернативы рассмотрите интересующий вас домен, а затем спроектируйте домен-ориентированный язык (DSL), который определяет решения проблем в этом домене. Вы упомянули ЛОГОС; это отличный пример DSL для домена "рисования линий". Регулярные выражения представляют собой DSL для домена «найти шаблон в строке». LINQ в C # / VB - это DSL для домена «фильтровать, объединять, сортировать и проектировать данные». HTML - это DSL для домена «опишите расположение текста на странице» и т. Д. Есть много доменов, которые поддаются языковым решениям. Одним из моих любимых является Inform7, который является DSL для домена "текстовая приключенческая игра"; это, наверное, самый серьезный язык программирования высшего уровня, который я когда-либо видел.

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

  1. лексический : каковы правила для слов в языке, какие символы являются законными, как выглядят числа и так далее.
  2. синтаксический : как слова языка объединяются в большие единицы? В C # более крупными единицами являются такие вещи, как выражения, операторы, методы, классы и так далее.
  3. семантическая : с учетом синтаксически легальной программы, как вы выясните, что делает программа ?

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

Один из лучших способов начать писать компилятор - это написать компилятор с языка высокого уровня на язык высокого уровня . Напишите компилятор, который принимает строки на вашем языке и выплевывает строки в C # или JavaScript или на любом другом языке, который вы знаете; пусть компилятор для этого языка позаботится о том, чтобы превратить его в работающий код.

Я пишу блог о дизайне C #, VB, VBScript, JavaScript и других языков и инструментов; если эта тема вас интересует, проверьте это. http://blogs.msdn.com/ericlippert (исторический) и http://ericlippert.com (текущий)

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

http://blogs.msdn.com/b/ericlippert/archive/2010/02/04/how-many-passes.aspx

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


Вы писали о том, в какой степени оптимизации компилятора больше не выполняются, поскольку CLR может выполнять их автоматически?

6
@ Thorbjørn: Давайте проясним терминологию. «Компилятор» - это любое устройство, которое переводит с одного языка программирования на другой. Одна из приятных особенностей наличия компилятора C #, который превращает C # в IL, и компилятора IL («джиттера»), который превращает IL в машинный код, заключается в том, что вы можете написать компилятор C # в IL (легко!), И поместите специфичные для процессора оптимизации в дрожание. Дело не в том, что оптимизация компилятора «не выполняется», а в том, что команда jit compiler делает их за нас. См. Blogs.msdn.com/b/ericlippert/archive/2009/06/11/…
Эрик Липперт,

6
@ Cyclotis04: Inform6 компилируется в Z-код, который является известным чрезвычайно ранним примером виртуальной машины на основе байт-кода. Вот почему все эти игры Infocom в 1980-х годах могли быть больше, чем память, и переносимы на несколько архитектур; игры были скомпилированы в z-код, а затем были реализованы интерпретаторы z-кода с разбиением на страницы памяти кода для нескольких машин. В настоящее время, конечно, вы можете запускать интерпретатор zcode на наручных часах, если нужно, но в те времена, когда это было в стиле хай-тек . Смотрите en.wikipedia.org/wiki/Z-machine для деталей.
Эрик Липперт

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

2
@dhams: устройство - это любая вещь, созданная для определенной цели. Каждый компилятор, который я когда-либо писал, запускался на оборудовании, специально разработанном для того, чтобы компиляторы могли существовать.
Эрик Липперт

127

Вы можете найти Lets Build a Compiler от Jack Crenshaw интересным введением в написание компиляторов и ассемблера.

Автор сделал это очень просто и сосредоточился на создании реальной функциональности.


2
Что интересно в вступлении Креншоу, так это в том, что оно заканчивается (спойлер: оно неполное), как раз в то время, когда вы столкнетесь с проблемами, которые заставят вас осознать, эй, я действительно должен был полностью разработать свой язык, прежде чем приступить к его реализации. И затем вы говорите: эй, если мне нужно написать полную языковую спецификацию, почему бы не сделать это в формальной нотации, которую я затем смогу ввести в инструмент для генерации парсера? И тогда вы делаете это, как и все остальные.
любезно

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

72

«Я бы очень хотел изучить этот материал». Если вы серьезны в долгосрочной перспективе:

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

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

  • Соберите / прочитайте тексты стандартного компилятора: Aho / Ullman и т. Д. Они содержат то, что сообщество обычно соглашается, является фундаментальным материалом. Вы можете не использовать все из этих книг, но вы должны знать, что оно существует, и вы должны знать, почему вы его не используете. Я думал, что Мучник был великолепен, но это для довольно продвинутых тем.

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

  • Хорошее начало - это изучение BNF (Backus Naur Form), парсеров и генераторов парсеров. BNF эффективно повсеместно используется на земле компиляторов, и вы не можете реально общаться с другими типами компиляторов, если вы этого не знаете.

Если вы хотите получить хорошее первое введение в компиляцию и прямое значение BNF не только для документации, но и для метаязыка, обрабатываемого инструментом, посмотрите этот учебник (не мой) по созданию «мета» компиляторов (компиляторов, которые создают компиляторы) на основе статья 1964 года (да, вы правильно прочитали) [«META II - синтаксически-ориентированный язык написания компиляторов», автор Val Schorre. (http://doi.acm.org/10.1145/800257.808896)] Это ИМХО является одним из лучших когда-либо написанных сочинений: оно учит вас создавать компиляторы-компиляторы на 10 страницах. Изначально я узнал из этой статьи.

То, о чем я писал выше, во многом основано на личном опыте, и я думаю, что оно мне очень помогло. YMMV, но ИМХО, не намного.


54
-1 Ничего из вышеперечисленного не нужно.
Нил Баттерворт

77
@nbt Ничего из вышеперечисленного не требуется. Но все вышеперечисленное помогает. Действительно много.
Конрад Рудольф

1
Я особенно не согласен с «Учим математику мыслить абстрактно!» предложение. Даже если вы думаете, что «учиться мыслить абстрактно» особенно полезно при создании собственного языка программирования и компилятора (я не считаю, что гораздо полезнее учиться на практике, чем на этих обходных, невероятно косвенных маршрутах) Математика - не единственное поле с абстрактным мышлением! (Кстати, я математик, поэтому я не отрицаю использование математики в целом, только ее применимость в данном конкретном случае ...)
grautur

26
Если вы хотите прочитать дополнительные технические статьи по теории компиляторов, вам лучше быть математически компетентным. Вы можете решить игнорировать эту литературу, и ваша теория и, следовательно, составители будут беднее для нее. Здесь все скептики утверждают, что вы можете создать компилятор без большого образования, и я согласен. Кажется, они подразумевают, что вы можете создавать действительно хорошие компиляторы без него. Это не пари, которую я хотел бы принять.
Ира Бакстер

7
CS - дисциплина, которая действительно полезна для разработки и реализации языка. Не обязательно, конечно, но были десятилетия исследований, которые можно и нужно использовать, и нет никаких причин повторять ошибки других.
Донал Феллоуз

46

Вот онлайн-книга / курс, который вы можете пройти под названием «Элементы вычислительных систем: создание современного компьютера из первых принципов» .

Используя симуляторы, вы фактически строите полную компьютерную систему с нуля. Хотя многие комментаторы заявляют, что ваш вопрос слишком широкий, эта книга фактически отвечает на него, оставаясь при этом очень управляемой. Когда вы закончите, вы напишите игру на языке высокого уровня (который вы разработали), который использует функциональность вашей собственной ОС, которая компилируется в язык VM (который вы разработали) вашим компилятором, который получает переведенный на язык ассемблера (который вы разработали) вашим транслятором VM, который собирается в машинный код (который вы спроектировали) вашим ассемблером, который работает на вашей компьютерной системе, которую вы собираете из микросхем, которые вы спроектировали с использованием логической логики и простой язык описания оборудования.

Главы:

  1. Беглый взгляд на курс
  2. Логическая логика
  3. Комбинаторные чипсы
  4. Последовательные фишки
  5. Машинный язык
  6. Компьютерная архитектура
  7. ассемблер
  8. Виртуальная машина I: Арифметика
  9. Виртуальная машина II: управление
  10. Язык программирования
  11. Компилятор I: синтаксический анализ
  12. Компилятор II: генерация кода
  13. Операционная система
  14. Элемент списка

Веселее идти


Спасибо за правки, неизвестный. Я пытался пару раз, но не мог сосредоточить свои мысли достаточно для описания ... но не хотел не упомянуть книгу. Книга теперь в Интернете по ссылке «План обучения»: www1.idc.ac.il/tecs/plan.html . Это также по очень разумной цене в Интернете. Наслаждайтесь всеми.
Джо Интернет

Я собирался предложить это сам ... для ленивых, ознакомьтесь с 10-минутным вступлением: от NAND до тетриса за 12 шагов @ youtube.com/watch?v=JtXvUoPx4Qs
Ричард Энтони Хейн,

46

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

Языки не обязательно должны быть языками программирования. Это может быть любой язык, правила которого можно записать. Вы, наверное, видели Google Translate ; это компилятор, потому что он может переводить один язык (скажем, немецкий) на другой (возможно, японский).

Другим примером компилятора является механизм рендеринга HTML. Его ввод представляет собой файл HTML, а вывод представляет собой серию инструкций для рисования пикселей на экране.

Когда большинство людей говорят о компиляторе, они обычно имеют в виду программу, которая переводит язык программирования высокого уровня (например, Java, C, Prolog) в язык низкого уровня (сборка или машинный код). Это может быть сложно. Но не так уж и плохо, если принять универсальное мнение, что компилятор - это программа, которая переводит один язык на другой.

Можете ли вы написать программу, которая меняет каждое слово в строке? Например:

When the cat's away, the mice will play.

становится

nehW eht s'tac yawa, eht ecim lliw yalp.

Это не сложная программа для написания, но вам нужно подумать о некоторых вещах:

  • Что такое слово? Можете ли вы определить, какие символы составляют слово?
  • Где слова начинаются и заканчиваются?
  • Слова разделены только одним пробелом, или их может быть больше - или меньше?
  • Нужно ли поменять пунктуацию?
  • Как насчет пунктуации внутри слова?
  • Что происходит с заглавными буквами?

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

Как насчет этого: Можете ли вы написать программу, которая берет серию инструкций для рисования и выводит файл PNG (или JPEG)? Может быть, что-то вроде этого:

image 100 100
background black
color red
line 20 55 93 105
color green
box 0 0 99 99

Опять же, вам нужно подумать, чтобы определить язык:

  • Каковы примитивные инструкции?
  • Что следует за словом «линия»? Что идет после «цвета»? Аналогично для "фона", "коробки" и т. Д.
  • Какой номер?
  • Разрешен ли пустой входной файл?
  • Можно ли использовать слова с заглавной буквы?
  • Разрешены ли отрицательные числа?
  • Что произойдет, если вы не дадите директиву image?
  • Можно ли не указывать цвет?

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

Видите ли, написание компилятора не так сложно. Компиляторы, которые вы использовали в Java или C, являются просто большими версиями этих двух примеров. Так что дерзай! Определите простой язык и напишите программу, чтобы этот язык что-то делал. Рано или поздно вы захотите расширить свой язык. Например, вы можете добавить переменные или арифметические выражения. Ваш компилятор станет более сложным, но вы поймете все до мелочей, потому что написали сами. Так появляются языки и компиляторы.


7
myFirstCompiler = (str) -> ("" + (str || "")). split (''). reverse (). join (''); jsfiddle.net/L7qSr
Ларри

21

Если вы заинтересованы в разработке компилятора, ознакомьтесь с « Книгой Дракона» (официальное название: «Компиляторы: принципы, методы и инструменты»). Это широко считается классической книгой на эту тему.


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

13
-1 Только тот, кто не читал его, может подумать, что книга дракона - это хорошо. и это особенно не касается вопроса.
Нил Баттерворт

33
Книга Дракона? Для увлеченного пятнадцатилетнего? Я бы предпочел, чтобы он сохранял свой энтузиазм еще дольше.
Дэвид Торнли

1
Более доступная альтернатива: «Прагматика языка программирования» 3e .
willjcroz

@DavidThornley Не считайте его полностью (Да, я понимаю, что это очень старый пост). Я начал изучать, как работают языки в 15 лет и сосредоточился именно на виртуальных машинах. Сейчас мне 16 лет, и после месяцев исследований, написания и переписывания у меня есть работающий интерпретатор и компилятор, которым я доволен.
Дэвид

10

«Давайте построим компилятор» уже предлагалось. Существует «модернизированная» версия, использующая Haskell вместо Turbo Pascal: http://alephnullplex.appspot.com/blog/view/2010/01/12/lbach-1-introduction

Вместе с Haskell, есть очень поучительный интерпретатор Scheme, который может дать дальнейшие идеи: напишите себе Scheme за 48 часов


10

Не верьте, что в компиляторе или в ОС есть что-то волшебное: нет. Помните программы, которые вы написали для подсчета всех гласных в строке или сложения чисел в массиве? Компилятор не отличается по концепции; это просто намного больше.

Каждая программа имеет три этапа:

  1. читать некоторые вещи
  2. обработать этот материал: преобразовать входные данные в выходные данные
  3. написать что-то другое - выходные данные

Подумайте об этом: что входит в состав компилятора? Строка символов из исходного файла.

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

Так что такое фаза «процесса» компилятора? Что делает эта фаза?

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


3
Как сказал Нил, правда, но не полезно. Фундаментальные аспекты компилятора, такие как рекурсивная грамматика и таблицы символов, не являются очевидными.
Мейсон Уилер

1
@ Мейсон Уилер: Я думаю, что любой, кто реально хочет написать компилятор (и спроектировать целевой язык?), Скорее всего, подумает, что рекурсивные грамматические таблицы и таблицы символов были довольно простыми понятиями.
FumbleFingers

8

Я не эксперт, но вот мой удар:

Вы, кажется, не спрашиваете о написании компилятора, просто ассемблер. Это не совсем волшебство.

Похищение чьего-либо ответа от SO ( https://stackoverflow.com/questions/3826692/how-do-i-translate-assembly-to-binary ), сборка выглядит так:

label:  LDA #$00
        JMP label

Затем вы запускаете его через ассемблер и превращаетесь во что-то вроде этого:

$A9 $00
$4C $10 $00

Только все это раздавлено, вот так:

$A9 $00 $4C $10 $00

Это действительно не волшебство.

Вы не можете написать это в блокноте, потому что блокнот использует ASCII (не шестнадцатеричный). Вы бы использовали шестнадцатеричный редактор или просто запрограммировали байты. Вы записываете этот шестнадцатеричный файл в файл, называете его «a.exe» или «a.out», а затем указываете ОС запустить его.

Конечно, современные процессоры и операционные системы действительно довольно сложны, но это основная идея.

Если вы хотите написать новый компилятор, вот как это делается:

1) Напишите интерпретируемый язык, используя что-то вроде примера калькулятора в pyparsing (или любой другой хорошей структуры синтаксического анализа). Это поможет вам освоить основы синтаксического анализа.

2) Написать переводчик. Переведите свой язык, скажем, в Javascript. Теперь ваш язык будет работать в браузере.

3) Напишите переводчик на более низкий уровень, например, LLVM, C или Assembly.

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

4) (Безумный) Написать оптимизатор. Большие команды десятилетиями работают над этим.

4) (вменяемый). Примите участие в существующем сообществе. GCC, LLVM, PyPy, основная команда, работающая над любым переводчиком.


8

Несколько других дали отличные ответы. Я просто добавлю еще несколько предложений. Во-первых, хорошая книга о том, что вы пытаетесь сделать, это тексты Аппеля по внедрению современного компилятора (выберите C , Java или Standard ML ). В этой книге рассказывается о полной реализации компилятора для простого языка Tiger для сборки MIPS, которую можно запустить в эмуляторе, а также минимальной библиотеки поддержки времени выполнения. Для одного прохода через все, что нужно для работы скомпилированного языка, это очень хорошая книга 1 .

Appel расскажет вам, как скомпилировать язык, который разработан заранее, но не тратит много времени на то, что означают различные языковые функции, или на то, как думать о них с точки зрения их относительных достоинств для разработки собственного. Для этого аспекта « Языки программирования: концепции и конструкции» вполне приемлемы. Концепции, методы и модели компьютерного программирования - также хорошая книга для глубокого осмысления языкового дизайна, хотя и в контексте одного языка ( унция ).

Наконец, я упомянул, что у Appel есть текст на C, Java и Standard ML - если вы серьезно относитесь к языкам конструирования компиляторов и программирования, я рекомендую изучать ML и использовать эту версию Appel. Языки семейства ML имеют сильные системы типов, в основном функциональные - функции, которые будут отличаться от многих других языков, поэтому изучение их, если вы еще не знаете функциональный язык, оттачивает ваше языковое ремесло. Кроме того, их сопоставление с образцом и функциональный образ мыслей чрезвычайно хорошо подходят для тех видов манипуляций, которые вам часто приходится выполнять в компиляторе, поэтому компиляторы, написанные на языках на основе ML, обычно намного короче и проще для понимания, чем компиляторы, написанные на C, Java или похожие языки. Книга Харперана Standard ML довольно хорошее руководство, чтобы вы начали; работа над этим должна подготовить вас к тому, чтобы вы взяли на себя книгу по реализации компилятора Standard ML от Appel. Если вы изучите стандарт ML, вам также будет довольно легко подобрать OCaml для дальнейшей работы; IMO, он имеет лучший инструментарий для работающего программиста (более аккуратно интегрируется с окружающей средой ОС, легко создает исполняемые программы и имеет некоторые впечатляющие инструменты компиляции, такие как ulex и Menhir).


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


Отредактировано : заменить неверную ссылку Aho на Sethi, упомянуть CTMCP.


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

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

6

Я должен был создать компилятор для класса в колледже.

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

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

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

Удачи!


8
Концептуально просто? Да. На самом деле просто? Нет
Нил Баттерворт

7
Эмм. После сканирования / синтаксического анализа компилятор должен выполнить проверку типа / вывод, оптимизацию, распределение регистров и т. Д. И т. Д. Эти шаги не являются простыми. (При использовании интерпретированного кода вы просто откладываете эти части на этапе выполнения.)
Macke

От меня не стоит голосовать: хотя компиляторы состоят из двух основных частей, одна из них состоит в создании абстрактного описания программы (которое обычно разбивается на сканирование и анализ), а другая - в некоторых версиях, чтобы снова написать версию этого абстрактного описания. другая форма (например, машинный код). (Примечание: оптимизирующие компиляторы обычно пытаются улучшить абстрактное описание, прежде чем писать его, но это уточнение.)
Донал Феллоуз

6

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

Теперь, когда я написал это, мне придется перечитать это.



5

В этой теме есть отличные ответы, но я просто хотел добавить свой, поскольку у меня тоже когда-то был тот же вопрос. (Также я хотел бы отметить, что книга, предложенная Joe-Internet, является отличным ресурсом.)

Сначала вопрос о том, как работает компьютер? Вот как: ввод -> вычисление -> вывод.

Сначала рассмотрим часть «Compute». Мы рассмотрим, как работает ввод и вывод.

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

Чтобы ответить на это, нам нужно понять структуру процессора. Ниже приведен довольно простой вид. Процессор по существу состоит из двух частей. Одним из них является набор областей памяти, встроенных в процессор, которые служат его рабочей памятью. Они называются «регистрами». Второй представляет собой набор электронных механизмов, созданных для выполнения определенных операций с использованием данных в регистрах. Есть два специальных регистра, называемых «счетчик программ» или ПК и «регистр команд» или ир. Процессор считает, что память разделена на три части. Первая часть - это «память программ», в которой хранится исполняемая компьютерная программа. Второе - это «память данных». Третий используется для каких-то особых целей, об этом мы поговорим позже. Счетчик программ содержит местоположение следующей инструкции для чтения из памяти программ. Счетчик команд содержит номер, который относится к текущей выполняемой операции. Каждая операция, которую может выполнить процессор, указывается номером, который называется кодом операции операции. Принцип работы компьютера заключается в том, что он считывает ячейку памяти, на которую ссылается счетчик программ, в регистр команд (и увеличивает счетчик программ так, чтобы он указывал на ячейку памяти следующей инструкции). Затем он читает регистр команд и выполняет требуемую операцию. Например, инструкция может состоять в том, чтобы считывать определенную ячейку памяти в регистр, или записывать в какой-либо регистр, или выполнять некоторую операцию, используя значения двух регистров, и записывать вывод в третий регистр. Счетчик команд содержит номер, который относится к текущей выполняемой операции. Каждая операция, которую может выполнить процессор, указывается номером, который называется кодом операции операции. Принцип работы компьютера заключается в том, что он считывает ячейку памяти, на которую ссылается счетчик программ, в регистр команд (и увеличивает счетчик программ так, чтобы он указывал на ячейку памяти следующей инструкции). Затем он читает регистр команд и выполняет требуемую операцию. Например, инструкция может состоять в том, чтобы считывать определенную ячейку памяти в регистр, или записывать в какой-либо регистр, или выполнять некоторую операцию, используя значения двух регистров, и записывать вывод в третий регистр. Счетчик команд содержит номер, который относится к текущей выполняемой операции. Каждая операция, которую может выполнить процессор, указывается номером, который называется кодом операции операции. Принцип работы компьютера заключается в том, что он считывает ячейку памяти, на которую ссылается счетчик программ, в регистр команд (и увеличивает счетчик программ так, чтобы он указывал на ячейку памяти следующей инструкции). Затем он читает регистр команд и выполняет требуемую операцию. Например, инструкция может состоять в том, чтобы считывать определенную ячейку памяти в регистр, или записывать в какой-либо регистр, или выполнять некоторую операцию, используя значения двух регистров, и записывать вывод в третий регистр. Каждая операция, которую может выполнить процессор, указывается номером, который называется кодом операции операции. Принцип работы компьютера заключается в том, что он считывает ячейку памяти, на которую ссылается счетчик программ, в регистр команд (и увеличивает счетчик программ так, чтобы он указывал на ячейку памяти следующей инструкции). Затем он читает регистр команд и выполняет требуемую операцию. Например, инструкция может состоять в том, чтобы считывать определенную ячейку памяти в регистр, или записывать в какой-либо регистр, или выполнять некоторую операцию, используя значения двух регистров, и записывать вывод в третий регистр. Каждая операция, которую может выполнить процессор, указывается номером, который называется кодом операции операции. Принцип работы компьютера заключается в том, что он считывает ячейку памяти, на которую ссылается счетчик программ, в регистр команд (и увеличивает счетчик программ так, чтобы он указывал на ячейку памяти следующей инструкции). Затем он читает регистр команд и выполняет требуемую операцию. Например, инструкция может состоять в том, чтобы считывать определенную ячейку памяти в регистр, или записывать в какой-либо регистр, или выполнять некоторую операцию, используя значения двух регистров, и записывать вывод в третий регистр. Принцип работы компьютера заключается в том, что он считывает ячейку памяти, на которую ссылается счетчик программ, в регистр команд (и увеличивает счетчик программ так, чтобы он указывал на ячейку памяти следующей инструкции). Затем он читает регистр команд и выполняет требуемую операцию. Например, инструкция может состоять в том, чтобы считывать определенную ячейку памяти в регистр, или записывать в какой-либо регистр, или выполнять некоторую операцию, используя значения двух регистров, и записывать вывод в третий регистр. Принцип работы компьютера заключается в том, что он считывает ячейку памяти, на которую ссылается счетчик программ, в регистр команд (и увеличивает счетчик программ так, чтобы он указывал на ячейку памяти следующей инструкции). Затем он читает регистр команд и выполняет требуемую операцию. Например, инструкция может состоять в том, чтобы считывать определенную ячейку памяти в регистр, или записывать в какой-либо регистр, или выполнять некоторую операцию, используя значения двух регистров, и записывать вывод в третий регистр.

Теперь, как компьютер выполняет ввод / вывод? Я приведу очень упрощенный ответ. См. Http://en.wikipedia.org/wiki/Input/output и http://en.wikipedia.org/wiki/Interrupt, для большего. Он использует две вещи, третью часть памяти и нечто, называемое прерываниями. Каждое устройство, подключенное к компьютеру, должно иметь возможность обмениваться данными с процессором. Это делается с использованием третьей части памяти, упомянутой ранее. Процессор выделяет часть памяти каждому устройству, и устройство и процессор обмениваются данными через этот фрагмент памяти. Но как процессор узнает, какое местоположение относится к какому устройству и когда ему нужно обмениваться данными? Это то место, где приходят прерывания. Прерывание - это, по сути, сигнал процессору приостановить текущее состояние и сохранить все свои регистры в известном месте, а затем начать делать что-то еще. Там много прерываний, каждое из которых идентифицируется уникальным номером. Для каждого прерывания существует специальная программа, связанная с ним. Когда происходит прерывание, процессор выполняет программу, соответствующую прерыванию. Теперь, в зависимости от BIOS и того, как аппаратные устройства подключены к материнской плате компьютера, каждое устройство получает уникальное прерывание и часть памяти. При загрузке операционной системы с помощью биоса определяется прерывание и место в памяти каждого устройства, а также настраиваются специальные программы для прерывания для правильной обработки устройств. Поэтому, когда устройству нужны какие-то данные или они хотят отправить какие-то данные, оно сигнализирует о прерывании. Процессор приостанавливает то, что он делает, обрабатывает прерывание и затем возвращается к тому, что он делает. Существует много видов прерываний, например, для жесткого диска, клавиатуры и т. Д. Важным является системный таймер, который вызывает прерывания через равные промежутки времени. Также есть коды операций, которые могут вызывать прерывания, называемые программными прерываниями.

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

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

Как можно разрабатывать процессор и язык ассемблера? Чтобы знать, что вы должны прочитать некоторые книги по компьютерной архитектуре. (см. главы 1-7 книги, на которую ссылается joe-internet). Это включает в себя изучение булевой алгебры, как создавать простые комбинаторные схемы для сложения, умножения и т. Д., Как строить память и последовательные схемы, как создавать микропроцессор и так далее.

Теперь, как писать компьютерные языки. Можно начать с написания простого ассемблера в машинном коде. Затем используйте этот ассемблер, чтобы написать компилятор для простого подмножества C. Затем используйте это подмножество C, чтобы написать более полную версию C. Наконец, используйте C, чтобы написать более сложный язык, такой как python или C ++. Конечно, чтобы написать язык, вы должны сначала спроектировать его (так же, как и процессор). Снова посмотрите на некоторые учебники по этому вопросу.

И как можно написать ОС. Сначала вы нацелены на платформу, такую ​​как x86. Затем вы выясните, как он загружается и когда будет запущен ваш ОС. Типичный компьютер загружается таким образом. Он запускается, и BIOS выполняет некоторые тесты. Затем биос читает первый сектор жесткого диска и загружает содержимое в определенное место в памяти. Затем он устанавливает процессор, чтобы начать выполнение этих загруженных данных. Это точка, которую вы вызываете. Типичная операционная система в этот момент загружает остальную часть памяти. Затем он инициализирует устройства и настраивает другие функции, и, наконец, он приветствует вас с помощью экрана входа.

Таким образом, чтобы написать ОС, вы должны написать «загрузчик». Затем вы должны написать код для обработки прерываний и устройств. Затем вы должны написать весь код для управления процессами, управления устройствами и т. Д. Затем вы должны написать API, который позволяет программам, запущенным в вашей ОС, получать доступ к устройствам и другим ресурсам. И, наконец, вы должны написать код, который читает программу с диска, устанавливает ее как процесс и начинает выполнять ее.

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


4

Я могу вспомнить момент в моей карьере программиста, когда я находился в состоянии, похожем на ваше замешательство: я немного прочитал теорию, книгу «Дракон», книгу «Тигр» (красный), но до сих пор мало подсказка, как собрать все это вместе.

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

Java VM предоставила мне хорошую отправную точку: концептуально это «процессор», но он сильно абстрагируется от грязных деталей реальных процессоров. Это также дает важную и часто упускаемую из виду часть процесса обучения: разбирать вещи перед тем, как собирать их снова (как дети делали с радиоприемниками в старые времена).

Поиграйте с декомпилятором и классом Hello, World на Java. Прочитайте спецификацию JVM и попытайтесь понять, что происходит. Это даст вам основательное представление о том, что делает компилятор .

Затем поиграйте с кодом, который создает класс Hello, World. (По сути, вы создаете компилятор для конкретного приложения для узкоспециализированного языка, на котором вы можете говорить только Hello, World.)

Попробуйте написать код, который сможет читать на Hello, World, написанном на другом языке, и вывести тот же класс. Сделайте так, чтобы вы могли изменить строку с «Hello, World» на что-то другое.

Теперь попробуйте скомпилировать (в Java) класс, который вычисляет некоторое арифметическое выражение, например «2 * (3 + 4)». Разберите этот класс на части, напишите «игрушечный компилятор», который может собрать его снова.


3

1) Большие видео лекции из Университета Вашингтона:

Построение компилятора CSE P 501 - осень 2009 г. www.cs.washington.edu/education/courses/csep501/09au/lectures/video.html *

2) SICP http://groups.csail.mit.edu/mac/classes/6.001/abelson-sussman-lectures/ И книга с таким же названием. Это на самом деле является обязательным для любого разработчика программного обеспечения.

3) Кроме того, о функциональном программировании, Haskell, лямбда-исчислении, семантике (включая денотацию) и реализации компилятора для функциональных языков. Вы можете начать с 2005-SS-FP.V10.2005-05-24.HDV, если вы уже знаете Haskell. Uxx видео - это ответы. Пожалуйста, следите за видео Vxx в первую очередь.

http://video.s-inf.de/#FP.2005-SS-Giesl.(COt).HD_Videoaufzeichnung

(видео на английском, другие курсы на немецком.)

  • Новые пользователи могут размещать не более двух гиперссылок.

3

ANTLR - хорошая отправная точка. Это основа создания языка, похожая на Lex и Yacc. Есть графический интерфейс ANTLRWorks, который упрощает процесс.

В мире .NET существует динамическая среда исполнения, которая может использоваться для генерации кода в мире .NET. Я написал язык выражений под названием Zentrum, который генерирует код с использованием DLR. Он покажет вам, как анализировать и выполнять статически и динамически типизированные выражения.


2

Для простого знакомства с тем, как работают компиляторы и как создавать свой собственный язык программирования, я бы порекомендовал новую книгу http://createyourproglang.com, в которой больше внимания уделяется теории проектирования языков без необходимости знать внутреннюю часть ОС / ЦП, то есть лексеры, парсеры переводчики и др.

Он использует те же инструменты, которые использовались для создания недавно популярных языков программирования Coffee Script и Fancy .


2

Если все, что вы говорите, правда, у вас есть профиль перспективного исследователя, и конкретное понимание можно получить только одним способом: учебой. И я не говорю « Прочитайте все эти книги по информатике высокого уровня (особенно эти ), написанные этим гением !»; Я имею в виду: вы должны быть с людьми высокого уровня, чтобы стать ученым, таким как Чарльз Бэббидж, Алан Тьюринг, Клод Шеннон или Деннис Ритчи. Я не презираю людей-самоучек (я один из них), но таких людей, как ты, не так много. Я настоятельно рекомендую Программу символических систем (SSP) в Стэнфордском университете . Как говорит их сайт:

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

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

2

Я собираюсь предложить кое-что немного из левого поля: изучать Python (или, возможно, Ruby, но у меня гораздо больше опыта в Python, так что об этом я и расскажу). И не просто баловаться этим, но действительно узнать это на глубоком уровне.

Есть несколько причин, по которым я предлагаю это:

  1. Python - это исключительно хорошо продуманный язык. Хотя у него есть несколько бородавок, у него меньше ИМХО, чем у многих других языков. Если вы являетесь начинающим языковым дизайнером, хорошо, чтобы вы открыли для себя как можно больше хороших языков.

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

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

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

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

  6. Python позволяет довольно легко расширять язык различными способами с помощью декораторов, метаклассов, ловушек импорта и т. Д., Так что вы можете играть с новыми возможностями языка в некоторой степени, фактически не покидая язык. (Кроме того: блоки кода являются первоклассными объектами в Ruby, так что вы можете написать новые управляющие структуры, такие как циклы! У меня сложилось впечатление, что программисты на Ruby не всегда считают, что расширение языка, это просто то, как Вы программируете на Ruby. Но это довольно круто.)

  7. В Python вы можете на самом деле дизассемблировать байт-код, сгенерированный компилятором, или даже написать свой собственный с нуля и заставить интерпретатор выполнить его (я сделал это сам, и это было потрясающе, но весело).

  8. В Python есть хорошие библиотеки для разбора. Вы можете проанализировать код Python в абстрактном синтаксическом дереве и затем манипулировать им, используя модуль AST. Модуль PyParsing полезен для анализа произвольных языков, например, тех, которые вы проектируете. Теоретически вы можете написать свой первый языковой компилятор на Python, если хотите (и он может генерировать C, ассемблер или даже вывод Python).

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

Веселиться!


Не копать на питоне, но дело не в этом. У ребенка уже есть N языков для больших N; увеличение N не будет иметь большого значения. Взять, к примеру, С. Это стандартно. В нем много библиотек. Это кроссплатформенный (когда вы придерживаетесь стандарта). Вы можете разобрать вывод. Вы можете написать CFront. И т.д.
Ян

1

Ну, я думаю, что ваш вопрос можно переписать так: «Каковы основные практические концепции степени информатики», и общий ответ, конечно же, состоит в том, чтобы получить свой собственный бакалавр в области компьютерных наук.

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

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

Ассемблер - это преобразование текста-> байта.

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

Я бы порекомендовал написать это на C; C является языком общения для этого уровня работы.


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

1

См. Книгу Кеннета Лоудена "Конструктор компиляторов"

http://www.cs.sjsu.edu/~louden/cmptext/

Это обеспечивает лучший практический подход к разработке компилятора.

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


1

Я был благословлен тем, что познакомился с PDP-8 как моим первым языком ассемблера. В PDP-8 было всего шесть инструкций, которые были настолько просты, что было легко представить, что они реализуются несколькими незаметными компонентами, которыми они на самом деле и являются. Это действительно убрало "магию" из компьютеров.

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


0

Компиляторы и языки программирования (и все, включая создание, например определение конечной грамматики и преобразование в сборку) - очень сложная задача, которая требует большого понимания систем в целом. Этот тип курса обычно предлагается в качестве третьего / четвертого курса Comp Sci в университете.

Я настоятельно рекомендую вам сначала лучше понять операционные системы в целом и то, как существующие языки компилируются / выполняются (т. Е. Изначально (C / C ++), в виртуальной машине (Java) или интерпретатором (Python / Javascript)).

Я полагаю, что мы использовали книгу «Концепции операционной системы» Авраама Зильбершаца, Питера Б. Гальвина, Грега Ганга в моем курсе «Операционные системы» (на втором курсе). Это была отличная книга, в которой подробно рассказывалось о каждом компоненте операционной системы - немного дорого, но оно того стоит, и старые / использованные копии должны быть в ходу.


ОС концепции? Очень мало этого нужно для компиляции. Требуется понимание архитектуры программного обеспечения: адреса пространств, стеков, потоков (если он хочет изучать компиляторы, он лучше узнает о параллелизме, о его будущем).
Ира Бакстер

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

@ Ира - согласился. Я никогда не говорил, что для построения компилятора / языка требуется понимание ОС, просто объяснял, что это может быть более простой отправной точкой. Каждый фокусируется на аспекте «компилятора» своего вопроса, но он также упомянул, что хочет лучшего понимания ОС »и библиотек. Для 15-летнего, все еще изучающего архитектуру, было бы гораздо полезнее понять управление памятью, многопоточность, блокировку, ввод-вывод и т. Д., Чем научиться определять грамматику с помощью yacc (IMHO)
plafond

Извините ... упустил момент о желании узнать об (сборке?) ОС. Моя точка зрения верна: ему не нужно много знаний об ОС для компиляторов. На самом деле это совершенно другая тема, за исключением случаев, когда компилятор и ОС взаимодействуют для достижения какой-то коллективной цели. (Multics требовал, чтобы компиляторы PL / 1 создавали вызовы функций определенным образом, например, для включения глобальной виртуальной машины).
Ира Бакстер

0

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

Большинство компиляторов и / или интерпретаторов работают так:

Токенизация : отсканируйте текст кода и разбейте его на список токенов.

Этот шаг может быть сложным, потому что вы не можете просто разбить строку на пробелы, вы должны признать, что if (bar) foo += "a string";это список из 8 токенов: WORD, OPEN_PAREN, WORD, CLOSE_PAREN, WORD, ASIGNMENT_ADD, STRING_LITERAL, TERMINATOR. Как вы можете видеть, простое разбиение исходного кода на пробелы не сработает, вы должны читать каждый символ как последовательность, поэтому, если вы встретите буквенно-цифровой символ, вы продолжаете читать символы, пока не нажмете не алфавитный символ и эту строку просто прочитайте это слово, которое будет классифицировано позже. Вы можете сами решить, насколько гранулирован ваш токенайзер: проглотит ли он "a string"один токен с именем STRING_LITERAL для дальнейшего анализа или он увидит"a string" как OPEN_QUOTE, UNPARSED_TEXT, CLOSE_QUOTE или что-то еще, это только один из многих вариантов, которые вы должны решить для себя, когда вы кодируете это.

Лекс : Итак, теперь у тебя есть список токенов. Возможно, вы пометили некоторые токены неоднозначной классификацией, такой как WORD, потому что во время первого прохода вы не тратите слишком много усилий, пытаясь выяснить контекст каждой строки символов. Так что теперь снова прочтите список исходных токенов и переклассифицируйте каждый из неоднозначных токенов с более конкретным типом токенов на основе ключевых слов на вашем языке. Таким образом, у вас есть WORD, например «if», и «if» в вашем списке специальных ключевых слов, называемых символом IF, поэтому вы меняете тип символа этого токена с WORD на IF, и любое слово, которого нет в списке специальных ключевых слов. , такой как WORD foo, является ИДЕНТИФИКАТОРОМ.

Разбор : Итак, вы перевернули if (bar) foo += "a string";список лексированных токенов, который выглядит следующим образом: IF OPEN_PAREN IDENTIFER CLOSE_PAREN IDENTIFIER ASIGN_ADD STRING_LITERAL TERMINATOR. Шаг - это распознавание последовательностей токенов как операторов. Это разбор. Вы делаете это с помощью грамматики, такой как:

ЗАЯВЛЕНИЕ: = ASIGN_EXPRESSION | IF_STATEMENT

IF_STATEMENT: = IF, PAREN_EXPRESSION, STATEMENT

ASIGN_EXPRESSION: = IDENTIFIER, ASIGN_OP, VALUE

PAREN_EXPRESSSION: = OPEN_PAREN, VALUE, CLOSE_PAREN

ЗНАЧЕНИЕ: = ИДЕНТИФИКАТОР | STRING_LITERAL | PAREN_EXPRESSION

ASIGN_OP: = EQUAL | ASIGN_ADD | ASIGN_SUBTRACT | ASIGN_MULT

Производства, которые используют "|" между терминами означает «соответствовать любому из них», если между терминами есть запятые, это означает «соответствовать этой последовательности терминов»

Как вы используете это? Начиная с первого токена, попытайтесь сопоставить свою последовательность токенов с этими произведениями. Итак, сначала вы пытаетесь сопоставить свой список токенов с STATEMENT, поэтому вы читаете правило для STATEMENT и там говорится, что «STATEMENT - это либо ASIGN_EXPRESSION, либо IF_STATEMENT», поэтому вы сначала пытаетесь сопоставить ASIGN_EXPRESSION, поэтому ищите правило грамматики для ASIGN_EXPRESSION. и он говорит: «ASIGN_EXPRESSION - это IDENTIFIER, за которым следует ASIGN_OP, за которым следует VALUE, поэтому вы просматриваете правило грамматики для IDENTIFIER, и вы видите, что для IDENTIFIER нет грамматического ruke, так что это означает, что IDENTIFIER является« терминалом », то есть для него больше не требуется анализ для сопоставления, так что вы можете попытаться сопоставить его напрямую с вашим токеном, но ваш первый исходный токен - IF, и IF - не то же самое, что и IDENTIFIER, поэтому сопоставление не удалось. Что теперь? Вы возвращаетесь к правилу STATEMENT и пытаетесь найти следующий термин: IF_STATEMENT. Вы ищите IF_STATEMENT, он начинается с IF, ищите IF, IF - это терминал, сравнивайте терминал с вашим первым токеном, IF токен совпадает, удивительно продолжайте, следующий термин - PAREN_EXPRESSION, ищите PAREN_EXPRESSION, это не терминал, каков его первый термин, PAREN_EXPRESSION начинается с OPEN_PAREN, поиск OPEN_PAREN, это терминал, соответствует OPEN_PAREN вашему следующему токену, он совпадает, .... и так далее.

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

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

Компилировать и / или выполнять : Для определенных производств в вашей грамматике вы создали функции-обработчики, которые при наличии структуры AST будут компилировать или выполнять этот фрагмент AST.

Итак, давайте посмотрим на часть вашего AST, которая имеет тип ASIGN_ADD. Поэтому в качестве интерпретатора у вас есть функция ASIGN_ADD_execute (). Эта функция передается как часть AST, которая соответствует дереву разбора foo += "a string", поэтому эта функция просматривает эту структуру и знает, что первый член в структуре должен быть IDENTIFIER, а второй - VALUE, поэтому ASIGN_ADD_execute () передает член VALUE в функцию VALUE_eval (), которая возвращает объект, представляющий оцененное значение в памяти, затем ASIGN_ADD_execute () выполняет поиск "foo" в таблице переменных и сохраняет ссылку на все, что было возвращено функцией eval_value () функция.

Это переводчик. Вместо этого компилятор будет иметь функции-обработчики для преобразования AST в байт-код или машинный код вместо его выполнения.

Шаги с 1 по 3, а некоторые 4, можно упростить с помощью таких инструментов, как Flex и Bison. (aka. Lex и Yacc), но написать переводчик самостоятельно с нуля, возможно, самое полезное упражнение, которое может выполнить любой программист. Все остальные проблемы программирования кажутся тривиальными после саммита.

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

Прочитайте это, и удачи!

http://www.iro.umontreal.ca/~felipe/IFT2030-Automne2002/Complements/tinyc.c

http://en.wikipedia.org/wiki/Recursive_descent_parser


2
Вы делаете то, что я считаю классической ошибкой, когда люди думают о компиляции: это значит, что проблема в синтаксическом анализе. Парсинг технически прост; Есть отличные технологии для этого. Сложная часть компиляции - это семантический анализ, оптимизация на высоких и низких уровнях представления программ и генерация кода, причем в наши дни все больший акцент делается на коде PARALLEL. Вы тривиализируете это полностью в своем ответе: «у компилятора будут функции-обработчики для преобразования AST в байт-код». Прошло 50 лет с тех пор, как теория компиляции и инженерия скрылись там.
Ира Бакстер

0

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

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

Дело в том, что трудно понять что-либо, не понимая, для чего это нужно . Удачи, и не просто читать вещи. Делай вещи.



-1

Другой хорошей вводной книгой является "Compilerbau" Н. Вирта 1986 года (конструкция компилятора), длина которого составляет около 100 страниц и объясняет лаконичный, хорошо разработанный код для игрушечного языка PL / 0, включая парсер, генератор кода и виртуальную машину. Также показано, как написать синтаксический анализатор, который читает грамматику для анализа в нотации EBNF. Книга написана на немецком языке, но я написал резюме и перевел код на Python в качестве упражнения, см. Http://www.d12k.org/cmplr/w86/intro.html .


-1

Если вы заинтересованы в понимании сути языков программирования, я бы предложил вам изучить книгу PLAI (http://www.cs.brown.edu/~sk/Publications/Books/ProgLangs/), чтобы понять концепции и их реализация. Это также поможет вам с дизайном вашего языка.


-1

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

  • Допустимые числа
  • Разрешенные операторы
  • Приоритеты оператора
  • Проверка синтаксиса
  • Механизм поиска переменной
  • Обнаружение цикла
  • оптимизация

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

a = 1
b = 2
c = a + b
d = (3 + b) * c
x = a - d / b

Это не очень сложный компилятор для начала, но он может заставить вас задуматься над некоторыми базовыми идеями о том, что такое компилятор, а также поможет вам улучшить свои навыки программирования и контролировать качество вашего кода (на самом деле это идеальная проблема, которая Test Driven Development TDD может применяться для улучшения качества программного обеспечения).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.