Гольф фіолетовий перекладач


13

Гольф фіолетовий перекладач

Purple - це esolang, який розроблений з двома основними цілями:

  • Щоб мінімізувати баклажани , оскільки просто не вистачає мод, що змінюють одну інструкцію.
  • Визнати можливість страшенно малих перекладачів гольфу. Мій перший пропуск у досить повнофункціонального інтерпретатора Python 2 - це лише 702 байти, і я впевнений, що досвідченіший гольфіст міг би злегка від цього поголитися.

Ваша мета - написати перекладача для цієї мови.

Інформація про Purple:

Фіолетова програма - це послідовність символів, розміщених у нескінченному, адресованому масиві пам'яті таким чином, що перший символ програми розміщується за нульовою адресою. Решта масиву (як до, так і після того, де зберігається програма Purple) ініціалізується до нуля.

У Фіолеті є три регістри, які називаються a і b і i , кожен з яких може містити підписане ціле число і ініціалізований до нуля. Я також є покажчиком інструкцій, і завжди вказує на поточну виконуючу команду Purple.

Кожен цикл перекладач буде читати послідовність з трьох суміжних символів, починаючи з місця пам'яті, зазначеного вказівником інструкції, і намагатиметься виконати цю послідовність як інструкцію Purple. Після цього покажчик інструкцій завжди збільшується на 3.

Синтаксично інструкція Purple складається з трьох символів (або їх кодувань) у рядку, як " xyz ".

Першим символом x може бути будь-який з наступних:

abABio

Ці символи мають таке значення:

a - Place the result in register a.
b - Place the result in register b.
A - Place the result in the location in memory referred to by register a.
B - Place the result in the location in memory referred to by register b.
i - Set the instruction pointer to the result.
o - Output the result to stdout.

Інші два байти y і z можуть бути будь-якими з наступних:

abABio1

Кожен із цих символів має таке значення:

a - Return the contents of register a.
b - Return the contents of register b.
A - Return the contents of the memory array at the address stored in register a.
B - Return the contents of the memory array at the address stored in register b.
i - Return the contents of register i (the instruction pointer).
o - Return the value of a single character read from stdin.
1 - Return the literal numeric value 1.

Після отримання інструкції фіолетовий інтерпретатор оцінить y, а потім z , відніме результат z від результату y , а потім виконає дію, вказану x на різницю.

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

Ваш перекладач повинен:

  • Будьте повноцінною програмою, а не функцією.
  • Ніколи не виводити на stderr, якщо тільки не буде прочитано EOF .
  • Поведіть ідентично реалізацію посилань на всіх добре сформованих входах, які не передбачають дуже великої кількості, включаючи тестові програми, наведені нижче. (Ну, ідентично до часу - це може працювати повільніше, але не занадто багато!)

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

Тестові приклади:

Програма

ooo

при запуску з введенням

z!

повинен дати вихід

Y

Програма

bbboobiii

при запуску з введенням

It's a cat program.

(або будь-який інший вхід) повинен дати результат

It's a cat program.

(або будь-який вхід, який він отримав), а потім почніть спочатку і зробіть те ж саме знову .


Програма

Aoab11bi1bABoAaiba

при запуску з введенням

0

повинен дати вихід

0

а потім зупиняти, але коли працювати з введенням

1

слід продовжувати виводити

1

навіки.


Програма

b1bbb1oAbabaa1ab1Ab1Bi1b

повинен дати вихід

b1bbb1oAbabaa1ab1Ab1Bi1b

Програма

aA1aa1bb1oAbbi1bb1bbAb1Bi1b Purple is the awesomest! Why haven't you tried it yet?
!dlroW ,olleG

повинен дати вихід

Hello, World!

Оцінка:

Це , тому виграє найкоротше джерело в байтах, потенційно модифіковане наступним бонусом.

Бонус:

  • -10%, якщо ваш перекладач читає ім'я файлу або з stdin, або з аргументу командного рядка і завантажує програму з файлу.

