Гольф Фиолетовый Переводчик

13

Гольф Фиолетовый Переводчик

Фиолетовый - это esolang, который предназначен для двух основных целей:

  • Чтобы минимизировать баклажаны , так как не хватает самоизменяющихся языков с одной инструкцией.
  • Чтобы допустить возможность ужасно маленьких переводчиков гольфа. Мой первый проход к достаточно полнофункциональному интерпретатору Python 2 составляет всего 702 байта, и я уверен, что более опытный игрок в гольф мог бы от этого немного избавиться.

Ваша цель - написать переводчика для этого языка.

Информация о Purple:

Программа Purple - это последовательность символов, помещенная в бесконечный, адресуемый массив памяти, так что первый символ программы помещается в нулевой адрес. Остальная часть массива (как до, так и после того места, где хранится программа 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.

После извлечения инструкции интерпретатор Purple вычислит y, а затем z , вычтет результат z из результата y , а затем выполнит действие, обозначенное x на разнице.

Если последовательность из трех символов (или их кодировка) не является допустимой инструкцией Purple, интерпретатор немедленно останавливается без каких-либо ошибок.

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

  • Будьте полной программой, а не функцией.
  • Никогда не выводите в 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 или из аргумента командной строки и загружает программу из файла.
quintopia
источник
1
Каков размер ячеек памяти? байты, символы (юникод?), (произвольные) большие целые числа? Похоже, вы используете «характер» и «байт» с одинаковым значением.
Паŭло Эберманн
@ PaŭloEbermann, я думаю, это зависит от реализации; например, мне нужно использовать uint32для символов и MAXINT для целых
кошка
2
@sysreq Это действительно блокировщик? Ваша реализация может просто иметь две ленты, одну для отрицательных и одну для положительных индексов. (Да, это займет немного больше кода, но не так много, я думаю.)
Paŭlo Ebermann
1
@sysreq, по сути, самоинтерпретатор Purple - это программа, которая читает программу Purple из stdin и затем делает все, что будет делать эта программа. Первая программа Purple (переводчик) может работать с любым переводчиком, который вам нравится. Программа, которая полностью перезаписывает младшие адреса памяти входными данными, затем удаляет себя, прежде чем каким-либо образом перейти к считанному коду чтения (хотя я не думаю, что это на самом деле возможно).
Quintopia
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!
isaacg
источник
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используется для замены обычной фразы, которая сохраняет несколько байтов. Развернутый и без 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
user81655
источник
2
Можно c="charCodeAt"ли заменить второе просто c?
Дендробиум
Работает ли доступ к массиву с отрицательными индексами в JavaScript?
Ними
@ Dendrobium Ух, я не знаю, как я скучал по этому ха-ха! Благодарю.
user81655
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 в стандартный вывод интерпретатор завершит работу с OverflowError. Аналогичное произойдет, если будет выведено целое число вне диапазона Unicode.)

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

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


При попытке выполнить команду (часть), которая не является одним из допустимых символов, nothingбудет вызываться ошибка AssertionError, которую мы затем перехватываем в блоке catch вокруг основного цикла.

Я думаю, что это, предпочтительно, должен быть пользовательский тип Exception (так как AssertionError может также возникнуть в других местах, если у меня есть ошибка), но это заняло бы гораздо больше места, съедая большинство улучшений, которые я сделал с первой версии.

Некоторые трюки, используемые для игры в гольф:

  • В предыдущих версиях использовался ceylon.collection.HashMap - вместо этого мы теперь используем неизменяемую карту, созданную mapфункцией, и каждый раз создаем новую Aили Bиспользуемую как x .
  • Я использую alias-import для всех идентификаторов из ceylon.language, которые используются более одного раза (включая variableаннотацию, которая есть сейчас l).
  • Я избавился от класса E(для среды) и sметода (шага) - теперь все происходит внутри 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