Dicas para criar poliglotas

48

Um é um programa que pode ser executado em 2 ou mais linguagens de programação diferentes.

Que dicas gerais você tem para criar poliglotas ou escolher idiomas fáceis de escrever poliglotas para uma tarefa específica?

Poste as dicas que podem ser aplicadas na maioria das situações. Ou seja, eles não devem funcionar apenas em poliglotas de dois idiomas específicos. (Você pode simplesmente postar uma resposta a uma pergunta poliglota se tiver uma dica muito específica.) Mas você pode introduzir recursos de um idioma que facilita o trabalho com muitos idiomas ou pode ser adicionado a qualquer poliglota existente.

Poste uma dica por resposta. E fique à vontade para sugerir edição se uma dica específica de idioma também se aplicar a outro idioma.

jimmy23013
fonte

Respostas:

25

Explorar símbolos de comentários

Uma maneira simples de criar um poliglota de dois idiomas é dividir o código em duas partes, da seguinte maneira:

  1. A primeira parte faz o trabalho real no idioma A, é inofensivo no idioma B (sem erros) e termina em um símbolo de comentário no idioma A, que oculta a segunda parte no idioma A.
  2. A segunda parte faz o trabalho real na linguagem B.

portanto

  • O idioma A vê a primeira parte, que faz o trabalho, e depois um comentário.
  • O idioma B vê uma primeira parte inútil e depois a segunda parte, que faz o trabalho.

A única parte difícil aqui é encontrar um conjunto de instruções (primeira parte) que executam o trabalho no idioma A, mas não apresentam erros no idioma B. Algumas sugestões para isso:

  • A maioria dos idiomas baseados em pilha permite exibir apenas a parte superior da pilha no final do programa (às vezes isso é o padrão, como em 05AB1E).
  • Alguns idiomas ignoram instruções indefinidas (por exemplo, Golfscript).

Um exemplo simples que usa essas diretrizes pode ser encontrado aqui . Os idiomas A e B são MATL e 05AB1E, respectivamente.

Luis Mendo
fonte
24

Use linguagens bidimensionais

Diferentemente das linguagens unidimensionais, que geralmente analisam todo o código-fonte e produzem erros de sintaxe ou efeitos de tempo de execução indesejados em coisas que eles não entendem (forçando-o a ocultar o código de outras linguagens), as linguagens bidimensionais tendem a apenas analisar o código no caminho da execução, o que significa que todo o restante do programa é ignorado. Também há muito mais espaço para separar os caminhos de execução em duas dimensões; você pode enviar o ponteiro de instruções girando em uma direção incomum, como para baixo ou mesmo para a esquerda (passando para o lado direito do programa), para tirá-lo do seu caminho muito rapidamente. As técnicas úteis em linguagens unidimensionais também generalizam para linguagens bidimensionais (por exemplo, você pode pular o código com;; no Befunge-98, além de apenas enviar o IP em uma direção estranha), tornando esse ganho apenas estrito em comparação com uma solução unidimensional.

Como bônus, vários idiomas bidimensionais têm um ponto de entrada diferente do canto superior esquerdo do programa, o que significa que você não precisa se esforçar para separá-los de outros idiomas; eles se separam do grupo naturalmente.


fonte
20

Conheça os seus verdadeiros e falsos

Cada idioma vê "verdadeiro" e "falso" de uma maneira ligeiramente diferente. Se eles tiverem sintaxe semelhante, você poderá explorar isso adicionando uma decisão que os idiomas manipularão de maneira diferente.

Um exemplo do segmento Trick or Treat usa ''uma sequência vazia. Em Lua, isso é avaliado como verdadeiro, mas falso em Python, portanto, o seguinte:

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

..imprimirá uma sequência diferente em cada idioma.

Só é preciso encontrar um valor como este. Por exemplo, você pode usar o '0'que é avaliado falseem PHP, mas trueem Python.

FlipTack
fonte
17

Citações de bloco em pelo menos um idioma

