Які властивості мови програмування унеможливлюють компіляцію?


71

Питання:

"Певні властивості мови програмування можуть зажадати, щоб єдиний спосіб отримати написаний на ньому код виконуватись шляхом інтерпретації. Іншими словами, компіляція до коду машинного типу традиційного процесора неможлива. Що це за властивості?"

Укладачі: принципи та практика Параг Х. Дейва та Хіманшу Б. Дейва (2 травня 2012 р.)

Книга не дає поняття щодо відповіді. Я намагався знайти відповідь на поняттях мов програмування (SEBESTA), але безрезультатно. Пошуки в Інтернеті теж мали малий успіх. У вас є якісь підказки?


31
Відомо, що Perl неможливо навіть розібрати . Крім цього, претензія, здається, є тривіально помилковою без додаткових припущень: якщо є перекладач, я завжди можу поєднати перекладача та коду в одному виконуваному файлі, вуалі.
Рафаель

4
@Raphael: Хороша ідея, але ... 1) Ви припускаєте, що код доступний до його виконання. Це не стосується інтерактивного використання. Звичайно, ви можете використовувати щойно вчасно компіляцію для вбудованого коду в bash-операторах або вмісті стеку PostScript, але це досить шалена ідея. 2) Ваша ідея насправді не компілює код: пакет не є компільованою версією коду, але все ще є інтерпретатором коду.
reinierpost

7
У старі добрі часи я мав самостійне редагування програм gwbasic (gwbasic зберігає основні програми у вигляді байт-коду). На даний момент я не можу придумати розумного способу компілювати їх до рідного машинного коду, зберігаючи свою здатність редагувати себе.
ПлазмаHH

15
@PlasmaHH: Код, що змінюється самостійно, починається з 1948 р. Перший компілятор був написаний у 1952 р. Поняття коду, що змінюється, був винайдений у кодовому машинному коді.
Mooing Duck

10
@reinierpost Рафаель займає теоретичну позицію з цього приводу. Це заслуга в тому, щоб показати концептуальні обмеження питання. Компіляція - це переклад з мови S на мову T. Мова T може бути розширенням S, до якого може бути доданий код інтерпретації в іншому мові. Тож зв'язування S та його перекладача - це програма мовою Т. Це інженеру здається абсурдним, але це показує, що сформулювати це питання неважко складно. Як ви відрізняєте прийнятний процес складання від неприйнятного (наприклад, Рафаеля) з інженерної точки зору?
бабу

Відповіді:


61

Відмінність інтерпретованого та складеного коду, ймовірно, вигадка, як підкреслено коментарем Рафаеля :

the claim seems to be trivially wrong without further assumptions: if there is
an interpreter, I can always bundle interpreter and code in one executable ...

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

Ви сприймаєте як компіляцію - процес перекладу з однієї мови (для джерела) на іншу мову (для цілі). І, інтерпретатор для , як правило , відрізняється від перекладача для .T S TSTST

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

Деякі компілятори фактично використовуються просто для зменшення розміру коду, а не для «покращення» виконання. Це стосувалося мови, що використовується в системі Платона (хоча вони не називали її складовою).

Ви можете розглянути ваш код повністю складений , якщо після процесу компіляції, вам більше не потрібен перекладач для . Принаймні, це єдиний спосіб я можу прочитати ваше запитання як інженерне, а не теоретичне питання (оскільки, теоретично, я завжди можу відновити перекладача).S

Одна річ, яка може викликати проблему, афаїк, - це метациркулярність . Саме тоді програма маніпулює синтаксичними структурами на своїй власній мові джерела , створюючи фрагмент програми, який потім інтерпретується так, ніби вони були частиною вихідної програми. Оскільки ви можете створити довільні фрагменти програми мовою в результаті довільних обчислень, що маніпулюють безглуздими синтаксичними фрагментами, я б припустив, що ви можете зробити майже неможливим (з інженерної точки зору) компілювати програму мовою , так що тепер генерувати фрагменти . Значить, знадобиться інтерпретатор для або, принаймні, компілятор від доS T T S S T SSSTTSST для логічного складання створених фрагментів у (див. Також цей документ ).S

