У меня есть самописный язык программирования, пока реализованный в виде интерпретатора. Он довольно мощный, похож на Basic и легко позволяет быстро писать довольно сложные программки. Например вот некоторые функции:
'копирование всех файлов с дискеты в каталог C:\WINDOWS
Drive("C").Folder($win).CopyFile Drive("A").ReadFileName("*.*")
'Распечатка всех файлов с расширением *.doc на принтере
Printer.Name("hp LaserJet 3015 PCL 6").PrintFile (Drive("C").Folder($MyDocuments).File("*.doc"))
'Удаление файлов с расширением *.tmp из всех папок на диске С
Drive("C").DeleteFile("*.tmp")
'Отображение структуры каталогов диска С в консольном окне
Console.Write(Drive("C").GetFolderName))
Сначала код выполнялся построчно и довольно медленно, поэтому я написал транслятор в байт код.
Например конструкция x=x+1 будет откомпилирована в такой bytecode:
0000: AddStack x
0001: AddStack 1
0002: SUM
0003: ReadStack x
0004: ClearStack
А например MsgBox ("Hello","World",1) в такой:
0000: Str_1 "Hello"
0001: Str_2 "World"
0002: Str_3 "user32.dll"
0003: Str_4 "MessageBoxA"
0004: AddStack @ Str_3
0005: AddStack @ Str_4
0006: AddStack 0
0007: AddStack @ Str_1
0008: AddStack @ Str_2
0009: AddStack 1
0010: Call_Invoke
0011: Stop
Теперь следующий шаг, написать транслятор из текстового байт-кода в бинарник состоящий из четырех-адресных инструкций такого вида:
;Тетрада - 1
0000: 00F1 <--- ID команды байт-кода
0001: 01A1 <--- Операнд1
0002: 1123 <--- Операнд2
0003: 1123 <--- Операнд3
;Тетрада - 2
0004: 00A0 <--- ID команды байт-кода
0005: 0000
0006: 0000
0007: 0001
;Тетрада - 3
0008: 1000 <--- ID команды байт-кода
0009: 0000
0010: 0000
0011: FF1A
;Тетрада - 4
0012: АА00 <--- ID команды байт-кода
0013: 0010
0014: 0100
0015: 0000
Похоже на пулеметную ленту с патронами, виртуальная машина по очереди "заглатывает" каждую тетраду, выполняя для каждой Call Dword (ID), т.е. ID является константным указателем на функцию-обработчик команды. Каждая команда сама "знает" сколько параметров ей надо снять со стека, а также следит за его сбалансированностью.
Проблема - куда девать текстовые литералы и константы?
Отображать например каждый байт литерала в виде тетрады, это слишком жирно будет, хочется сделать компактный бинарник.
Или к примеру, может сделать отдельную секцию для литералов? И отдельную для констант?
Но это очень сильно усложнит процесс трансляции, т.к. необходимо увязывать ссылки на таблицу литералов с адресами их вызова и в процессе трансляции пересчитывать смещения всех переходов по коду, надо будет сначала собрать таблицу литералов, вычислить ее размер. Потом оттранслировать блок тетрад, оставляя в точках переходов "пустышки", слить все таблицы и тетрады в единый бинарник, и в финале пробежаться по коду пропатчивая "пустышки" правильными ссылками на литералы и константы...
Вот такой гемморой, вырисовывается. Практически во всех книжках по компиляции, афторы стыдливо пропускают момент кодогенерации, отделываясь туманными общими фразами, что это очень просто и элементарно...
Может это и так, но что-то ничего лучше придумать не смог, просто уперся рогами в землю.
Если кто-нибудь делал такие вещи, поделитесь идеями, как проще всего реализовать трансляцию из пост/префиксной формы в тетрады, с учетом литералов, адресации, условных переходов и т.д.