Интерпретировать свой язык, но не себя?

21

Есть много проблем, которые говорят «интерпретировать X», где X - простой язык. На мой взгляд, это слишком скучно. Чтобы дать всем откладывающим людям в Интернете что-то интересное, вы можете попытаться сделать это:

Вызов

Выберите язык $LANG. $LANGможет быть любым полным языком программирования Тьюринга или полным подмножеством языка программирования Тьюринга. Помните, что если вы опускаете функцию вашего языка $LANGдля устного перевода, вы не должны использовать ее и для своей собственной программы, поскольку ваша заявка также должна быть записана $LANG.

Написать компилятор / интерпретатор для $LANGнаписанного в $LANG. Вы можете использовать все возможности (в том числе evalи друзей) вашего языка, которые доступны для написания этого компилятора. Чтобы сделать задачу более сложной, есть одно ограничение: $LANGваша программа должна иметь возможность интерпретировать / компилировать все допустимые программы, кроме самого вашего интерпретатора / компилятора. Если выяснится, что интерпретируемая / компилируемая программа является вашим интерпретатором или самим компилятором (независимо от имени файла), ваша программа должна сделать что-то совершенно не связанное с функциональностью интерпретатора или компилятора (например, перебор или печать Hello, world!).

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

Характеристики

  • Эта задача - код гольф. Представление с наименьшим количеством правильных символов выигрывает. В случае ничьей первое решение выигрывает.
  • Ваша программа / скрипт должен читать программу, которую нужно интерпретировать из файла. Вы можете жестко указать его путь и имя. Когда файл читается, вы можете скомпилировать файл в другой файл (который должен быть исполняемым в вашей системе) или запустить его напрямую. Если вам $LANGне хватает возможностей чтения файлов, вы можете выбрать другой способ чтения в подходящем коде $LANG. Вы не можете выбрать $LANGв качестве подмножества другой язык, но с удаленными возможностями чтения файлов.
  • Применяются обычные правила игры в гольф. То есть: ваш личный язык домашнего питомца, который вы создали для решения этой задачи, запрещен, если решение становится тривиальным с его использованием (например, определение программы с одним символом, которая точно реализует решение). Нарушение правил приветствуется.
FUZxxl
источник
Разрешено ли нам определять язык для этого, если он завершен?
Cruncher
@ Cruncher Да, ты. Смотрите последний пункт спецификации для получения более подробной информации.
FUZxxl

Ответы:

8

Руби, 63

b=$<.read
t="b=$<.read\nt=%p\nb!=t%%t&&eval(b)"
b!=t%t&&eval(b)
Lowjacker
источник
Ответ принят до тех пор, пока не найдется меньшее решение.
FUZxxl
11

Perl, 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's ), который читает и возвращает содержимое любых файлов, указанных в командной строке. Этот взлом необходим, потому что сам GolfScript не предоставляет явных возможностей ввода / вывода файлов.
  • .@ дублирует и перемешивает элементы в верхней части стека, так что в стеке содержатся две копии содержимого файла, за которым следует исходный код этой программы.
  • =! сравнивает два верхних элемента в стеке (т. е. содержимое файла и источник), возвращая 1, если они разные, и 0, если они одинаковые.
  • {~}*оценивает оставшуюся копию содержимого файла как код GolfScript, но только если результат сравнения равен 1. (Технически, он выполняет блок кода {~}столько раз, сколько задано числом в стеке, то есть 0 или 1 раз. Внутри блок, ~является оператором eval GolfScript.)

Ps. Если чтение кода для выполнения из 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)

И это может быть еще короче, если вы разрешите интерактивную оболочку Python, и в этом случае вы можете сделать:

'%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)}

Дайте мне знать, если кто-нибудь знает лучшую технику для формирования Quine в Javascript.

Питер Олсон
источник
1
Ниже я разместил решение JS на 135 символов, основанное на вашем коде и моем решении на Perl. +1 за вдохновение!
Ильмари Каронен
2
read p<p;read c<c;[ "$p" = "$c" ]||. ./c

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

