Consejos para crear políglotas

48

Un es un programa que se puede ejecutar en 2 o más lenguajes de programación diferentes.

¿Qué consejos generales tiene para hacer políglotas o elegir idiomas que sean fáciles de escribir políglotas para una tarea específica?

Publique los consejos que podrían aplicarse en la mayoría de las situaciones. Es decir, no deberían funcionar solo en políglotas de dos idiomas específicos. (Simplemente puede publicar una respuesta a una pregunta de políglotas si tiene una sugerencia demasiado específica). Pero puede introducir características de un lenguaje que facilite el trabajo con muchos idiomas, o sea fácil de agregar a cualquier políglota existente.

Por favor, publique un consejo por respuesta. Y siéntase libre de sugerir editar si un consejo específico de idioma también se aplica a otro idioma.

jimmy23013
fuente

Respuestas:

25

Explotar símbolos de comentario

Una forma sencilla de crear un políglota bilingüe es dividir el código en dos partes de la siguiente manera:

  1. La primera parte hace el trabajo real en el lenguaje A, es inofensivo en el lenguaje B (sin errores) y termina en un símbolo de comentario en el lenguaje A, que oculta la segunda parte en el lenguaje A.
  2. La segunda parte hace el trabajo real en el lenguaje B.

Así

  • El lenguaje A ve la primera parte, que hace el trabajo, y luego un comentario.
  • El lenguaje B ve una primera parte inútil y luego la segunda parte, que hace el trabajo.

La única parte difícil aquí es encontrar un conjunto de declaraciones (primera parte) que hacen el trabajo en el lenguaje A sin dar errores en el lenguaje B. Algunas sugerencias para esto:

  • La mayoría de los lenguajes basados ​​en la pila permiten mostrar solo la parte superior de la pila al final del programa (a veces esto es incluso predeterminado, como en 05AB1E).
  • Algunos idiomas ignoran las declaraciones indefinidas (por ejemplo, Golfscript).

Puede encontrar un ejemplo simple que utiliza estas pautas aquí . Lenguas A y B son MAT y 05AB1E respectivamente.

Luis Mendo
fuente
24

Usa lenguajes bidimensionales

A diferencia de los lenguajes unidimensionales, que generalmente analizan todo el código fuente y producirán errores de sintaxis o efectos de tiempo de ejecución no deseados en cosas que no entienden (lo que le obliga a ocultar el código de otros idiomas), los lenguajes bidimensionales tienden a solo analizar el código en la ruta de ejecución, lo que significa que se ignora todo el resto del programa. También hay mucho más espacio para dividir las rutas de ejecución en dos dimensiones; puede enviar el puntero de instrucciones girando en una dirección inusual, como hacia abajo o incluso hacia la izquierda (envolviendo el lado derecho del programa), para quitarlo de su camino muy rápidamente. Las técnicas útiles en lenguajes unidimensionales también se generalizan a lenguajes bidimensionales (por ejemplo, puede omitir el código con;; en Befunge-98, además de enviar la IP en una dirección extraña), lo que hace que esto sea solo una ganancia estricta en comparación con una solución unidimensional.

Como beneficio adicional, varios idiomas bidimensionales tienen un punto de entrada que no es la esquina superior izquierda del programa, lo que significa que no necesita hacer ningún esfuerzo para separarlos de otros idiomas; se separarán del grupo naturalmente.


fuente
20

Conoce tus verdaderas y falsas

Cada idioma ve "verdadero" y "falso" de una manera ligeramente diferente. Si tienen una sintaxis similar, puede explotar esto agregando una decisión de que los idiomas se manejarán de manera diferente.

Un ejemplo de los usos del hilo Truco o trato '', una cadena vacía. En Lua, esto se evalúa como verdadero, pero falso en Python, por lo que lo siguiente:

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

... imprimirá una cadena diferente en cada idioma.

Todo lo que se necesita es encontrar un valor como este. Por ejemplo, podría usar '0', que se evalúa falseen PHP pero trueen Python.

FlipTack
fuente
17

Citas en bloque en al menos un idioma

