Лекции по построению компилятора на Pascal
промежуточную форму, чтобы было легче выполнять синтаксический анализ в реальном
режиме времени.
Другой пример – ассемблер. Целью ассемблера, конечно, является получение] объектного
кода и он обычно выполняет это по однозначному принципу: одна инструкция на строку
исходного кода. Но почти все ассемблеры также разрешают использовать выражения как
параметры. В этом случае выражения всегда являются константами, и ассемблер не
предназначен выдавать для них объектный код. Скорее он "интерпретирует" выражение и
вычисляет соответствующее значение, которое фактически и выдается с объектным кодом.
Фактически, мы могли бы использовать часть этого сами. Транслятор, который мы создали в
предыдущей главе, будет покорно выплевывать объектный код для сложных выражений,
даже если каждый терм в выражении будет константой. В этом случае было бы гораздо
лучше, если бы транслятор вел себя немного как интерпретатор и просто вычислял
соответствующее значение константы.
В теории компиляции существует понятие, называемое "ленивой" трансляцией. Идея состоит
в том, что вы не просто выдаете код при каждом действии. Фактически, в крайнем случае вы
не выдаете что-либо вообще до тех пор, пока это не будет абсолютно необходимо. Для
выполнения этого, действия, связанные с подпрограммами анализа, обычно не просто
выдают код. Иногда они это делают, но часто они просто возвращают информацию обратно
вызвавшей программе. Вооружившись этой информацией, вызывающая программа может
затем сделать лучший выбор того, что делать.
К примеру, для данного выражения
#### x = x + 3 - 2 - (5 - 4)
наш компилятор будет покорно выплевывать поток из 18 инструкций для загрузки каждого
параметра в регистры, выполнения арифметических действий и сохранения результата.
Ленивая оценка распознала бы, что выражение, содержащее константы, может быть
рассчитано во время компиляции и уменьшила бы выражение до
#### x = x + 0
Даже ленивая оценка была бы затем достаточно умной, чтобы понять, что это эквивалентно
#### x = x,
что совсем не требует никаких действий. Мы смогли уменьшить 18 инструкций до нуля!
Обратите внимание, что нет никакой возможности оптимизировать таким способом наш
компилятор, потому что каждое действие выполняется в нем немедленно.
Ленивая оценка выражений может произвести значительно лучший объектный код чем тот
который мы могли произвести. Я, тем не менее, предупреждаю вас: это значительно
усложняет код синтаксического анализатора, потому что каждая подпрограмма теперь
должна принять решение] относительно того, выдать объектный код или нет. Ленивая оценка
конечно же названа так не потому, что она проще для создателей компиляторов!
Так как мы действуем в основном по принципу KISS, я не буду более углубляться в эту тему.
Я только хочу, чтобы вы знали, что вы можете получить некоторую оптимизацию кода,]
объединяя методы компиляции и интерпретации. В частности Вы должны знать, что
подпрограммы синтаксического анализа в более интеллектуальном трансляторе обычно что-
то возвращают вызвавшей их программе и иногда сами ожидают этого. Эта главная причина
обсуждения интерпретаторов в этой главе.
ИНТЕРПРЕТАТОР
Итак, теперь, когда вы знаете почему мы принялись за все это, давайте начнем.] Просто для
того, чтобы дать вам практику, мы начнем с пустого Cradle и создадим транслятор заново. На
этот раз, конечно, мы сможем двигаться немного быстрее.