Інтерпретувати свій язик, а не себе?


21

Існує багато проблем, які кажуть "інтерпретувати X", де X - проста мова. На мою думку, це занадто нудно. Щоб дати людям, що зволікають в Інтернеті, щось цікаве зробити, ви можете спробувати зробити це завдання:

Виклик

Виберіть мову $LANG. $LANGможе бути будь-якою повною мовою програмування або повним підмножиною мови програмування. Пам’ятайте, що якщо ви пропустите функцію своєї мови $LANGдля інтерпретації, ви також не повинні використовувати її для власної програми, оскільки ваша публікація також повинна бути написана $LANG.

Напишіть компілятор / перекладач для $LANGнаписаного в $LANG. Ви можете використовувати всі засоби (включаючи evalта друзів) своєї мови, які доступні для написання цього компілятора. Щоб зробити завдання більш складним, існує одне обмеження: Ви маєте змогу інтерпретувати / компілювати всі дійсні програми, $LANGкрім самого вашого перекладача / компілятора. Якщо трапляється, що програма, яку слід інтерпретувати / компілювати, - це ваш інтерпретатор чи компілятор (незалежно від імені файлу), ваша програма повинна зробити щось зовсім не пов'язане з функціональністю інтерпретатора чи компілятора (наприклад, переробки або друку Hello, world!).

Щоб зробити це завдання ще складнішим, ваша програма не повинна читати власне джерело при складанні чи інтерпретації.

Технічні умови

  • Це завдання - код гольфу. Виграє подання з найменшими значеннями, що є правильним. У випадку зрівноваження рішення, яке було подано першим, виграє.
  • Ваша програма / сценарій має читати програму, яку слід інтерпретувати з файлу. Ви можете жорстко кодувати його шлях та ім'я. Коли файл читається, ви можете або компілювати його в інший файл (Це повинен бути виконаний у вашій системі), або запустити його безпосередньо. Якщо $LANGне вистачає можливостей читання файлів, ви можете вибрати інший спосіб читання у відповідному коді $LANG. Ви не можете вибрати $LANGяк підмножину іншої мови, але з вилученими можливостями читання файлів.
  • Діють звичайні правила гольф-коду. Тобто: ваша особиста мова для домашніх тварин, яку ви створили тільки для вирішення цього завдання, заборонена, якщо рішення стає тривіальним за її допомогою (наприклад, визначення одночасної програми, яка точно реалізує рішення). Зловживання правилами заохочується.

Чи дозволено нам визначити мову для цього, доки вона завершена?
Cruncher

@Cruncher Так, ти. Докладнішу інформацію див. В останній точці позначення специфікацій.
FUZxxl

Відповіді:


8

Рубі, 63

b=$<.read
t="b=$<.read\nt=%p\nb!=t%%t&&eval(b)"
b!=t%t&&eval(b)

Відповідь приймається до тих пір, поки не буде меншого рішення.
FUZxxl

11

Перл, 89 годин, жодного обману

$_=q($_=q(Q);s/Q/$_/;($q=join"",<>)eq$_?die:eval$q);s/Q/$_/;($q=join"",<>)eq$_?die:eval$q

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

$ perl unquine.pl unquine.pl
Died at unquine.pl line 1, <> line 1.

Пам'ятайте, що unquine.plфайл повинен бути рівно 89 байт, ні більше, ні менше. Запуск його за допомогою іншого сценарію Perl як вхід просто виконує інший сценарій, як і слід:

$ perl unquine.pl hello.pl
Hello, world!

Як випливає з назви, реалізація базується на лайці - конкретно на цій:

$_=q($_=q(Q);s/Q/$_/);s/Q/$_/

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


Ви можете замінити цю &&/ ;пару на трійку (одна відмітка, подвоєна відведенням). Чудова ідея та реалізація!
JB

@JB: Гарний лов! Зараз до 89 символів.
Ільмарі Каронен

5

GolfScript, 30 символів

