Перекладач самоперекладу


25

Спираючись на коментар Джорджа Едісона до цього питання , напишіть найменшого самоперекладача.

  • Ви можете використовувати мову, яку ви вибрали.
  • Порожні мови не рахуються. У вашій програмі має бути не менше двох символів.
  • Програмі не потрібно інтерпретувати всю мову, лише повний набір Тюрінга набору мовних функцій (що містить перекладача).
  • Лави не рахуються.
  • Не використовуйте вбудовану evalфункцію вашої мови або її аналогію. Те саме стосується і applyт.д.

1
(Хм .. я повинен щось робити /usr/bin/cat) що з Тюрінг-повнотою?
Мінг-Тан

@ SHiNKiROU: Спасибі, я не вважав це тестом. Оновлено.
Хоа Лонг Там

Пов’язано: Мова з найменшим перекладачем, написаним самим на стеку Overflow, хоча є кілька (лише один?) Відповідей, які насправді дотримуються наведених тут правил.
dmckee

1
Чи потрібно переписувати цей аналізатор схеми sexp, чи ми можемо вважати мову хоста нормальною?
JB

@JB: Утиліти обробки рядків мови є нормальними, включаючи sexpаналізатор.
Хоа Лонг Там

Відповіді:


19

CI - 260

,(1p0(2d())(41(2d())('#((1p0()(10()(1d,1p$)=)<)$2d,1p$)(40(1d,1c$^)(''(1d,^)('0
'9('0-(,'0'9('0-2p10*+1p$)(!1d)~)$^)(0($)'$(^)'^(&)'&(c)'c(p)'p(d)'d(=)'=(<)'<(
>)'>(~)'~(.)'.(,)',(!)'!(+)'+(-)'-(*)'*(/)'/(%)'%()38p(1p3p0(3d)((2)(3)=p1d1p$)
=)$)~)=)=,2p$&)=)=)<)$$

320 → 260: Натисніть на прості відображення символів до інструкцій, а потім складіть їх. Це вдвічі зменшує розмір коду на кожний випадок (є 18 випадків), але коштує 30 символів для складання.

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

1^      # Push a 1, and "lift" it to be a code literal.
(5 +)   # Define a code literal.
&       # Join the two code literals, forming (1 5 +)
$       # Execute the code literal.

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

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


Довідкова мова:

Знайдіть перекладача тут

Блоки

  • ( ... )

    Створіть "блок", який фактично є переліком інструкцій без контексту. Всередині це може бути навіть машинний код.

  • блок $

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

  • значення ^

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

    Приклад :

       1 ^
    == (1)
    
  • block1 block2 &

    Об’єднайте два блоки, утворюючи той, який працює обидва в послідовності.

    Приклад :

       (1) (2) &
    == (1 2)
    

Маніпуляція стеком

  • н c

    Скопіюйте n-е значення стека.

    Приклад :

       5 4 3 2 1 0 3c
    == 5 4 3 2 1 0 3
    
  • н p

    Вирвіть n-е значення стека (вийміть його та виведіть на фронт).

    Приклад :

       5 4 3 2 1 0 3p
    == 5 4 2 1 0 3
    
  • н d

    Викиньте n значень із стека. 0dє неоперативним.

    Приклад :

       5 4 3 2 1 0 3d
    == 5 4 3
    