Aquí hay un ejemplo que funciona tanto en Python como en C ++

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

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

Luis Mendo lanzó lo que creo que es, con mucho, la solución más fácil, que es usar comentarios.

Busca un idioma que tenga comentarios en bloque y otro idioma donde la sintaxis regular en el primero sea la sintaxis de comentarios en el segundo.

Aún más fácil son dos idiomas con diferentes estilos de comentario de bloque que son sintaxis intercambiablemente correctas, pero no me molesté en verificarlo.

Compruébalo en Python 3.5 y C ++

dexgecko
fuente
2
La primera línea allí no debería tener un punto y coma.
Cierto. Buen punto
dexgecko
15

Divide y conquistaras

Cuando escribe un políglota en una gran cantidad de idiomas, no necesariamente podrá separar todos los flujos de control del idioma entre sí inmediatamente. Por lo tanto, necesitará "verdadero políglota" de algunos de los idiomas durante un período de tiempo, permitiendo que se ejecute el mismo código en cada uno de ellos. Hay dos reglas principales a tener en cuenta al hacer esto:

  • El flujo de control en cualquiera de los dos idiomas debe ser muy similar o muy diferente . Intentar manejar una gran cantidad de flujos de control intercalados es una receta para confundirse y hace que su programa sea difícil de modificar. En cambio, debe limitar la cantidad de trabajo que tiene que hacer asegurándose de que todos los programas que están en el mismo lugar estén allí por la misma razón y puedan ejecutarse en paralelo durante el tiempo que necesite. Mientras tanto, si un idioma es muy diferente de los demás, desea que su ejecución se mueva a una ubicación muy diferente lo antes posible, para que no tenga que intentar que su código se ajuste a dos modelos sintácticos diferentes a la vez.

  • Busque oportunidades para dividir un idioma, o un grupo de idiomas similares, separados unos de otros. Trabajar desde grupos más grandes hasta grupos más pequeños. Una vez que tenga un grupo de idiomas similares, todos en un determinado punto del programa, deberá dividirlos en algún momento. Al comienzo del programa, es posible que, por ejemplo, desee dividir los idiomas que se usan #como marcador de comentarios de los idiomas que usan algún otro marcador de comentarios. Más adelante, quizás tenga un punto en el que todos los idiomas usan la f(x)sintaxis para las llamadas a funciones, separan los comandos con punto y coma y tienen similitudes sintácticas similares. En ese momento, podría usar algo mucho más específico del idioma para dividirlos, por ejemplo, el hecho de que Ruby y Perl no procesan secuencias de escape en ''cadenas, pero Python y JavaScript sí.

En general, el flujo lógico de su programa debe terminar como un árbol, dividiéndose repetidamente en grupos de idiomas que son más similares entre sí. Esto pone la mayor parte de la dificultad en escribir el políglota justo al comienzo, antes de la primera división. A medida que el flujo de control se ramifica más y más, y los idiomas que se ejecutan en un punto dado se vuelven cada vez más similares, su tarea se vuelve más fácil porque puede usar una sintaxis más avanzada sin causar que los idiomas involucrados tengan un error de sintaxis.

Un buen ejemplo es el conjunto {JavaScript, Ruby, Perl, Python 3}; Todos estos idiomas aceptan llamadas a funciones con paréntesis y pueden separar las declaraciones con punto y coma. Todos ellos también admiten una evaldeclaración, que efectivamente le permite controlar el flujo de forma portátil. (Perl es el mejor de estos idiomas para separarse temprano del grupo, porque tiene una sintaxis diferente para las variables de los otros).

trichoplax
fuente
13

Ocultar código dentro de literales de cadena

En la mayoría de los idiomas, un literal de cadena por sí solo no hace nada o hace algo que se puede revertir fácilmente (como empujar la cadena a la pila). La sintaxis literal de cadenas también está relativamente no estandarizada, especialmente para las sintaxis alternativas que muchos lenguajes usan para manejar cadenas con líneas nuevas incrustadas; por ejemplo, Python tiene """ ... """, Perl tiene q( ... )y Lua tiene [[ ... ]].

