LINUX.ORG.RU

Emulek-style programming


2

1

Почитал я вот этот тред, и (может быть ошибочно) понял, что участники дискуссии разделились на 2 лагеря. Первый — за emulek-style, другие против.

Взгляд emulek на процесс заключается, насколько я понял, в следующем. Поскольку строки представлены для проца маш кодом, можно считать их скомпилированными прогами. Поэтому, можно взять компилятор, вызвать из него интерпретатор, исполнить этим интерпретатором код, понятный этому интерпретатору, и, вуаля, у нас скомпилированная прога, в виде строки выхлопа интерпретатора (представленая в машкоде).

Давайте оформим эту мысль вот так. Есть 2 ЯП l1 и l2. Есть транслятор с l1 в l2 — это компилятор, назовем его К, и есть транслятор с l1 в l1 — это интерпретатор — И.

Представим себе, что мы выполнили текст на языке l1 c помощью И

text(l1) --> И --> text(l1)
и с помощью K
text(l1) --> K --> text(l2)
Пока все вроде ок, все работает одинаково. А теперь представим себе такую ситуацию
(text(l1) --> И --> text(l1)) --> И --> text(l1)
Мы выполнили выходную строку текста как программу — подали ее вновь на вход транслятору. Иными словами мы выполнили сгенерированную в рантайме программу.

Может ли транслятор K сделать то же самое? Только с помощью костылей, типа макросов и препроцессоров. Но есть тонкий момент. Что если нам нужно сделать

((text(l1) --> И --> text(l1)) --> И --> text(l1)) --> И --> text(l1)
?

Тут у нас всплывает, ИМХО, суть того, что принято называть мощностью ЯП.

А Вы за Emulek-style или против?:)


Ответ на: комментарий от emulek

своими словами можно?

Почитай ссылку, я её привёл чтобы по ней сходить и посмотреть :) Хотя нужную цитату оттуда ты уже процитировал.

О чём я и говорил

Что-то ты не так понял. Там конкретные вещи, а ты сразу за обобщения. Эмуляция машкода это специальная задача (а не сразу «современный интерпретатор»). И у неё может быть решение с трансляцией (вот прямо так разные target <-> разные host или как в QEMU хитро), либо простой интерпретацией после декодера (что противопоставляется по ссылкам, вообще-то).

Т.о. современный интерпретатор можно с лёгкостью переделать в компилятор, ибо он является «динамическим транслятором».

Нельзя. Да и даже если было бы можно — как в шутке про бабушку с дедушкой будет :) Называем вещи своими именами, мало ли что там можнозделать.

На практике «чисто интерпретирующие эмуляторы» AFAIK никто уже давно не применяет. Даже bash не может просто взять, и эмулировать while true; do, ему надо ещё done для трансляции.

Зачем ты смешал эмуляторы и интерпретаторы ЯП? Ну и про ЯП в прошлом треде уже обсудили десять раз, и выше картинка была — http://i.imgur.com/QHTDpsa.png, пре-проверками и/или пре-трансляцией в байткод они занимаются, который VM выполняет, может потом JIT компилятор транслирует в машинный код, который непосредственно выполняется.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от quasimoto

Т.о. современный интерпретатор можно с лёгкостью переделать в компилятор, ибо он является «динамическим транслятором».

Нельзя. Да и даже если было бы можно — как в шутке про бабушку с дедушкой будет :) Называем вещи своими именами, мало ли что там можнозделать.

заметь, я не говорил, что это одно и то же. Про операции перемены пола слышал? Значит — можно, при желании. ☺

На практике «чисто интерпретирующие эмуляторы» AFAIK никто уже давно не применяет. Даже bash не может просто взять, и эмулировать while true; do, ему надо ещё done для трансляции.

Зачем ты смешал эмуляторы и интерпретаторы ЯП

я кавычки не просто так поставил. Это цитата из твоего поста.

И не усложняй, всем понятно, что нет никакого принципиального различия в трансляции в LLVM и трансляции в x86. Просто другой выходной ЯП.

emulek
()
Ответ на: комментарий от emulek

я кавычки не просто так поставил. Это цитата из твоего поста.

Ты спросил про x86 и про ЯП — я ответил про то и другое отдельно, так как это разные вещи (в одном контексте «интерпретация» это одно, в другом — немного другое, но тоже интерпретация в широком смысле, конечно), смешал ты :)

