Conseils pour créer des polyglottes

48

Un est un programme qui peut être exécuté dans au moins deux langages de programmation différents.

Quels conseils généraux avez-vous pour faire des polyglottes ou pour choisir des langues faciles à écrire pour une tâche spécifique?

S'il vous plaît poster les conseils qui pourraient être appliqués dans la plupart des situations. C'est-à-dire qu'ils ne devraient pas fonctionner uniquement dans les polyglottes de deux langues spécifiques. (Vous pouvez simplement poster une réponse à une question polyglotte si vous avez une astuce trop spécifique.) Mais vous pouvez introduire des fonctionnalités d'une langue qui facilitent l'utilisation de plusieurs langues ou qui peuvent facilement être ajoutées à des polyglots existants.

Merci de poster un pourboire par réponse. Et n'hésitez pas à suggérer une modification si un conseil spécifique à une langue s'applique également à une autre langue.

jimmy23013
la source

Réponses:

25

Exploiter les symboles de commentaire

Un moyen simple de créer un polyglotte bilingue est de diviser le code en deux parties, comme suit:

  1. La première partie effectue le travail réel dans la langue A, est inoffensive dans la langue B (aucune erreur) et se termine par un symbole de commentaire langue-A, qui masque la deuxième partie de la langue A.
  2. La deuxième partie fait le travail dans la langue B.

Ainsi

  • La langue A voit la première partie, qui fait le travail, puis un commentaire.
  • La langue B voit une première partie inutile, puis la deuxième partie, qui fait le travail.

La seule partie difficile consiste ici à trouver un ensemble d’énoncés (première partie) faisant le travail dans la langue A sans générer d’erreurs dans la langue B. Quelques suggestions à cet effet:

  • La plupart des langues basées sur des piles permettent d'afficher uniquement le haut de la pile à la fin du programme (parfois même par défaut, comme dans 05AB1E).
  • Certaines langues ignorent les instructions non définies (par exemple, Golfscript).

Vous trouverez un exemple simple d'utilisation de ces instructions ici . Les langues A et B sont MATL et 05AB1E respectivement.

Luis Mendo
la source
24

Utiliser des langues à deux dimensions

Contrairement aux langages unidimensionnels, qui analysent généralement tout le code source et génèrent des erreurs de syntaxe ou des effets d’exécution indésirables sur des éléments qu’ils ne comprennent pas (ce qui vous oblige à leur cacher le code d’autres langages), les langages bidimensionnels ont tendance à ne analyser le code dans le chemin d'exécution, ce qui signifie que tout le reste du programme est ignoré. Il y a aussi beaucoup plus de place pour séparer les chemins d'exécution les uns des autres en deux dimensions; vous pouvez envoyer le pointeur d'instructions dans un sens inhabituel, comme vers le bas ou même vers la gauche (en se tournant vers la droite du programme), pour vous en libérer très rapidement. Les techniques utiles dans les langages unidimensionnels se généralisent également aux langages bidimensionnels (par exemple, vous pouvez passer du code avec;; dans Befunge-98, en plus d’envoyer simplement l’adresse IP dans un sens étrange), ce qui en fait un avantage strict par rapport à une solution unidimensionnelle.

En prime, plusieurs langues à deux dimensions ont un point d’entrée autre que le coin supérieur gauche du programme, ce qui signifie que vous n’avez pas besoin de vous efforcer de les séparer d’autres langues; ils se sépareront naturellement du groupe.


la source
20

Connaissez vos vrais et faux

Chaque langue voit "vrai" et "faux" d'une manière légèrement différente. S'ils ont une syntaxe similaire, vous pouvez l'exploiter en ajoutant une décision que les langues géreront différemment.

Un exemple du fil Trick or Treat utilise '', une chaîne vide. En Lua, cela revient à la vérité, mais à la fausseté en Python, donc à ce qui suit:

print(''and'trick'or'treat')

..will imprimer une chaîne différente dans chaque langue.

Il suffit de trouver une valeur comme celle-ci. Par exemple, vous pouvez utiliser '0', qui évalue falseen PHP mais trueen Python.

FlipTack
la source
17

Des devis dans au moins une langue

Voici un exemple qui fonctionne à la fois en Python et en C ++

#include <iostream> /*
""" */
int main() {
    std::cout << "Hello World!\n";
}

/* """
print("Hello World!")
# */

Luis Mendo a présenté ce que je pense être de loin la solution la plus simple, à savoir utiliser des commentaires.

Vous recherchez une langue qui a des commentaires de bloc et une autre où la syntaxe normale dans le premier est la syntaxe de commentaire dans le second.

