Golf un interprète violet

13

Golf un interprète violet

Le violet est un esolang conçu avec deux objectifs principaux:

  • Être une minimisation d' Aubergine , car il n'y a tout simplement pas assez de langues à instruction unique auto-modifiantes.
  • Pour admettre la possibilité d' interprètes de golf terriblement petits . Ma première passe à un interprète Python 2 raisonnablement complet n'est que de 702 octets, et je suis sûr qu'un golfeur plus expérimenté pourrait en raser un peu.

Votre objectif est d'écrire un interprète pour cette langue.

Informations sur le violet:

Un programme violet est une séquence de caractères placée dans une matrice de mémoire adressable infinie de telle sorte que le premier caractère du programme soit placé à l'adresse zéro. Le reste du tableau (avant et après le stockage du programme Purple) est initialisé à zéro.

Il existe trois registres en violet, appelés a et b et i , chacun pouvant contenir un entier signé et est initialisé à zéro. i est également le pointeur d'instruction et pointe toujours sur l'instruction Purple en cours d'exécution.

À chaque cycle, l'interpréteur lira une séquence de trois caractères contigus à partir de l'emplacement de mémoire indiqué par le pointeur d'instruction et tentera d'exécuter cette séquence en tant qu'instruction violette. Ensuite, le pointeur d'instruction est toujours incrémenté de 3.

Syntaxiquement, l'instruction Purple se compose de trois caractères (ou de leurs codages) consécutifs, comme " xyz ".

Le premier caractère x peut être l'un des suivants:

abABio

Ces symboles ont la signification suivante:

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.

Les deux autres octets y et z peuvent être l'un des suivants:

abABio1

Chacun de ces symboles a la signification suivante:

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.

Après avoir récupéré l'instruction, l'interpréteur Purple évaluera y puis z , soustraira le résultat de z du résultat de y , puis effectuera l'action indiquée par x sur la différence.

Si la séquence de trois caractères (ou leurs codages) n'est pas une instruction Purple valide, l'interpréteur s'arrête immédiatement sans donner d'erreurs.

Votre interprète doit:

  • Soyez un programme complet, pas une fonction.
  • Ne sortez jamais vers stderr, sauf si EOF est lu .
  • Comportement identique à l'implémentation de référence sur toutes les entrées bien formées qui n'impliquent pas de très grands nombres, y compris les programmes de test donnés ci-dessous. (Eh bien, à l'identique jusqu'au moment - il peut fonctionner plus lentement, mais pas trop!)

Vous pouvez fournir le programme à l'interpréteur sous la forme que vous souhaitez: le lire à partir d'un fichier, l'intégrer dans le programme sous forme de chaîne ou le lire à partir de stdin.

Cas de test:

Le programme

ooo

lorsqu'il est exécuté avec une entrée

z!

devrait céder

Y

Le programme

bbboobiii

lorsqu'il est exécuté avec une entrée

It's a cat program.

(ou toute autre entrée) devrait donner

It's a cat program.

(ou toute autre entrée reçue), puis recommencez et recommencez .


Le programme

Aoab11bi1bABoAaiba

lorsqu'il est exécuté avec une entrée

0

devrait céder

0

puis s'arrêter, mais lorsqu'il est exécuté avec une entrée

1

devrait continuer à sortir

1

pour toujours.


Le programme

b1bbb1oAbabaa1ab1Ab1Bi1b

devrait céder

b1bbb1oAbabaa1ab1Ab1Bi1b

Le programme

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

devrait céder

Hello, World!

Notation:

C'est le , donc la source la plus courte en octets, telle que potentiellement modifiée par le bonus suivant, gagne.

Prime:

  • -10% si votre interprète lit un nom de fichier depuis stdin ou depuis un argument de ligne de commande et charge le programme à partir du fichier.