В остальном — интерпретация это выполнение представления, трансляция — перевод из одного представления в другое. Понятно, что трансляция будет предшествовать выполнению — вместо выполнения исходного представления мы транслируем исходное представление в целевое, которое потом и будем выполнять — потому что так эффективнее, чтобы таскать целевое представление (как JVM) или какие ещё могут быть причины. Если взять готовый компилятор и спросить, может ли он выполнять роль интерпретатора, то ответом будет «нет». И наоборот — интерпретатор не будет выполнять роль компилятора. Если спросить, можно ли одно «легко» превратить в другое — тоже «нет». Назначение разное. Всё общее что у них есть — они могут работать с одним представлением похожими методами (синтаксически ориентированная обработка, например), да и то, различий в деталях будет сколько хочешь. С этим представлением такими методами что угодно может работать — статический анализатор, например.

И не усложняй, всем понятно, что нет никакого принципиального различия в трансляции в LLVM и трансляции в x86. Просто другой выходной ЯП.

Это два разных компилятора. А ты на интерпретацию посягаешь :)

Ну вот выбери пару инструкций посложнее, напиши декодер, интерпретатор и покажи как он легко превращается в динамический транслятор (и без фокусов с микро-операциями и перекидыванием работы на gcc). Так, что ты будешь просто писать новый код на том же языке цэ, чиста принципиальна используя те же синтаксические конструкции языка цэ и представление после декодера? Легко, ага :)

И про ЯП я уже спрашивал — интерпретатор лямбда-исчисления (HOAS trick — несколько строчек) в компилятор? В нормальный компилятор?

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от quasimoto

ладно, сойдёмся на том, что у нас разные представления о слове «легко».

И про ЯП я уже спрашивал — интерпретатор лямбда-исчисления (HOAS trick — несколько строчек) в компилятор? В нормальный компилятор?

там ты специально выбрал такой ЯП, который на компилятор не ложится. Конечно, сделать нормальный интерпретатор x86 тоже будет очень сложно, и он будет никому не нужен. Просто каждый ЯП явно и неявно заточен на интерпретацию/компиляцию, и только самые простые(вроде brainfuck) легко отображаются в код, и одновременно допускают непосредственное выполнение.

emulek
()
Ответ на: комментарий от emulek

Да нет, трансляция — алгоритм, интерпретация — программа, не алгоритм. То есть ты можешь сделать компилятор как алгоритм которому будет пофиг для какого бакенда генерировать выхлоп, да, но превратить компилятор в интерпретатор и наоборот лёгким движением руки («вот тут я инструкции генерировал, а буду действия делать» и наоборот) без изменения структуры кода (выкидывания и, обратно, написания алгоритма) не получится. И функциональное назначение у них разное.

там ты специально выбрал такой ЯП, который на компилятор не ложится.

Всё ложится.

Конечно, сделать нормальный интерпретатор x86 тоже будет очень сложно, и он будет никому не нужен.

Есть же QEMU и т.п., интерпретация была бы даже проще чем то что они делают.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Да нет, трансляция — алгоритм, интерпретация — программа, не алгоритм.

транслятор — программа, интерпретация — алгоритм. Зависит от точки зрения.

То есть ты можешь сделать компилятор как алгоритм которому будет пофиг для какого бакенда генерировать выхлоп, да, но превратить компилятор в интерпретатор и наоборот лёгким движением руки («вот тут я инструкции генерировал, а буду действия делать» и наоборот) без изменения структуры кода (выкидывания и, обратно, написания алгоритма) не получится.

а это уже проблема семантики ЯП. Если там eval есть, то компилятор из интерпретатора так просто не сделать, ибо для eval по любому нужен интерпретатор.

И функциональное назначение у них разное.

я уже устал объяснять, что с этим я не спорю. Да, назначение разное. И называются по разному. Я о другом.

там ты специально выбрал такой ЯП, который на компилятор не ложится.

Всё ложится.