Hay dos usos principales de estos. Una es permitirle intercalar secciones destinadas a diferentes idiomas al comenzar una cadena al final de la primera sección de un idioma y reanudarla al comienzo de la segunda: debería ser bastante fácil evitar cerrar accidentalmente la cadena debido a la variedad de delimitadores de cadena entre diferentes idiomas. El otro es que muchos delimitadores de cadena son significativos como un comando en otros idiomas (a menudo más que los marcadores de comentarios), por lo que puede hacer algo como x = [[4] ], que es una asignación inofensiva en idiomas que usan notación JSON para listas, pero que comienza una cadena en Lua (y por lo tanto le permite dividir el código Lua del resto, dado que efectivamente "salta" al siguiente ]]).


fuente
13

Finalizando el programa

Puede finalizar el programa abruptamente en un idioma para que ignore el código en otro idioma.

Básicamente, este formato se puede usar

code_in_language1 end_program_in_language1 code_for_language2 end_program_in_language2 ...

donde end_program_in_languageNestá el comando para finalizar el programa.

Por ejemplo, en mi respuesta en ¿Qué traerá para el Día de Acción de Gracias? , Terminé el programa en Dip, y luego escribí código para otro idioma, V, para que el intérprete de Dip lo ignorara.

"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

Pero entonces, no todos los idiomas tienen un comando que pueda finalizar el programa así como así. Sin embargo, si dicho lenguaje tiene la característica, debe usarse con prudencia.

Como sugirió @LuisMendo, puede crear un error (si está permitido) para finalizar el programa si el idioma aún no tiene un "programa final" incorporado.

Kritixi Lithos
fuente
2
Incluso si el idioma no tiene una función o una declaración para finalizar el programa, generalmente se producirá un error
Luis Mendo
1
@LuisMendo: De acuerdo, aunque tenga en cuenta que muchos problemas de poliglotación prohíben específicamente la salida por caída porque hace las cosas demasiado fáciles. Sin embargo, es una buena idea explotarlo cuando no lo hacen.
1
Probablemente debería mencionar que el código de la segunda parte aún debe ser sintácticamente correcto en el primer idioma o la mayoría de los lenguajes prácticos arrojarán un error.
MilkyWay90
13

Variable o código dentro de literales de cadena

Los literales de cadena entre comillas dobles son en su mayoría inofensivos en muchos idiomas. Pero en algunos idiomas también podrían contener código.

En Bash, puede usar `...`(no termina el programa):

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

En Tcl, puedes usar [...]:

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

En PHP, puede usar ${...}(esto genera un error en Bash, por lo que debe aparecer después del código Bash):

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

En Ruby, puedes usar #{...}:

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

Puede haber también otros.

Estas gramáticas no son compatibles. Eso significa que puede colocar todo el código de estos idiomas en una cadena en una ubicación inofensiva. Y simplemente ignorará el código no reconocido en otros idiomas y los interpretará como contenido de cadena.

En muchos casos, también podría comentar fácilmente un carácter de comillas dobles allí y hacer un políglota más tradicional.

jimmy23013
fuente
12

Alias ​​Variable

Este es probablemente uno de los trucos más importantes (IMO) más simples de usar, especialmente porque puede llegar a tantos idiomas.

Ejemplo:

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

Esto funcionará no solo en Javascript, sino también en Python, Ruby, etc. Más ejemplos más adelante cuando pienso en algunos otros. Por supuesto, las sugerencias de comentarios / ediciones de publicaciones son bienvenidas.

Mama Fun Roll
fuente
55
Tenga en cuenta que cuando se hace por ejemplo, JS / Python, por lo general es más corto que el alias alertque printen Python (3 solamente), porque la sintaxis de comentario de JS, //, puede ser fácilmente trabajado en un programa Python, mientras que Python #no se puede trabajar en JS.
ETHproductions
11

#comentarios basados ​​en

Este consejo es un subconjunto de símbolos de comentario de explotación y citas en bloque en al menos un idioma