Il est encore plus facile d'utiliser deux langues avec des styles de commentaire de bloc différents, dont la syntaxe est interchangeable, mais je n'ai pas eu la peine de vérifier.

Découvrez-le en Python 3.5 et C ++

dexgecko
la source
2
La première ligne ne devrait pas comporter de point-virgule.
Vrai. Bon point
dexgecko
15

Diviser et conquérir

Lorsque vous écrivez un polyglotte dans un grand nombre de langues, vous ne pourrez pas nécessairement séparer immédiatement tous les flux de contrôle de la langue. Ainsi, vous aurez besoin de "vraies polyglottes" de certaines langues pendant un certain temps, permettant au même code de s'exécuter dans chacune d'elles. Tenez compte de deux règles principales:

  • Le flux de contrôle dans deux langues quelconques doit être très similaire ou très différent . Essayer de gérer un grand nombre de flux de contrôle entrelacés est une recette pour semer la confusion et rend votre programme difficile à modifier. Au lieu de cela, vous devriez limiter la quantité de travail que vous avez à faire en vous assurant que tous les programmes qui se trouvent au même endroit sont là pour la même raison et peuvent être exécutés en parallèle aussi longtemps que vous le souhaitez. En attendant, si une langue est très différente des autres, vous souhaitez que son exécution soit déplacée le plus rapidement possible vers un emplacement très différent, de sorte que vous n'ayez pas à essayer de rendre votre code conforme à deux modèles syntaxiques différents à la fois.

  • Cherchez des occasions de séparer une langue ou un groupe de langues similaires. Travaillez à partir de groupes plus importants jusqu'à des groupes plus petits. Une fois que vous avez un groupe de langues similaires à un moment donné du programme, vous devrez les séparer à un moment donné. Au début du programme, vous voudrez peut-être, par exemple, séparer les langues qui servent #de marqueur de commentaire des langues qui utilisent un autre marqueur de commentaire. Plus tard, vous aurez peut-être un point où toutes les langues utilisent la f(x)syntaxe pour les appels de fonction, les commandes séparées avec des points-virgules et présentent des similitudes syntaxiques similaires. À ce stade, vous pouvez utiliser quelque chose de beaucoup plus spécifique à la langue pour les séparer, par exemple le fait que Ruby et Perl ne traitent pas les séquences d'échappement dans les ''chaînes, contrairement à Python et JavaScript.

En général, le flux logique de votre programme devrait se transformer en un arbre, se divisant à plusieurs reprises en groupes de langues plus similaires les unes aux autres. Cela met la majeure partie de la difficulté à écrire le polyglotte dès le début, avant la première division. À mesure que le flux de contrôle se sépare de plus en plus et que les langues en cours d'exécution se ressemblent de plus en plus, votre tâche devient plus facile car vous pouvez utiliser une syntaxe plus avancée sans provoquer d'erreur de syntaxe pour les langues impliquées.

Un bon exemple est l'ensemble {JavaScript, Ruby, Perl, Python 3}; toutes ces langues acceptent les appels de fonction avec des parenthèses et peuvent séparer des instructions avec des points-virgules. Ils supportent également tous une evaldéclaration qui vous permet effectivement de contrôler le flux de manière portable. (Perl est le meilleur de ces langages à séparer du groupe à l’avance, car il a une syntaxe différente pour les variables des autres).

trichoplax
la source
13

Masquer le code dans les littéraux de chaîne

Dans la plupart des langues, un littéral de chaîne ne fait rien, ou fait quelque chose qui peut être facilement inversé (comme enfoncer la chaîne dans la pile). La syntaxe littérale de chaîne est également relativement non standardisée, en particulier pour les syntaxes alternatives utilisées par de nombreux langages pour gérer les chaînes avec des retours à la ligne incorporés; Par exemple, Python """ ... """, Perl q( ... )et Lua [[ ... ]].