аппликация как часть β-редукции не ложится на компилятор для неизвестного аргумента в общем случае (если аргумент сам является неизвестной функцией, а не неизвестным числом). В C/C++ ты попросту не сможешь построить такое выражение(без костылей типа boost'а), а в выбранном тобой ЯП это тривиально.

Есть же QEMU и т.п., интерпретация была бы даже проще чем то что они делают.

просто это настолько не нужно, что этого никто не сделал. И мне лень делать просто что-бы поставить точку в этом споре...

emulek
()
Ответ на: комментарий от emulek

В C/C++ ты попросту не сможешь построить такое выражение(без костылей типа boost'а)

Ну boost, как раз, и написан на C++, т.е. такое выражение построить можно. У тебя, как всегда, две части предлажения противоречат друг другу. :)

BRE ★★
()
Ответ на: комментарий от BRE

Но буст - костыль, что бы не говорили, хотя да, иногда это удобный костыль. Больше скажу, на C++ можно и интерпретатор написать...

peregrine ★★★★★
()
Ответ на: комментарий от BRE

В C/C++ ты попросту не сможешь построить такое выражение(без костылей типа boost'а)

Ну boost, как раз, и написан на C++, т.е. такое выражение построить можно. У тебя, как всегда, две части предлажения противоречат друг другу. :)

ты как всегда осилил только одну часть предложения.

emulek
()
Ответ на: комментарий от peregrine

Больше скажу, на C++ можно и интерпретатор написать...

можно конечно. Вот только не с C++, а с какого-нить другого ЯП. Например с boost'а.

emulek
()
Ответ на: комментарий от emulek

Это меня и пугает. :)

Твои предложения лучше не осиливать полностью.:)

BRE ★★
()
Ответ на: комментарий от avtoritetniy-expert

Интерпретатор можно написать на любом ЯП. С++ чем то принципиально отличается в этом смысле?

ты путаешь «НА любом ЯП» и «С любого ЯП». На C++ конечно можно написать что угодно, но вот интерпретировать сам C++ не очень хорошо. Как и компилировать bash например. Т.е. можно в теории, но на практике не очень. А уж на каком ЯП ты будешь писать компилятор/интерпретатор — другой вопрос.

emulek
()
Ответ на: комментарий от avtoritetniy-expert

в машкод, как ты вообще себе это представляешь?

а где ты видишь проблему? Я вижу проблему только в eval'е, в который можно запихивать разные программы на bash'е скажем из каких-нить файлов. Всё остальное вполне реализуемо на уровне системных вызовов ОС(и немножко машкода, особенно хорошо на amd64, ибо арифметика в bash 64х битная).

emulek
()
Ответ на: комментарий от emulek

а где ты видишь проблему?

Проблема в том, что в выражении echo `ls` инструкция echo должна получить аргумент в виде строки на баше. И так далее.

avtoritetniy-expert
()
Ответ на: комментарий от emulek

Ладно, допустим, ты смог написать такую фигню, которая переводит текст на баше в бинарь. Нажал энтер, у тебя бинарь на выходе, дальше что? Ты будешь этот бинарь выполнять, чтобы получить выходную строку? Тогда ты получаешь интерпретатор педальный? Сам вместо компа будешь работать?

avtoritetniy-expert
()
Ответ на: комментарий от emulek

я говорил

А интерпретатор любого компилируемого языка написать — это плевое дело, ИМХО. Пишешь строку кода, подаешь на вход интерпретатора, он ее компилирует, выполняет, и выводит тебе результат выполнения.

anonymous
()
Ответ на: комментарий от phill

Итак, для идиотов.

Интерпретация - это функция evalLangN: Term Data -> Data, которая принимает программу (терм некторого языка) и начальные данные и возвращает данные Трансляция - функция translateLang1ToLang2: Term -> Term, которая берет терм на языке 1 и делает из него терм на языке 2, при этом должно быть evalLang1(x) = evalLang2(translateLang1ToLang2(x)) для любого x: Term.

любому видно, что у транслятора и интерпретатора РАЗНЫЕ ТИПЫ. По-этому интерпретатор НЕ МОЖЕТ БЫТЬ ТРАНСЛЯТОРОМ. По определению. компилятору НЕ НУЖНО данных, чтобы работать. Ему нужен только терм (исходный код). Интерпретатору НУЖНЫ (в общем случае) входные данные. Он без них работать не будет.

В том треде с емулеком мы просто играли в игру как бы «не замечая» этих очевидных фактов.

anonymous
()
Ответ на: комментарий от phill

Оно и видно. Посмотри на ссылку в начале топика. Больше десяти страниц срача, и к единому мнению так и не пришли.

10 страниц страча доказывают, что емулек - безграмотный долбоеб, не более того. Не становись таким же.

anonymous
()
Ответ на: комментарий от anonymous

А теперь для полных идиотов. Для компилятора входные данные - это строки текста программы. Я даже не представляю, каким надо быть идиотом, чтобы этого не понимать. Как ты с Эмулеком мог вообще разговаривать о чем то с такими мозгами. Его тут все дурачат, типа, но в сравнении с тобой он просто академик, епт.