Але я не впевнений, як це можна формалізувати належним чином (і не маю часу на це зараз). І неможливе велике слово для проблеми, яка не формалізована.

Подальші зауваження

Додано через 36 годин. Ви можете пропустити це дуже довге продовження.

Чимало коментарів до цього питання показують два погляди на проблему: теоретичний погляд, який вважає її безглуздою, та інженерний погляд, який, на жаль, не так легко формалізується.

Є багато способів поглянути на інтерпретацію та компіляцію, і я спробую замалювати декілька. Я спробую бути настільки ж неформальним, наскільки вмію

Діаграма надгробних плит

Однією з ранніх формалізацій (з початку 1960-х до кінця 1990-х років) є діаграми T або Tombstone . Ці діаграми представлені в складових графічних елементах мовою реалізації інтерпретатора або компілятора, мовою-джерелом, що інтерпретується або компілюється, та цільовою мовою у випадку компіляторів. Більш досконалі версії можуть додавати атрибути. Ці графічні зображення можна розглядати як аксіоми, правила виводу, застосовні для механічного отримання генерації процесорів із підтвердження їх існування з аксіоми, а-ля Curry-Howard (хоча я не впевнений, що це було зроблено в шістдесяті роки :).

Часткова оцінка

Ще один цікавий погляд - парадигма часткової оцінки . Я сприймаю просте уявлення програм як свого роду реалізацію функції, яка обчислює відповідь на деякі вхідні дані. Потім інтерпретатор для мови являє собою програму, прийняти програму , записану в і дані для цієї програми, і обчислює результат в відповідно до семантикою . Часткове оцінювання - це методика спеціалізації програми з двох аргументів та , коли відомий лише один аргумент, скажімо . Наміром є швидша оцінка, коли ви нарешті отримаєте другий аргумент S p S S d S a 1 a 2 a 1 a 2 a 2 a 1 a 1 a 2ISSpSSdSa1a2a1a2 . Особливо корисно, якщо змінюється частіше, ніж оскільки вартість часткової оцінки може бути амортизована для всіх обчислень, де змінюється лише .a2a1a1a2

Це часта ситуація в розробці алгоритмів (часто це тема першого коментаря до SE-CS), коли якась статична частина даних попередньо обробляється, так що вартість попередньої обробки може бути амортизована у всіх програмах алгоритму з більш змінними частинами вхідних даних.

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

Теорема Smn

Приємним моментом у підході до часткової оцінки є те, що він бере свої корені в теорії (хоча теорія може бути брехуном), особливо в теоремі Кміна Кліна . Я намагаюся тут дати інтуїтивне уявлення про це, сподіваючись, що це не засмутить чистих теоретиків.

Враховуючи нумерування Gödel рекурсивних функцій, ви можете розглядати як ваше обладнання, так що задане число Gödel (читати код об'єкта ) програми є функцією, визначеною (тобто обчислюється кодом об'єкта на ваше обладнання).φ p φ p pφφpφpp

У своїй найпростішій формі теорема викладена у Вікіпедії таким чином (аж до невеликої зміни позначень):

Враховуючи нумерацію Gödel рекурсивних функцій, існує примітивна рекурсивна функція двох аргументів із наступною властивістю: для кожного числа Ґеделя часткової обчислюваної функції з двома аргументами, вирази і визначаються для однакових комбінацій натуральних чисел і , і їх значення рівні для будь-якого такого поєднання. Іншими словами, для кожного виконується наступна рівність функцій розширення : φσqfφσ(q,x)(y)f(x,y)xyxφσ(q,x)λy.φq(x,y).

Тепер, взявши як інтерпретатор , як вихідний код програми , а як дані для цієї програми, ми можемо записати: qISxpSydφσ(IS,pS)λd.φIS(pS,d).

φIS можна розглядати як виконання інтерпретатора на обладнанні, тобто, як чорний ящик , готовий інтерпретувати програми , написані на мові .ISS

Функція може розглядатися як функція, яка спеціалізується на інтерпретаторі для програми , як у частковій оцінці. Таким чином може бути видно номер Gödel що має об'єктний код, який є компільованою версією програми .σISPSσ(IS,pS)pS