Il y a deux utilisations principales de ceux-ci. La première consiste à vous permettre d’entrelacer des sections destinées à différentes langues en commençant une chaîne à la fin de la première section d’une langue et en la reprenant au début de la seconde: il devrait être assez facile d’éviter de fermer accidentellement la chaîne en raison de la diversité des fonctions. délimiteurs de chaînes entre différentes langues. L’autre est que de nombreux délimiteurs de chaîne ont une signification en tant que commande dans d’autres langues (souvent plus que des marqueurs de commentaire), ce qui vous permet de faire quelque chose du genre x = [[4] ]: une affectation sans danger dans les langues qui utilisent la notation JSON pour les listes, mais qui commence par la suite. une chaîne dans Lua (et vous permet ainsi de séparer le code Lua du reste, étant donné qu'il "saute" effectivement au suivant ]]).


la source
13

Fin du programme

Vous pouvez terminer le programme brusquement dans une langue pour ignorer le code dans une autre langue.

Donc, fondamentalement, ce format peut être utilisé

code_in_language1 end_program_in_language1 code_for_language2 end_program_in_language2 ...

end_program_in_languageNest la commande pour terminer le programme.

Par exemple, dans ma réponse dans Qu'apporterez-vous pour Thanksgiving? , J’ai terminé le programme en Dip, puis j’ai écrit le code d’une autre langue, V, afin que l’interprète Dip l’ignore.

"turkey"e#"corn"??"gravy"p&Ssalad
"turkey"e#"corn"??"gravy"                 
                         p&            # print stack and exit program (Dip) 
                           Ssalad      # Now that the program ended in Dip,
                                       # I can write V code that would otherwise
                                       # have caused errors in Dip

Mais dans ce cas, toutes les langues n’ont pas une commande permettant de terminer le programme comme cela. Cependant, si un tel langage possède cette fonctionnalité, il convient de l'utiliser à bon escient.

Comme @LuisMendo l'a suggéré, vous pouvez créer une erreur (si elle est autorisée) pour terminer le programme si la langue ne possède pas déjà un "programme final" intégré.

Kritixi Lithos
la source
2
Même si la langue n’a pas de fonction ni d’instruction pour mettre fin au programme, une erreur suffira généralement
Luis Mendo
1
@LuisMendo: D'accord, mais notez que de nombreux problèmes de polyglotte interdisent spécifiquement la sortie par le crash, car cela rend les choses trop faciles. C'est une bonne idée de l'exploiter quand ce n'est pas le cas.
1
Vous devriez probablement mentionner que le code de la deuxième partie doit toujours être syntaxiquement correct dans la première langue, sinon la plupart des langues pratiques généreront une erreur.
MilkyWay90
13

Variable ou code à l'intérieur de littéraux de chaîne

Les littéraux de chaîne entre guillemets sont généralement inoffensifs dans de nombreuses langues. Mais dans certaines langues, ils pourraient aussi contenir du code.

Dans Bash, vous pouvez utiliser `...`(cela ne termine pas le programme):

"`echo Hello world! >/proc/$$/fd/1`"

En Tcl, vous pouvez utiliser [...]:

"[puts {hello world!};exit]"

En PHP, vous pouvez utiliser ${...}(cela génère une erreur dans Bash, il doit donc apparaître après le code Bash):

"${die(print(Hello.chr(32).world.chr(33)))}";

En Ruby, vous pouvez utiliser #{...}:

"#{puts 'Hello world!';exit}"

Il pourrait y avoir aussi d'autres.

Ces grammaires ne sont pas compatibles. Cela signifie que vous pouvez mettre tout le code de ces langues dans une chaîne dans un emplacement inoffensif. Et il ignorera simplement le code non reconnu dans d'autres langues et les interprétera comme un contenu de chaîne.

Dans de nombreux cas, vous pouvez aussi facilement commenter un caractère de citation double et créer un polyglotte plus traditionnel.

jimmy23013
la source
12

Aliasing variable

C’est probablement l’un des trucs les plus simples à utiliser (OMI), surtout qu’il peut atteindre de nombreuses langues.

Exemple:

print=alert;print("Hello World!")

Cela fonctionnera non seulement en Javascript, mais aussi en Python, Ruby, etc. Plus d'exemples plus tard, lorsque je penserai à d'autres. Bien entendu, les suggestions de commentaires / modifications de post sont les bienvenues.

Maman Fun Roll
la source
5
Notez que lorsque vous faites par exemple JS / Python, il est généralement plus court alias alertpour printen Python (3 seulement) parce que la syntaxe de commentaire de JS, //peut facilement être travaillé dans un programme Python, alors que celui de Python #ne peut être utilisée dans JS.
ETHproductions
11

#commentaires

Cette astuce est un sous-ensemble de symboles de commentaire Exploit et de citations de bloc dans au moins une langue.

Lors de la création de polyglottes avec plusieurs langues, en particulier les langues prêtes pour la production, par opposition aux esolangs, il peut être utile d'examiner les langues utilisées #dans les commentaires en bloc ou sur une seule ligne.

  • Il existe de nombreuses langues dont la syntaxe des commentaires de bloc commence par #, et il y a beaucoup de variété dans les caractères suivant le caractère #.
  • La plupart de ces langues autorisent également un simple #commentaire de ligne, ce qui signifie que le fait de commencer un commentaire de bloc dans une langue n’est qu’un commentaire ordinaire dans une autre, ce qui facilite son intégration.

Voici une liste récapitulative rapide des langues qui utilisent #un commentaire de bloc (non exhaustif):

Language            Start       End      Single-line #?     Notes
------------------------------------------------------------------------------------------
Agena               #/          /#             ✓
AutoIt              #cs         #ce
Brat                #*          *#             ✓
C                   #if 0       #endif                      Not actually a comment
CoffeeScript        ###         ###            ✓            Needs to be on separate line
Common Lisp         #|          |#
Julia               #=          =#             ✓
Lily                #[          ]#             ✓
Objeck              #~          ~#             ✓
Perl 6              #`{         }#             ✓            Any bracketing chars will do
Picolisp            #{          }#             ✓
Scheme              #|          |#

Pour plus d'exemples, voir Rosetta Code .

Voici un exemple simple et rapide, à titre de démonstration:

#|
###
#`[

print("Julia")
#=

|#
(format t "Common Lisp")
#|

###
alert("CoffeeScript")
###

]#
say "Perl 6"
#`[

