Проблему можно решить двумя способами. Оба эти способа включают в себя разработку новых команд, которые более удобны для человека, чем встроенные машинные команды. Эти новые команды в совокупности формируют язык, который мы будем называть Я 1. Встроенные машинные команды тоже формируют язык, и мы будем называть его Я 0. Компьютер может выполнять только программы, написанные на его машинном языке Я 0. Два способа решения проблемы различаются тем, каким образом компьютер будет выполнять программы, написанные на языке Я 1.
Первый способ выполнения программы, написанной на языке Я 1, — замена каждой команды на эквивалентный набор команд в языке Я 0. В этом случае компьютер выполняет новую программу, написанную на языке Я 0, вместо старой программы, написанной на Я 1. Эта технология называется трансляцией.
Второй способ — написание программы на языке Я 0, которая берет программы, написанные на языке Я 1, в качестве входных данных, рассматривает каждую команду по очереди и сразу выполняет эквивалентный набор команд языка Я 0. Эта технология не требует составления новой программы на Я 0. Она называется интерпретацией, а программа, которая осуществляет интерпретацию, называется интерпретатором.
Трансляция и интерпретация сходны. При применении обоих методов компьютер в конечном итоге выполняет набор команд на языке Я 0, эквивалентных командам Я 1. Различие лишь в том, что при трансляции вся программа Я 1 переделывается в программу Я 0, программа Я 1 отбрасывается, а новая программа на Я 0 загружается в память компьютера и затем выполняется.
При интерпретации каждая команда программы на Я 1 перекодируется в Я 0 и сразу же выполняется. В отличие от трансляции, здесь не создается новая программа на Я 0, а происходит последовательная перекодировка и выполнение команд. Оба подхода широко используются как вместе так и по отдельности..
Обычно гораздо проще представить себе существование гипотетического компьютера или виртуальной машины, для которой машинным языком является язык Я 1, чем думать о трансляции и интерпретации. Назовем такую виртуальную машину М 1, а виртуальную машину с языком Я 0 — М 0. Если бы такую машину М 1 можно было бы сконструировать без больших денежных затрат, язык Я 0, да и машина, которая выполняет программы на языке Я 0, были бы не нужны. Можно было бы просто писать программы на языке Я 1, а компьютер сразу бы их выполнял. Но поскольку такую машину дороже и труднее сконструировать, а программа на языке Я1 может выполняться путем трансляции или интерпритации фактически существующим компьютером, следовательно можно писать программы для виртуальных машин, как будто они действительно существуют.
Чтобы трансляция и интерпретация были целесообразными, языки Я 0 и Я 1 не должны сильно различаться. Это значит, что язык Я 1 хотя и лучше, чем Я 0, но все же далек от идеала и не освобождает программиста от бремени написания программ на языке, который более удобен для компьютера, чем для человека.
Очевидное решение этой проблемы — создание еще одного набора команд, которые в большей степени ориентированы на человека и в меньшей степени на компьютер, чем Я 1. Этот третий набор команд также формирует язык, который мы будем называть Я 2, а соответствующую виртуальную машину — М 2. Человек может писать программы на языке Я 2, как будто виртуальная машина с машинным языком Я 2 действительно существует. Такие программы могут или транслироваться на язык Я 1, или выполняться интерпретатором, написанным на языке Я 1.
Изобретение целого ряда языков, каждый из которых более удобен для человека, чем предыдущий, может продолжаться до тех пор, пока мы не дойдем до подходящего нам языка. Каждый такой язык использует своего предшественника как основу, поэтому мы можем рассматривать компьютер в виде ряда уровней, как показано на рис. 1.1. Язык, находящийся в самом низу иерархической структуры — самый примитивный, а находящийся на самом верху — самый сложный. Таким образом программист пишет программу словами, далее путем трансляции пройдя через несколько уровней программа преобразовывается и машина выполняет ее, воспринимая на языке машинных кодов.
Рис. 1.1. Многоуровневая машина
Между языком и виртуальной машиной существует важная зависимость. У каждой машины есть какой-то определенный машинный язык, состоящий из всех команд, которые эта машина может выполнять. В сущности, машина определяет язык. Сходным образом язык определяет машину, которая может выполнять все программы, написанные на этом языке. Машину, задающуюся определенным языком, очень сложно и дорого сконструировать из электронных схем, но мы можем представить себе такую машину. Компьютер с машинным языком C++ или COBOL был бы слишком сложным, но его можно было бы сконструировать, если учитывать высокий уровень современных технологий. Однако существуют веские причины не создавать такой компьютер: это слишком сложно по сравнению с другими техническими приемами.
Компьютер с n уровнями можно рассматривать как n разных виртуальных машин, у каждой из которых есть свой машинный язык. Термины «уровень» и «виртуальная машина» мы будем использовать как синонимы. Только программы, написанные на Я 0, могут выполняться компьютером без применения трансляции и интерпретации.
Человеку, который пишет программы для виртуальной машины уровня n, не обязательно знать о трансляторах и интерпретаторах более низких уровней. Машина выполнит эти программы, и не важно, будут ли они выполняться шаг за шагом интерпретатором или их будет выполнять сама машина. В обоих случаях результат один и тот же: программа будет выполнена.
Большинство программистов, использующих машину уровня n, интересуются только самым верхним уровнем, то есть уровнем, который меньше всего сходен с машинным языком. Однако те, кто хочет понять, как в действительности работает компьютер, должны изучить все уровни. Те, кто проектирует новые компьютеры или новые уровни (то есть новые виртуальные машины), также должны быть знакомы со всеми уровнями. Понятия и технические приемы конструирования машин как системы уровней, а также детали уровней составляют главный предмет нашего занятия.