anonymous
()
Ответ на: комментарий от phill

Да и вообще, почему же тогда в CS упорно все называют интерпретатор транслятором

Вообще-то никто кроме емулека не называет. А емулек не имеет отношения к CS. Такие дела.

anonymous
()
Ответ на: комментарий от emulek

в той, другой теме, с которой всё и началось, я изначальнро писал «то, что все называют интерпретатором, можно теоретически переделать в компилятор

Как только ты предоставишь этот способ, спор сразу закончится. Но ты только кукарекаешь на счет того, что способ якобы есть, но ничего об этом способе сказать не можешь.

anonymous
()
Ответ на: комментарий от anonymous

Для компилятора входные данные - это строки текста программы.

Я это и написал. У тебя проблемы с чтением?

anonymous
()
Ответ на: комментарий от anonymous

Я это и написал

Нет ты написал

компилятору НЕ НУЖНО данных, чтобы работать

А еще ты написал

Интерпретатору НУЖНЫ (в общем случае) входные данные. Он без них работать не будет.

Теперь открой свой шелл, нажми Энтер, и убейся апстену.

anonymous
()
Ответ на: комментарий от anonymous

Нет ты написал

Зачем же ты вырываешь слова из контекста? Интерпретатору не нужны входные данные интерпретируемой программы (Data), но, естественно, нужна сама программа (Term).

Теперь открой свой шелл, нажми Энтер, и убейся апстену.

Открыл, и? Как это опровергает мои слова?

anonymous
()
Ответ на: комментарий от anonymous

Любому транслятору, будь то компилятор или интерпретатор, нужны входные данные — текст, чтобы его оттранслировать. На этом я заканчиваю эту детсадовскую дискуссию.

anonymous
()
Ответ на: комментарий от emulek

транслятор — программа, интерпретация — алгоритм. Зависит от точки зрения.

Это не зависит от точки зрения, то что ты написал это просто неправда.

Если там eval есть, то компилятор из интерпретатора так просто не сделать, ибо для eval по любому нужен интерпретатор.

Простейший случай, никакого eval — у алгоритма компилятора одна структура, а у кода интерпретатора уже другая. Интерпретатор может работать сколько угодно долго на терме, тогда как транслятор должен все термы переводить на равных правах и сколько угодно долго работать не должен («алгоритм» же).