Aqui está um exemplo que funciona tanto em Python quanto em C ++

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

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

Luis Mendo apresentou o que eu acho que é de longe a solução mais fácil, que é usar comentários.

Você procura um idioma que bloqueie os comentários e outro idioma em que a sintaxe regular no primeiro está comentando a sintaxe no segundo.

Ainda mais fácil são dois idiomas com estilos de comentários de bloco diferentes que têm sintaxe intercambiavelmente correta, mas não me preocupei em verificar.

Confira em Python 3.5 e C ++

dexgecko
fonte
2
A primeira linha não deve ter ponto e vírgula.
Verdadeiro. Bom ponto
dexgecko 26/11/16
15

Dividir e conquistar

Quando você estiver escrevendo um poliglota em um grande número de idiomas, não poderá necessariamente separar todos os fluxos de controle do idioma imediatamente. Portanto, você precisará "poliglota verdadeira" em alguns idiomas por um certo período de tempo, permitindo que o mesmo código seja executado em cada um deles. Há duas regras principais a serem lembradas enquanto você faz isso:

  • O fluxo de controle em dois idiomas deve ser muito semelhante ou muito diferente . Tentar lidar com um grande número de fluxos de controle intercalados é uma receita para ficar confuso e dificulta a modificação do seu programa. Em vez disso, você deve limitar a quantidade de trabalho que você deve realizar, garantindo que todos os programas que estão no mesmo local estejam lá pelo mesmo motivo e possam ser executados em paralelo, contanto que você precise. Enquanto isso, se um idioma é muito diferente dos outros, você deseja que sua execução seja movida para um local muito diferente o mais rápido possível, para que você não precise tentar tornar seu código conforme dois modelos sintáticos diferentes ao mesmo tempo.

  • Procure oportunidades para dividir um idioma ou um grupo de idiomas semelhantes um do outro. Trabalhe de grupos maiores para grupos menores. Depois de ter um grupo de idiomas semelhantes, todos em um determinado ponto do programa, será necessário dividi-los em algum momento. No início do programa, você pode, digamos, querer dividir os idiomas que usam #como marcador de comentário dos idiomas que usam outro marcador de comentário. Posteriormente, talvez você tenha um ponto em que todos os idiomas usem f(x)sintaxe para chamadas de função, separe comandos com ponto e vírgula e tenham semelhanças sintáticas semelhantes. Nesse ponto, você poderia usar algo muito mais específico da linguagem para dividi-los, por exemplo, o fato de Ruby e Perl não processarem seqüências de escape em ''strings, mas Python e JavaScript.

Em geral, o fluxo lógico do seu programa deve terminar como uma árvore, dividindo-se repetidamente em grupos de idiomas que são mais semelhantes entre si. Isso coloca a maior parte da dificuldade em escrever o poliglota logo no início, antes da primeira divisão. À medida que o fluxo de controle se ramifica cada vez mais, e os idiomas que estão sendo executados em um determinado ponto se tornam cada vez mais semelhantes, sua tarefa fica mais fácil porque você pode usar uma sintaxe mais avançada sem causar erros de sintaxe nos idiomas envolvidos.

Um bom exemplo é o conjunto {JavaScript, Ruby, Perl, Python 3}; todos esses idiomas aceitam chamadas de função entre parênteses e podem separar instruções com ponto e vírgula. Todos eles também suportam uma evaldeclaração, que efetivamente permite que você faça o controle de fluxo de maneira portátil. (Perl é o melhor desses idiomas para se separar do grupo mais cedo, porque possui uma sintaxe diferente para variáveis ​​dos outros.)

Trichoplax
fonte
13

Ocultar código dentro de literais de string

Na maioria dos idiomas, uma string literal por si só não faz nada ou pode ser facilmente revertida (como empurrar a string para a pilha). A sintaxe literal de string também é relativamente não padronizada, especialmente para as sintaxes alternativas que muitos idiomas usam para lidar com strings com novas linhas incorporadas; por exemplo, Python possui """ ... """, Perl possui q( ... )e Lua possui [[ ... ]].