Код для самого интерпретатора должен быть в файле ./p, так что я думаю, что меня обманули, хотя задача, похоже, не запрещает этого. Или это лишит меня права на «язык» как «язык программирования, полный по Тьюрингу»?

Используя инструмент, который обычно является внешним исполняемым файлом, но теоретически может быть встроен в оболочку, код можно сократить:

cmp -s p c||. ./c

Это 18 символов, и -sбит просто для того, чтобы подавить строку, которая в противном случае всегда выводилась бы для допустимых (не-self) программ.

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

И тогда вы всегда можете создать программу, которая, когда вход состоит из одного '.' - или, черт возьми, пустая строка - оценивает содержимое другого файла как обычный код и называет это языком программирования. Таким образом, пустая строка будет вашим решением проблемы на языке, который вы создали. На самом деле, вот переводчик для такого языка:

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

Используя язык, который интерпретирует приведенный выше скрипт, решением является пустая строка. И местоположение кода больше не нужно жестко кодировать.

Проблема?

TaylanUB
источник
2
Задача говорит, что «ваша программа не должна читать свой собственный источник».
Илмари Каронен
Дарнит, впустую какое-то время. И я вижу, что даже говорится, что вы не должны использовать функции, которые вы пропустите. Это пошло бы против функции пустой строки. С другой стороны, интерпретатор должен пропускать / изменять функциональность, если код самого компилятора / интерпретатора вызывает другое поведение на новом языке. В любом случае, мне было весело писать ошибки.
TaylanUB
@TaylanUB Ну, на самом деле вы должны интерпретировать все действительные программы $ lang, кроме самого интерпретатора.
FUZxxl
@FUZxxl Да, язык "sh + empty string" в остальном эквивалентен 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. Yay, на улице снег! Зима наконец-то здесь!)
Ильмари Каронен
1
@Ilmari Ничто не связано с интерпретацией Javascript ИМХО.
FUZxxl
Вы могли бы пойти p=>...вместоfunction c(p)
FireCubez
2

Common Lisp, 59

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

Зачем?

Потому что в первый раз вы вставили :~ключевое слово в *features*. Теперь ваша среда знает об этой ~функции, и макрос считывателя #+, после оценки ~ выражения функции , преуспеет и прочитает следующую форму вместо того, чтобы пропустить ее, как это было в первый раз. В вашем файле следующая форма #.(#:a), которая просит оценить (#:a)во время чтения и использовать полученное значение в качестве читаемого кода. Но (#:a)вызывает функцию, связанную с неинтернизированным символом #:a. Так как он не #:aявляется внутренним, он является новым символом, который не привязан ни к какой функции (т.е. нет fboundp). Ошибка.

CoreDump
источник
1

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

Схема - это язык с множеством различных реализаций. Несмотря на то, что реализации должны соответствовать последнему RnRS, последний рабочий стандарт (R6RS) был непопулярным из-за отсутствия минимализма. Скоро R7RS будет выпущен в качестве исправления, разделив язык на 2. Первый язык - мощный и минималистичный, а второй - расширенный набор первых, предназначенный для предоставления расширений функций для взаимодействия между реализациями. До тех пор мы полагаемся на SRFI (запросы схемы для реализации), которые обеспечивают (если реализовано в реализации хоста или вручную (как это обычно бывает в схеме)) средство для переносимого выполнения общих задач. Все это говорит о том, что первый фрагмент кода (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)}
'''

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

Armand
источник
В какой строке вы читаете программу для интерпретации? Ваш код интересен, хотя он не является верным представлением для этой задачи.
FUZxxl
Я предполагаю, что ошибка что-то вроде "превышен лимит рекурсии"?
Ильмари Каронен
1

Javascript ES6, 45 байт

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

Все еще конкурентоспособны! (THX @ Downgoat)

Mama Fun Roll
источник
Ах, я выходил из выпуска стандарта. Я не знал, что в 2011 году была работающая реализация. Возможно, вы захотите включить в текст ответа объяснение и ссылку на коммит.
Mego