Al crear políglotas con muchos idiomas, especialmente lenguajes listos para producción en lugar de esolangs, puede ser útil observar los idiomas que se usan #en comentarios de bloque o de una sola línea.

  • Hay muchos idiomas con sintaxis de comentarios de bloque que comienzan #, y hay mucha variedad en los caracteres que siguen a #.
  • La mayoría de estos idiomas también permiten un solo #comentario de línea, lo que significa que algo que podría comenzar un comentario de bloque en un idioma es solo un comentario ordinario en otro, lo que facilita su adaptación.

Aquí hay una lista de resumen rápido de idiomas que se usan #en un comentario de bloque (no exhaustivo):

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

Para más ejemplos, vea el Código de Rosetta .

Aquí hay un ejemplo rápido y fácil, como demostración:

#|
###
#`[

print("Julia")
#=

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

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

]#
say "Perl 6"
#`[

...

# ]# # ### # |# ; =#
Sp3000
fuente
Zephyr tiene #- ... -#.
DLosc
11

Discrepancias de operador aritmético

Para lenguajes similares o políglotas simples, a veces es útil buscar diferencias en cómo los lenguajes realizan la aritmética. Esto se debe a que la mayoría de los lenguajes (no esotéricos) tienen operadores aritméticos infijados y la aritmética puede ser una forma rápida y fácil de introducir una diferencia.

Por ejemplo:

  • ^ es XOR bit a bit en algunos idiomas y exponenciación en otros
  • / es la división de enteros en algunos idiomas y la división de coma flotante en otros
    • Para los idiomas de división entera, -1/2está -1en algunos idiomas (redondear hacia abajo) y 0en otros (redondear a cero)
  • -1%2está -1en algunos idiomas y 1en otros
  • --x es un no-op en algunos idiomas (doble negación) y pre-decrement en otros
  • 1/0 da infinito en algunos idiomas y errores en otros
  • 1<<64da 0 en algunos idiomas (desbordamiento) y 36893488147419103232en otros
Sp3000
fuente
3
Un ejemplo simple sería x=1;["JS","Python"][--x], que devuelve el nombre del idioma en el que se ejecuta (entre JS y Python).
ETHproductions
10

Usa Brainfuck

Casi todas las implementaciones de BF arrojan caracteres que no lo son +-<>[].,, ¡lo que funciona a nuestro favor!

BF es probablemente uno de los lenguajes más fáciles de trabajar en un políglota debido a esta característica, siempre que primero escriba la parte BF. Una vez que haya escrito su código BF, es solo cuestión de modelar cualquier otro código que tenga alrededor de la estructura BF.

Aquí hay un ejemplo realmente simple:

.+[.+]

Esto prácticamente aumenta y produce salidas de código de código "para siempre" (dependiendo de la configuración del tiempo de ejecución). Ahora, si quisiera escribir un código aleatorio, por ejemplo, en JS, podría hacer:

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

Observe cómo se moldea el JS alrededor del BF.

Asegúrese de saber que esto funciona mejor si realmente está listo para comenzar con BF; es bastante más difícil comenzar con otro idioma e intentar incorporar BF.

Mama Fun Roll
fuente
66
Para los políglotas más grandes donde unos pocos bytes de ahorro al integrar el BF no ayudan mucho, escribiría el BF al final y envolvería el otro código en tantos []como sea necesario.
Sp3000
66
Esto se aplica no solo al brainfuck sino a la gran cantidad de lenguajes similares al brainfuck y a muchas otras lonas de Turing.
0 '
2
El primero x=>cambia la celda, que en este caso no importa, pero solo quería decir
Roman Gräf el
7

Use idiomas en los que la mayoría de los caracteres no importan