Existem dois usos principais deles. Uma é permitir intercalar seções destinadas a diferentes idiomas, iniciando uma sequência no final da primeira seção de uma língua e retomando-a no início da segunda: deve ser bastante fácil evitar o fechamento acidental da sequência devido à variedade de delimitadores de string entre diferentes idiomas. A outra é que muitos delimitadores de cadeia são significativos como um comando em outros idiomas (geralmente mais do que marcadores de comentários), para que você possa fazer algo como x = [[4] ], que é uma atribuição inofensiva em idiomas que usam a notação JSON para listas, mas que inicia uma cadeia de caracteres em Lua (e, portanto, permite que você divida o código Lua do resto, pois ele efetivamente "salta" para o próximo ]]).


fonte
13

Finalizando o programa

Você pode encerrar o programa abruptamente em um idioma para que ele ignore o código em outro idioma.

Então, basicamente, esse formato pode ser usado

code_in_language1 end_program_in_language1 code_for_language2 end_program_in_language2 ...

Onde end_program_in_languageNestá o comando para finalizar o programa.

Por exemplo, na minha resposta em O que você trará para o Dia de Ação de Graças? , Encerrei o programa no Dip e, em seguida, escrevi o código para outro idioma, V, para que o intérprete do Dip o ignorasse.

"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

Mas nem todos os idiomas têm um comando que pode finalizar o programa assim. No entanto, se esse idioma tiver esse recurso, ele deverá ser usado com sabedoria.

Como o @LuisMendo sugeriu, você pode criar um erro (se for permitido) para finalizar o programa se o idioma ainda não tiver um "programa final" embutido.

Kritixi Lithos
fonte
2
Mesmo se a língua não tem uma função ou instrução para finalizar o programa, um erro normalmente irá fazer
Luis Mendo
11
@LuisMendo: Concordo, embora observe que muitos problemas de poliglota proíbem especificamente a saída por travamento, porque isso facilita demais as coisas. É uma boa idéia explorá-lo quando não o fizerem.
11
Você provavelmente deve mencionar que o código da segunda parte ainda deve estar sintaticamente correto no primeiro idioma, caso contrário, os idiomas mais práticos gerarão um erro.
MilkyWay90
13

Variável ou código dentro de literais de string

Os literais de cadeia de caracteres com aspas duplas são inofensivos em muitos idiomas. Mas em alguns idiomas eles também podem conter código.

No Bash, você pode usar `...`(não termina o programa):

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

No Tcl, você pode usar [...]:

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

No PHP, você pode usar ${...}(isso gera um erro no Bash, portanto deve aparecer após o código do Bash):

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

No Ruby, você pode usar #{...}:

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

Pode haver também outros.

Essas gramáticas não são compatíveis. Isso significa que você pode colocar todo o código desses idiomas em uma string em um local inofensivo. E apenas ignorará o código não reconhecido em outros idiomas e os interpretará como conteúdo da string.

Em muitos casos, você também pode comentar facilmente um caractere de aspas duplas e criar um poliglota mais tradicional.

jimmy23013
fonte
12

Aliasing variável

Este é provavelmente um dos truques mais simples ainda (IMO) mais importantes a serem usados, especialmente porque ele pode alcançar muitos idiomas.

Exemplo:

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

Isso funcionará não apenas em Javascript, mas também em Python, Ruby, etc. Mais exemplos depois, quando penso em outros. Obviamente, sugestões de comentários / edições de post são bem-vindas.

Mama Fun Roll
fonte
5
Note-se que ao fazer eg JS / Python, é geralmente mais curto para pseudônimo alertpara printem Python (3 apenas) porque a sintaxe comentário de JS, //podem ser facilmente trabalhado em um programa Python, enquanto Python #não pode ser trabalhado em JS.
ETHproductions
11

#com base em comentários

Esta dica é um subconjunto de símbolos de comentários de exploração e citações de bloco em pelo menos um idioma