...

# ]# # ### # |# ; =#
Sp3000
la source
Zéphyr a #- ... -#.
DLosc
11

Différences d'opérateurs arithmétiques

Pour des langages similaires ou de simples polyglottes, il est parfois utile de rechercher des différences dans la manière dont les langages exécutent l'arithmétique. En effet, la plupart des langues (non-ésotériques) ont des opérateurs arithmétiques infixes et l'arithmétique peut être un moyen rapide et facile d'introduire une différence.

Par exemple:

  • ^ est bitwise XOR dans certaines langues et exponentiation dans d'autres
  • / est la division entière dans certaines langues et la division à virgule flottante dans d'autres
    • Pour les langues de la division entière, -1/2est -1dans certaines langues (arrondi au bas) et 0dans d'autres (arrondi à zéro)
  • -1%2est -1dans certaines langues et 1dans d'autres
  • --x est un non-op dans certaines langues (double négation) et pré-décrémentation dans d'autres
  • 1/0 donne l'infini dans certaines langues et des erreurs dans d'autres
  • 1<<64donne 0 dans certaines langues (débordement) et 36893488147419103232dans d'autres
Sp3000
la source
3
Un exemple simple serait x=1;["JS","Python"][--x], qui renvoie le nom du langage dans lequel il est exécuté (entre JS et Python).
ETHproductions
10

Utilisez Brainfuck

Presque toutes les implémentations de BF rejettent les caractères qui ne le sont pas +-<>[].,, ce qui est tout à notre avantage!

Le BF est probablement l’un des langages les plus faciles à intégrer dans un polyglotte à cause de cette fonctionnalité, tant que vous écrivez la partie BF en premier. Une fois que vous avez écrit votre code BF, il vous suffit de modéliser tout autre code que vous avez autour de la structure BF.

Voici un exemple très simple:

.+[.+]