Esta es una generalización del punto de Mama Fun Roll sobre BF . Un esolang que ignora la mayoría de los caracteres es muy útil en políglotas. También es útil: un esolang en el que un gran conjunto de caracteres son intercambiables. Algunos ejemplos:

  • El espacio en blanco ignora todo lo que no sea espacio, tabulación o nueva línea.
  • Brain-Flak básicamente ignora todo además ()[]{}<>. (a @veces causa un error cuando el intérprete intenta analizarlo como el inicio de un indicador de depuración).
  • oOo CODE ignora todo excepto las letras. Además, todas las letras minúsculas son intercambiables, al igual que todas las letras mayúsculas.
  • Wierd solo distingue entre espacios en blanco y no espacios en blanco.
  • En Wordy , algunos caracteres de puntuación se ignoran y todas las letras son intercambiables.
  • Tanto el paréntesis como el infierno entre paréntesis ignoran todo excepto los paréntesis.
DLosc
fuente
Arreglé ese @error.
Wheat Wizard
Intente combinar Whitespace con Python
enedil
@enedil No necesitas tener pestañas con Python. Puedes usarexec('''...\t\n\40''')
MilkyWay90
5

Tenga cuidado con los comentarios de bloque anidados

A veces, varios idiomas usarán la misma sintaxis para los comentarios de bloque, que a menudo es un factor decisivo para crear un políglota con los dos idiomas. Sin embargo, muy ocasionalmente, uno de los idiomas permitirá comentarios de bloque anidados, que se pueden abusar para crear rutas de código separadas.

Por ejemplo, considere este políglota:

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

Nim y Lily usan #[y ]#para comenzar y finalizar comentarios de bloque, pero solo Nim permite comentarios de bloque anidados.

Lily considera que el segundo #[es parte del comentario de bloque singular y el primero ]#como que termina el comentario de bloque. (La #siguiente declaración impresa de Lily es un comentario de línea que oculta el código de Nim).

Nim, alternativamente, lo ve #[]#como un comentario de bloque anidado (aunque vacío) y print("Lily")#como el comentario de bloque externo.

Oportunidad
fuente
4

No estoy seguro si esto cuenta, pero ...

Use una línea shebang para convertir todo en un perlprograma válido

De acuerdo con esta respuesta y la documentación de Perl, si pasa cualquier archivo que comience con una línea shebang perl, invoca el programa apropiado para ejecutarlo. Por ejemplo, esto

#!/usr/bin/python

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

se ejecuta por el intérprete de Python si llama perl filename.py.

Federico Poloni
fuente
3
Si bien se puede llamar al programa perl, no se convierte en un programa Perl.
Paŭlo Ebermann
2
@ PaŭloEbermann Me doy cuenta de que está en el límite, por eso comencé mi respuesta con "no estoy seguro de si cuenta". :) Pero, ¿qué define a Perl verdadero, si no "lo que está escrito en la documentación y devuelto por la implementación de referencia perl"? Suena como un buen meme filosófico ...
Federico Poloni
1
(Ver también esta meta respuesta .)
Federico Poloni
4

Llame a funciones inexistentes, luego salga mientras evalúa sus argumentos

Muchos lenguajes de programación son capaces de analizar un identificador arbitrario seguido de un par de paréntesis con expresiones dentro:

identifier(1 + 1)

A veces, la forma del identificador en cuestión puede ser fija, debido a que es necesario dar el código a un idioma diferente que está utilizando. Al principio, eso podría parecer problemático si el identificador no corresponde a una función que realmente tiene el lenguaje.

Sin embargo, muchos lenguajes de programación evaluarán los argumentos de una función antes de verificar si la función en sí existe (por ejemplo, Lua) y, de todos modos, puede usar este tipo de construcción; todo lo que necesita es salir del programa en algún lugar dentro de los argumentos de la función.

Aquí hay un ejemplo, un políglota dc / Lua:

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

c2pqes un programa de CC para imprimir 2 y salir; Lua ve esto como el nombre de una función, pero se puede evitar que Lua cometa un error al colocar un comando de salida en su argumento. La gran ventaja de esta construcción es que, a diferencia de una asignación ( c2pq =), no es automáticamente incompatible con los idiomas en los que los nombres de las variables comienzan con un sigilo; la sintaxis del nombre de la función es mucho más coherente en todos los idiomas que la sintaxis del nombre de la variable.


fuente