Ao criar poliglotas com muitos idiomas, especialmente idiomas prontos para produção, em vez de esolangs, pode ser útil examinar os idiomas usados #em comentários de bloco ou de linha única.

  • Existem muitos idiomas com sintaxe de comentários em bloco começando com #, e há muita variedade nos caracteres após o #.
  • A maioria desses idiomas também permite um comentário único #como uma linha, o que significa que algo que pode iniciar um comentário em bloco em um idioma é apenas um comentário comum em outro, facilitando a inserção.

Aqui está uma rápida lista resumida de idiomas que são usados #em um comentário em bloco (não exaustivo):

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 mais exemplos, consulte Código Rosetta .

Aqui está um exemplo rápido e fácil, como uma demonstração:

#|
###
#`[

print("Julia")
#=

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

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

]#
say "Perl 6"
#`[

...

# ]# # ### # |# ; =#
Sp3000
fonte
Zephyr tem #- ... -#.
DLosc
11

Discrepâncias aritméticas do operador

Para idiomas semelhantes ou poliglotas simples, às vezes é útil procurar diferenças na forma como os idiomas executam aritmética. Isso ocorre porque a maioria das linguagens (não esotéricas) possui operadores aritméticos infix e a aritmética pode ser uma maneira rápida e fácil de introduzir uma diferença.

Por exemplo:

  • ^ é XOR bit a bit em alguns idiomas e exponenciação em outros
  • / é divisão inteira em alguns idiomas e divisão de ponto flutuante em outros
    • Para os idiomas de divisão inteira, -1/2está -1em alguns idiomas (arredondar para baixo) e 0em outros (arredondar para zero)
  • -1%2está -1em alguns idiomas e 1em outros
  • --x é um não operacional em alguns idiomas (dupla negação) e pré-decremento em outros
  • 1/0 dá infinito em alguns idiomas e erros em outros
  • 1<<64dá 0 em alguns idiomas (overflow) e 36893488147419103232em outros
Sp3000
fonte
3
Um exemplo simples seria x=1;["JS","Python"][--x], que retorna o nome da linguagem em que é executado (entre JS e Python).
ETHproductions
10

Use Brainfuck

Praticamente todas as implementações do BF lançam caracteres que não são +-<>[].,, o que acontece a nosso favor!

O BF é provavelmente um dos idiomas mais fáceis de trabalhar em um poliglota por causa desse recurso, desde que você escreva a parte do BF primeiro. Depois de escrever seu código BF, é apenas uma questão de modelar qualquer outro código que você tenha em torno da estrutura BF.

Aqui está um exemplo muito simples:

.+[.+]

Isso praticamente incrementa e gera códigos de saída "para sempre" (dependendo das configurações de tempo de execução). Agora, se você quiser escrever um código aleatório, digamos, em JS, você pode:

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

Observe como o JS é moldado em torno do BF.

Certifique-se de saber que isso funciona melhor se você realmente começar a usar o BF; decentemente é mais difícil começar com outro idioma e tentar incorporar o AM.

Mama Fun Roll
fonte
6
Para poliglotas maiores, em que alguns bytes de economia ao integrar o BF não ajudam muito, eu escreveria o BF por último e agruparia o outro código no número []necessário.
SP3000
6
Isso se aplica não apenas ao cérebro, mas ao grande número de linguagens similares ao cérebro e a muitos outros tarpits de Turing.
0 '
2
A primeira x=>muda a célula, o que, neste caso, não importa, mas só queria dizer #
7897 Roman Gräf 27/11
7

Use idiomas nos quais a maioria dos caracteres não importa