1
Який розмір комірок пам'яті? байти, символи (унікодні?), (довільні) великі цілі числа? Схоже, ви використовуєте "символ" і "байт" з тим самим значенням.
Paŭlo Ebermann

@ PaŭloEbermann я здогадуюсь, що це конкретна реалізація; наприклад, мені потрібно використовувати uint32символи та MAXINT для ints
cat

2
@sysreq Це дійсно блокатор? Ваша реалізація може просто мати дві стрічки: одну для негативних та одну для позитивних показників. (Так, для цього знадобиться трохи більше коду, але не так вже й багато, я думаю.)
Paŭlo Ebermann

1
@sysreq, в основному, фіолетовий інтерпретатор буде програмою, яка читає програму Purple зі stdin, а потім робить все, що зробила б ця програма. Перша програма Purple (перекладач) може працювати в будь-якому перекладачі, який вам подобається. Програма, яка повністю перезаписує найменші адреси пам'яті з введенням, а потім видаляє себе, перш ніж якось перейти до прочитаного коду, буде кваліфікованою (хоча я не думаю, що це насправді можливо).
Кінтопія

2
Я наблизився до виконання часу, здатного до самоінтерпретації, але я запізнився.
кіт

Відповіді:


7

Pyth, 148 128 121 байт (або 124 * .9 = 111,6, див. Знизу)

J,00=kjb.z .eXHkCbz#=b-Fm?=zx"oabABi1"C@H+Zd@s[0Jm.x@Hk0JZ1H)zCh~tkS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3

Тестовий набір

Код, наведений у першому рядку STDIN, вхід до програми Purple на решті STDIN. Щоб використовувати код з новими рядками, використовуйте альтернативну версію внизу.

Розумно гольф. Ось це з переривами рядків і відступом для наочності:

J,00
=kjb.z
 .eXHkCbz
#
  =b-Fm
    ?=zx"oabABi1"C@H+Zd
      @
        s[0Jm.x@Hk0JZ1H)
        z
      Ch~tk
    S2
   ?hKx"abAB"=YC@HZ
    ?PK
      XH@JKb
      XJKb
  ?qY\i=Zb
  ?qY\opCb
  vN
  =+Z3

В основному #цикл виконує виконання та зупинку за допомогою помилки.

aі bоб'єднані в одну змінну, J. Z- покажчик інструкцій. kє входом до програми Purple. H- стрічка, представлена ​​як словник. b- поточний результат. Yце поточний перший байт інструкції.

Читання з файлу:

J,00=kjb.z .eXHkCbjb'z#=b-Fm?q\o=zC@H+ZdCh~tk@s[Jm.x@Hk0JZ1H)x"abABi1"zS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3

Дайте ім'я файлу як перший рядок STDIN. Пробіг:

$ cat purple-final.pyth 
J,00=kjb.z .eXHkCbjb'z#=b-Fm?=zx"oabABi1"C@H+Zd@s[0Jm.x@Hk0JZ1H)zCh~tkS2 ?hKx"abAB"=YC@HZ?PKXH@JKbXJKb?qY\i=Zb?qY\opCbvN=+Z3
$ cat purple-code.txt 
aA1aa1bb1oAbbi1bb1bbAb1Bi1b Purple is the awesomest! Why haven't you tried it yet?
!dlroW ,olleG
$ pyth purple-final.pyth <<< 'purple-code.txt' 
Hello, World!

5

JavaScript (ES6), 292 байти

eval(`a=b=i=d=0;v=n=>(x=m[i+n])==97?a_98?b_65?m[a]_66?m[b]_105?i_111?p()[c]()_49?1:d=1;for(m=[...(p=prompt)()].map(b=>b[c="charCodeAt"]());!d;i+=3)(y=v(1),d)||(z=v(2),d)?1:(x=m[r=y-z,i])==97?a=r_98?b=r_65?m[a]=r_66?m[b]=r_105?i=r-3_111?alert(String.fromCharCode(r)):d=1`.replace(/_/g,":x=="))

Пояснення

Відповіді на JavaScript завжди дивні, коли STDINі STDOUTпотрібні ...

