Загальні правила написання X-компілятора до Z у Y


9

Припустимо, X - мова вводу, Z - мова виводу, тоді f - компілятор, який записаний мовою Y.

f = X -> Z

Оскільки f - це лише програма, я думаю, що Y може бути будь-якою мовою, правда? Таким чином, ми можемо мати компілятори f1, f2, кожен записаний у Y1, Y2.

f1 = f Y1    
f2 = f Y2

g = Z -> M
h = g . f    # We get a compiler X -> M

Візьмемо, наприклад, компілятор cpython, X - Python, Z - код VM Python, Y - C.

cpython = Python -> PythonVMCode C
interpreter = PythonVMCode -> Nothing
interpreter2 = PythonVMCode -> MachineCode

Джерела Python компілюються у код VM Python, файли .pyc, а потім інтерпретуються інтерпретатором. Схоже, можливо, існує компілятор, який може безпосередньо робити Python -> MachineCode, хоча це важко реалізувати:

   hardpython = interpreter2 . cpython 

Ми можемо також написати інший компілятор, виконувати роботу Python -> PythonVMCode, іншою мовою, скажімо, сам Python.

mypython = Python -> PythonVMCode Python
mypython2 = Python -> PythonVMCode Ruby

Тепер ось складний приклад PyPy. Я просто новачок PyPy, виправте мене, якщо я помиляюся:

PyPy doc http://doc.pypy.org/en/latest/architecture.html#pypy-the-translation-framework

Наша мета полягає у наданні можливого рішення проблеми мовних реалізаторів: необхідність писати інтерпретатори l * o * p для l динамічних мов і p платформ з o важливими дизайнерськими рішеннями.

Можна подумати, що l - X, p - Y. Існує програма, яка переводить всі програми RPython на C:

 rpython_compiler = RPython -> C  Python

 pypy = Python -> Nothing RPython

 translate = compile the program pypy written in RPython using rpython_compiler

 py2rpy = Python -> RPython  Python
 py2c = Python -> C Python 
 py2c = rpython_compiler . py2rpy

Програми RPython - це як вказівки щодо VM, rpython_compiler - це VM.

q1. pypy - це інтерпретатор, програма RPython, яка може інтерпретувати код Python, мови виводу немає, тому ми не можемо розглядати це як компілятор, правда?

Додано:

  • Я щойно виявив, що навіть якщо після перекладу, pypy - це все ще перекладач, лише цього разу написаний на C.
  • Якщо ми заглянемо глибоко в pypy інтерпретатора, я вважаю, що повинен існувати якийсь компілятор, який компілює джерела Python в якийсь AST, а потім виконати

подобається це:

compiler_inside_pypy = Python -> AST_or_so

q2. Чи може існувати компілятор py2rpy, перетворюючи всі програми Python в RPython? Якою мовою це написано, не має значення. Якщо так, ми отримуємо інший компілятор py2c. Яка різниця між pypy та py2rpy у природі? Py2rpy набагато складніше написати, ніж pypy?

q3. Чи є якісь загальні правила чи теорія щодо цього?

Більше компіляторів:

gcc_c = C -> asm? C  # not sure, gimple or rtl?
g++ =   C++ -> asm? C
clang = C -> LLVM_IR  C++
jython = Python -> JVMCode java
ironpython = Python -> CLI C#

q4. Дано f = X -> Z, програму P, написану на X. Коли ми хочемо прискорити P, що ми можемо зробити? Можливі способи:

  • перепишіть P у більш ефективний алгоритм

  • перепишіть f для отримання кращого Z

  • якщо інтерпретується Z, напишіть кращий інтерпретатор Z (PyPy тут?)

  • прискорювати програми, записані на Z рекурсивно

  • отримати кращу машину

пс. Це питання стосується не технічних речей, як написати компілятор, а доцільності та складності написання компілятора певного виду.


Не пов'язане безпосередньо, але дещо схоже поняття: en.wikipedia.org/wiki/Суперкомпіляція
SK-логіка