{`".~"+"#{$<.read}".@=!{~}*}.~

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

Це досить пряма адаптація цієї самоідентифікуючої програми . Конкретно:

  • { } - буквальний блок коду в GolfScript.
  • .~, застосований до блоку коду, дублює блок і виконує копію.

Всередині кодового блоку:

  • ` строфіфікує копію блоку коду.
  • ".~"+додає символи .~до нього, отримуючи рядок, що містить вихідний код програми.
  • "#{$<.read}"являє собою документований злом, який дозволяє виконувати код Ruby в GolfScript. У цьому випадку він виконує оператор Ruby $<.read(безсоромно викрадений з рішення Ruby Lowjacker ), який читає і повертає вміст будь-яких файлів, вказаних у командному рядку. Цей злом необхідний, оскільки сам GolfScript не забезпечує явних можливостей вводу / виводу файлів.
  • .@ копіює та переміщує елементи на вершині стека, щоб стек містив дві копії вмісту файлу, а потім вихідний код цієї програми.
  • =! порівнює два верхні пункти стека (тобто вміст файлу та джерело), ​​повертаючи 1, якщо вони різні, і 0, якщо вони однакові.
  • {~}*оцінює решту копій вмісту файлу як код GolfScript, але лише якщо результат порівняння дорівнює 1. (Технічно він виконує блок коду {~}стільки разів, скільки задається числом у стеку, тобто 0 або 1 раз. Всередині блоком ~є оператор GolfScript eval.)

Пс. Якщо дозволено зчитування коду для виконання зі stdin, цей виклик може бути вирішений у 21 символі, не маючи оболонки на Ruby:

{`".~"+1$=!{""\~}*}.~

Ця програма зчитує вхідний рядок із stdin і, якщо він не відповідає власному джерелу, виконує його (з порожнім входом). Як і програма, подана вище, вхід, який відповідає джерелу, просто відгукується назад.


Виглядає приємно, але це не здається, що ви читаєте дані з файлу.
FUZxxl

Виправлено, тепер він читається з файлу (точно), як рішення Lowjacker.
Ільмарі Каронен

5

Python, 167 130 118 байт

Це моя перша спроба гольфу, тож ось що! Він інтерпретує будь-яку програму, крім себе

Поліпшена версія:

i=open(raw_input()).read();q='i=open(raw_input()).read();q=%s;i==q%%repr(q)and a;exec(i)\n';i==q%repr(q)and a;exec(i)

Якщо він отримує себе, то він перетворюється на:

Traceback (most recent call last):
  File "pygolf.py", line 1, in <module>
    i=open(raw_input()).read();q='i=open(raw_input()).read();q=%s;i==q%%repr(q)and a;exec(i)\n';i==q%repr(q)and a;exec(i)
NameError: name 'a' is not defined

Я думаю, що це рішення працює приблизно так само, як і Ільмарі Каронен, основна ідея - це щось на кшталт:

input = read_some_file()
if input == some_quine()
    barf()
interpret(input)

Квінка, яку я використовував, базувалася на цій:

(lambda x: x + repr((x,)))('(lambda x: x + repr((x,)))',)

Але я зрозумів, що набагато коротша квітка:

q='q=%s;q%%repr(q)';q%repr(q)

І це може бути ще коротше, якщо ви дозволите інтерактивну оболонку пітона, і в цьому випадку ви можете:

'%s;_%%repr(_)';_%repr(_)

Оскільки у python немає короткого шляху отримати аргументи командного рядка, я пішов з raw_input () (який ще досить довгий, але не такий довгий, як

import sys;sys.argv[1]

Використання:

echo "foobar.py" | python quinterpretter.py

або

python quinterpretter.py
<type filename and hit enter>

Я знайшов коротку королеву для використання, але ось моя стара версія (для нащадків):

i=open(raw_input()).read();a if i==(lambda x,y:x+repr((x,y))+y)('i=open(raw_input()).read();a if i==(lambda x,y:x+repr((x,y))+y)', ' else 1;exec(i)\n') else 1;exec(i)

Замініть% s на% r і видаліть повтор. % r означає сировину, і в основному він те саме.
Loovjo

4

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

Це, мабуть, не так гольф, як це могло б бути, але тут все одно:

Javascript, 252

function c(p){q='\"';s='\\';a="function c(p){q='\"';s='\\';a=%;a=a.slice(0,17)+s+a.slice(17,24)+a[23]+a.slice(24);a=q+a.replace('%',q+a+q)+q;alert(a);}";a=a.slice(0,17)+s+a.slice(17,24)+a[23]+a.slice(24);a=a.replace('%',q+a+q);alert(a);if(p!=a)eval(p)}

Дайте мені знати, чи хтось знає кращу техніку формування квінки в Javascript.


1
Нижче я розмістив 135-char JS-рішення, виходячи з вашого коду та мого рішення Perl. +1 за натхнення!
Ільмарі Каронен

2
read p<p;read c<c;[ "$p" = "$c" ]||. ./c

45 символів sh (оболонка POSIX). Код, який потрібно запустити, повинен бути у файлі ./c.

Код самого перекладача повинен бути у файлі ./p, тому я думаю, що я наче обдурив, хоча завдання, схоже, не забороняє його. Або це позбавить мою "мову" не бути "повною мовою програмування"?

Використовуючи інструмент, який зазвичай є зовнішнім виконуваним файлом, але теоретично він може бути вбудований в оболонку, код можна скоротити:

cmp -s p c||. ./c

Це 18 символів, і -sбіт полягає лише в тому, щоб придушити рядок, який інакше завжди був би надрукований для дійсних (несамостійних) програм.

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

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

read code; if [ "$code" ]; then eval "$code"; else . ./othercode; fi

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

Проблема?


2
Проблема говорить про те, що "ваша програма не повинна читати це власне джерело".
Ільмарі Каронен

Дарніт, даремно тоді витрачався. І я бачу, це навіть говорить, що ви не повинні використовувати функції, які ви будете опускати. Це буде суперечити функції порожнього рядка. Потім перекладач повинен опускати / змінювати функціональність, якщо код для компілятора / інтерпретатора сам викликає різну поведінку в новій мові. У будь-якому випадку, мені було весело писати помилки.
TaylanUB

@TaylanUB Ну, ви насправді повинні інтерпретувати всі дійсні програми $ lang, крім самого інтерпретатора.
FUZxxl

@FUZxxl Так, мова "sh + порожній рядок" в іншому випадку еквівалентна sh (якщо код не порожній рядок), а програма порожня рядок, написана в ній, також інтерпретує код sh (який потрібно ввести ./othercode), і робить нічого, коли код - порожній рядок. Я не повинен був називати файл ./othercode, це вводить в оману; це просто код, який інтерпретує інтерпретатор, написаний порожньою мовою рядка.
TaylanUB

2

JavaScript, 135 символів

function c(p){q='function c(p){q=%27Q%27;p!=unescape(q).replace(/Q/,q)?eval(p):alert()}';p!=unescape(q).replace(/Q/,q)?eval(p):alert()}

Рішення JavaScript Пітера Олсона надихнуло мене на спробу перенесення мого рішення Perl до JS. Як і його рішення, цей код визначає функцію, cяка приймає рядок, і оцінює її, якщо вона не дорівнює коду вище.

Мені знадобилося певний час, щоб розібратися з хорошим способом вирішити питання про відсутність збалансованих рядкових роздільників у JavaScript, поки я не знайшов, що очевидне рішення - очевидне рішення unescape().

Зручно, що в моєму коді немає зворотних нахилів або подвійних лапок, тому його можна безпечно зберігати у дворядних цитатах. Це полегшує тестування:

e = "function c(p){q='function c(p){q=%27Q%27;p!=unescape(q).replace(/Q/,q)?eval(p):alert()}';p!=unescape(q).replace(/Q/,q)?eval(p):alert()}"
h = "alert('Hello, world!')"

eval(e)  // defines the function c()

c(h)     // evaluates h
c(e)     // does not evaluate e, alerts "undefined" instead

Ви можете замінити alert()на, 0щоб змусити нічого не робити замість того, щоб оповістити undefinedта зберегти 13 символів.
Пітер Олсон

@PeterOlson: Так, але завдання говорить, що "ваша програма повинна робити щось абсолютно не пов'язане", якщо вона виявить себе. Я вважаю, що це означає, що він повинен щось робити - бажано, щось бачне для користувача, я б припускав. Крім того, мені просто подобається так краще. :) (Ps. Так, надворі сніг! Зима нарешті тут!)
Ilmari Karonen

1
@Ilmari Нічого не пов'язане з інтерпретацією Javascript IMHO.
FUZxxl

Ви могли піти p=>...замістьfunction c(p)
FireCubez

2

Common Lisp, 59

#+~ #.(#:a)(defun L(p)(compile-file p))(push :~ *features*)
  • У новому текстовому відказі Lisp складіть свій файл (наприклад sbcl --load)
  • Тепер у вас є функція L, яка може компілювати файли Common Lisp
  • Однак, якщо ви телефонуєте (L <your file>), під час читання файлу подається сигнал про помилку .

Чому?

Тому що перший раз ви натиснули :~ключове слово на *features*. Тепер ваше середовище знає про ~функцію, і макрос читача #+, оцінюючи ~ вираз функції , досягне успіху і прочитає наступну форму, а не пропустивши її, як це було в перший раз. У вашому файлі є така форма #.(#:a), яка просить оцінити (#:a)під час читання та використовувати отримане значення як код, який читається. Але (#:a)викликає функцію, пов'язану з символом безперебійної дії #:a. Оскільки він #:aє безперервним, це свіжий символ, який не пов'язаний ні з якою функцією (тобто ні fboundp). Помилка.


1

Схема, 48 або 51 символ

Схема - це мова з багатьма різними реалізаціями. Незважаючи на те, що реалізація повинна відповідати останнім RnRS, останній робочий стандарт (R6RS) був непопулярним через відсутність мінімалізму. Незабаром R7RS буде випущений як засіб, розбиваючи мову на 2. Перша мова є потужною і мінімалістичною, а друга, надгруппою першої, призначеної для розширення функцій для сумісності між реалізаціями. До цього часу ми покладаємось на SRFI (Scheme Requests For Implementation), які надають (якщо вони реалізовані в хостовій реалізації або вручну (як це прийнято в схемі)) засобом для портативного виконання загальних завдань. Все це говорить про те, що перший фрагмент коду (51 символ), хоча він залишається настільки портативним, наскільки це можливо, покладається на SRFI-22 (виконання сценаріїв схеми в UNIX) для доступу до аргументів командного рядка:

(define(main x y)(case y(x => error)(else => load)))

або більш зрозуміло:

(define (main current-file arg)
  (case arg
    [current-file => error]
    [else => load]))

Другий (48 символів) - це файл, який не потребує файлів, для інтерпретації, який не може оцінити себе (у нульовому середовищі):

(define(e)(write(eval(read)null-environment))(e))

або більш зрозуміло:

(define (interpret)
  (write (eval (read) null-environment))
  (interpret))

Ваш код не працює, якщо ви копіюєте перекладача.
FUZxxl

1
Залиште це схемою відповіді, щоб містити вкладені дужки в своїй прозі.
Cyoce

1

Groovy, 13 байт

{Eval.me(it)}

Це має тлумачити підмножину Groovy.

тестові справи:

p={Eval.me(it)}

p'''
    (0..37).each{println"1234567890JIHGFEDCBAKLMNOPQRST!?,.ZYXWVU"[it..it+2]}
'''

p'''
    {Eval.me(it)}
'''

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


У якому рядку ви читаєте програму для інтерпретації? Ваш код цікавий, хоча це неправдиве подання для цього завдання.
FUZxxl

Я припускаю, що помилка чимось на кшталт "перевищена межа рекурсії"?
Ільмарі Каронен

1

Javascript ES6, 45 байт

$=(_=prompt())=>eval(_==`$=${$};$()`?0:_);$()

Все ще конкурентоспроможний! (thx @Downgoat)



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