Перша підказка - це введення для рядка програми. Кожне підказка, отримане в результаті oінструкції, читатиме лише перший символ.

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

// Initialisation
a=b=i=                            // initialise registers to 0
  d=0;                            // d is set to true when the program should die

// Gets the result of Y or Z
v=n=>                             // n = offset from i
  (x=m[i+n])==97?a:               // x = value of instruction
  x==98?b:
  x==65?m[a]:
  x==66?m[b]:
  x==105?i:
  x==111?p()[c]():
  x==49?1:
  d=1;                            // if it was none of the valid values, die

// Execution loop
for(
  m=                              // m = memory array
    [...(p=prompt)()]             // receive the program
    .map(b=>b[c="charCodeAt"]()); // initialise m to the ASCII values of the program
  !d;                             // finish if an error occured
  i+=3                            // increment i
)
  (y=v(1),d)||                    // get the value of Y and check for errors
  (z=v(2),d)?1:                   // get the value of Z and check for errors

    // Get the result of X
    (x=m[r=y-z,i])==97?a=r:       // r = result of y - z
    x==98?b=r:
    x==65?m[a]=r:
    x==66?m[b]=r:
    x==105?i=r-3:
    x==111?alert(String.fromCharCode(r)):
    d=1

2
Чи можна другу c="charCodeAt"замінити справедливою c?
Дендробіум

Чи працює доступ до масиву з негативними індексами в JavaScript?
німі

@Dendrobium Ого, я не знаю, як я пропустив цю ха-ха! Спасибі.
користувач81655

2
@nimi Це працює. Самі масиви не підтримують негативні індекси, але це використовує той факт, що вони також поводяться як об'єкти. array[-1] = 1те саме, що array = { "-1": 1 }. З ними можна отримати доступ до обох array[-1].
user81655

@ user81655: Добре, не знав цього.
німі

3

Цейлон, 827 792 671 байт

import ceylon.language{l=variable,I=Integer,x=nothing,p=process,m=map}shared void run(){try{if(exists d=p.arguments[0]){l value t=m{*d*.hash.indexed};l I a=0;l I b=0;l I i=0;I g(I j)=>t[j]else 0;l{I*}c=[];I o{if(c==[]){if(exists e=p.readLine()){c=e*.hash.chain{10};}else{c={-1}.cycled;}}assert(is I r=c.first);c=c.rest;return r;}value f=m{97->{a},98->{b},65->{g(a)},66->{g(b)},105->{i},111->{o},49->{1}};value s=m{97->((I v)=>a=v),98->((I v)=>b=v),65->((I v)=>t=m{a->v,*t}),66->((I v)=>t=m{b->v,*t}),105->((I v)=>i=v),111->((I v)=>p.write("``v.character``"))};I h(I v)=>f[v]?.first else x;while(0<1){(s[g(i)]else x)(h(g(i+1))-h(g(i+2)));i+=3;}}}catch(AssertionError e){}}

Він поводиться дещо інакше, ніж референтна реалізація, коли програма намагається прочитати вхід на EOF - реалізація посилань виходить з ладу з TypeError, що занадто дорого для відтворення тут (і, ймовірно, помилка), тому це повернеться натомість -1 ( повторно, якщо необхідно).

(При спробі записати цей -1 в stdout, інтерпретатор закінчить, однак, OverflowError. Подібне станеться, якщо буде виведено Integer поза діапазоном Unicode.)

Інтерпретатор сприймає програму як свій перший аргумент командного рядка (обов’язково цитуйте її для своєї оболонки, коли вона містить пробіли чи інші цікаві речі).

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


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

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

