Interprétez votre langue, mais pas vous-même?

21

Il existe de nombreux défis qui disent "interpréter X", où X est un langage simple. À mon avis, c'est beaucoup trop ennuyeux. Pour donner à toutes les personnes qui procrastinent sur Internet quelque chose d'intéressant à faire, vous pouvez essayer de relever ce défi:

Défi

Choisissez une langue $LANG. $LANGpeut être n'importe quel langage de programmation complet turing ou un sous-ensemble complet turing d'un langage de programmation. Sachez que si vous omettez une caractéristique de votre langue $LANGpour l'interprétation, vous ne devez pas non plus l'utiliser pour votre propre programme, car votre soumission doit également être écrite $LANG.

Écrivez un compilateur / interprète pour l' $LANGécrire $LANG. Vous pouvez utiliser toutes les fonctionnalités (y compris evalet les amis) de votre langue qui sont disponibles pour écrire ce compilateur. Pour rendre la tâche plus difficile, il y a une restriction: votre programme devrait être capable d'interpréter / compiler tous les programmes valides à l' $LANGexception de votre interprète / compilateur lui-même. S'il se produit que le programme à interpréter / compiler est votre interprète ou compilateur lui-même (quel que soit le nom de fichier), votre programme doit faire quelque chose de complètement indépendant de la fonctionnalité d'un interprète ou d'un compilateur (comme l'écrochage ou l'impression Hello, world!).

Pour rendre cette tâche encore plus complexe, votre programme ne doit pas lire sa propre source lors de la compilation ou de l'interprétation.

Caractéristiques

  • Cette tâche est le golf de code. La soumission avec le moins de caractères qui est correcte gagne. En cas d'égalité, la solution soumise en premier l'emporte.
  • Votre programme / script doit lire le programme à interpréter à partir d'un fichier. Vous pouvez coder en dur son chemin et son nom. Lorsque le fichier est lu, vous pouvez soit le compiler dans un autre fichier (qui doit être exécutable sur votre système) soit l'exécuter directement. S'il $LANGmanque des capacités de lecture de fichiers, vous pouvez choisir une autre façon de lire le code qui convient $LANG. Vous ne pouvez pas choisir $LANGcomme sous-ensemble d'une autre langue mais avec les capacités de lecture de fichiers supprimées.
  • Les règles habituelles de code-golf s'appliquent. C'est-à-dire: votre langage personnel que vous avez créé juste pour résoudre ce défi est interdit, si la solution devient triviale en l'utilisant (comme la définition d'un programme à un seul caractère qui implémente exactement la solution). L'abus de règles est encouragé.
FUZxxl
la source
Sommes-nous autorisés à définir un langage pour cela, tant qu'il est terminé?
Cruncher
@Cruncher Oui, vous l'êtes. Voir la dernière puce des spécifications pour plus de détails.
FUZxxl

Réponses:

8

Rubis, 63

b=$<.read
t="b=$<.read\nt=%p\nb!=t%%t&&eval(b)"
b!=t%t&&eval(b)
Lowjacker
la source
Réponse acceptée tant qu'il n'y a pas de solution plus petite.
FUZxxl
11

Perl, 89 caractères, pas de triche

$_=q($_=q(Q);s/Q/$_/;($q=join"",<>)eq$_?die:eval$q);s/Q/$_/;($q=join"",<>)eq$_?die:eval$q

Notez que ce code est extrêmement pointilleux sur ce qui compte comme "lui-même". En particulier, il ne se reconnaîtra pas s'il y a des retours à la ligne de fin ou d'autres espaces blancs supplémentaires dans l'entrée. Pour le tester, enregistrez-le dans un fichier nommé (par exemple) unquine.plet procédez comme suit:

$ perl unquine.pl unquine.pl
Died at unquine.pl line 1, <> line 1.

Se souvenir du unquine.pl fichier doit faire exactement 89 octets, ni plus, ni moins. L'exécuter avec un autre script Perl en entrée exécute simplement l'autre script, comme il se doit:

$ perl unquine.pl hello.pl
Hello, world!

Comme son nom l'indique, l'implémentation est basée sur un quine - en particulier, celui-ci:

$_=q($_=q(Q);s/Q/$_/);s/Q/$_/

