Разумеется, компилятор, будучи программой, должен быть сам написан на каком-либо языке. Первые компиляторы были созданы на языке ассемблера вместо машинного языка; таким образом полностью использовались преимущества подъема на одну ступеньку над машинным языком. Краткое описание этих довольно сложных понятий представлено на рис. 58.
По мере того, как программирование становилось более изощренным, было замечено, что частично законченный компилятор может быть использован для того, чтобы компилировать собственные продолжения. Иными словами, когда создано определенное минимальное ядро компилятора, это минимальное ядро может переводить большие компиляторы на машинный язык, пока таким образом не создастся окончательный, полный компилятор. Этот процесс известен под именем «самонастройки»; он несколько напоминает достижение ребенком критического уровня владения своим родным языком, после чего его словарь и грамматическое мастерство растут как снежный ком, так как для изучения языка он может
Языки компиляторов обычно не отражают структуры машин, на которых будут выполняться написанные на этих языках программы. Это одно из их основных преимуществ по сравнению с весьма специализированными языками ассемблера и машинным языком. Разумеется, когда программа на языке компилятора переводится на язык машины, получается программа, зависящая от машины. Таким образом, возможно описать программу, которая исполняется либо зависящим от машины путем, либо не зависящим, подобно тому, как мы можем описать абзац в книге по его содержанию (описание, не зависящее от издания) или по номеру страницы и его расположению на ней (описание, зависящее от издания).
Пока программа работает хорошо, то, как мы ее описываем и что мы о ней думаем, не столь важно. Но как только возникают неполадки, становится важным умение увидеть программу на разных уровнях. Если, например, компьютеру дана задача в какой-то момент разделить на нуль, он остановится и сообщит пользователю о возникшей проблеме, указав при этом, в каком месте программы произошло это неприятное событие. Однако эти детали часто сообщаются на более низком уровне, чем тот, на котором написана сама программа. Вот три параллельных описания забуксовавшей программы:
Уровень машинного языка:
«Выполнение программы прекратилось по адресу 1110010101110111»
Уровень языка ассемблера:
«Выполнение программы прекратилось, когда она дошла до команды РАЗДЕЛИТЬ».
Уровень языка компилятора:
«Выполнение программы прекратилось в момент оценки алгебраического выражения „(А + B)/Z“».
Одна из основных задач программистов (людей, которые создают компиляторы, интерпретаторы, ассемблеры и другие программы, которые затем используются многими людьми) — это создание находящих ошибки подпрограмм. Необходимо, чтобы информация, которую эти подпрограммы выдают пользователю, в чьей программе обнаружен дефект, представляла бы описание проблемы на высшем, а не на низшем, уровне. Интересно, что если сбой обнаруживается в генетической «программе» (например, мутация), то происходит обратное, ошибка бывает заметна только на