Реляційні оператори

  • ab (on_true) (on_false) =

    Перевірте, чи дорівнює b. Вживайте всі, крім першого аргументу, і зателефонуйте on_true або on_false. Якщо один аргумент дорівнює нулю, а інший - будь-який інший тип, результат буде помилковим. В іншому випадку, a і b повинні бути цілими числами.

    Приклад :

       3 3 ('0+ .) (1d) =
    == 3 '0+ .
    
  • ab (on_true) (on_false) <

    Перевірте, якщо a менше b. a і b повинні бути цілими числами.

    Приклад :

       3 5 (1d 5) () <
    == 3 1d 5
    == 5
    
  • ab (on_true) (on_false) >

    Перевірте, чи a більше b. a і b повинні бути цілими числами.

    Приклад :

       3 5 (1d 5) () >
    == 3
    
  • a lo hi (on_true) (on_false) ~

    Перевірте, чи lo <= a <= hi. a, lo і hi має бути цілим числом.

    Приклад :

       3 0 10 ('0+ .) (1d) ~
    == 3 '0+ .
    

I / O

  • c .

    Поставте символ c (споживаючи його зі стека).

  • ,

    Отримайте персонаж і натисніть на стек. Якщо кінець файлу досягнуто, натискається -1.

  • c !

    Видаліть символ. Так само, як ungetc в C, дозволяється лише одна віддача.

Цілі літерали

  • 'c

    Натисніть на символ c.

  • [0-9] +

    Натисніть на десяткове ціле число.

Арифметика

  • аб +
  • аб -
  • аб *

    Додавання / віднімання / множення двох чисел.

    Приклад :

       3 5 + 7 3 + *
    == 80
    
  • аб /

  • аб %

    Поділ і модуль. На відміну від C, вони круглі до негативної нескінченності.

Різне

  • #коментар до коду

    #Характер закоментуйте все до кінця рядка.

  • )

    Використовується для закінчення блоків. Можна також використовувати для завершення всієї програми.

  • Усі інші символи ігноруються.


24

Двійковий обчислення лямбда, 232 біт (29 байт)

0101000110100000000101011000000000011110000101111110011110000101110011110000001111000010110110111001111100001111100001011110100111010010110011100001101100001011111000011111000011100110111101111100111101110110000110010001101000011010

Докладні відомості див. У розділі http://en.wikipedia.org/wiki/Binary_lambda_calculus#Lambda_encoding.


2
Чому це не прийнята відповідь D: BLC - це дивовижно!
кіт

Чи можете ви це взагалі пояснити?
PyRulez

1
На жаль, Вікіпедія видалила сторінку Бінарного обчислення лямбда. Моя сторінка tromp.github.io/cl/cl.html посилається на збережену копію та на документ, який я написав, пояснюючи роботу перекладача.
Джон Тромп

13

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

Brainf *** (423)

>>>+[[-]>>[-]++>+>+++++++[<++++>>++<-]++>>+>+>+++++[>++>++++++<<-]+>>>,<++[[>[
->>]<[>>]<<-]<[<]<+>>[>]>[<+>-[[<+>-]>]<[[[-]<]++<-[<+++++++++>[<->-]>>]>>]]<<
]<]<[[<]>[[>]>>[>>]+[<<]<[<]<+>>-]>[>]+[->>]<<<<[[<<]<[<]+<<[+>+<<-[>-->+<<-[>
+<[>>+<<-]]]>[<+>-]<]++>>-->[>]>>[>>]]<<[>>+<[[<]<]>[[<<]<[<]+[-<+>>-[<<+>++>-
[<->[<<+>>-]]]<[>+<-]>]>[>]>]>[>>]>>]<<[>>+>>+>>]<<[->>>>>>>>]<<[>.>>>>>>>]<<[
>->>>>>]<<[>,>>>]<<[>+>]<<[+<<]<]

10

BlockScript - 535

{[B':=?0:B';=?0:B'}=?0:B'{=?,A!,A!d1c&:B'?=?,A!,A!2e&:B''=?,,A!d3c&:B{[B'0<?0:B
'9>?0:1}!?B'0-{[,g!?c'0-B10*d+A!:Bd]A!d3c&}!:B'#=?{[,10=?,]A!:A!}!:,A!Bb&}{[AC[
B]DB?[AB{[Bh&hbhn!}{[B[AB]C?1-eA!:b}&[C1=?E[C]FHc&B!:C2=?{G?D:E[C}!FHcI!:C3=?E[
C]B!:C'!=?G[ABC]Hc&dbh&D?b@I!B!:b@I!:C'&=?HB!:C'@=?FGDI!:C'[=?GF&HDI!:C']=?F[A]
HDI!:C',=?,B!:C'.=?G.FHDI!:C'a'z{[DC<?0:DB>?0:1}!?Ce-HA!B!:C'A'Ze!?F[B]Cg-dA!B!
:{C'+=?{[CB+}:C'-=?{[CB-}:C'*=?{[CB*}:C'/=?{[CB/}:C'%=?{[CB%}:C'<=?{[CB<}:C'>=?
{[CB>}:C'==?{[CB=}:0}!?H[A][B]Ge!B!:FHDI!:c},c!0ac&0&0&0bho!;

BlockScript - це тривіальна мова, основана на стеці спагетті, яку я створив спеціально для цього завдання. Базовим перекладачем є blockcript.c .

Зразок програми (друкує перші 15 чисел Фібоначчі):

{[B?B10/A!B10%d&:0}
{[B0<?'-.0B-A!:{B?Bh!{[B?B[A]A!B[B]'0+.:}!:'0.}!10.}
{[B?Dd!DC+B1-CecA!:}
0 1 15d!
;

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

# Level 1
{[B':=?0:B';=?0:B'}=?0:B'{=?,A!,A!d1c&:B'?=?,A!,A!2e&:B''=?,,A!d3c&:B{[B'0<?0:B
'9>?0:1}!?B'0-{[,g!?c'0-B10*d+A!:Bd]A!d3c&}!:B'#=?{[,10=?,]A!:A!}!:,A!Bb&}{[AC[
B]DB?[AB{[Bh&hbhn!}{[B[AB]C?1-eA!:b}&[C1=?E[C]FHc&B!:C2=?{G?D:E[C}!FHcI!:C3=?E[
C]B!:C'!=?G[ABC]Hc&dbh&D?b@I!B!:b@I!:C'&=?HB!:C'@=?FGDI!:C'[=?GF&HDI!:C']=?F[A]
HDI!:C',=?,B!:C'.=?G.FHDI!:C'a'z{[DC<?0:DB>?0:1}!?Ce-HA!B!:C'A'Ze!?F[B]Cg-dA!B!
:{C'+=?{[CB+}:C'-=?{[CB-}:C'*=?{[CB*}:C'/=?{[CB/}:C'%=?{[CB%}:C'<=?{[CB<}:C'>=?
{[CB>}:C'==?{[CB=}:0}!?H[A][B]Ge!B!:FHDI!:c},c!0ac&0&0&0bho!;

# Level 2
{[B':=?0:B';=?0:B'}=?0:B'{=?,A!,A!d1c&:B'?=?,A!,A!2e&:B''=?,,A!d3c&:B{[B'0<?0:B
'9>?0:1}!?B'0-{[,g!?c'0-B10*d+A!:Bd]A!d3c&}!:B'#=?{[,10=?,]A!:A!}!:,A!Bb&}{[AC[
B]DB?[AB{[Bh&hbhn!}{[B[AB]C?1-eA!:b}&[C1=?E[C]FHc&B!:C2=?{G?D:E[C}!FHcI!:C3=?E[
C]B!:C'!=?G[ABC]Hc&dbh&D?b@I!B!:b@I!:C'&=?HB!:C'@=?FGDI!:C'[=?GF&HDI!:C']=?F[A]
HDI!:C',=?,B!:C'.=?G.FHDI!:C'a'z{[DC<?0:DB>?0:1}!?Ce-HA!B!:C'A'Ze!?F[B]Cg-dA!B!
:{C'+=?{[CB+}:C'-=?{[CB-}:C'*=?{[CB*}:C'/=?{[CB/}:C'%=?{[CB%}:C'<=?{[CB<}:C'>=?
{[CB>}:C'==?{[CB=}:0}!?H[A][B]Ge!B!:FHDI!:c},c!0ac&0&0&0bho!;

# Level 3
{[B?B10/A!B10%d&:0}
{[B0<?'-.0B-A!:{B?Bh!{[B?B[A]A!B[B]'0+.:}!:'0.}!10.}
{[B?Dd!DC+B1-CecA!:}
0 1 15d!
;

Як і фільм « Початок» , ви майже не можете пройти глибше трьох рівнів. Це не питання часу, а простору. BlockScript рясно просочує пам'ять, і це пов'язано з тим, як створена сама мова.


Довідкова мова:

Знайдіть перекладача тут

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

Вибір значення

  • a наскрізь z

    Дістаньте 0-25-й елемент із стека та натисніть на стек. aпосилається на голову або останній висунутий предмет стопки.

  • A наскрізь Z

    Витягніть 0-25-й елемент поточного кадру та натисніть на стек.

  • [

    Відкрийте "кадр" для вибору елементів із посилання на стек (див. Нижче) на голові стека. [не вимагає відповідності ], але кадри мають лексичний діапазон. У BlockScript "область" визначається дужками ( {... }), які утворюють блоки. Таким чином, відкриття кадру всередині блоку не матиме впливу на код поза блоком.

  • ]

    Закрийте поточний кадр, повертаючись до попереднього кадру (якщо такий є).

Блоки

  • { ... }

    Створіть "блок" і натисніть на стек. Всередині блоку стек починатиметься з того, що був до блоку, за винятком того, що стек абонента буде висунутий зверху. Стеки стійкі та незмінні в BlockScript, тому блоки є закриттями. Ідіома {[означає відкрити блок, а потім відкрити кадр для початку вибору аргументів (за допомогою Aнаскрізного Z). Повернене значення блоку - це голова стека, коли }буде досягнуто.

    Приклад:

    '3 '2 '1 {[ b. d. f. B. C. D. A! } 'D 'C 'B d!;
    

    Це відбитки 123BCD123DCB123BCD123DCB…. Малі літери відносяться до значень стека, а великі літери - до аргументів (тому що кадр встановлений у стеку абонента). A!приймає голову абонента (що гарантовано називається блоком, що викликається) і називає його. Якщо вам цікаво, чому він реверсується BCDкожен раз, це тому, що B. C. D.підштовхує ці аргументи у зворотному порядку прямо перед тим, як блокує дзвінок.

  • !

    Викличте блок. Натисніть повернення на стек.

Посилання на стеки

  • &

    Створіть посилання на стек та натисніть на стек. Подумайте про це як про «супер-мінуси», оскільки воно ефективно бере кожен предмет у стеку і утворює з нього «кортеж». Ідіоми &[означає , що все a, b, cзгадані дотепер часу можуть бути доступні з A, B, C(для решти блоку або поки ]не зустрінеться).

    Почасти тому, що &фіксує більше значень, ніж зазвичай потрібно, BlockScript просочує пам'ять за конструкцією.

  • @

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

Умовне вираження

  • ? <на правду> : <на помилково>

    Умовне вираз, як і потрійний оператор у C. Тобто, якщо a"true" (тобто не дорівнює цілому нулю), тоді зробіть <on true> , інакше зробіть <on false> .

I / O

Примітка. Введення та виведення здійснюються в UTF-8. "Символ" - це ціле число, що відповідає індексу Unicode.

  • ,

    Отримайте наступний символ введення та натисніть його на стек. Якщо кінець вводу досягнуто, натисніть -1 замість цього.

  • .

    Виведіть символ на голову стека.

Цілі літери / символи

Примітка. Цілі символи та символи - це одне і те ж саме в BlockScript.

  • 'c

    Натисніть на символ c.

  • [0-9] +

    Натисніть на десяткове ціле число.

Арифметика

Ці оператори працюють лише на цілі значення.

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

Реляційні оператори

Ці оператори працюють лише на цілі значення.

  • <Якщо bменше a, натисніть 1, інакше натисніть 0.
  • >
  • =

Різне

  • # Прокоментувати до кінця рядка
  • Програма повинна закінчуватися ;
  • Усі інші символи ігноруються.

2
Христос. Хочете поділитися специфікацією, як ви робили для CI?
Кейсі

@Casey: Додано посилання.
Joey Adams

1
Вам було б цікаво знати, що ви мрієте? ... На рівні 4
Mateen Ulhaq

3

Zozotez LISP : 414

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

((\(E V A L)(E(r)'(())))(\(x e)(?(s x)(V x e)((\(b j)(?(= b ")(a j)(?(= b \)x
(?(= b ?)(?(E(a j)e)(E(a(d j))e)(E(a(d(d j)))e))(?(s b)(A b(E(a j)e)(E(a(d j)
)e))(E(a(d(d b)))(L(a(d b))j e)))))))(E(a x)e)(d x))))(\(x g)(? g(?(= x(a(a
g)))(d(a g))(V x(d g)))x))(\(f h v)(?(= f r)(r)(?(= f p)(p h)(?(= f s)(s h)(?
(= f a)(a h)(?(= f d)(d h)(?(= f =)(= h v)(c h v))))))))(\(k v i)(? k(L(d k)(
d v)(c(c(a k)(E(a v)i))i))i)))

Теоретично це має бути в змозі запустити сам, але оскільки оригінальний інтерпретатор є двійковим продуктом BrainFuck, а сам інтерпретатор, мені вдалося протестувати кожну частину. Коли я даю собі і простий вираз, (p p)я думаю, що на це потрібно більше часу, ніж 40 хвилин, яких я чекав до цього часу, і я використовую свій швидкий, jitbfщоб запустити його, який (міс) використовує Perl Inline-C для запуску коду С на льоту.

Неможливо реалізувати весь Zozotez в Zozotez, оскільки він не має засобів для мутації мінусів і :(setq / define) потребує цього для оновлення прив’язок. Я також не реалізував явний аргумент start / progn або & rest, макроси та спеціальні аргументи друку, оскільки не використовував його в інтерпретаторі. Я включив p(роздрукував), навіть не використовуючи його, тому програмам потрібно чітко друкувати свої розрахунки так само, як оригінальний інтерпретатор.

Той самий недозволений:

;; A stand alone Zozotez script need to be
;; contained in one expression, here with
;; all functions provided as arguments to
;; get them bound in the dynamic environment
((\ (E V A L)
  (E(r)'(())))
 ;; E (EVAL)
 (\ (x e)
   (? (s x)
      (V x e)
      ((\ (b j)
         (? (= b ") (a j)
         (? (= b \) x
         (? (= b ?) (? (E(a j)e) (E(a(d j))e) (E(a(d(d j)))e))
         (? (s b)
            (A b (E(a j)e) (E (a(d j))e))
            (E (a(d(d b))) (L(a(d b))j e)))))))
       (E (a x) e)(d x))))
 ;; V (VALUE / symbol->value)
 (\ (x g)
   (? g
      (? (= x (a (a g)))
         (d (a g))
         (V x (d g)))
      x))
 ;; A (APPLY) but just for primitives
 (\ (f h v)
   (? (= f r) (r)
   (? (= f p) (p h)
   (? (= f s) (s h)
   (? (= f a) (a h)
   (? (= f d) (d h)
   (? (= f =)
      (= h v)
      (c h v))))))))
 ;; L ( joint evLis / extend-env)
 (\ (k v i)
   (? k
      (L (d k) 
         (d v)
     (c (c (a k) 
           (E (a v) i)) 
        i))
      i)))

0

CHIQRSX9 + (можливо, не конкуруючий), 2 байти

+I

Немає можливості написати інтерпретатора самоінтерпретації на цій мові, що базується на HQ9 +, без використання I, який запускає вбудований інтерпретатор, який обробляє STDIN.


1
Ніде в правилах не сказано, що вбудовані самоперекладачі заборонені. Це говорить про те eval, що для виразів, а не для програм.
Ерік Аутгольфер

Як один обчислювати праймери в цій мові?
pppery

@ppperry Xповинен зробити мову Turing-завершеною (таким чином, здатною обчислювати праймери) залежно від реалізації способом.
користувач8397947

На сторінці Esolang Xкоманда інтерпретатора Perl випадковим чином збурює програму та виконує її, а це означає, що не можна використовувати, ніж команду, для детермінованого обчислення простих ліній. Чи можете ви надати мені інтерпретатора прикладу, який дозволяє вам використовувати Xвказаний вами спосіб?
pppery

@ppperry Мабуть, перекладач, написаний на Perl, є єдиним перекладачем, тому ні. Крім того, що робити, якщо існує програма, яка обчислює праймери, коли "рандомізована" з певним значенням?
користувач8397947

0

Паралельна файлова система Befunge 98 - 53 \ 18 байт (майже напевно обман)

Повний 53-байтний інтерпретатор без обмежень (хоча я не перевіряв складні взаємодії в часі, що включають розділення та обгортання IP-адрес):

v ;;;;;;;;
>]390'ai@
 t;;;;;;;;
;>zzzzz#;

Читає дані з файлу з ім'ям aта виконує його. У правилах не вказано, що ми не можемо використовувати код, що змінюється.

18-байтний інтерпретатор, який не дозволяє завертати (IP переміщується по одному краю коду і починається з протилежного краю):

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