Отже, функція може розглядатися як функція, яка бере аргумент вихідний код програми написана мовою , і повертає версію об'єктного коду для цієї програми Отже, - це те, що зазвичай називають компілятором.CS=λqS.σ((IS,qS)qSSCS

Деякі висновки

Однак, як я вже сказав: "теорія може бути брехуном", або насправді здається, що вона є однією. Проблема полягає в тому, що ми нічого не знаємо про функцію . Таких функцій насправді багато, і я здогадуюсь, що доказ теореми може використовувати для неї дуже просте визначення, яке, з інженерної точки зору, може бути не кращим, ніж рішення, запропоноване Рафаелем: просто зв'язати вихідний код з інтерпретатором . Це завжди можна зробити, щоб ми могли сказати: компіляція завжди можлива.q S I SσqSIS

Для формування більш обмежувального поняття, що таке компілятор, потрібен був би більш тонкий теоретичний підхід. Я не знаю, що, можливо, було зроблено в тому напрямку. Сама реальна робота з часткової оцінки є більш реалістичною з інженерної точки зору. Звичайно, існують інші методи написання компіляторів, включаючи вилучення програм із підтвердження їх специфікації, розроблені в контексті теорії типів, засновані на ізоморфізмі Кері-Говарда (але я виходжу за межі своєї компетенції) .

Моя мета тут полягала в тому, щоб показати, що зауваження Рафаеля не "божевільне", а розумне нагадування про те, що речі не очевидні і навіть не прості. Говорити про те, що щось неможливо - це сильне твердження, яке вимагає точних визначень та доказів, аби тільки точне розуміння того, як і чому це неможливо . Але побудувати належну формалізацію для висловлення такого доказу може бути досить складно.

Це говорить, навіть якщо певна особливість не є компілюваною, у розумінні, яку розуміють інженери, стандартні методи компіляції завжди можуть застосовуватися до частин програм, які не використовують таку функцію, як це зазначається у відповіді Жилла.

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

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

Зауважте, що сказати, що процес компіляції повинен створювати машинний код - це не допоможе. Це саме те, що пакет може виконувати як інтерпретатор, це машинний код (ну, річ може стати трохи складнішою при перехресній компіляції).


3
" неможливо велике слово" Дуже дуже велике слово. =)
Брайан S

3
Якщо визначається "компіляція", щоб посилатися на послідовність етапів, які повністю проводяться до того, як програма, що виконує, отримує перший вхід, і інтерпретація як процес управління потоком програми управління даними засобами, які не є частиною абстрактної моделі машини програми. , то для того, щоб скомпілювати мову, повинен мати можливість компілятор ідентифікувати перед початком виконання всі можливі значення мовної конструкції. У мовах, де мовна конструкція могла мати необмежену кількість значень, компіляція не працюватиме.
supercat

@BrianS Ні, це не так, і довести інше неможливо;)
Майкл Газонда

@supercat Це все ще не є визначенням. Яке «значення» мовної конструкції?
Римоїд

Я люблю концепцію розглядати компілятор / інтерпретатор як якусь часткову виконання!
Бергі

17

Питання не в тому, що компіляція неможлива . Якщо мова може бути інтерпретована¹, то її можна скласти тривіальним способом шляхом зв’язування перекладача з вихідним кодом. Питання полягає в тому, які мовні особливості роблять це, по суті, єдиним способом.

Інтерпретатор - це програма, яка приймає вихідний код як вхідний і поводиться так, як визначено семантикою цього вихідного коду. Якщо потрібен перекладач, це означає, що мова включає спосіб інтерпретації вихідного коду. Ця функція називається eval. Якщо інтерпретатор потрібен як частина середовища виконання мови, це означає, що мова включає eval: або evalіснує як примітив, або його можна кодувати певним чином. Мови, відомі як мови сценаріїв, зазвичай містять evalособливість, як і більшість діалектів Ліспа .

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

evalє вищою особливістю інтерпретації потреб, але є й інші особливості, які вимагають чогось не вистачає для перекладача. Розглянемо кілька типових фаз компілятора:

  1. Розбір
  2. Перевірка типу
  3. Генерація коду
  4. Зв’язування