1
Я не впевнений, що це питання справді відповідає Stack Overflow, тим більше, що в ньому дуже багато підзаконних запитань, але я все ще захоплююся думкою, яка впадала в це.

4
Незважаючи на те, що вас, можливо, навчили, AST не потрібно - це просто стратегія, яку використовують деякі компілятори.

1
Ймовірно, це належить до cstheory.stackexchange.com
9000

3
Реалізація PyPy, як і більшість "інтерпретаторів", - це компілятор байт-кодів та інтерпретатор цього формату байт-коду в одному.

Відповіді:


4

q1. pypy - це інтерпретатор, програма RPython, яка може інтерпретувати код Python, мови виводу немає, тому ми не можемо розглядати це як компілятор, правда?

PyPy схожий на CPython, обидва мають компілятор + інтерпретатор. У CPython є компілятор, написаний на C, який компілює Python в Python VM bytecode, потім виконує байт-код в інтерпретаторі, написаному на C. PyPy має компілятор, написаний в RPython, який компілює Python в Python VM, а потім виконує його в PyPy Interpreter, написаному на RPython.

q2. Чи може існувати компілятор py2rpy, перетворюючи всі програми Python в RPython? Якою мовою це написано, не має значення. Якщо так, ми отримуємо інший компілятор py2c. Яка різниця між pypy та py2rpy у природі? Py2rpy набагато складніше написати, ніж pypy?

Чи може існувати компілятор py2rpy? Теоретично так. Це гарантує повноту .

Один із способів побудови py2rpy- просто включити вихідний код інтерпретатора Python, записаний на RPython, в створений вихідний код. Приклад компілятора py2rpy, написаний на Bash:

// suppose that /pypy/source/ contains the source code for pypy (i.e. Python -> Nothing RPython)
cp /pypy/source/ /tmp/py2rpy/pypy/

// suppose $inputfile contains an arbitrary Python source code
cp $inputfile /tmp/py2rpy/prog.py

// generate the main.rpy
echo "import pypy; pypy.execfile('prog.py')" > /tmp/py2rpy/main.rpy

cp /tmp/py2rpy/ $outputdir

тепер, коли вам потрібно перевести код Python в код RPython, ви викликаєте цей скрипт, який створює - у $ outputdir - RPython main.rpy, вихідний код інтерпретатора Python Interpreter RPYt і двійковий блок prog.py. І тоді ви можете виконати створений сценарій RPython, зателефонувавши rpython main.rpy.

(зауважте: оскільки я не знайомий з проектом rpython, синтаксис виклику інтерпретатора rpython, можливість імпортувати pypy та робити pypy.execfile, а також .rpy розширення суто складено, але я думаю, ви зрозуміли, що ви зрозуміли)

q3. Чи є якісь загальні правила чи теорія щодо цього?

Так, будь-яка мова повного Тюрінга теоретично може бути переведена на будь-яку мову повного Тюрінга. Деякі мови можуть бути набагато складнішими для перекладу, ніж інші мови, але якщо питання "чи це можливо?", Відповідь "так"

q4. ...

Тут немає жодного питання.


Ваш компілятор py2rpy дійсно розумний. Це підводить мене до іншої ідеї. 1. Чи потрібно писати pypy в RPython у вашому компіляторі? Все, що вам потрібно, - це те, що можна інтерпретувати файли Python, правда? 2. os.system ('python $ inputfile') також може працювати, якщо він підтримується в RPython. Не впевнений, чи його все ще можна назвати компілятором, принаймні, не буквально.

Чи все ще pypy використовує програму Python VM? Тепер зрозуміло. pypy_the_compiler = Python -> PythonVMCode RPython, pypy_the_interpreter = PythonVMCode -> Нічого RPython, cpython_the_compiler = Python -> PythonVMCode C, cpython_the_interpreter = PythonVMCode -> Нічого C

@jaimechen: Does pypy have to be written in RPython in your compiler?Ні, це не потрібно писати в RPython, але RPython повинен бути в змозі сказати "допоміжному інтерпретатору" / "виконанню" для виконання коду Python. Так, це правда, це не «компілятор» у практичному розумінні, але це конструктивний доказ того, що можна писати Python -> RPython. Is pypy still using the Python VM?Я вважаю, що pypy взагалі не використовує CPython (я можу помилятися), натомість PyPy має власну реалізацію "Python VM", яка написана в RPython.
Лежи Райан

