По своей сути, шитый код — это набор адресов для нерекурсивного (нестекового) вызова подпрограм, каждая из которых передаёт управление (классическое NEXT) следующей (потому и «шитый» — аналогия с нитками на швейной машинке).
Прямой — когда в блоке кода хранятся адреса процедур.
Косвенный — когда в блоке кода хранятся адреса ячеек CFA процедур (что позволяет более гибко управлять исполнением).
А подпрограммный — это обычный машинный код, где слова не занимаются передачей управления.
Для линейного выполнения шитого кода примитивов не нужен стек. Для исполнения подпрограммного — нужен.
Ну, и если грубо — при шитом коде работает «виртуальная машина» (при всей её примитивности), при подпрограммном — идёт обычное исполнение машинного кода.
Да, ещё, вдогонку. Шитый код позволяет писать машиннонезависимый переносимый код, чем и пользовались в ряде Фортов (Из современных, DS, кажется? — x86/ARM). Подпрограммный, как легко понять, пишется только под одну архитектуру.
Просто, по-видимому устаревший материал из советских книжек.
В том-то и дело, что вся моя аргументация — из советских книжек :D И она описывает любые [которые мне пока попадались] сложные случаи. В отличии того, что пишут в Вики :)
Хм, но тут вопрос. Ведь перед тем как выполнить программу ее нужно привести к соответствующему машинному представлению. Это разве не трансляция?
Где ошибка?
В том-то и дело, что вся моя аргументация — из советских книжек
в советское время типичный интерпретатор обрабатывал команду за командой. А в наше время типичный же интерпретатор обрабатывает _весь_ текст целиком, делает явно/неявно что-то типа компиляции, а потом уже выполняет. Выше я приводил пруф с bash'ем.
Потому советские книжки ввели тебя в заблуждение. Покомандное исполнение уже давно никто не применяет, и в интерпретаторах тоже.
Хм, но тут вопрос. Ведь перед тем как выполнить программу ее нужно привести к соответствующему машинному представлению.
Нет, не нужно. Тем интерпретатор и отличается от компилятора, что ничего к машинному представлению не приводит. Более того - иногда такое приведение в принципе невозможно (нельзя сопоставить тексту интерпретируемой программы какой-либо машинный код), но интерпретатор все равно интерпретирует.
Хорошо, раз уж я ввязался в этот тред, тогда продолжим. В конечном счете, ведь тот же интерпретатор выполняется на конкретном ЦП, а не в абстрактном математическом вакууме, ведь так? Что тогда представляют из себя команды интерпретатора с точки зрения центрального процессора ПК ?!!
А именно есть ли такое преобразование, которое переводит систему команд интерпретатора в систему команд CPU?
иногда такое приведение в принципе невозможно (нельзя сопоставить тексту интерпретируемой программы какой-либо машинный код)
ты бредишь: если нет, то как же этот код _исполняется_? Может ты хотел сказать «нельзя сопоставить заранее»? Ну да, такое есть. Ну и что?
PS: вот только что написал калькулятор, прям как в K&R. Там тоже интерпретация выражения вроде 2+2*2=?, выдаёт ессно 6. Да, какой именно код сопоставляется — от выражения зависит, и тем не менее, это всё на C++.
Исполняется код интерпретатора — он один на все интерпретируемые программы, которым ничего не соответствует кроме такого-то способа их исполнения этим интерпретатором.
Ну и что?
Ну и это интерпретатор, потому что если было бы сопоставление, был бы уже компилятор.
только что написал калькулятор
Теперь сделай функцию дампа своего машинного кода который «сопоставляется» в файл для любых выражений. Хотя погоди... тебе же придётся выкинуть свой интерпретатор и написать компилятор :)
Что тогда представляют из себя команды интерпретатора с точки зрения центрального процессора ПК ?!!
Ничего не представляют.
А именно есть ли такое преобразование, которое переводит систему команд интерпретатора в систему команд CPU?
В общем случае - нет, нету такого преобразования. Это зависит от конкретного языка/интерпретатора, конечно, но для динамичных и выразительных языков с евалом и мощной интроспекцией можно написать такую программу, которую в принципе невозможно скомпилировать, то есть перевести в какой-то программный код.
Самый очевидный пример - языки с фекспрами. Они не могут быть компилированы. То есть вообще никак, даже теоретически.
А именно есть ли такое преобразование, которое переводит систему команд интерпретатора в систему команд CPU?
Интерпретатор принимает выражения своего языка, какой-то внешний вход и производит исполнение с эффектами, возвращает выход. То есть представляет из себя функцию/программу написанную на языке своей реализации — при компиляции она вполне может быть скомпилирована в такой-то код, как и всякая функция. Делать подобное выполнение это всё что он должен — остальное это детали реализации. А если под его системой команд понимаются не они, а выражение его языка, то им не соответствует кода — код один, это код интерпретатора, его входным выражениям соответствуют конкретные пути его исполнения.
Ещё см. Чем отличается компилятор от интерпретатора? (комментарий) — мы можем на любую функцию смотреть как на интерпретатор (то же исполнение). Например, strlen (максимально упрощённый случай) притворяется функцией возвращающей 5 если на вход подаётся строка длинной в пять символов, 7 — семь символов и т.д. Какой код CPU соответствует strlen? Какой код CPU соответствует строке в пять символов?
Исполняется код интерпретатора — он один на все интерпретируемые программы, которым ничего не соответствует кроме такого-то способа их исполнения этим интерпретатором.
да говорю же — ты бредишь. Выполни $ while true; do true; done. Выполнил? И чем у тебя CPU загружен?
Ну и это интерпретатор, потому что если было бы сопоставление, был бы уже компилятор.
дык есть сопоставление. В любом интерпретаторе.
Теперь сделай функцию дампа своего машинного кода который «сопоставляется» в файл для любых выражений. Хотя погоди... тебе же придётся выкинуть свой интерпретатор и написать компилятор :)
чего «годить»? Могу и сопоставить. Каждая строка C++ кода порождает вполне конкретный машинный код. Если его сохранить, получится компилятор. Для каждого выражения получается свой код.
но для динамичных и выразительных языков с евалом и мощной интроспекцией можно написать такую программу, которую в принципе невозможно скомпилировать, то есть перевести в какой-то программный код.
в таком случае, выполнить её на IA-32/amd64 тоже невозможно.
Самый очевидный пример - языки с фекспрами. Они не могут быть компилированы. То есть вообще никак, даже теоретически.
Может ты хотел сказать «нельзя сопоставить заранее»? Ну да, такое есть. Ну и что?
Ну и все. Нельзя данной интерпретируемой программе сопоставить конкретную последовательность команд. То есть перевода просто не существует.
с какого перепугу перевод обязан быть однозначным? Ещё раз, для тех кто в танке: обычный цикл выполняется неизвестно сколько раз. Рекурсия выполняется неизвестно сколько раз. Вот у меня например для выражения 1+2+3 две команды add, а для 1+2+3+4 четыре команды add. И что? Код один.
Сформулируй точнее возражение, а то я не понял как возможность выполнения интерпретатором выражения обозначающего бесконечный цикл соотносится с цитируемым утверждением.
дык есть сопоставление.
Есть код интерпретатора, один. Интерпретируемые выражения соответствуют путям исполнения этого кода, одного и того же.
А в случае компилятора — свой код для каждой программы.
Как бы разница в том, что этот «свой код для каждой программы» из «кода, одного и того же» не вытаскивается тривиально.
Могу и сопоставить.
Ну выкатывай код.
Каждая строка C++ кода порождает вполне конкретный машинный код.
Это код интерпретатора. Теперь показывай код CPU в виде инструкций соответствующий его интерпретируемым программам, то есть строкам — «12345», например (5 при интерпретации).
Но конкретно в момент исполнения команд на ЦП эта последовательность задана и однозначна, они же машинные, если программа работает. Иначе такой интерпретатор это сферический конь Бора в вакууме :)
В том то и подмена понятий, что SQL формальный язык запросов, а Си императивный и для него верно утверждение, что на данной аппаратной архитектуре, при заданной программной конфигурации в некий момент времени есть взамное и однозначное представление исходного текста на Си в Ассемблерный код данной архитектуры.
Так что приплетать сюда исчисление кортежей, как аргумент, несколько излишне.
Нет подмены понятий, есть понятия языков, грамматик (это к синтаксису), трансляторов, интерпретаторов (это к соответствующим семантикам), это общие понятия. SQL — пример (никаких исчислений кортежей не подразумевалось), Си — пример, как и любые другие языки, будь то DSL или ЯП общего назначения.
И вообще я уже не знаю что мы обсуждаем.
Для си есть трансляция в машинный код? Ну да, поэтому существует и практикуется компиляция (или наоборот :)). Взаимно-однозначное? Нет, поэтому декомпиляция это гораздо более сложная задача и практикуется обратная разработка как поиск её решения. Каким боком это к теме?
Сформулируй точнее возражение, а то я не понял как возможность выполнения интерпретатором выражения обозначающего бесконечный цикл соотносится с цитируемым утверждением.
цикл транслируется в команды CPU, которые и выполняются. Эти команды можно записать файл, и тогда получится компилятор(да, плохой и тормозной, но это не важно).
Есть код интерпретатора, один. Интерпретируемые выражения соответствуют путям исполнения этого кода, одного и того же. А в случае компилятора — свой код для каждой программы.
x+y всегда транслируется компилятором в команду add, и интерпретатором тоже(если без оптимизации конечно). Не вижу разницы в упор.
Как бы разница в том, что этот «свой код для каждой программы» из «кода, одного и того же» не вытаскивается тривиально.
всё там прекрасно вытаскивается.
Ну выкатывай код.
не понимаю, код чего?
Ну возьми gdb и посмотри.
Так ты калькулятор или интерпретатор C++ написал?
калькулятор, который транслирует ЯП арифметических выражений(а это тоже алгоритмический ЯП) в машинный код, и его сразу выполняет. При чём тут C++? Кто вообще сказал, что программа на C++ должна обязательно компилироваться в C++, а уж потом в ассемблер? Мой калькулятор вообще никакого отношения к C++ не имеет, у него на входе арифметическое выражение, а на выходе машинный код, который прямо сразу и выполняется. И сам он на машинном коде, а на C++ только его исходник, который можно на что угодно переписать.
И да, скобки мой калькулятор умеет, а следовательно там есть «мощный eval». Как вы и заказывали.
Это код интерпретатора. Теперь показывай код CPU в виде инструкций соответствующий его интерпретируемым программам, то есть строкам — «12345», например (5 при интерпретации).
это машинный код. Для любых ASCIIZ строк(ну в смысле <2³¹). Для «12345» оно тоже работает(правда там 6 итераций).
Для си есть трансляция в машинный код? Ну да, поэтому существует и практикуется компиляция (или наоборот :)). Взаимно-однозначное? Нет, поэтому декомпиляция это гораздо более сложная задача
декомпиляция очень простая задача на самом деле. Вот только код после декомпиляции невозможно читать людям. Но это не относится к нашему спору.
И да, именно взаимно-однозначное существует. Просто людям неудобно им пользоваться. Мне проще писать ⅔x, а компилятор переделывает это в x*0xAAAAAAAA. Также мне нравится использовать все три типа циклов (for, while, do{}while), но компилятор юзает только последний тип. Ну а ещё компилятор через строку пихает goto, и прочую гадость. А мне так читать неудобно.
Теоретически возможно и действительно взаимо-однозначное, вот только _моё_ понятие «удобочитаемости» абсолютно не формализуется. Потому при двойном переводе получается нечитаемый говнокод. За то этот говнокод
1. эквивалентен моему коду
2. взаимо-однозначен
ссылки
ты путаешь подходы и определения. Формально компилятор и интерпретатор ничем не отличается, однако для построения удобного и годного DSL важно учитывать специфику. Т.е. в принципе можно запихать замкнутый eval в C→C, вот только оно нафиг никому не нужно. Сам ЯП заточен не компиляцию в машинный код. Также и введение оптимизируещего компилятора в какой-нить PHP тоже ничего не даст и не нужно. Компилятор всё равно не сможет оптимизировать утиную типизацию, которая определиться только в рантайме. Вот и будет на массив char'ов в 10 штук, хранить и пережёвывать 20 указателей в 8 char'ов каждый. Смысл?
цикл транслируется в команды CPU, которые и выполняются.
Цикл у тебя сразу выполняется в случае pure interpretation. Команды CPU у тебя уже тут, в коде откомпилированного интерпретатора, его бинарника, он берёт твой текст/AST и делает своё дело как любая другая функция — нечего ему транслировать, так как выходного языка не предполагается.
Эти команды можно записать файл, и тогда получится компилятор(да, плохой и тормозной, но это не важно).
Ну если взять интерпретатор, выкинуть интерпретатор и запилить компилятор, то получится компилятор вместо интерпретатора, да.
x+y всегда транслируется компилятором в команду add, и интерпретатором тоже(если без оптимизации конечно).
Да ладно, во что хочет, в то и транслируется, как хочет, так и интерпретирует, то что в C++ _+_ не имеет отношения к тому что в реализуемом языке _+_ (gmp_add или matrix_add, например) — поскипано во имя перегрузки.
Не вижу разницы в упор.
Ты просто привязался к своему «интерпретатор транслирует свои программы в инструкции CPU и выполняет их», хотя привязываться не нужно — нужно коммуницировать :)
транслирует (во время выполнения, чо) вход 2 в CPU инструкции block. Они полученные уже, правда, заранее компиляцией этого кода, да и не работает это — сделай block не локальным, но нетривиальным control flow и непонятно где твоё соответствие (в формальном смысле этого слова, то есть отображения из язык в язык, а то может ты «примерно» говоришь).
всё там прекрасно вытаскивается.
Ну возьми gdb и посмотри.
Это не вытаскивается, это каша — ты либо берешь компилятор вместо интерпретатора и получаешь честную трансляцию, либо пишешь что-то вроде специализации интерпретатора под данное значение аргумента каждый раз сам (можешь в gdb подсматривать), чего никто не делает.
При чём тут C++?
Ты говоришь
Каждая строка C++ кода порождает вполне конкретный машинный код.
А тебе надо, чтобы каждое арифметическое выражение транслировалось в машинный код (раз всё вытаскивается). Т.е. я тебе даю, например, (2+3)*5 — твой велосипед мне кидает кусок ассемблера/машинного кода, а если ты интерпретируешь, то нечего рассказывать, ты путаешь код самого интерпретатора с кодом который бы соответствовал (2+3)*5 при компиляции.
Мой калькулятор вообще никакого отношения к C++ не имеет, у него на входе арифметическое выражение, а на выходе машинный код, который прямо сразу и выполняется.
Да, я уже понял что ты пишешь, «у меня на входе strlen строки, а на выходе машинный код [который ты в виде *.s уже запостил], который прямо сразу и выполняется».
это машинный код. Для любых ASCIIZ строк
Ну то есть транслятора из строк в CPU код у тебя нет, я и говорю, что strlen это вырожденный пример интерпретатора (и транслятора, если говорить о трансляции в чистые значения, то есть о constant folding).
Это относится к тому, что compile/decompile не «взаимно-однозначны» — это сложно и невозможно, если только на классах эквивалентности modulo сохранению семантики — это возможно и легко, но не интересно и есть вопрос о том что есть сохранение семантики.
взаимно-однозначное существует
То есть ты можешь скомпилировать программу, потом декомпилировать её обратно и получить ту же программу?
Вот только код после декомпиляции невозможно читать людям
Нет, значит не взаимно-однозначное, если тебе не пофиг на читаемость и сохранение абстракций и не нужно только «работает так же» (да и то, не факт, что компиляция с оптимизациями, декомпиляция и повторная компиляция с оптимизациями будет «так же» работать).
Формально компилятор и интерпретатор ничем не отличается
А пруф? А то пишуть альтёрнатив апроач.
Но правда, что и то и другое можно объединить под общим «processing», «обработка».
З.Ы. в математике, тащемто, интерпретация и трансляция (гомоморфизм) это примерно то же, что эти понятия по ссылкам.
Цикл у тебя сразу выполняется в случае pure interpretation. Команды CPU⁽¹ у тебя уже тут, в коде откомпилированного интерпретатора, его бинарника, он берёт твой текст/AST и делает своё дело как любая другая функция — нечего ему транслировать⁽², так как выходного языка не предполагается.
т.е. ты утверждаешь, что (1 берётся из хрустального шара или другой магией(благодатным огнём), да?
Эти команды можно записать файл, и тогда получится компилятор(да, плохой и тормозной, но это не важно).
Ну если взять интерпретатор, выкинуть интерпретатор и запилить компилятор, то получится компилятор вместо интерпретатора, да.
выкидывать ничего не нужно. Не передёргивай мои слова.
x+y всегда транслируется компилятором в команду add, и интерпретатором тоже(если без оптимизации конечно).
Да ладно, во что хочет, в то и транслируется, как хочет, так и интерпретирует, то что в C++ _+_ не имеет отношения к тому что в реализуемом языке _+_
ЯП и его стандарт не имеет. Однако компилятор в IA-32 других вариантов не знает. Как и интерпретатор. И не приплетай сюда перегрузку, она меняет сам смысл operator+(). Речь лишь про встроенный тип int/float. Я уже говорил выше, что если у тебя «утиная» типизация, то что компилятор, что интерпретатор, могут только в рантайме решить, каким кодом складывать числа. Посему тащат за собой всё множество кодов для любых сложений.
Ты просто привязался к своему «интерпретатор транслирует свои программы в инструкции CPU и выполняет их», хотя привязываться не нужно — нужно коммуницировать :)
просто это реальность такая, чудес не бывает.
транслирует (во время выполнения, чо) вход 2 в CPU инструкции block. Они полученные уже, правда, заранее компиляцией этого кода, да и не работает это — сделай block не локальным, но нетривиальным control flow и непонятно где твоё соответствие (в формальном смысле этого слова, то есть отображения из язык в язык, а то может ты «примерно» говоришь).
всё работает. Сделай block не локальным, и код будет находится в другом месте. Будет. И с интерпретатором также.
Ну возьми gdb и посмотри.
Это не вытаскивается, это каша
это не доказательство. Выхлоп интерпретатора никто не готовил специально для тебя. Твой CPU эту кашу жрёт, и просит ещё, а что тебе не пошло — твоя проблема. Выхлоп gcc -O2 тоже не слишком просто осилить, если программа нетривиальная.
Т.е. я тебе даю, например, (2+3)*5 — твой велосипед мне кидает кусок ассемблера/машинного кода, а если ты интерпретируешь, то нечего рассказывать, ты путаешь код самого интерпретатора с кодом который бы соответствовал (2+3)*5 при компиляции.
вот «код самого интерпретатора» и есть выход. Этот код может вычислить (2+3)*5, и дать ответ 25. Т.о. данный код является трансляцией любого выражения в машинный код, даже если само выражение заранее неизвестно. Как видишь, условие известности алгоритмов не обязательно. Алгоритм можно задать во время выполнения. Т.е. можно 2+(3*5), а можно (2+3)*5. Тут числа одинаковые, но алгоритм вычисления разный. Ответ тоже разный. Но интерпретатор один единственный, хотя машинный код порождается и выполняется разный.
Да, я уже понял что ты пишешь, «у меня на входе strlen строки, а на выходе машинный код [который ты в виде *.s уже запостил], который прямо сразу и выполняется».
у тебя тоже.
Ну то есть транслятора из строк в CPU код у тебя нет, я и говорю, что strlen это вырожденный пример интерпретатора (и транслятора, если говорить о трансляции в чистые значения, то есть о constant folding).
осталось сделать преобразование кода x в адрес определённой ячейки памяти. И компилятор готов. А если этого не делать, а просто занести x в edx, и выполнить мой код, то получится интерпретатор.
Ну то есть транслятора из строк в CPU код у тебя нет
зачем тебе такой транслятор? Что тебе мешает хранить строки как наборы байт? Зачем тебе их транслятор?
То есть ты можешь скомпилировать программу, потом декомпилировать её обратно и получить ту же программу?
я на C/C++ — нет. Но теоретический такой код возможен и такой компилятор тоже. Просто оно никому не нужно. Если я пишу ⅔, то компилятор это превращает в 0x55555556, откуда ты ⅔ уже не вытащишь однозначно. Если я пишу x*=2, компилятор пишет x<<=1. Это тоже обратно переводится в x<<=1.
Нет, значит не взаимно-однозначное, если тебе не пофиг на читаемость и сохранение абстракций и не нужно только «работает так же»
при чём тут читаемость? Возможность есть. Просто она никому не нужна. Проще исходник приложить. Потому никто и не реализовал.
да и то, не факт, что компиляция с оптимизациями, декомпиляция и повторная компиляция с оптимизациями будет «так же» работать
ну дык с оптимизацией оно и так «не так» работает. А быстрее. Т.е. это не баг, а фича. Если я пишу 1+2, компилятору плевать на однозначность, и он пишет 3, откуда естественно не вытащить 1+2. Неужели ты думаешь, что невозможно написать такой компилятор, который будет в рантайме Over9000 раз складывать 1+2? Ради вселенской однозначности...
Формально компилятор и интерпретатор ничем не отличается
А пруф?
пруф — возможность исполнения и того и того в семантике одного и того же CPU. Потому что в CPU нет «двух режимов», а есть только один — исполнение кода. Потому, хоть в лоб, хоть полбу, если написано x+y, то это надо перевести в add.
a program written in a source language S is directly executed by an S-interpreter, which is a program written in an implementation language
⁽¹ тебе по большому счёту не нужно, так как у тебя есть S-интерпретатор, который на своём уровне абстракций выполняет свои программы без всяких CPU. Сравни, допустим, с интерфейсом к файловым системам — ты можешь работать с ним как с API не зная ничего про архитектуру, код CPU, драйвера или ОС, тебе это не интересно на этом уровне. Есть представление о семантике implementation language и ладно — дальше нам не нужно.
Ты смешиваешь разные вещи и получаешь путаницу. Достаточно сказать, что обработка языка интерпретатором это выполнение в соответствии с семантикой, то что выполнитель на чём-то там написан и считает не на пальцах, а в железке вообще не относящийся к делу вопрос.
(S-terms -> S-Machine) by S-interpreter
B.
Но если хочешь — пусть S-интерпретатор написан на своём языке Inter и может быть скомпилирован в язык CPU компилятором Inter -> CPU, это будет ⁽², язык CPU может быть выполнен CPU-интерпретатором — вот это уже ⁽¹. Интерпретация это семантическая обработка, с ней связан абстрактный вычислитель, то есть в случае S-интерпретатора совсем абстрактный, в случае CPU — и конкретный тоже.
(S-terms -> S-Machine) by S-interpreter
(Inter-terms -> CPU-terms) by Inter-to-CPU compiler for this particular S-interpreter
(CPU-terms -> CPU-machine) by CPU-interpreter
C.
In the translation approach, an S program is translated to a program in the target language T, which can be executed by a T-interpreter.
Чистый интерпретатор не делает S-terms -> T-terms, он берёт S, загружает в вычислитель и запускает его — S-terms -> S-machine. То есть с одной стороны у нас трансляция — синтаксическая обработка в виде чёрного ящика который работает как функция (http://en.wikipedia.org/wiki/Natural_transformation, http://en.wikipedia.org/wiki/Homomorphism, гомоморфизмы синтаксических структур из синтаксических категорий с естественностью по отношению к модельным функторам в семантические домены, то есть вообще говоря синтаксическая трансляция учитывает сохранение семантики), то есть принимает программы (синтаксис) и возвращает программы (он же), а с другой интерпретация — семантическая в виде ящика который работает как исполнитель/машина которая просто принимает программы и их выполняет (семантика, http://en.wikipedia.org/wiki/Functor, http://en.wikipedia.org/wiki/Interpretation_(logic), модели, семантические домены).
Речь лишь про встроенный тип int/float.
Сделай block не локальным, и код будет находится в другом месте.
Т.е. можно 2+(3*5), а можно (2+3)*5.
Ты как-то в сторону и на частности опять. Код не будет нигде находиться — см. A. выше. Но если берёшь B., то не путай и пойми простую вещь — терму S не соответствует CPU-терм (в ситуации к конкретно написанным (Inter-терм) и скомпилированным в конкретный CPU-терм интерпретатором), ему соответствует конкретный пробег CPU-машины/интерпретатора. Попросту говоря, block не будет в другом месте (это же тоже локально), он будет кусками размазан где попало, то есть это не конкретные инструкции, а нетривиальные отношения на них — вытащить их во что-то вменяемое не тривиально, поэтому компиляция отличается от интерпретации.
просто это реальность такая, чудес не бывает.
Да никакая «это» не «реальность» :)
вот «код самого интерпретатора» и есть выход.
То есть как у тебя получается — я спрошу что возвращает функция при вычислении с аргументом, ты скажешь, что свой код (машинный. А почему машинный? А для какой архитектуры? А почему этой, а не той?), но погоди — я ж спрашиваю про вычисление, а он (этот машинный код который у тебя возвращается) что возвращает при вычислении? Опять сам себя и так до бесконечности? Фигня это всё :)
Ок, напишу
Не так. Принимаешь source программу, то есть строку, во время компиляции вычисляешь её длину n и генерируешь target программу ret n. Это будет правильное продолжение аналогии.
Любая виснущая программа, например :) Ей никакого машинного кода при интерпретации соответствовать не может (т.к. выполняется по факту бесконечная последовательность команд), но однако она прекрасно интерпретируется :)