@Gudleifr
Почитал тут твое "О чем умолчал Мур".
Часть идей Форта я увидел АВТОМАТИЧЕСКИ возникающими при трансляции алголоподобного языка, пока драл у Вирта компилятор Оберон. Распишу промежуточные мысли 1.) Этот компилятор неизбежно транслирует в код какой-то виртуальной машины. Кодогенерацию я не драл у Вирта, он написал транслятор в код несуществующей RISC-машины, а мне это не надо. Кодогенерацию в код 8086 я писал сам, но при этом все равно кодогенерация свелась к узкому набору неких псевдоопкодов и я даже дал им мнемоники, вот такие LDA STA LEA LDI MOV BEQ BNE BLT BLE BGT BGE JMP JSR RET PSH POP NEG TST ADD SUB CMP MUL DIV REM ADI SBI CPI MLI DVI RMI Т.е. по факту компилятор алголоподобного языка генерирует псевдокод для какой-то виртуальной машины, даже если тебе это и не нужно. Отсюда идея Вирта с P-кодом для компилятора Паскаля. 2.) Компилятор естественным образом транслирует в псевдокод для виртуальной СТЕКОВОЙ машины. В код для регистровой машины его приходится превращать искусственно с помощью дополнительного алгоритма. 3.) Если действительно сдалать компиляцию для виртуальной стековой машины, но не сразу в код, а сначала в ассемблерный текст для нее, то возможна однопроходная трансляция преобразованием потоков, я писал об этом в этом топике раньше. Если ассемблер при этом сделать псевдоассемблером для интерпретации виртуальной машиной, заменив мнемоники LDA на @ , ADD на + , MUL на * и STA на ! , то выражение x := a * (b + c) ; при применении определенных алгоритмов трансляции раскроется так : a @ b @ c @ + * x ! А именно - из-за метода рекурсивного спуска инфиксное выражение автоматом вывернется естественным для стек-машины постфиксным - от использования отсроченной генерации кода, когда код генерируется не сразу, а только тогда когда без этого уже невозможно обойтись, переменная x , фигурирующая в исходном определении первой, в псевдоассемблерном результате трансляции появится последней. А у Вирта компилятор Оберон так и сделан. 4.) Вот если создавать компилятор для виртуальной стековой машины, выдающий результат трансляции в виде псевдоассемблерного текста для интерпретации этой машиной, то этот псевдоассемблерный текст будет выглядеть вполне по фортовски. После чего может естественным образом появится мысль - а зачем тогда вообще компилятор ? И без него писать для виртуальной машины напрямую в ее псевдоассемблере неплохо получается.
Т.е. может Мур не рассказывает полной истории создания Форта потому, что начал писать компилятор чего-то алголоподобного, но так и не дописал, а вместо этого начал играться с созданной для этого компилятора виртуальной машиной и эта история ему не кажется предметом гордости ? Мне такая мысль приходит потому, что если в моем (или виртовском) оберончике заменить пару определений в секции кодогенерации, не трогая ничего в секциях лексического и синтаксического анализа, то без каких-либо дополнительных усилий получится транслятор Оберон->протоФорт. Собственно даже не только в части вычисления выражений, а и в части вызова подпрограмм, если вместо кода входа в процедуру выдавать ": имя процедуры", а вместо кода выхода "NIP ... NIP ;" , где столько NIP сколько у нее параметров. А сами параметры в процедуре извлекать из глубины стека через соответствующий PICK. Да и с условными операторами все именно так будет - они должны будут на лету разрешать ссылки вперед и конструкции Паскаля (не знаю Алгола) IF UNTIL WHILE что-то аналогичное фортовское и дадут. Т.е. Форт похож на виртуальную интерпретирующую стек-машину для псевдоассемблерного выхода алголоподобной трансляции. Единственное, что не вписывается в подобную картинку, так это ДВА стека. Но с одним стеком такая схема получается не очень удобной - параметры процедуры лежат на стеке ПОД адресом возврата. А если сделать два стека, то они окажутся как на блюдечке, NIP .. NIP на выходе процедуры станет DROP .. DROP, да и вообще параметры процедуры можно будет "расходовать" и терять их на стеке сразу как в них минет надобность. Т.е. два стека тут как-бы уже напрашиваются.
|