аппликация как часть β-редукции не ложится на компилятор для неизвестного аргумента в общем случае (если аргумент сам является неизвестной функцией, а не неизвестным числом). В C/C++ ты попросту не сможешь построить такое выражение(без костылей типа boost'а), а в выбранном тобой ЯП это тривиально.

Покажи что конкретно (пример аппликации) не ложится и что не получается написать на C++ (если ты на нём компилятор реализуешь, то тем более любая аппликация спокойно пишется, так как представляется AST).

Как бы Scheme, SML, C++11 и т.д. и т.п. спокойно компилируются.

просто это настолько не нужно, что этого никто не сделал.

Это нужно для эмуляции, очевидно, и делалось 100500 раз. Просто теперешние решения делают это лучше.

И мне лень делать просто что-бы поставить точку в этом споре...

Удивительно, что это так не ясно. Аналогично — интерпретирующий эмулятор может крутить машинный код сколько угодно долго, а транслятор в транслирующем устроен иначе — просто другой код (он же машкод другой архитектуры должен генерировать, а не действия выполнять), он не должен что-то крутить, он должен просто провести трансляцию и завершиться. Один и тот же код не может работать и не работать одновременно.

quasimoto ★★★★
()
Ответ на: комментарий от anonymous

Именно это я и сказал. Но у тебя пиздоглазие. Или ты просто читать не умеешь.

anonymous
()
Ответ на: комментарий от quasimoto

Скорее частичная функция Term * Input -> Output (тип и класс сложности).

Ну под Data подразумевалось что-то типа World, да. Тут как назвать уже не так важно, смысл в том, что компилятору данных кроме поданного на вход терма не нужно. В отличии от интерпретатора.

anonymous
()
Ответ на: комментарий от anonymous

на практике, его всегда можно сделать доступным. если нет — то система столь огорожена или примитивна, что там интерпретатор языка х всё равно не запустится (например, на мк)

next_time ★★★★★
()
Ответ на: комментарий от next_time

ты непонел наверное. твой main выполняется в своем изолированном окружении, которое к текущему коду не имеет никакого отношения. И ничо ты не сделаешь «на практике». Проблема курицы и яйца получается.

anonymous
()
Ответ на: комментарий от quasimoto

собственно в разделе с явой, насколько я понял, как раз то, о чём я говорю, только уже в класс обёрнуто

next_time ★★★★★
()
Ответ на: комментарий от anonymous

которое к текущему коду не имеет никакого отношения

ну ок, вставь строку в исходники текущей проги, да скомпиль

И ничо ты не сделаешь «на практике»

сделаю. если юзать чистый компилер плюсов, завернув нужный фу-ал в классы и макросы, передам ровно все параметры окружения, которые только потребуется. другое дело, что некоторые вещи придётся делать вручную, которые обычный eval сделает автоматом. ну ок. всегда можно сверху компилера юзануть свой самописный парсер, который и эту проблему разрешит.

next_time ★★★★★
()
Ответ на: комментарий от anonymous

любому видно, что у транслятора и интерпретатора РАЗНЫЕ ТИПЫ. По-этому интерпретатор НЕ МОЖЕТ БЫТЬ ТРАНСЛЯТОРОМ. По определению. компилятору НЕ НУЖНО данных, чтобы работать. Ему нужен только терм (исходный код). Интерпретатору НУЖНЫ (в общем случае) входные данные. Он без них работать не будет.

твоя проблема в том, что ты не понимаешь того, что разделение входных данных на «данные» и на «код» довольно условно. К примеру возьми факториал. Очевидно, факториал вычисляется как произведение от 1 до n. А чему равен 0! по этому алгоритму? Даже в этом примере видно, что входные данные прямо зависят на код, порождаемый компилятором. Компилятору необходимо создать два алгоритма(или даже четыре, если мы хотим считать факториал любого действительного числа. А на практике и ещё больше, т.к. факториал больших целых чисел с помощью умножения считать неудобно). Посему, никакого взаимооднозначного соответствия нет, кроме самых примитивных случаев. На практике компилятор порождает множество алгоритмов, выбор которых зависит от входных данных. Если _компилятор_ на входе получает строку-выражение, то на выходе он порождает код, который интерпретирует эту строку в рантайме.

Во _всех_ практических случаях, программист не имеет не малейшего понятия о том, _как_ именно будут выполняться его алгоритмы, какие именно и в какой последовательности. В _любой_ программе, исключая самые примитивные учебные примеры.

Посему, отличие компилятора от интерпретатора кажущееся, и заметно лишь на самых простых учебных задачках. IRL различие стирается.

emulek
()
Ответ на: комментарий от quasimoto

аппликация как часть β-редукции не ложится на компилятор для неизвестного аргумента в общем случае (если аргумент сам является неизвестной функцией, а не неизвестным числом). В C/C++ ты попросту не сможешь построить такое выражение(без костылей типа boost'а), а в выбранном тобой ЯП это тривиально.

Покажи что конкретно (пример аппликации) не ложится и что не получается написать на C++ (если ты на нём компилятор реализуешь, то тем более любая аппликация спокойно пишется, так как представляется AST).

Как бы Scheme, SML, C++11 и т.д. и т.п. спокойно компилируются.

я говорил про обычный C/C++, в котором _единственное_, что ты можешь менять в коде рантайма — указатели на функции(виртуальные функции, это тоже указатели на функции, которые прилеплены к структуре, которая в C++ называется «абстрактный базовый класс»).

Показать это наверное можно(написать бесконечную нетривиальную функцию не реализуемую _набором_ функций), но я не думаю, что в этом есть какой-то смысл.

Удивительно, что это так не ясно. Аналогично — интерпретирующий эмулятор может крутить машинный код сколько угодно долго, а транслятор в транслирующем устроен иначе — просто другой код (он же машкод другой архитектуры должен генерировать, а не действия выполнять), он не должен что-то крутить, он должен просто провести трансляцию и завершиться. Один и тот же код не может работать и не работать одновременно.

с чего ты взял, что трансляция обязана именно _завершиться_? А почему нет? Ну мы же используем асимптотический ряд, который не сходится, для вычислений? Это тебе пример бесконечного алгоритма, число итераций которого(и сами итерации) зависит от данных.

Да даже самый простой цикл на bash интерпретируется тоже один раз(т.к. если не написать done, то ни одной итерации вообще не будет. Следовательно даже bash как-то _компилирует_ цикл, переводя его в некий промежуточный формат, хотя конечно и не машинный код, но это не имеет значения).

emulek
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.