Cela incrémente et génère des codes de caractères "pour toujours" (en fonction des paramètres d'exécution). Maintenant, si vous vouliez écrire un morceau de code au hasard, par exemple dans JS, vous pourriez faire:

x=>"asdf".repeat(+x)[x*Math.random()*2+1|0]

Remarquez comment le JS est moulé autour du BF.

Assurez-vous de savoir que cela fonctionne mieux si vous êtes vraiment prêt à commencer avec BF; il est décemment plus difficile de commencer avec une autre langue et d'essayer d'intégrer le BF.

Maman Fun Roll
la source
6
Pour les polyglottes plus grandes pour lesquelles quelques octets d'économies résultant de l'intégration du BF ne sont pas d'une grande aide, j'écrivais le BF en dernier et encapsulais l'autre code autant []que nécessaire.
Sp3000
6
Cela s'applique non seulement au brainfuck, mais au grand nombre de langages similaires au brainfuck et à beaucoup d'autres tarpits de Turing.
0 '26
2
Le premier x=>change la cellule, ce qui dans ce cas importe peu, mais je voulais juste dire
Roman Gräf
7

Utilisez des langues dans lesquelles la plupart des caractères n'ont pas d'importance

Ceci est une généralisation de l'argument de Mama Fun Roll à propos de BF . Un esolang qui ignore la plupart des caractères est très utile dans les polyglottes. Également utile: un esolang dans lequel un grand nombre de caractères est interchangeable. Quelques exemples:

  • Les espaces blancs ignorent tout ce qui n'est ni espace, ni tabulation, ni nouvelle ligne.
  • Brain-Flak ignore essentiellement tout ()[]{}<>. ( @Parfois, une erreur se produit lorsque l'interprète tente de l'analyser au début d'un indicateur de débogage.)
  • oOo CODE ignore tout sauf les lettres. De plus, toutes les lettres minuscules sont interchangeables, de même que toutes les lettres majuscules.
  • Wierd fait uniquement la distinction entre les caractères blancs et non blancs.
  • Dans Wordy , certains caractères de ponctuation sont ignorés et toutes les lettres sont interchangeables.
  • L’ enfer des parenthèses et des parenthèses ignore tout sauf les parenthèses.
DLosc
la source
J'ai corrigé cette @erreur.
Assistant de blé
Essayez de combiner Whitespace avec Python
enedil
@enedil Vous n'avez pas besoin d'avoir des onglets avec Python. Vous pouvez utiliserexec('''...\t\n\40''')
MilkyWay90
5

Soyez conscient des commentaires de bloc imbriqués

Parfois, plusieurs langues utilisent la même syntaxe pour les commentaires de bloc, ce qui est le plus souvent un facteur de rupture pour la création d'un polyglotte avec les deux langues. Très rarement cependant, l’une des langues autorise les commentaires de bloc imbriqués, qui peuvent être utilisés pour créer des chemins de code distincts.

Par exemple, considérons ce polyglotte:

#[#[]#print("Lily")#]#echo"Nim"

Nim et Lily utilisent #[et ]#pour commencer et terminer les commentaires de bloc, mais seul Nim autorise les commentaires de bloc imbriqués.

Lily considère le second #[comme faisant partie du commentaire de bloc singulier et le premier ]#comme terminant le commentaire de bloc. (L' #instruction print suivante de Lily est un commentaire de ligne qui cache le code de Nim.)

Nim peut également voir le #[]#commentaire de bloc imbriqué (bien que vide) et print("Lily")#le commentaire de bloc externe.

Chance
la source
4

Je ne sais pas si cela compte, mais ...

Utilisez une ligne shebang pour tout transformer en un perlprogramme valide

Selon cette réponse et la documentation Perl, si vous transmettez un fichier commençant par une ligne shebang perl, il appelle le programme approprié pour l'exécuter. Par exemple, cela

#!/usr/bin/python

for i in range(6):
    print i**2

est exécuté par l'interpréteur Python si vous appelez perl filename.py.

Federico Poloni
la source
3
Bien que le programme puisse être appelé avec perl, il ne devient pas un programme Perl.
Paŭlo Ebermann
2
@ PaŭloEbermann Je me rends compte que c'est limite, c'est pourquoi j'ai commencé ma réponse avec "ne sais pas si ça compte". :) Mais qu'est-ce qui définit le vrai Perl, sinon "ce qui est écrit dans la documentation et renvoyé par l'implémentation de référence perl"? Cela ressemble à un bon mème philosoraptor ...
Federico Poloni
1
(Voir aussi cette méta réponse .)
Federico Poloni
4

Appelez des fonctions non existantes, puis quittez en évaluant leurs arguments

De nombreux langages de programmation sont capables d'analyser un identifiant arbitraire suivi d'une paire de parenthèses avec une expression à l'intérieur:

identifier(1 + 1)

Parfois, la forme de l'identifiant en question peut être corrigée, car elle est nécessaire pour donner du code à une autre langue que vous utilisez. Cela pourrait au premier abord sembler poser problème, si l'identifiant ne correspond pas à une fonction du langage.

Cependant, de nombreux langages de programmation évaluent les arguments d'une fonction avant de vérifier si la fonction elle-même existe réellement (par exemple, Lua) et vous pouvez donc utiliser ce type de construction de toute façon. tout ce dont vous avez besoin est de quitter le programme quelque part dans les arguments de la fonction.

Voici un exemple, un polyglotte dc / Lua:

c2pq(1 + #os.exit(print(3)))

c2pqest un programme en continu pour imprimer 2 et quitter; Lua voit cela comme le nom d'une fonction, mais il est possible d'empêcher l'erreur de placer une commande exit dans son argument. Le gros avantage de cette construction réside dans le fait que contrairement à une affectation ( c2pq =), elle n'est pas automatiquement incompatible avec les langages dans lesquels les noms de variables commencent par un sigil; La syntaxe du nom de fonction est beaucoup plus cohérente dans les langues que la syntaxe du nom de variable.


la source