Основной блок компилятора.
К моменту, когда вы уже готовы сесть и начать писать текст этого блока, вы должны знать, что у вас делают остальные части (сканер, процедуры работы с таблицами и т.д.). Причем желательно, чтобы остальные части делали побольше, а эта поменьше. Отдельную небольшую подпрограмму вы всегда отладите, а вот процедуру, которая вызыввает несколько других процедур, отладить несколько сложнее. Короче, главные проблеы у вас будут все-таки с основной частью.
Посмотрите на текст программы (Пример 3). Что должна делать основная часть? Читать описания переменных и заполнать таблицу имен. Потом содержимое таблицы имен должно быть вовремя записано в выходной файл в виде описаний переменных на языке Паскаль. Кроме того, для каждого оператора на языке SIMPLE в выходной файл должен быть записан эквивалентный ему оператор языка Паскаль.
В описаниях основная проблема - это описания записей. "Разобраться" с ними - значит поместить их в таблицу имен. В данной программе это сделано рекурсивно. А действительно, как это еще сделать? Ведь и записи определены рекурсивно: "...содержит в качестве полей целые числа и [другие]_записи".
Есть также определенные проблемы и с трансляцией описаний. Оператор присваивания или печати мы всегда можем считать целиком в память и уже потом компилировать. А вот оператор цикла сам может содержать внутри себя тысячи операторов. Поэтому нам остается только одно - встретив while...do, записать в выходной файл начало оператора цикла, потом как ни в чем ни бывало компилировать внутренние операторы цикла, а встретив close и вспомнив, что когда-то мы встречали while...do, записать в выходной файл все, что останется. К счастью, мы копилируе в Паскаль, а не в коды. И запоминать на самом деле нам ничего не надо. Достаточно, встретив while...do, написать "while ... do begin", а встретив close, написать "end" - и мы никогда не прогадаем, потому что "begin" ставим всегда. А вот если бы мы транслировали в машинный код нам необходимо было бы знать, с какого адреса начинался этот оператор. А для этого нужна структура данных, называемая стеком.
Куда идти дальше?
У тех, кто дошел до этого места, возможно, возникнут вопросы. Я не берусь предугадать их все, но на некоторые можно ответить заранее.
- Что еще нужно сделать, если с компилятором будут работать часто?
- Необходимо вставить диагностику ошибок. На практике компилятор в большинстве случаев выдает не скомпилированный текст, а сообщение об ошибке. Поэтому и писать компилятор нужно так, чтобы проверка на ошибки производилась как можно легче.
В частности, чем больше хитрых приемов вы используете (например, ускоряющих компиляцию), тем больше вам придется ломать голову над тем, куда этот алгоритм забредет в случае ошибки и как сделать, чтобы копилятор не сбился с пути и обнаружил не только первую встретившуюся ошибку, но еще и какие-то другие.
- Как вставлять новые конструкции в язык?
- Если речь идет о структурах данных, то лучше добавить новое поле в запись таблицы имен, чем ломать голову над тем, как сэкономить лишнее поле.
Если речь идет об операторах - добавьте еще одну специализированную процедуру, копилирующую данный оператор. Соответственно, вызов этой процедуры должен быть вставлен в процедуру Code.
- А если хочется все-таки сделать компилятор более эффективным?
- Ради Бога! Только сначала пусть он заработает. А потом его можно оптимизировать по вкусу. Кроме того, прежде чем применить многообещающий алгоритм, хорошенько проверьте, будет ли он столь эффективен в вашем случае. А то ведь все эти алгоритмы на практике хороши в весьма специальных случаях. Подумайте, стоит ли овчинка выделки.
- Как отлаживать компилятор?
- Компилятор - очень коварная программа. И выловить все ошибки из него обычно не удается. Не полагайтесь на свою интуицию. Сделайте себе набор тестов - небольших, но сам набор пусть будет представительнымм. И когда компилятор будет подходить к готовности, прогоните их все. (Да, в том числе и те, что уже когда-то проходили!)
И напоследок еще один совет. Больше простоты! Если бы программисты всегда ему следовали, то работающих программ было бы несомненно больше (а недоотлаженных, брошенных на полдороге - меньше).