Ce code est $_égal à lui-même; le reste du programme (qui, bien sûr, doit être dupliqué à l'intérieur $_) se compare simplement $_à l'entrée, meurt si elles correspondent et évalue l'entrée autrement.

Ilmari Karonen
la source
Vous pouvez remplacer cette &&/ ;paire par un ternaire (un caractère éteint, doublé par quining). Excellente idée et mise en œuvre!
JB
@JB: Bonne prise! Jusqu'à 89 caractères maintenant.
Ilmari Karonen
5

GolfScript, 30 caractères

{`".~"+"#{$<.read}".@=!{~}*}.~

Ce programme lit le contenu d'un fichier nommé sur la ligne de commande et, s'il n'est pas exactement égal au code ci-dessus, l'interprète comme GolfScript. Si l'entrée est exactement égale au code ci-dessus, elle sera simplement imprimée inchangée (sauf pour une nouvelle ligne ajoutée à la fin).

Il s'agit d'une adaptation assez simple de ce programme d'auto-identification . Plus précisément:

  • { } est un littéral de bloc de code dans GolfScript.
  • .~, appliqué à un bloc de code, duplique le bloc et exécute la copie.

À l'intérieur du bloc de code:

  • ` stringifie la copie du bloc de code.
  • ".~"+y ajoute les caractères .~, donnant une chaîne contenant le code source du programme.
  • "#{$<.read}"est un hack documenté qui permet l'exécution de code Ruby dans GolfScript. Dans ce cas, il exécute la déclaration Ruby $<.read(volée sans vergogne à la solution Ruby de Lowjacker ), qui lit et renvoie le contenu de tous les fichiers spécifiés sur la ligne de commande. Ce hack est nécessaire car GolfScript lui-même ne fournit aucune capacité d'E / S de fichier explicite.
  • .@ duplique et mélange les éléments au-dessus de la pile afin que la pile contienne deux copies du contenu du fichier suivies du code source de ce programme.
  • =! compare les deux premiers éléments de la pile (c'est-à-dire le contenu du fichier et la source), renvoyant 1 s'ils sont différents et 0 s'ils sont les mêmes.
  • {~}*évalue la copie restante du contenu du fichier en tant que code GolfScript, mais uniquement si le résultat de la comparaison est 1. (Techniquement, il exécute le bloc de code {~}autant de fois que le donne le numéro sur la pile, c'est-à-dire 0 ou 1 fois. À l'intérieur le bloc, ~est l'opérateur eval GolfScript.)

Ps. Si la lecture du code à exécuter à partir de stdin est autorisée, ce défi peut être résolu en 21 caractères sans avoir à débourser pour Ruby:

{`".~"+1$=!{""\~}*}.~

Ce programme lira une chaîne d'entrée depuis stdin et, s'il ne correspond pas à sa propre source, l'exécute (avec une entrée vide). Comme le programme ci-dessus, les entrées qui correspondent à la source sont simplement renvoyées en écho.

Ilmari Karonen
la source
Cela a l'air bien, mais il ne semble pas que vous lisiez l'entrée d'un fichier.
FUZxxl
Corrigé, maintenant il lit à partir d'un fichier (exactement) comme la solution de Lowjacker.
Ilmari Karonen du
5

Python, 167 130 118 octets

C'est ma première tentative de golf, alors voilà! Il interprète n'importe quel programme sauf lui-même

Version améliorée:

i=open(raw_input()).read();q='i=open(raw_input()).read();q=%s;i==q%%repr(q)and a;exec(i)\n';i==q%repr(q)and a;exec(i)

S'il se récupère alors il barfs avec:

Traceback (most recent call last):
  File "pygolf.py", line 1, in <module>
    i=open(raw_input()).read();q='i=open(raw_input()).read();q=%s;i==q%%repr(q)and a;exec(i)\n';i==q%repr(q)and a;exec(i)
NameError: name 'a' is not defined

Je pense que cette solution fonctionne à peu près de la même manière que celle d'Ilmari Karonen, l'idée de base est quelque chose comme:

input = read_some_file()
if input == some_quine()
    barf()
interpret(input)

La quine que j'ai utilisée était basée sur celle-ci:

(lambda x: x + repr((x,)))('(lambda x: x + repr((x,)))',)

Mais depuis, j'ai réalisé qu'un quine beaucoup plus court est:

q='q=%s;q%%repr(q)';q%repr(q)

Et cela peut être encore plus court si vous autorisez le shell python interactif, auquel cas vous pouvez faire:

'%s;_%%repr(_)';_%repr(_)

Étant donné que python n'a pas un moyen court d'obtenir des arguments de ligne de commande, je suis allé avec raw_input () (qui est encore assez long, mais pas aussi longtemps que

import sys;sys.argv[1]

L'utilisation est:

echo "foobar.py" | python quinterpretter.py

ou

python quinterpretter.py
<type filename and hit enter>

J'ai trouvé un quine plus court à utiliser, mais voici mon ancienne version (pour la postérité):

i=open(raw_input()).read();a if i==(lambda x,y:x+repr((x,y))+y)('i=open(raw_input()).read();a if i==(lambda x,y:x+repr((x,y))+y)', ' else 1;exec(i)\n') else 1;exec(i)
Gordon Bailey
la source
Remplacez le% s par% r et supprimez le repr. % r signifie brut et c'est fondamentalement la même chose.
Loovjo
4

Je ne peux pas lire exactement à partir d'un fichier en utilisant Javascript (ok, je pourrais, en utilisant la chose HTML5 FileReader, mais cela rend les choses beaucoup plus compliquées que ce dont j'ai besoin). Il s'agit donc d'une fonction qui accepte un programme Javascript en tant que chaîne et l'exécute.

Ce n'est probablement pas aussi golfé qu'il pourrait l'être, mais le voici quand même:

Javascript, 252

function c(p){q='\"';s='\\';a="function c(p){q='\"';s='\\';a=%;a=a.slice(0,17)+s+a.slice(17,24)+a[23]+a.slice(24);a=q+a.replace('%',q+a+q)+q;alert(a);}";a=a.slice(0,17)+s+a.slice(17,24)+a[23]+a.slice(24);a=a.replace('%',q+a+q);alert(a);if(p!=a)eval(p)}

Faites-moi savoir si quelqu'un connaît une meilleure technique pour former une quine en Javascript.

Peter Olson
la source
1
J'ai posté une solution JS de 135 caractères ci-dessous, basée sur votre code et ma solution Perl. +1 pour l'inspiration!
Ilmari Karonen
2
read p<p;read c<c;[ "$p" = "$c" ]||. ./c

45 caractères de sh (shell POSIX). Le code à exécuter doit être dans le fichier./c .

Le code de l'interprète lui-même doit être dans le fichier ./p , donc je suppose que j'ai un peu triché, bien que le défi ne semble pas l'interdire. Ou est-ce que cela disqualifierait mon «langage» d'être un «langage de programmation complet»?

En utilisant un outil qui est normalement un exécutable externe, mais qui pourrait théoriquement être intégré au shell, le code peut être raccourci:

cmp -s p c||. ./c

Cela fait 18 caractères, et le -s bit est juste pour supprimer une ligne qui autrement serait toujours imprimée pour les programmes valides (non auto).

Et puis, vous pouvez toujours créer une version du langage shell qui fait ce qui précède avec une syntaxe plus concise.

Et puis, vous pouvez toujours créer un programme qui, lorsque l'entrée se compose d'un seul "." --ou l'enfer, la chaîne vide - évalue le contenu d'un autre fichier comme du code normal, et appelle cela un langage de programmation. La chaîne vide serait donc votre solution au défi, dans le langage que vous avez créé. En fait, voici un interprète pour une telle langue:

read code; if [ "$code" ]; then eval "$code"; else . ./othercode; fi

En utilisant le langage interprété par le script ci-dessus, la solution est la chaîne vide. Et l'emplacement du code n'a plus besoin d'être codé en dur.

Problème?

TaylanUB
la source
2
Le défi dit que "votre programme ne doit pas lire sa propre source".
Ilmari Karonen
Darnit, perdu du temps alors. Et je vois même que vous ne devez pas utiliser des fonctionnalités que vous omettez. Cela irait à l'encontre de la fonction de chaîne vide. Là encore, l'interpréteur doit omettre / changer de fonctionnalité si le code du compilateur / interprète lui-même provoque un comportement différent dans le nouveau langage. En tout cas, je me suis amusé à écrire l'erreur.
TaylanUB
@TaylanUB Eh bien, vous devez en fait interpréter tous les programmes $ lang valides à l'exception de l'interpréteur lui-même.
FUZxxl
@FUZxxl Oui, le langage "sh + chaîne vide" est par ailleurs équivalent à sh (si le code n'est pas la chaîne vide), et le programme de chaîne vide qui y est écrit interprète également le code sh (qui doit être inséré ./othercode), et ne rien lorsque le code est la chaîne vide. Je n'aurais pas dû appeler le fichier ./othercode, c'est trompeur; c'est juste le code que l'interprète écrit dans le langage de chaîne vide interprétera.
TaylanUB
2

JavaScript, 135 caractères

function c(p){q='function c(p){q=%27Q%27;p!=unescape(q).replace(/Q/,q)?eval(p):alert()}';p!=unescape(q).replace(/Q/,q)?eval(p):alert()}

La solution JavaScript de Peter Olson m'a inspiré pour essayer de porter ma solution Perl sur JS. Comme sa solution, ce code définit une fonctionc qui accepte une chaîne, et l'évale si elle n'est pas égale au code ci-dessus.

Il m'a fallu un certain temps pour trouver un bon moyen de faire face à l'absence de délimiteurs de chaînes équilibrés dans JavaScript, jusqu'à ce que je trouve avec le recul la solution évidente: unescape() .

Idéalement, mon code ne contient aucune barre oblique inverse ni guillemets doubles, il peut donc être stocké en toute sécurité dans des chaînes entre guillemets doubles. Cela permet de tester facilement:

e = "function c(p){q='function c(p){q=%27Q%27;p!=unescape(q).replace(/Q/,q)?eval(p):alert()}';p!=unescape(q).replace(/Q/,q)?eval(p):alert()}"
h = "alert('Hello, world!')"

eval(e)  // defines the function c()

c(h)     // evaluates h
c(e)     // does not evaluate e, alerts "undefined" instead
Ilmari Karonen
la source
Vous pouvez remplacer alert()par 0pour ne rien faire au lieu d'alerter undefinedet enregistrer 13 caractères.
Peter Olson
@PeterOlson: Oui, mais la tâche dit que "votre programme devrait faire quelque chose de complètement indépendant" s'il se détecte. J'interprète cela comme signifiant qu'il devrait faire quelque chose - de préférence quelque chose de visible pour l'utilisateur, je suppose. En plus, je l'aime mieux de cette façon. :) (Ps. Yay, il neige dehors! L'hiver est enfin là!)
Ilmari Karonen
1
@Ilmari Ne rien faire n'a rien à voir avec l'interprétation de Javascript IMHO.
FUZxxl
Vous pourriez aller p=>...au lieu defunction c(p)
FireCubez
2

Lisp commun, 59

#+~ #.(#:a)(defun L(p)(compile-file p))(push :~ *features*)
  • Dans un nouveau Lisp REPL, compilez votre fichier (par exemple sbcl --load)
  • Vous avez maintenant une fonction L , qui peut compiler des fichiers Common Lisp
  • Cependant, si vous appelez (L <your file>), une erreur est signalée lors de la lecture du fichier.

Pourquoi?

Parce que la première fois, vous avez inséré le :~mot clé dans *features*. À présent, votre environnement connaît la ~fonctionnalité et la macro de lecture #+, lors de l'évaluation de l' ~ expression de la fonctionnalité , réussira et lira le formulaire suivant au lieu de la sauter comme elle l'a fait la première fois. Dans votre fichier, le formulaire suivant est #.(#:a), qui demande d'évaluer (#:a)au moment de la lecture et d'utiliser la valeur résultante comme code en cours de lecture. Mais (#:a)appelle la fonction associée au symbole non interne #:a. Étant donné qu'il #:an'est pas interne, c'est un nouveau symbole qui n'est lié à aucune fonction (c'est-à-dire non fboundp). Erreur.

coredump
la source
1

Schéma, 48 ou 51 caractères

Scheme est un langage avec de nombreuses implémentations différentes. Malgré les implémentations devant être conformes au dernier RnRS, le dernier standard de travail (R6RS) a été impopulaire en raison de son manque de minimalisme. R7RS sera bientôt publié comme remède, tout en divisant le langage en 2. Le premier langage étant puissant et minimaliste et le second, un sur-ensemble du premier destiné à fournir des extensions de fonctionnalités pour l'interopérabilité entre les implémentations. Jusque-là, nous comptons sur les SRFI (Scheme Requests For Implementation), qui fournissent (si elles sont implémentées dans l'implémentation hôte ou manuellement (comme cela est courant dans le schéma)) un moyen d'accomplir de manière portable des tâches communes. Tout cela pour dire que le premier extrait de code (51 caractères), tout en restant aussi portable que possible, s'appuie sur SRFI-22 (exécutant des scripts de schéma sous UNIX) pour accéder aux arguments de ligne de commande:

(define(main x y)(case y(x => error)(else => load)))

ou plus lisible:

(define (main current-file arg)
  (case arg
    [current-file => error]
    [else => load]))

Le second (48 caractères) est un moyen d'interprétation sans fichier qui ne peut pas s'évaluer lui-même (dans un environnement nul):

(define(e)(write(eval(read)null-environment))(e))

ou plus lisible:

(define (interpret)
  (write (eval (read) null-environment))
  (interpret))
Samuel Duclos
la source
Votre code ne fonctionne pas si vous copiez votre interprète.
FUZxxl
1
Laissez-le à une réponse de schéma pour contenir des parenthèses imbriquées dans sa prose.
Cyoce
1

Groovy, 13 octets

{Eval.me(it)}

Cela devrait interpréter un sous-ensemble de Groovy.

cas de test:

p={Eval.me(it)}

p'''
    (0..37).each{println"1234567890JIHGFEDCBAKLMNOPQRST!?,.ZYXWVU"[it..it+2]}
'''

p'''
    {Eval.me(it)}
'''

Malheureusement, bien qu'il écorche certainement, il le fait de manière complètement interprétative, et il le fait pour beaucoup d'entrées.

Armand
la source
Dans quelle ligne lisez-vous le programme à interpréter? Votre code est intéressant bien qu'il ne s'agisse pas d'une soumission valide pour cette tâche.
FUZxxl
Je suppose que l'erreur est quelque chose comme "limite de récursivité dépassée"?
Ilmari Karonen