evalозначає, що всі ці фази потрібно виконувати під час виконання. Є й інші особливості, які ускладнюють нативну компіляцію. Взявши його знизу, деякі мови заохочують до пізнього посилання, надаючи способи, в яких функції (методи, процедури тощо) та змінні (об'єкти, посилання тощо) можуть залежати від змін не локальних кодів. Це ускладнює (але не неможливо) генерувати ефективний власний код: простіше зберігати посилання на об'єкти як дзвінки у віртуальній машині, а двигун VM нехай керує зв'язками на ходу.

Взагалі кажучи, відображення, як правило, ускладнює складання мов до рідного коду. Еваль примітив - крайній випадок роздумів; багато мов не так далеко, але, тим не менш, мають семантику, визначену у віртуальній машині, що дозволяє, наприклад, коду отримати клас по імені, перевірити його спадщину, перелічити його методи, викликати метод тощо. Java з JVM і C # з .NET - два відомих приклади. Найпростішим способом реалізації цих мов є компіляція їх у байт-коді , але, тим не менш, є вбудовані компілятори (багато що вчасно ), які збирають принаймні фрагменти програми, які не використовують розширені засоби відображення.

Перевірка типу визначає, чи діє програма. У різних мовах є різні стандарти щодо того, скільки аналізу виконується за час компіляції та час виконання: мова відома як "статично набрана", якщо вона виконує багато перевірок перед початком запуску коду, та "динамічно набирається", якщо її немає. Деякі мови включають в себе функцію динамічного відтворення або відмінити функцію перевірки клавіш; ці функції вимагають вбудовування контролера тексту в середовище виконання. Це ортогонально вимогам включення генератора коду або інтерпретатора в середовищі виконання.

¹ Вправа: визначити мову , який не може бути витлумачено.


(1) Я не погоджуюся з приводу поєднання інтерпретатора з підрахунком вихідного коду як збірним, але решта вашої публікації чудова. (2) Повна згода щодо eval. (3) Я не бачу, чому роздуми ускладнюватимуть мови для збору до рідного коду. Objective-C має відображення, і (я припускаю) він зазвичай складається. (4) Неясно пов'язана примітка, метамагічний шаблон C ++ шаблону зазвичай інтерпретується, а не компілюється та виконується.
Mooing Duck

Щойно сталося зі мною, складається Люа. evalПросто компілює байт - код, а потім в якості окремої стадії двійковий виконує байт - код. І це, безумовно, має відображення у складеному бінарному.
Mooing Duck

На машині Гарвардської архітектури компіляція повинна дати код, який ніколи не повинен бути доступним як "дані". Я б сказав, що інформація з вихідного файлу, яка в кінцевому підсумку повинна зберігатися як дані, а не як код, насправді не "збирається". Немає нічого поганого в тому, що компілятор взяв декларацію на зразок int arr[] = {1,2,5};і створив розділ ініціалізованих даних, що містить [1,2,5], але я б не описав її поведінку як переклад [1,2,5] в машинний код. Якщо майже вся програма повинна зберігатися як дані, то яка частина її насправді була б "складена"?
supercat

2
@supercat Ось що означають математики та інформатики під тривіальним. Він відповідає математичному визначенню, але нічого цікавого не відбувається.
Жиль

@Gilles: Якщо термін "компілювати" зарезервований для перекладу на машинні інструкції (без збереження пов'язаних "даних") і приймає, що в мові компіляції поведінка декларації масиву не "збирає" масив, тоді є деякі мови, на яких неможливо скласти будь-яку змістовну частину коду.
supercat

13

Я думаю, що автори припускають, що компіляція означає

  • вихідна програма не повинна бути присутнім під час виконання, і
  • жоден компілятор чи перекладач не повинен бути присутнім під час виконання.

Ось деякі зразкові функції, які зробили б проблематичним, якщо не "неможливим" для такої схеми:

  1. Якщо ви можете допитати значення змінної під час виконання, посилаючись на змінну за її назвою (що є рядком), тоді вам знадобляться імена змінних, які знаходяться навколо під час виконання.

  2. Якщо ви можете викликати функцію / процедуру під час виконання, посилаючись на неї за своїм ім'ям (яке є рядком), тоді вам знадобляться імена функції / процедури під час виконання.

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

У Lisp є всі три функції. Отже, у системах Lisp завжди працює інтерпретатор, завантажений під час роботи. Мови Java та C # мають назви функцій, доступних під час виконання, та таблиці, щоб переглянути, що вони означають. Можливо, такі мови, як Basic та Python, також мають імена змінних під час виконання. (Я не впевнений у цьому на 100%).


Що робити, якщо "перекладач" буде зібраний у код? Наприклад, використовуючи диспетчерські таблиці для виклику віртуальних методів, це приклад інтерпретації чи компіляції?
Ервін Болвідт

2
"жоден компілятор чи перекладач не повинен бути присутнім під час виконання", так? Добре, якщо це правда, то в глибокому сенсі С також не може бути "складений" на більшості платформ. Виконання C не має великого значення: запуск, налаштування стеків тощо, і вимкнення для atexitобробки. Але воно все одно має бути там.
Псевдонім

1
"У системах Lisp завжди працює перекладач, завантажений під час роботи." - Не обов'язково. Багато систем Lisp мають компілятор під час виконання. Деякі взагалі не мають перекладача.
Jörg W Mittag

2
Приємна спроба, але en.wikipedia.org/wiki/Lisp_machine#Technical_overview . Вони компілюють Lisp і призначені для ефективного виконання результату.
Пітер А. Шнайдер

@ Псевдонім: Час виконання C - це бібліотека, а не компілятор і не інтерпретатор.
Mooing Duck

8

можливо, поточні відповіді "переосмислюють" твердження / відповіді. можливо, те, про що говорять автори, є наступним явищем. у багатьох мовах є така команда, як "eval"; наприклад, див. JavaScript eval і його поведінку зазвичай вивчають як особливу частину теорії CS (наприклад, Lisp). функція цієї команди полягає в оцінці рядка в контексті визначення мови. тому фактично він має схожість з "вбудованим компілятором". компілятор не може знати вміст рядка до часу виконання. тому компілювати результат eval у машинному коді неможливо під час компіляції.

інші відповіді вказують на те, що відмінність інтерпретованих мов від компільованих мов може значно розмитись у багатьох випадках, особливо з більш сучасними мовами, як, наприклад, Java, "компілятором щойно в часі", який називається "Точка доступу" (javascript двигуни, наприклад, V8 все частіше використовують цю саму методику). "eval-like" функціональність, безумовно, одна з них.


2
V8 - хороший приклад. Це чистий компілятор, жодного тлумачення не відбувається. Але він все ще підтримує повну семантику ECMAScript, включаючи необмежену eval.
Йорг W Міттаг

1
Луа робить те ж саме.
Mooing Duck

3

LISP - жахливий приклад, оскільки він був задуманий як свого роду "машинна" мова вищого рівня як основа для "реальної" мови. Сказана "справжня" мова ніколи не здійснилася. Машини LISP були побудовані на ідеї робити (більшу частину) LISP в апараті. Оскільки інтерпретатор LISP - це просто програма, в принципі це можливо реалізувати в схемі. Мабуть, не практично; але далеко не неможливо.

Крім того, є багато перекладачів, запрограмованих у кремнію, які зазвичай називаються "CPU". І часто корисно інтерпретувати (ще не існує, під рукою, ...) машинні коди. Наприклад, Linux 'x86_64 був вперше написаний та протестований на емуляторах. Під час появи чіпів на ринок з'явилися повні дистрибуції, навіть лише для раннього прийняття / тестування. Java часто компілюється в код JVM, який є інтерпретатором, який би не надто важко писати в кремній.

Більшість "інтерпретованих" мов складаються у внутрішню форму, яка оптимізується і потім інтерпретується. Це, наприклад, те, що роблять Perl і Python. Існують також компілятори для призначених для інтерпретації мов, як оболонка Unix. З іншого боку, можна інтерпретувати традиційно складені мови. Я був дещо крайнім прикладом - редактор, який використовував інтерпретовану C як мову розширення. Це C може запускати звичайні, але прості програми без проблем.

З іншого боку, сучасні процесори навіть приймають введення «машинної мови» і переводять його в інструкції нижчого рівня, які потім переупорядковуються та оптимізуються (тобто «компілюються») перед тим, як передаватись на виконання.

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


1

Реальність полягає в тому, що між інтерпретацією якоїсь програми Basic і виконанням асемблера існує велика різниця. І є місця між П-кодом / байт-кодом з або без (щойно) компіляторів. Тому я спробую узагальнити деякі моменти в контексті цієї реальності.

  • Якщо спосіб розбору вихідного коду залежить від умов виконання, написання компілятора може стати неможливим або настільки важким, що ніхто не заважатиме.

  • Код, який модифікує себе, в загальному випадку неможливо скласти.

  • Програма, яка використовує функцію, подібну до eval, зазвичай не може бути повністю скомпільована заздалегідь (якщо ви вважаєте рядок, поданий до неї, як частину програми), хоча якщо ви збираєтеся виконувати код eval'ed повторно, він все одно може бути корисно, щоб ваша функція, схожа на eval, викликала компілятор. Деякі мови надають API для компілятора, щоб зробити це просто.

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

  • Слабке введення тексту (незалежно від вашого визначення) робить компіляцію складнішою і, можливо, результат менш ефективним, але часто не є неможливим, якщо різні значення не викликають різний аналіз. Тут є розсувна шкала: якщо компілятор не може вивести фактичний тип, йому потрібно буде випромінювати гілки, виклики функцій і таке, що інакше б там не було, ефективно вбудовуючи біти інтерпретатора у виконуваний файл.


1

Я б припустив, що головна особливість мови програмування, яка робить компілятор для мови неможливою (у строгому сенсі див. також самохостинг ), є функцією самомодифікації . Значення мови дозволяє змінювати вихідний код під час виконання (sth, компілятор, генеруючий, фіксований та статичний, об'єктний код не може робити). Класичний приклад - Лісп (див. Також Гомоїконічність ). Подібна функціональність надається за допомогою мовної конструкції, наприклад eval , включеної у багато мов (наприклад, javaScript). Eval насправді викликає інтерпретатора (як функцію) під час виконання .

Іншими словами, мова може представляти власну метасистему (див. Також Метапрограмування )

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

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


LISP - жахливий приклад, оскільки він був задуманий як спринт
фонбранд

@vonbrand, можливо, але відображає як концепцію гомоконічності, так і єдину подвійність коду даних
Нікос М.

-1

Я вважаю, що оригінальне запитання недостатньо сформоване. Автори запитання, можливо, мали намір задати дещо інше запитання: Які властивості мови, що розповсюджується, полегшують написання компілятора для неї?

Наприклад, простіше написати компілятор для мов, що не мають контексту, ніж мову, залежну від контексту. Граматика, яка визначає мову, також може мати проблеми, що ускладнюють її складання, наприклад, двозначності. Такі питання можна вирішити, але потребують додаткових зусиль. Так само мови, визначені необмеженими граматиками, важче розбирати, ніж контекстно-чутливі мови (див. Ієрархія Хомського ). Наскільки мені відомо, найбільш часто використовувані процедурні мови програмування близькі до контексту, але мають кілька контекстно-чутливих елементів, що робить їх відносно простим у складанні.


2
Питання, очевидно, має намір протиставити / порівняти укладачів та перекладачів. Хоча вони можуть працювати інакше, і, як правило, за винятком лімітного випадку @Raphael, вони мають абсолютно однакові проблеми щодо аналізу синтаксису та неоднозначності. Тож синтаксис не може бути проблемою. Я також вважаю, що синтаксична проблема зазвичай не є основною проблемою у написанні компілятора, хоча це було раніше. Я не прихильник: я вважаю за краще коментувати.
babou

-1

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

Скомпілювати вихідний код у машинний код неможливо, якщо у вас ще немає вихідного коду.

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


2
"Я втратив вихідний код" - це не властивість мови програмування, а певної програми, так що це не відповідає на запитання. І ви , безумовно , потрібна цитата для затвердження , що уникнути втрат вихідного коду є «основною причиною , чому існують інтерпретовані мови», або навіть причина , чому вони існують.
Девід Річербі

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

@DavidRicherby та Рафаель, я кажу, що автор цієї публікації має на увазі (що я описую у своїй відповіді) як функцію самомодифікації, яка, звичайно, є мовною конструкцією за дизайном, а не артефактом якоїсь конкретної програми
Нікос М.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.