Деякі хитрощі, які використовуються для гольфу:

  • У попередніх версіях використовувався ceylon.collection.HashMap - замість цього ми зараз використовуємо незмінну карту, створену mapфункцією, і створюємо нову кожен раз Aабо Bвикористовується як x .
  • Я використовую псевдонім-імпорт для всіх ідентифікаторів з ceylon.language, які використовуються не один раз (включаючи variableанотацію, яка є зараз l).
  • Я позбувся класу E(для середовища) та sметоду (step) - все зараз відбувається всередині runфункції.
  • Замість використання .integerдля отримання кодової точки символу, .hashдає той же результат. Таким чином, string*.hashце те саме, що string.map(Character.integer)(дає ітерабельний код з рядків).
  • Якщо тип імпортується псевдонімом, is I ...він коротший, ніж exists ....
  • Коли перетворення чогось (наприклад x) у рядок "``t``"є коротшим, ніж t.string(або, що я використовував для символу, String{t}).
  • Функції, що використовуються лише один раз, часто можна вписати.

Ось форматована (та коментована) версія:

// Purple – a self-modifying, "one-instruction" language.
//
// Question:  http://codegolf.stackexchange.com/q/65411/2338
// My answer: http://codegolf.stackexchange.com/a/65492/2338

import ceylon.language {
    l=variable,
    I=Integer,
    x=nothing,
    p=process,
    m=map
}

shared void run() {
    try {
        // Reading code from file certainly takes more than 73 characters,
        // this isn't worth the 10% bonus.
        if (exists d = p.arguments[0]) {

            // The memory tape, as a Map<Integer, Integer>.
            // We can't modify the map itself, but we
            // can replace it by a new map when update is needed.
            l value t = m {
                // It is initialized with the code converted to Integers.
                // We use `.hash` instead of `.integer` because it is shorter.
                *d*.hash.indexed };

            // three registers
            l I a = 0;
            l I b = 0;
            l I i = 0;

            // get value from memory
            I g(I j) =>
                    t[j] else 0;

            // cached input which is still to be read
            l {I*} c = [];

            // get value from stdin.
            // we can only comfortably access stdin by line, so we read a whole line
            // and cache the rest for later.
            I o {
                if (c == []) {
                    if (exists e = p.readLine()) {
                        c = e*.hash.chain { 10 }; // convert string into ints, append \n
                    } else {
                        // EOF – return just -1 from now on.
                        c = { -1 }.cycled;
                    }
                }
                assert (is I r = c.first);
                c = c.rest;
                return r;
            }


            // Map of "functions" for fetching values.
            // We wrap the values in iterable constructors for lazy evaluation
            //  – this is shorter than using (() => ...).
            // The keys are the (Unicode/ASCII) code points of the mapped
            // source code characters.
            value f = m {
                // a
                97 -> { a },
                // b
                98 -> { b },
                // A
                65 -> { g(a) },
                // B
                66 -> { g(b) },
                // i
                105 -> { i },
                // o
                111 -> { o },
                // 1
                49 -> { 1 }
            };

            // Map of functions for "storing" results.
            // The values are void functions taking an Integer,
            // the keys are the ASCII/Unicode code points of the corresponding
            // source code characters.
            value s = m {
                // a
                97 -> ((I v) => a = v),
                // b
                98 -> ((I v) => b = v),
                // Modification of the memory works by replacing the map with a new one.
                // This is certainly not runtime-efficient, but shorter than importing
                // ceylon.collections.HashMap.
                // A
                65 -> ((I v) => t = m { a->v, *t }),
                // B
                66 -> ((I v) => t = m { b->v, *t }),
                // i
                105 -> ((I v) => i = v),
                // o – output as a character.
                111 -> ((I v) => p.write("``v.character``"))
            };

            // accessor function for the f map
            I h(I v) =>
                    f[v]?.first else x;

            // the main loop, can only be left by exception
            while (0 < 1) {
                (s[g(i)] else x)(h(g(i + 1)) - h(g(i + 2)));
                i += 3;
            }
        }
    } catch (AssertionError e) {
        // abort silently
    }
}

Я повторно використовував частину цього коду для "паралельного перекладача", намагаючись знайти всі програми зупинки. (Їх багато.) (Там я використав версію Purple, що не вводить-виводить, оскільки введення-виведення займає багато місця і не використовується в цьому завданні.)
Paŭlo Ebermann
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.