[PYTHON] Nous recommandons Malbolge à ceux qui disent "Si vous utilisez un langage de programmation, vous pouvez le faire"

Quand j'ai regardé Twitter, il y avait une réponse comme "Pouvez-vous dire la même chose avec Malbolge?" En réponse à la déclaration "Si vous utilisez un langage de programmation, vous pouvez faire autre chose". J'étais un élève superficiel et j'ai demandé "Qu'est-ce que Malbolge?", Mais c'était une spécification de langage très puissante.

J'ai aussi marmonné le type "Si vous en faites un" ci-dessus, mais je suis désolé d'être une grenouille.

En regardant les spécifications du langage, je n'avais pas envie d'écrire le code moi-même, mais j'ai réalisé que la quantité d'implémentation ne serait pas si grande s'il s'agissait d'un interpréteur, alors je l'ai fait.

Vérifiez l'environnement ci-dessous. Je ne sais pas si ça marche ailleurs ... je suis désolé ...

À propos de Malbolge

S'il s'agit de pedia, les spécifications sont également décrites ci-dessous s'il s'agit de l'anglais.

Après cela, l'article suivant de Robert (id: Robe) a été très utile. Merci beaucoup.

https://robe.hatenablog.jp/entry/20060824/1156353550

Comme vous pouvez le voir sur ce qui précède, il est très difficile d'écrire ... ou y a-t-il quelqu'un qui peut écrire avec un crayon? Vous pouvez voir la difficulté en regardant Hello World (ci-dessous) posté sur pedia.