quintopie
la source
1
Quelle est la taille des cellules mémoire? octets, caractères (unicode?), grands nombres (arbitraires)? Il semble que vous utilisiez "caractère" et "octet" avec la même signification.
Paŭlo Ebermann
@ PaŭloEbermann, je suppose que c'est spécifique à l'implémentation; par exemple, je dois utiliser uint32pour les caractères et MAXINT pour les caractères
chat
2
@sysreq Est-ce vraiment un bloqueur? Votre implémentation pourrait simplement avoir deux bandes, une pour les indices négatifs et l'autre pour les indices positifs. (Oui, cela prendra un peu plus de code, mais pas tant que ça, je pense.)
Paŭlo Ebermann
1
@sysreq fondamentalement, un auto-interprète violet serait un programme qui lit un programme violet depuis stdin et fait ensuite ce que ce programme ferait. Le premier programme Purple (l'interpréteur) peut s'exécuter dans l'interpréteur de votre choix. Un programme qui écrase complètement les adresses mémoire les plus basses avec l'entrée, puis se supprime avant de passer en quelque sorte au code lu serait admissible (bien que je ne pense pas que ce soit réellement possible).
quintopie du
2
J'étais si près d'avoir un runtime capable d'auto-interprétation, mais j'étais trop tard.
chat

Réponses:

7

Pyth, 148 128 121 121 octets (ou 124 * .9 = 111,6, voir en bas)

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

Suite de tests

Code donné sur la première ligne de STDIN, entré dans le programme Purple sur le reste de STDIN. Pour utiliser du code avec des sauts de ligne, utilisez une autre version en bas.

Golfé raisonnablement. Voici les sauts de ligne et l'indentation pour plus de clarté:

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

Fondamentalement, une #boucle effectue l'exécution et l'arrêt via une interruption d'erreur.

aet bsont combinés en une seule variable, J. Zest le pointeur d'instruction. kest entré dans le programme Violet. Hest la bande, représentée comme un dictionnaire. best le résultat actuel.Yest le premier octet actuel de l'instruction.

Lecture à partir d'un fichier:

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

Donnez le nom du fichier comme première ligne de STDIN. Essai:

$ 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
la source
5

JavaScript (ES6), 292 octets

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=="))

Explication

JavaScript réponses sont toujours bizarre quand STDINet STDOUTsont nécessaires ...

La première invite est l'entrée de la chaîne de programme. Chaque invite résultant d'une oinstruction ne lit que le premier caractère.

evalest utilisé pour remplacer une phrase courante qui économise quelques octets. Non golfé et sans le evalprogramme ressemble à ceci:

// 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
la source
2
Le second c="charCodeAt"peut-il être remplacé par juste c?
Dendrobium du
L'accès aux tableaux avec des indices négatifs fonctionne-t-il en JavaScript?
nimi
@Dendrobium Wow, je ne sais pas comment j'ai raté ce haha! Merci.
user81655
2
@nimi Ça marche. Les tableaux eux-mêmes ne prennent pas en charge les indices négatifs, mais cela profite du fait qu'ils se comportent également comme des objets. array[-1] = 1est le même que array = { "-1": 1 }. Les deux sont accessibles avec array[-1].
user81655
@ user81655: Ah bien, je ne le savais pas.
nimi
3

Ceylan, 827 792 671 octets

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){}}

Il se comporte un peu différemment de l'implémentation de référence lorsque le programme essaie de lire l'entrée à EOF - l'implémentation de référence se bloque avec une TypeError, qui est trop coûteuse à reproduire ici (et aussi probablement un bogue), donc cela renverra -1 à la place ( si nécessaire).

(Cependant, lorsque vous essayez d'écrire ce -1 sur stdout, l'interpréteur se terminera par une erreur OverflowErreur. Un problème similaire se produira si un nombre entier en dehors de la plage Unicode est généré.)

L'interpréteur prend le programme comme premier argument de ligne de commande (assurez-vous de le citer pour votre shell lorsqu'il contient des espaces ou d'autres choses intéressantes).

À Ceylan, nous ne pouvons que lire facilement les lignes d'entrée (je suppose que cela changera dans l'une des prochaines versions), donc quand il oest utilisé pour la lecture, je lis une ligne entière et tampon les parties pour de futures utilisations. Je suppose que cela fonctionne de manière similaire dans l'implémentation Python lorsqu'il est connecté à un terminal.


Lorsque vous essayez d'exécuter une commande (partie) qui n'est pas l'un des caractères valides, le nothingprovoquera une AssertionError à lancer, que nous interceptons ensuite dans le bloc catch autour de la boucle principale.

Je pense que cela devrait être de préférence un type d'exception personnalisé (car AssertionError pourrait également se produire à d'autres endroits si j'ai un bogue), mais cela prendrait beaucoup plus d'espace, mangeant la plupart des améliorations que j'ai apportées à partir de la première version.

Quelques astuces utilisées pour le golf:

  • Les versions précédentes utilisaient un ceylon.collection.HashMap - à la place, nous utilisons maintenant une carte immuable telle que créée par la mapfonction, et en créons une nouvelle à chaque fois Aou Best utilisée comme x .
  • J'utilise des alias-imports pour tous les identifiants de ceylon.language qui sont utilisés plusieurs fois (y compris l' variableannotation, qui est maintenant l).
  • Je me suis débarrassé de la classe E(pour l'environnement) et de la sméthode (step) - tout se passe maintenant à l'intérieur de la runfonction.
  • Au lieu d'utiliser .integerpour obtenir le point de code d'un caractère, .hashdonne le même résultat. C'est donc string*.hashla même chose que string.map(Character.integer)(donne un itérable des points de code d'une chaîne).
  • Lorsqu'un type est importé par alias, is I ...est plus court que exists ....
  • Lors de la conversion de quelque chose (par exemple x) en chaîne, "``t``"est plus court que t.string(ou, ce que j'ai utilisé pour un caractère String{t}).
  • les fonctions utilisées une seule fois peuvent souvent être intégrées.

Voici la version formatée (et commentée):

// 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
    }
}
Paŭlo Ebermann
la source
J'ai réutilisé une partie de ce code pour un "interprète parallèle" essayant de trouver tous les programmes d'arrêt. (Il y en a beaucoup.) (Là, j'ai utilisé une version non-E / S de Purple, car les E / S prennent beaucoup d'espace et ne sont pas utilisées dans cette tâche.)
Paŭlo Ebermann