"Джек Креншоу. Давайте создадим компилятор! " - читать интересную книгу автора

правильно сообщает об ошибке.
Поздравляю! Вы только что написали работающий транслятор!
Конечно, я понимаю, что он очень ограничен. Но не отмахивайтесь от
него. Этот маленький "компилятор" в ограниченных масштабах делает точно то
же, что делает любой большой компилятор: он корректно распознает допустимые
утверждения на входном "языке", который мы для него определили, и производит
корректный, выполнимый ассемблерный код, пригодный для перевода в объектный
формат. И, что важно, корректно распознает недопустимые утверждения, выдавая
сообщение об ошибке. Кому требовалось больше?
Имеются некоторые другие особенности этой маленькой программы,
заслуживающие внимания. Во первых, вы видите, что мы не отделяем генерацию
кода от синтаксического анализа... как только анализатор узнает что нам
нужно, он непосредственно генерирует объектный код. В настоящих
компиляторах, конечно, чтение в GetChar должно происходить из файла и затем
выполняться запись в другой файл, но этот способ намного проще пока мы
экспериментируем.
Также обратите внимание, что выражение должно где-то сохранить
результат. Я выбрал регистр D0 процессора 68000. Я мог бы выбрать другой
регистр, но в данном случае это имеет смысл.

ВЫРАЖЕНИЯ С ДВУМЯ ЦИФРАМИ

Теперь, давайте немного улучшим то, что у нас есть. По общему
признанию, выражение, состоящее только из одного символа, не удовлетворит
наших потребностей надолго, так что давайте посмотрим, как мы можем
расширить возможности компилятора. Предположим, что мы хотим обрабатывать
выражения вида:
1+2
или 4-3
или в общем  +/-  (это часть формы Бэкуса-Наура или БНФ.)
Для того, чтобы сделать это, нам нужна процедура, распознающая термы и
сохраняющая результат, и другая процедура, которая распознает и различает
"+" и "-" и генерирует соответствующий код. Но если процедура
Expression сохраняет свои результаты в регистре D0, то где процедура Term
сохранит свои результаты? Ответ: на том же месте. Мы окажемся перед
необходимостью сохранять первый результат процедуры Term где-нибудь, прежде
чем мы получим следующий.
В основном, что нам необходимо сделать - создать процедуру Term,
выполняющую то что раннее выполняла процедура Expression. Поэтому просто
переименуйте процедуру Expression в Term и наберите новую версию Expression:

{-}
{ Parse and Translate an Expression }
procedure Expression;
begin
Term;
EmitLn('MOVE D0,D1');
case Look of
'+': Add;
'-': Subtract;