Esta é uma generalização do argumento de Mama Fun Roll sobre AM . Um esolang que ignora a maioria dos caracteres é muito útil em poliglotas. Também é útil: um esolang no qual um grande conjunto de caracteres é intercambiável. Alguns exemplos:

  • Espaço em branco ignora tudo o que não é espaço, tabulação ou nova linha.
  • Brain-Flak basicamente ignora tudo o mais ()[]{}<>. ( @às vezes causa um erro quando o intérprete tenta analisá-lo como o início de um sinalizador de depuração.)
  • oOO CODE ignora tudo, exceto as letras. Além disso, todas as letras minúsculas são intercambiáveis, assim como todas as letras maiúsculas.
  • O Wierd distingue apenas caracteres em branco e não em branco.
  • No Wordy , alguns caracteres de pontuação são ignorados e todas as letras são intercambiáveis.
  • Tanto o Parêntese quanto o Inferno entre Parênteses ignoram tudo, exceto parênteses.
DLosc
fonte
Corrigi esse @erro.
Wheat Wizard
Tente combinar espaços em branco com Python
enedil
@enedil Você não precisa ter guias com Python. Você pode usarexec('''...\t\n\40''')
MilkyWay90
5

Esteja ciente dos comentários de blocos aninhados

Às vezes, vários idiomas usam a mesma sintaxe para comentários em bloco, o que é mais comum do que nunca, para criar um poliglota com os dois idiomas. No entanto, muito ocasionalmente, um dos idiomas permitirá comentários de bloco aninhados, que podem ser abusados ​​para criar caminhos de código separados.

Por exemplo, considere este poliglota:

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

Nim e Lily usam #[e ]#iniciam e encerram comentários de bloco, mas apenas Nim permite comentários de bloco aninhados.

Lily considera o segundo #[como parte do comentário em bloco singular e o primeiro ]#como finalizando o comentário em bloco. (A #declaração impressa de Lily a seguir é um comentário de linha que oculta o código de Nim.)

Nim como alternativa, vê o #[]#comentário de bloco aninhado (embora vazio) e print("Lily")#o comentário de bloco externo.

Chance
fonte
4

Não tenho certeza se isso conta, mas ...

Use uma linha shebang para transformar tudo em um perlprograma válido

De acordo com esta resposta e a documentação do Perl, se você passar qualquer arquivo que comece com uma linha shebang perl, ele chamará o programa apropriado para executá-lo. Por exemplo, isso

#!/usr/bin/python

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

é executado pelo interpretador Python se você chamar perl filename.py.

Federico Poloni
fonte
3
Embora o programa possa ser chamado perl, ele não se torna um programa Perl.
Paŭlo Ebermann 27/11
2
@ PaŭloEbermann Percebo que é limítrofe, é por isso que comecei minha resposta com "não tenho certeza se conta". :) Mas o que define Perl verdadeiro, se não "o que está escrito na documentação e retornado pela implementação de referência perl"? Soa como um meme boa philosoraptor ...
Federico Poloni
11
(Veja também esta resposta meta .)
Federico Poloni
4

Chame funções inexistentes e saia enquanto avalia seus argumentos

Muitas linguagens de programação são capazes de analisar um identificador arbitrário seguido por um par de parênteses com expressões dentro:

identifier(1 + 1)

Às vezes, a forma do identificador em questão pode ser corrigida, devido à necessidade de fornecer código para um idioma diferente que você está usando. Isso pode parecer a princípio causar problemas, se o identificador não corresponder a uma função que o idioma realmente possui.

No entanto, muitas linguagens de programação avaliarão os argumentos de uma função antes de verificar se a própria função realmente existe (por exemplo, Lua) e, portanto, você pode usar esse tipo de construção de qualquer maneira; tudo o que você precisa é sair do programa em algum lugar dentro dos argumentos da função.

Aqui está um exemplo, um poliglota dc / Lua:

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

c2pqé um programa dc para imprimir 2 e sair; Lua vê isso como o nome de uma função, mas Lua pode ser impedida de erro através da colocação de um comando de saída em seu argumento. A grande vantagem dessa construção é que, diferentemente de uma atribuição ( c2pq =), ela não é automaticamente incompatível com idiomas nos quais os nomes de variáveis ​​começam com um sigilo; A sintaxe do nome da função é muito mais consistente entre os idiomas do que a sintaxe do nome da variável.


fonte