@jaimechen: більш практичний компілятор, можливо, може проаналізувати вхідний файл для послідовностей коду, щоб він знав, як їх компілювати та компілювати окремо, а також спосіб переходити назад і назад між Python "перекомпільований в RPython" і "interpreter- допоміг "Python. Він також може використовувати методи, що зазвичай використовуються в компіляції JIT, щоб виявити, чи певний вхід може давати різний вихід через різниці в семантиці RPython і Python та зворотній інтерпретації в цих випадках. Все це витонченість, яку можна побачити в більш практичному Python -> RPythonкомпіляторі.
Лежи Райан

Можливо, тут слід додати обмеження: перетворіть державну машину X на державну машину Z без допомоги існуючої 3-ї машини. Це той випадок, коли X абсолютно новий, досі не існує жодного компілятора чи інтерпретатора.
jaimechen

2

Щоб відповісти лише на q2, існує книга компілятора Вільяма Маккімена, в якій теорія компіляторів для мови X, написаної мовою Y, що створює вихідну мову Z, досліджується за допомогою системи T-діаграм. Опублікований у 1970-х, заголовок не передавати, вибач.



1

q1. Взагалі перекладач не є компілятором. Ключова відмінність між компілятором і перекладачем полягає в тому, що перекладач починається щоразу, із вихідним кодом мовою джерела, щоразу. Якщо ваш pypy замість цього був pyAST або pyP-код, а тоді у вас був інтерпретатор AST або P-коду, ви могли б назвати pyAST компілятором. Ось так працював старий компілятор UCSD PASCAL (як і ще чимало інших): вони компілювались в якийсь P-код, який інтерпретували під час запуску програми. (Навіть .NET надає щось подібне, коли компактність згенерованого об'єктного коду набагато важливіша за швидкість.)

q2. Так, звісно. Див. ПАСКАЛ UCSD (та ще ряд інших).

q3. Копайте класичні тексти з інформатики. Прочитайте на паралельному ПАСКАЛі, від Пер Брінча-Хансена (якщо пам'ять мені служить). Про компілятори та генерацію коду написано багато. Генерувати незалежний від машини псевдокод зазвичай набагато простіше, ніж генерувати машинний код: псевдокод, як правило, не містить вигадок, які справді містять машини.

q4. Якщо ви хочете, щоб ваш генерований об'єкт запускався швидше, ви робите компілятор розумнішим, щоб зробити кращу оптимізацію. Якщо ваш об'єкт інтерпретується, ви розглядаєте можливість підштовхувати більш складні операції до примітивних псевдоінструкцій (аналогія CISC проти RISC), тоді ви зробите все можливе, щоб оптимізувати фракцію з вашого інтерпретатора.

Якщо ви хочете, щоб ваш компілятор запустився швидше, вам слід переглянути ВСЕ, що він робить, включаючи переосмислення вашого вихідного коду. Після завантаження самого компілятора найбільш трудомісткою частиною компіляції є ЗАВЖДИ зчитування вихідного коду в компілятор. (Розглянемо, наприклад, C ++. Усі інші речі порівняно рівні), компілятор, який повинен зібрати 9000 (а може, і 50 000) рядків файлів #include, щоб скласти просту програму "Привіт, світ" ніколи не буде такою швидкою, як одна що має прочитати лише чотири-п’ять рядків.)

Я не пам’ятаю, де я це читав, але оригінальний компілятор Oberon в ETH-Zurich мав дуже складний механізм таблиці символів, досить елегантний. Орієнтиром Wirth для продуктивності компілятора був час, який знадобився компілятору для складання. Одного ранку він зайшов, витягнув чудову багатопов’язану таблицю символів ультра-дерева та замінив її простим лінійним масивом та прямими лінійними пошуками. Аспіранти в його групі були ШОКОВО. Після зміни компілятор був швидшим, тому що модулі, які він компілював, завжди були досить малі, щоб елегантний монстр наклав більше загальних накладних витрат, ніж лінійний масив та лінійний пошук.


1
Дякую. Компілятор "компілює", а перекладач "виконує", чи може бути більше розуміння програм двох типів, як їхні типи різні?
jaimechen

1

Викладені вами запитання приводять мене до думки, що те, що ви насправді хочете / потребуєте, - це пояснення того, що таке компілятор, що таке перекладач та відмінності між ними.

Компілятор відображає програму, написану мовою X, на функціонально еквівалентну програму, написану мовою Y. Наприклад, компілятор від Pascal до C може компілювати

function Square(i: Integer)
begin
    Square := i * i
end

до

int Square(int i)
{
    return i * i;
}

Більшість компіляторів компілюють "вниз", тому вони компілюють мови програмування вищого рівня на мови нижчого рівня, остаточна мова нижчого рівня - машинний код.

Більшість компіляторів компілюються безпосередньо до машинного коду, але деякі (зокрема Java та .NET мови) компілюються у "байт-код" ( байт-код Java та CIL ). Подумайте про байт-код як машинний код для гіпотетичного комп'ютера. Цей байт-код потім інтерпретується або JITted під час його запуску (докладніше про це пізніше).

Інтерпретатор виконує програму, написану на якійсь мові Z. Інтерпретатор читає програму побіжно, виконуючи її по ходу. Наприклад:

int i = 0;
while (i < 1)
{
    i++
}
return i;

Уявіть, що перекладач дивиться на цю програму для рядка, вивчає лінію, виконує те, що вона робить, дивлячись на наступний рядок тощо.

Найкращий приклад перекладача - це процесор вашого комп'ютера. Він інтерпретує машинний код і виконує його. Як працює процесор, визначається фізично побудований. Як працює програма перекладача, визначається, як виглядає її код. Тому процесор інтерпретує та виконує програму інтерпретатора, яка, в свою чергу, інтерпретує та виконує його введення. Ви можете зв’язувати перекладачів таким чином.

JITter - компілятор Just-in-Time. JITter - компілятор. Єдина відмінність - це час його виконання: більшість програм пишеться, компілюється, відправляється їх користувачам, а потім виконується, але байт-код Java та CIL надсилаються спочатку своїм користувачам, а потім безпосередньо перед їх виконанням вони складаються на машину. код своїх користувачів.

C # -> (компілювати) -> CIL -> відправляється замовнику -> (компілювати безпосередньо перед виконанням) -> машинний код -> (виконати)

Останнє, про що ви хочете дізнатися, - це повнота Тьюрінга ( посилання ). Мова програмування - це Тьюрінг завершений, якщо він може обчислити все, що може бути " машиною Тюрінга ", тобто є принаймні таким же "потужним", як і машина Тьюрінга. У тезі Церкви-Тьюрінга зазначається, що машина Тьюрінга є принаймні такою потужною, як і будь-яка машина, яку ми коли-небудь можемо побудувати. Звідси випливає, що вся повна мова Тьюрінга є такою ж потужною, як і машина Тьюрінга, і тому всі цільні мови Тьюрінга однаково потужні.

Іншими словами, поки ваша мова програмування є Тюрінг повною (майже всі вони є), не має значення, яку мову ви виберете, оскільки вони можуть обчислювати одне і те ж. Це також означає, що не дуже важливо, яку мову програмування ви вибрали для написання компілятора чи вашого перекладача. І останнє, але не менш важливе, це означає, що ви завжди можете написати компілятор з мови X на Y, якщо X і Y обидва Turing завершені.

Зауважте, що Turing завершений нічого не говорить про ефективність вашої мови, а також про всі деталі реалізації вашого процесора та іншого обладнання або про якість компілятора, який ви використовуєте для цієї мови. Крім того, ваша операційна система може вирішити, що програма не має прав на відкриття файлу, але це не перешкоджає вашій можливості обчислити що- небудь - я навмисно не визначав обчислення, оскільки це займе іншу стінку тексту.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.