(=<`:9876Z4321UT.-Q+*)M'&%$H"!~}|Bzy?=|{z]KwZY44Eq0/{mlk**
hKs_dG5[m_BA{?-Y;;Vb'rR5431M}/.zHGwEDCBA@98\6543W10/.R,+O<

Personnellement, j'avais l'impression qu'il s'agissait d'un CPU virtuel plutôt que d'un langage. En gros les fonctionnalités que j'ai comprises.

En bref, c'est comme écrire en assembleur ou en langage machine avec du code obscurci.

Essayez de déplacer l'interprète

Si vous le faites sur un Mac, la page suivante de Takaaki Yamamoto | Kazuaki Yamamoto vous sera très utile. Merci beaucoup.

https://blog.monophile.net/posts/20150118_malbolge_hello.html

La source de l'interpréteur (langage C) est la suivante.

http://www.lscheffer.com/malbolge_interp.html

C'est un bulletin lors de l'exécution sur Mac.

J'ai essayé de faire un interprète

La création d'un interpréteur est beaucoup plus facile que la création d'un programme car le nombre d'instructions est petit et la valeur de mémoire maximale n'est pas un gros problème.

Donc je l'ai fait à peu près avec Python.

** Bien sûr, ce n'est pas parfait selon les spécifications, alors soyez prudent. Je pense qu'il y a un bug quelque part. ** **


#!/usr/bin/env python
# coding: utf-8

#
# malbolge_test.Écrit comme py
#

import sys
import hexdump


class MalbolgeInterpreterModoki:
    """
Interprète Malbolge (Modoki)
→ Veuillez me pardonner car je n'ai pas confirmé l'opération détaillée!
    """

    def __init__(self, debug_mode):
        """
Initialisation
        """
        self.debug_mode = debug_mode

        self.OP_INDEX_CODE = 0
        self.OP_INDEX_FUNC = 1

        #Répartition du code (instruction) converti par xlat1
        self.op_table = [           
            [106,   self.op_mod_d],
            [105,   self.op_jmp],
            [42,    self.op_rotate],
            [112,   self.op_crz],
            [60,    self.op_print],
            [47,    self.op_input],
            [118,   self.op_end],
            [111,   self.op_nop]
        ]

        #En regardant le code d'origine, il semble qu'un mot soit composé de 10 chiffres décimaux?
        self.BASE3_DIGITS = 10      

        #Taille de la mémoire (pas d'octets, de mots-unsigned short-)
        self.MEMORY_SIZE = 59049    

        #xlat1 est[C]C'est une table de conversion utilisée lors de la prise du code d'instruction.
        self.xlat1 = "+b(29e*j1VMEKLyC})8&m#~W>qxdRp0wkrUo[D7,XTcA\"lI"
        self.xlat1 += ".v%{gJh4G\\-=O@5`_3i<?Z';FNQuY]szf$!BS/|t:Pn6^Ha"

        #Après avoir exécuté xlat2[C]Il s'agit d'une table de conversion utilisée pour réécrire le contenu de.
        self.xlat2 = "5z]&gqtyfr$(we4{WP)H-Zn,[%\\3dL+Q;>U!pJS72FhOA1C"
        self.xlat2 += "B6v^=I_0/8|jsb9m<.TVac`uY*MK'X~xDl}REokN:#?G\"i@"

        self.cpu_reset()

    def cpu_reset(self):
        """
Initialisation des registres et de la mémoire (et des variables associées)
Cela ressemble à du BSS, mais cela n'a pas beaucoup de sens car il sera bientôt rempli ...
        """
        self.memory = [0] * self.MEMORY_SIZE
        self.A=int(0)
        self.C=int(0)
        self.D=int(0)
        self.seq = 0
        self.end = False
        self.console = ""

    def show_regs(self):
        """
Enregistrer l'affichage. Veuillez l'utiliser si nécessaire pour le débogage.
        """
        print (self.seq, ": A=", self.A, "C=", self.C, "D=", self.D)

    def dump_memory(self, max):
        """
Vidage de la mémoire. Veuillez l'utiliser si nécessaire pour le débogage.
# Cela peut être difficile sans adresse (;^ω^)
        """
        print ("memory: size=", len(self.memory), " max=", max)
        text = "["
        i = 0
        for data in self.memory:
            text += hex(data) + " "
            i += 1
            if i == max:
                break
        text += "]"
        print(text)

    def to_base3(self, data):
        """
Malbolge effectue l'arithmétique ternaire, vous devez donc créer un nombre ternaire.
Cela fait d'un nombre décimal un nombre ternaire. Je fais la pire méthode de conversion (transpiration)
        """

        #Tout d'abord, extrayez chaque chiffre des données d'origine.
        digits = []
        while True:
            digits.append(data % 3)
            if data <= 2:
                break
            data = int(data / 3)

        #Puisque les chiffres sont fixes dans Malbolge, le reste est rempli de 0.
        remain = self.BASE3_DIGITS - len(digits)
        for i in range(0, remain):
            digits.append(0)

        return digits

    def from_base3(self, digits):
        """
Malbolge effectue l'arithmétique ternaire, vous devez donc créer un nombre ternaire.
Cela transforme un nombre ternaire en un nombre décimal. Je fais la pire méthode de conversion (transpiration)
        """

        #Exemple: 123-> 3 + 2 * 3 + 1 * 9 = 18
        data = 0
        base = 1
        i = 0
        for digit in digits:
            data += digit * base
            i += 1
            if i == 1:
                base = 3
            else:
                base *= 3
        return data


    def crazy(self, x, y):
        """
Traitement arithmétique de Malbolge. Calcule deux nombres ternaires et génère un nombre ternaire
Le calcul suivant est effectué pour chaque chiffre. Puisque le nombre de chiffres est fixe, vous pouvez le décrire avec cela

        x/y   0   1   2
        ----------------
        0     1   0   0          
        1     1   0   2
        2     2   2   1

Exemple: fou(x=10, y=12) =12 (5 en décimal car c'est un nombre ternaire)
        """
        crazy_table = [
            [1, 0, 0],
            [1, 0, 2],
            [2, 2, 1]
        ]
        digits_x = self.to_base3(x)
        digits_y = self.to_base3(y)
        result = []
        data = 0

        for i in range(0, self.BASE3_DIGITS):
            data = crazy_table[digits_x[i]][digits_y[i]]
            result.append(data)

        return result

    def op_mod_d(self):
        """
Instruction MOV D= [D];
        """
        ref_D = self.memory[self.D]
        if self.debug_mode:
            print (self.seq, "C=", self.C, ":106: D = [D]; # D=", self.D, "[D]=" , ref_D)
        self.D = ref_D

    def op_jmp(self):
        """
Instruction JUMP C= [D]; jmp [D];
        """
        ref_D = self.memory[self.D]
        if self.debug_mode:
            print (self.seq, "C=", self.C, ":105: C = *D; jmp [D]; # D=", self.D, "[D]=", ref_D)
        self.C = ref_D

    def op_rotate(self):
        """
Commande de décalage vers la droite rotation_right [D]; A=D;
        """
        ref_D = self.memory[self.D]

        #Le décalage à droite réduit d'un chiffre (123 en décimal)/10 ->Comme 12)
        result = int(ref_D / 3) 

        #Déplacez le contenu du chiffre le moins significatif vers le chiffre le plus significatif (123 pour la suite ci-dessus)->Sentiment d'en faire 312)
        #19683 est un numéro ternaire 1000000000
        result = result + (ref_D % 3) * 19683 

        if self.debug_mode:
            print (self.seq, "C=", self.C, ": 42: rotate_right [D]; A=D; # D=", self.D, "[D]=", ref_D, "result=", result)
        self.memory[self.D] = result
        self.A = result

    def op_crz(self):
        """
Commande arithmétique[D] = crazy(A, [D]); A=[D]; 
        """
        ref_D = self.memory[self.D]
        result_digits = self.crazy(ref_D, self.A)
        result = self.from_base3(result_digits)

        if self.debug_mode:
            print (self.seq, "C=", self.C, ":112: [D] = crazy(A, [D]); A=[D]; # D=", self.D, "[D]=", ref_D, "A=", self.A, "result=", result)

        self.memory[self.D] = result
        self.A = result

    def op_print(self):
        """
Impression de sortie de caractère unique A; 
Je suis désolé, c'est difficile à traiter, donc je vais casser chaque personnage m(__)m
        """

        #Si vous regardez le code original, laissez-moi le diffuser
        #Il semble que ce soit correct de sortir
        #(S'agit-il d'une référence lors de la création d'une chaîne de caractères?)
        ascii = chr(self.A % 256) 

        if self.debug_mode:
            print (self.seq, "C=", self.C, ": 60: print A; # put(\"",  ascii ,"\") A=", self.A, ",", hex(self.A))
        else:
            print("put(\"",  ascii ,"\")=", self.A)
        self.console += ascii

    def op_input(self):
        """
Entrée d'entrée de caractère unique A
Je suis désolé, c'est difficile à traiter<enter>Est nécessaire m(__)m
        @ :nouvelle ligne
        # :Arrêt du système (géré comme une commande spéciale interne)
        """
        print ("(m_o_m) Sorry, please input 1 char(@=cr,#=exit) and <enet>:")
        text = input()
        if text[0] == "@":
            data = 0x0a
        elif text[0] == "#":
            print("exit this program.")
            sys.exit(0)
        else:
            data = ord(text[0])
        if self.debug_mode:
            print (self.seq, "C=", self.C, ": 47: input A; # putc=", ord(text[0]), hex(data))
        self.A = data

    def op_end(self):
        """
Fin fin;
Le programme se termine ici.
        """
        if self.debug_mode:
            print (self.seq, "C=", self.C, ":118: end;")
        print ("end of program.")
        print("console:")
        print(self.console)
        print("--------completed.")
        sys.exit(0)

    def op_nop(self):
        """
        NOP nop
        """
        if self.debug_mode:
            print (self.seq, "C=", self.C, ":111: nop;")
        return

    def execute_opcode(self, op_code):
        """
Exécute le traitement en fonction du code d'instruction
Chaque code d'instruction et son traitement sont auto.op_Parce que je l'ai listé dans le tableau
Mon travail est de comparer et de sauter
        """
        for op in self.op_table:
            if op[self.OP_INDEX_CODE] == None or op[self.OP_INDEX_CODE] == op_code:
                op[self.OP_INDEX_FUNC]()
                return
        if self.debug_mode:
            print ("illegal op code ", op_code, " : -> nop.")

    def inc_regs(self):
        """
Dans Malbolge, C et D sont définis une fois que chaque processus est terminé.+1
Lorsqu'il atteint la fin de la mémoire, il revient au début.
        """
        if self.C == self.MEMORY_SIZE-1:
            self.C = 0
        else:
            self.C += 1

        if self.D == self.MEMORY_SIZE-1:
            self.D = 0
        else:
            self.D += 1

        self.seq += 1

    def execute_1step(self):
        """
Exécute une seule instruction.
Si vous voulez faire quelque chose de bien avec l'exécution des étapes
Cela peut être pratique.
        """

        #Dans Malbolge le code en mémoire
        #Je ne le fais pas simplement.
        #Table convertie comme suit
        #Utilisez le code.
        #En d'autres termes, soyez conscient de cela
        #Il semble que vous ayez besoin d'écrire une instruction.
        ref_C = self.memory[self.C]
        t_index = (ref_C - 33 + self.C) % 94
        op_code = ord(self.xlat1[t_index])

        #Exécutez l'instruction
        self.execute_opcode(op_code)

        #Après avoir exécuté l'instruction, la mémoire sera réécrite.
        #Vous devez en être conscient lors de l'écriture d'opérations de mémoire
        #Je ne pense pas que cela devrait être fait.
        ref_C = self.memory[self.C]
        self.memory[self.C] = ord(self.xlat2[ref_C - 33])

        self.inc_regs()

    def execute(self):
        """
Exécutez le programme.
Vous pouvez soit obtenir une fin, soit mourir d'un bug
(Oh, vous pouvez quitter avec # après l'entrée)
        """
        while True:
            self.execute_1step()

    def is_this_valid_code(self, i, b):
        """
Vérification grammaticale simple
        valid_Vérifiez s'il ne s'agit pas des instructions de la liste.
Si cela ne fonctionne pas, il se terminera.
        """
        #NG sauf pour les commandes suivantes (commandes?).
        valid_list = "ji*p</vo"
        #( x - 33 + i ) % 94
        t_index = (b - 33 + i) % 94
        c = self.xlat1[t_index]
        for valid in valid_list:
            if valid == c:
                return 
        print("Illegal opcode= \"" + c + "\"(org:"+ str(b) + ")")  
        sys.exit(1) 

    def load_code(self, path):
        """
Extrayez le programme en mémoire.
        """

        #Initialisation
        self.cpu_reset()
        cnt = 0

        #Chargement du code
        with open(path, "rb") as f:
            data = f.read()
            for b in data:

                #Ignorer les sauts d'espace
                if b == 0x20 or b == 0x0d or b == 0x0a:
                    continue

                #Vérification grammaticale simple. Si ça ne marche pas, quittez
                self.is_this_valid_code(cnt, b)

                #Ecrire le programme en mémoire
                #
                #Le modèle de mémoire de Malbolge
                #C'est court non signé
                #Le programme est un caractère non signé.
                #Qu'est-ce que tu dis
                # memory(0) <- code(0)
                # memory(1) <-Mouche
                # memory(2) <- code(1)
                # memory(3) <-Mouche
                #ça ira. Cela semble inutile s'il est emballé
                #Puisqu'il s'agit de Python, je viens de le mettre dans un tableau ...
                self.memory[cnt] = b
                cnt += 1

        #En langage C, le reste de la mémoire doit être rempli de 0.
        #Malbolge ne l'a pas permis, comme ça
        #Vous devez calculer Crazy et le mettre.
        while cnt < self.MEMORY_SIZE:
            x = self.memory[cnt - 2]
            y = self.memory[cnt - 1]
            self.memory[cnt] = self.from_base3(self.crazy(x,y))
            cnt += 1

if __name__ == "__main__":

    #Vérification des arguments
    if len(sys.argv) < 2 or (len(sys.argv) == 3 and sys.argv[1] != "debug"):
        print ("python malbolge_test.py (debug) <program file>")
        sys.exit(1)

    #Définir la présence ou l'absence du mode débogage et le fichier programme à partir de l'argument
    debug = False    
    path = sys.argv[1]
    if len(sys.argv) == 3:
        debug = True
        path = sys.argv[2]

    #Exécutez Malbolge
    m = MalbolgeInterpreterModoki(debug)
    m.load_code(path)   #Extraire le programme en mémoire
    m.execute()         #Exécution du programme

Dans cet article, ce qui précède sera traité comme malbolge_test.py au moment de l'explication.

Comment utiliser

Il existe deux manières de l'utiliser.

Mode normal

C'est le mode à exécuter normalement. Cependant, les points suivants sont différents entre mon manque de capacité et mon omission (sueur).

Si vous pensez que c'est caché ici, j'aimerais que vous corrigiez la source (sueur)

Un exemple d'exécution est le suivant (hello_ma est un programme Hello world Malbolge)

 python ./malbolge_test.py hello_ma <enter>

Mode débogage

Il s'agit d'un mode dans lequel l'état d'exécution de chaque instruction peut être retracé en plus de ce qui précède. Cependant, veuillez noter qu'il sera imprimé sans bosse, donc il sera doublé.

De même, un exemple d'exécution. Ajoutez le débogage.

 python ./malbolge_test.py debug hello_ma <enter>

État du contrôle de fonctionnement

Les opérations suivantes ont été confirmées. Pour le dire autrement, je ne fais que cela. ..

À propos du code

Merci à ceux qui sont pleins de compassion que vous pouvez voir même un putain d'interprète (genre) fait par une telle défaite. Si vous le regardez de la principale ci-dessous, je pense que vous pouvez le suivre dans un flux.

Au fait, je ne savais pas d'après les spécifications, mais parfois je le trouvais à partir du code de l'interpréteur.

Comment Hello World est géré.

En fait, c'est le sentiment que je voulais le plus faire. J'ai essayé de visualiser comment cette source compliquée et mystérieuse est réellement traitée.

Puisqu'il est long de tout faire, exécutez-le en mode débogage et réimprimez le journal jusqu'au milieu (jusqu'à "Hell").


0 C= 0 :106: D = [D]; # D= 0 [D]= 40
1 C= 1 :112: [D] = crazy(A, [D]); A=[D]; # D= 41 [D]= 93 A= 0 result= 29524
2 C= 2 :112: [D] = crazy(A, [D]); A=[D]; # D= 42 [D]= 75 A= 29524 result= 72
3 C= 3 : 60: print A; # put(" H ") A= 72 , 0x48
4 C= 4 :112: [D] = crazy(A, [D]); A=[D]; # D= 44 [D]= 90 A= 72 result= 29506
5 C= 5 :112: [D] = crazy(A, [D]); A=[D]; # D= 45 [D]= 89 A= 29506 result= 35
6 C= 6 :112: [D] = crazy(A, [D]); A=[D]; # D= 46 [D]= 52 A= 35 result= 29507
7 C= 7 :112: [D] = crazy(A, [D]); A=[D]; # D= 47 [D]= 52 A= 29507 result= 44
8 C= 8 :112: [D] = crazy(A, [D]); A=[D]; # D= 48 [D]= 69 A= 44 result= 29541
9 C= 9 : 60: print A; # put(" e ") A= 29541 , 0x7365
10 C= 10 :112: [D] = crazy(A, [D]); A=[D]; # D= 50 [D]= 48 A= 29541 result= 73
11 C= 11 :112: [D] = crazy(A, [D]); A=[D]; # D= 51 [D]= 47 A= 73 result= 29552
12 C= 12 :112: [D] = crazy(A, [D]); A=[D]; # D= 52 [D]= 123 A= 29552 result= 60
13 C= 13 :112: [D] = crazy(A, [D]); A=[D]; # D= 53 [D]= 109 A= 60 result= 29548
14 C= 14 : 60: print A; # put(" l ") A= 29548 , 0x736c
15 C= 15 : 60: print A; # put(" l ") A= 29548 , 0x736c

Jusqu'à présent, il semble que je fais de mon mieux avec des calculs fous et en créant la chaîne de caractères souhaitée. Si vous allez à l'arrière, il semble que vous utilisez également la commande jump.

Je n'ai pas utilisé la commande de rotation. À propos, 99 bouteilles de bière ont également utilisé Rotate. Vous pouvez bien écrire ce programme, n'est-ce pas? J'ai juste du respect pour lui. En tant que niveau débutant, moi.

Bonus: un petit cas addictif

En fait, il y a des cas où j'étais accro au débogage. Il s'agit d'une "copie de l'entrée (auteur: Lou Scheffer)" dans le site de Rober. Réimprimez le code.


D'BA@?>=<;:9876543210/.-,+*)('&%$#"!~}|{zyxwvutsrqponmlkji
hgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/
.-,+*)('&%$#"!~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTS
RQPONMLKJIHGFEDC&_Suss Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo
Suss Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo
Suss Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo
Suss Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo
Suss Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo Soo

(Réimprimé à partir du site de Rober (URL ci-dessous))

Cela fonctionne avec l'interpréteur d'origine, mais lorsque je le fais avec mon interprète, j'obtiens l'erreur suivante:

Illegal opcode= "~"(org:239)

J'ai pensé que ça me ferait mal de toucher mon propre interprète à mi-chemin du niveau débutant, mais il semble que même l'interprète d'origine se comporte de manière inattendue.

J'ai modifié l'original comme suit. J'ai un contrôle de plage dans la fonction exec.


#if 1 /*Partie supplémentaire: essayez d'ajouter une vérification de plage*/
    if (mem[c] -33 >= sizeof(xlat2))
    {
      printf("!!!!!!!!!!!!!!! %d >= %lu \n", mem[c] -33, sizeof(xlat2));
      return;
    }
#endif
    mem[c] = xlat2[mem[c] - 33];

    if ( c == 59048 ) c = 0; else c++;
    if ( d == 59048 ) d = 0; else d++;

Lorsqu'il est exécuté avec ceci, c'est comme suit.

!!!!!!!!!!!!!!! 156 >= 95

En d'autres termes, il semble qu'il s'établisse en accédant à des données en dehors de la plage de la table de conversion appelée xlat2. Je me suis demandé ce qui se passerait dans ce cas.

Je suis un débutant à Malbolge, donc je n'ai aucune connaissance à ce sujet. Veuillez également pardonner qu'il est possible que certains des éléments ci-dessus soient mal compris.

finalement

Alors, donnons Malbolge lorsque le montage est pris, en disant: "Si vous faites un langage de programmation, le reste sera géré" (sourire amer).

Oh, c'est une blague, n'est-ce pas?

Recommended Posts

Nous recommandons Malbolge à ceux qui disent "Si vous utilisez un langage de programmation, vous pouvez le faire"
La programmation parallèle est-elle difficile, et si oui, que pouvez-vous y faire?
Si vous écrivez TinderBot en Python, elle peut le faire
Ce que vous pouvez faire avec des compétences en programmation
Résumons ce que vous voulez faire.
Si vous les gars dans la cuisine de portée pouvez le faire avec une marge ~ ♪
Que faire lorsque le shell pipenv devient impossible
Que faire quand "Aucun noyau pour le langage python trouvé" apparaît dans Hydrogen
Que faire si vous obtenez "(35, 'Erreur de connexion SSL')" dans pycurl (l'un d'entre eux)
Vous pouvez le faire en 3 minutes! Comment créer un code QR (GIF) fonctionnel!