Interprète auto-interprète


25

Sur la base d'un commentaire de George Edison à cette question , écrivez le plus petit interprète auto-interprète.

  • Vous pouvez utiliser la langue de votre choix.
  • Les langues vides ne comptent pas. Votre programme doit comporter au moins deux caractères.
  • Le programme n'a pas besoin d'interpréter la langue entière , juste un sous-ensemble complet de Turing de fonctionnalités linguistiques (qui contient l'interpréteur).
  • Les quines ne comptent pas.
  • N'utilisez pas la fonction intégrée de votre langue evalou l'équivalent. Il en va de même apply, etc.

1
(Hmm .. je devrais faire quelque chose avec /usr/bin/cat) qu'en est-il de Turing-complétude?
Ming-Tang

@ SHiNKiROU: Merci, je n'y ai pas pensé comme un test. Mis à jour.
Hoa Long Tam

En relation: Langue avec le plus petit interprète écrit en lui-même sur Stack Overflow, bien qu'il y ait peu (une seule?) Réponses qui respectent réellement les règles données ici.
dmckee

1
Devons-nous réécrire cet analyseur sexp de Scheme, ou pouvons-nous considérer que la langue de l'hôte est correcte?
JB

@JB: Les utilitaires de traitement de chaîne du langage sont très bien, y compris l' sexpanalyseur.
Hoa Long Tam

Réponses:


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: Poussez des mappages de caractère à instruction simples, puis repliez-les. Cela réduit de moitié la taille du code par cas (il y a 18 cas), mais coûte 30 caractères pour faire le pliage.

Ceci est une autre de mes langues construites (interprète de base hébergé sur Gist ). Il est unique en ce sens que le langage réifie les fragments de code. Autrement dit, les chaînes d'instructions dans ce langage basé sur la pile sont utilisées dans le même sens que les structures de données ou les fermetures dans d'autres langues:

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.

L'interpréteur construit un fragment de code de l'ensemble du programme avant de l'exécuter, il peut donc également être considéré comme un compilateur. Pour cette raison, l' empilement de l'interpréteur n'entraîne pas de surcharge d'exécution exponentielle.

L'interpréteur dérive tous ses opérateurs directement de l'interpréteur hôte. Cependant, il effectue l'analyse par lui-même, donc la majorité du code n'est que des séquences qui traduisent les caractères dans leurs littéraux de code respectifs. Ce n'est pas la même chose que l'utilisation eval, mais cela révèle à quel point toute implémentation de langage de programmation dépend de la sémantique de son langage / architecture hôte.


Référence du langage:

Obtenez l'interprète ici

Blocs

  • ( ... )

    Créez un "bloc", qui est en fait une liste d'instructions sans contexte. En interne, il pourrait même s'agir d'un code machine.

  • bloc $

    Appelez un bloc. L'appelé reçoit la pile globale, qui comprend le bloc appelé.

  • valeur ^

    Soulevez une valeur. À savoir, le transformer en un bloc qui pousse cette valeur.

    Exemple :

       1 ^
    == (1)
    
  • block1 block2 &

    Joignez deux blocs, en formant un qui s'exécute les deux en séquence.

    Exemple :

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

Manipulation de pile

  • n c

    Copiez la nième valeur de la pile.

    Exemple :

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

    Cueillez la nième valeur de la pile (supprimez-la et amenez-la au premier plan).

    Exemple :

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

    Supprimez n valeurs de la pile. 0dest un no-op.

    Exemple :

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

Opérateurs relationnels

  • ab (on_true) (on_false) =

    Testez si a est égal à b. Consommez tout sauf le premier argument, et appelez on_true ou on_false. Si un argument est nul et que l'autre est de tout autre type, le résultat sera faux. Sinon, a et b doivent être des entiers.

    Exemple :

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

    Testez si a est inférieur à b. a et b doivent être des entiers.

    Exemple :

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

    Testez si a est supérieur à b. a et b doivent être des entiers.

    Exemple :

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

    Testez si lo <= a <= hi. a, lo et hi doivent être des entiers.

    Exemple :

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

E / S

  • c .

    Mettez le personnage c (en le consommant de la pile).

  • ,

    Obtenez un personnage et poussez-le dans la pile. Si la fin du fichier est atteinte, -1 est poussé.

  • c !

    Oubliez un personnage. Tout comme ungetc en C, un seul pushback est autorisé.

Littéraux entiers

  • 'c

    Poussez le caractère c.

  • [0-9] +

    Appuyez sur un entier décimal.

Arithmétique

  • un B +
  • un B -
  • un B *

    Additionnez / soustrayez / multipliez deux nombres.

    Exemple :

       3 5 + 7 3 + *
    == 80
    
  • un B /

  • un B %

    Division et module. Contrairement à C, ils arrondissent vers l'infini négatif.

Divers

  • code #commentaire de

    Le #personnage commente tout jusqu'à la fin de la ligne.

  • )

    Utilisé pour terminer les blocs. Peut également être utilisé pour terminer l'ensemble du programme.

  • Tous les autres personnages sont ignorés.


24

Calcul lambda binaire, 232 bits (29 octets)

0101000110100000000101011000000000011110000101111110011110000101110011110000001111000010110110111001111100001111100001011110100111010010110011100001101100001011111000011111000011100110111101111100111101110110000110010001101000011010

Voir http://en.wikipedia.org/wiki/Binary_lambda_calculus#Lambda_encoding pour plus de détails


2
Pourquoi n'est-ce pas la réponse acceptée D: BLC est incroyable!
cat

Pouvez-vous l'expliquer?
PyRulez

1
Malheureusement, Wikipedia a supprimé la page de calcul binaire Lambda. Ma page tromp.github.io/cl/cl.html renvoie à une copie préservée et à un article que j'ai écrit expliquant le fonctionnement de l'interprète.
John Tromp

13

Je ne peux pas prendre le crédit pour celui-ci , mais j'ai pensé partager cet incroyable:

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 est un langage trivial basé sur la pile de spaghetti que j'ai créé spécifiquement pour ce défi. L'interpréteur de base est blockscript.c .

Exemple de programme (imprime les 15 premiers numéros de Fibonacci):

{[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!
;

L'interprète lit à la fois le code source et l'entrée du programme à partir de l'entrée standard, dans cet ordre. Cela signifie que pour exécuter un interpréteur au sein d'un interprète au sein d'un interprète, copiez et collez simplement:

# 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!
;

Comme le film Inception , vous ne pouvez pas aller plus loin que trois niveaux. Ce n'est pas une question de temps, mais d'espace. BlockScript perd énormément de mémoire, et cela a à voir avec la façon dont le langage lui-même est conçu.


Référence du langage:

Obtenez l'interprète ici

Dans BlockScript, la «pile» n'est pas un tableau qui est écrasé par des opérations ultérieures comme vous en avez l'habitude. Il est en fait implémenté comme une liste liée immuable et une pile persiste pendant la durée du programme. De plus, aucun opérateur (sauf@ ) ne supprime les valeurs de la pile. Cependant, les modifications de pile n'affectent que le bloc dans lequel elles se produisent.

Sélection de valeur

  • a à travers z

    Récupérez l'élément 0-25th de la pile et poussez-le dans la pile. afait référence à la tête, ou au dernier élément poussé, de la pile.

  • A à travers Z

    Récupérez le 0-25ème élément de l'image actuelle et poussez-le dans la pile.

  • [

    Ouvrez un "cadre" pour sélectionner les éléments de la référence de pile (voir ci-dessous) sur la tête de la pile. [ne nécessite pas de correspondance ], mais les cadres ont une portée lexicale. Dans BlockScript, la "portée" est déterminée par des accolades ( {... }) qui forment des blocs. Ainsi, l'ouverture d'un cadre à l'intérieur d'un bloc n'aura aucun effet sur le code à l'extérieur du bloc.

  • ]

    Fermez l'image actuelle, en revenant à l'image précédente (le cas échéant).

Blocs

  • { ... }

    Créez un "bloc" et poussez-le dans la pile. À l'intérieur d'un bloc, la pile commencera à ce qu'elle était avant le bloc, sauf que la pile de l'appelant sera poussée en haut. Les piles sont persistantes et immuables dans BlockScript, donc les blocs sont des fermetures. L'idiome {[signifie ouvrir un bloc, puis ouvrir un cadre pour commencer à sélectionner des arguments (en utilisant Athrough Z). La valeur de retour d'un bloc est la tête de la pile lorsqu'elle }est atteinte.

    Exemple:

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

    Cela imprime 123BCD123DCB123BCD123DCB…. Les lettres minuscules font référence aux valeurs de la pile, tandis que les lettres majuscules font référence aux arguments (car le cadre est défini sur la pile de l'appelant). A!prend la tête de l'appelant (qui est garanti d'être le bloc appelé) et l'appelle. Si vous vous demandez pourquoi il s'inverse à BCDchaque fois, c'est parce qu'il B. C. D.pousse ces arguments dans l'ordre inverse juste avant que le bloc ne s'appelle.

  • !

    Appelez un bloc. Poussez la valeur de retour dans la pile.

Références de pile

  • &

    Créez une référence de pile et poussez-la dans la pile. Considérez cela comme des «super-inconvénients», car il prend efficacement chaque élément de la pile et en forme un «tuple». L'idiome &[signifie que tout a, b, cappelés avant peut maintenant être consulté avec A, B, C(pour le reste du bloc ou jusqu'à ce que] l' on rencontre).

    En partie parce qu'il &capture plus de valeurs qu'il n'en a généralement besoin, BlockScript perd de la mémoire par conception.

  • @

    Basculez vers la pile pointée par la référence de pile a. Cet opérateur est plutôt bizarre, mais l'auto-interprète BlockScript l'utilise plusieurs fois pour éviter d'avoir à pousser deux fois les mêmes arguments. Les effets de @(ou de toute opération de pile, d'ailleurs) sont limités au bloc dans lequel il est invoqué. De plus, le cadre n'est pas affecté par @, donc le cadre peut être utilisé pour saisir les valeurs dont vous avez besoin après avoir changé de pile.

Expression conditionnelle

  • ? <sur vrai> : <sur faux>

    Expression conditionnelle, tout comme l'opérateur ternaire en C. Autrement dit, si aest "vrai" (c'est-à-dire différent de zéro entier), alors faites <sur vrai> , sinon faites <sur faux> .

E / S

Remarque: L'entrée et la sortie se font en UTF-8. Un "caractère" est un entier correspondant à un index Unicode.

  • ,

    Obtenez le prochain caractère d'entrée et poussez-le dans la pile. Si la fin de l'entrée est atteinte, appuyez sur -1 à la place.

  • .

    Sortez le personnage sur la tête de la pile.

Littéraux entiers / caractères

Remarque: les entiers et les caractères sont la même chose dans BlockScript.

  • 'c

    Poussez le caractère c.

  • [0-9] +

    Appuyez sur un entier décimal.

Arithmétique

Ces opérateurs ne fonctionnent que sur des valeurs entières.

  • +Calculer b+ a(en poussant le résultat, mais sans ignorer aucune valeur).
  • -Calculer b- a.
  • *Calculer b* a.
  • /Calculer b/ a(division entière; arrondit vers l'infini négatif).
  • %Calculer le b% a(module entier; arrondi vers l'infini négatif).

Opérateurs relationnels

Ces opérateurs ne fonctionnent que sur des valeurs entières.

  • <Si best inférieur à a, appuyez sur 1, sinon appuyez sur 0.
  • >
  • =

Divers

  • # Commentaire à la fin de la ligne
  • Le programme doit se terminer par ;
  • Tous les autres personnages sont ignorés.

2
Christ. Vous souhaitez partager les spécifications comme vous l'avez fait pour CI?
Casey

@Casey: Ajout d'une référence.
Joey Adams

1
Seriez-vous intéressé à savoir que vous rêvez? ... Au niveau 4.
Mateen Ulhaq

3

Zozotez LISP : 414

les sauts de ligne ajoutés pour obtenir un joli bloc ne sont pas nécessaires et ne sont pas comptés.

((\(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)))

En théorie, il devrait être capable de s'exécuter lui-même, mais comme l'interpréteur d'origine est un binaire BrainFuck et lui-même un interprète, je n'ai pu tester que chaque partie. Une fois donné lui-même et une expression simple, (p p)je pense qu'il a besoin de plus de temps que les 40 minutes que j'ai attendu jusqu'à présent et j'utilise mon jeûnejitbf pour l'exécuter qui (mal) utilise Perl Inline-C pour exécuter du code C à la volée.

Il est impossible d'implémenter l'ensemble de Zozotez dans Zozotez car il n'a pas de moyen de muter contre et :(setq / define) en a besoin pour mettre à jour les liaisons. Je n'ai pas non plus implémenté d'argument explicite begin / progn ou & rest, des macros et des arguments d'impression spéciaux car je ne l'ai pas utilisé dans l'interpréteur. J'ai inclus p(print) même si je ne l'utilise pas, les programmes doivent donc explicitement imprimer leurs calculs comme l'interpréteur d'origine.

Le même non golfé:

;; 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 + (probablement non concurrent), 2 octets

+I

Il n'y a aucun moyen d'écrire un interpréteur auto-interprète dans ce langage basé sur HQ9 + sans utiliser I, qui exécute un interpréteur intégré qui traite STDIN.


1
Nulle part dans les règles, il n'est dit que les auto-interprètes intégrés sont interdits. Il dit pour eval, qui est pour les expressions, pas pour les programmes.
Erik the Outgolfer

Comment calcule-t-on des nombres premiers dans cette langue?
pppery

@ppperry Xest censé rendre le langage Turing complet (donc capable de calculer des nombres premiers) d'une manière dépendante de l'implémentation.
user8397947

Selon la page Esolang, la Xcommande de l'interpréteur Perl perturbe aléatoirement le programme et l'exécute, ce qui signifie que l'on ne peut pas utiliser cette commande pour calculer de façon déterministe des nombres premiers. Pouvez-vous me donner un exemple d'interprète qui vous permet d'utiliser Xde la manière que vous avez spécifiée?
pppery

@ppperry Apparemment, l'interprète écrit en Perl est le seul interprète, donc non. Aussi, que se passe-t-il s'il existe un programme qui calcule des nombres premiers lorsqu'ils sont "randomisés" avec une valeur spécifique?
user8397947

0

Système de fichiers simultané Befunge 98 - 53 \ 18 octets (triche presque certainement)

Interprète complet de 53 octets sans restrictions (bien que je n'ai pas testé d'interactions de synchronisation compliquées impliquant le fractionnement et le conditionnement IP):

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

Lit l'entrée d'un fichier nommé a et l'exécute. Il n'est pas spécifié dans les règles que nous ne pouvons pas utiliser de code auto-modifiant.

Interprète de 18 octets qui ne permet pas le wrapping (le déplacement IP d'un bord du code et en commençant par le bord opposé):

]210'ai@
t
><
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.