Suggerimenti per la creazione di poliglotti


48

Un è un programma che può essere eseguito in 2 o più linguaggi di programmazione diversi.

Quali suggerimenti generali hai per creare poliglotti o scegliere lingue facili da scrivere per un compito specifico?

Si prega di pubblicare i suggerimenti che potrebbero essere applicati nella maggior parte delle situazioni. Cioè non dovrebbero funzionare solo in poliglotti di due lingue specifiche. (Potresti semplicemente pubblicare una risposta a una domanda poliglotta se hai un suggerimento troppo specifico.) Ma puoi introdurre funzionalità di una lingua che semplifica il lavoro con molte lingue o che può essere aggiunto a qualsiasi poliglotta esistente.

Si prega di inviare un suggerimento per risposta. Sentiti libero di suggerire la modifica se un suggerimento specifico per una lingua si applica anche a un'altra lingua.

Risposte:


25

Sfrutta i simboli dei commenti

Un modo semplice per creare un poliglotta in due lingue è di dividere il codice in due parti come segue:

  1. La prima parte esegue il lavoro effettivo nella lingua A, è innocua nella lingua B (nessun errore) e termina con un simbolo di commento lingua-A, che nasconde la seconda parte alla lingua A.
  2. La seconda parte esegue il lavoro effettivo nella lingua B.

così

  • La lingua A vede la prima parte, che fa il lavoro, e quindi un commento.
  • La lingua B vede una prima parte inutile e poi la seconda parte, che fa il lavoro.

L'unica parte difficile qui è trovare una serie di dichiarazioni (prima parte) che fanno il lavoro nella lingua A senza dare errori nella lingua B. Alcuni suggerimenti per questo:

  • La maggior parte delle lingue basate su stack consente di visualizzare solo la parte superiore dello stack alla fine del programma (a volte questo è persino predefinito, come in 05AB1E).
  • Alcune lingue ignorano le dichiarazioni indefinite (ad esempio Golfscript).

Un semplice esempio che utilizza queste linee guida può essere trovato qui . Le lingue A e B sono MATL e 05AB1E rispettivamente.


24

Usa linguaggi bidimensionali

A differenza dei linguaggi monodimensionali, che generalmente analizzano l'intero codice sorgente e producono errori di sintassi o effetti di runtime indesiderati su cose che non comprendono (costringendoti così a nascondere il codice di altri linguaggi da essi), i linguaggi bidimensionali tendono solo a codice di analisi nel percorso di esecuzione, il che significa che l'intero resto del programma viene ignorato. C'è anche molto più spazio per dividere i percorsi di esecuzione l'uno dall'altro in due dimensioni; puoi inviare il puntatore dell'istruzione ruotando in una direzione insolita, come verso il basso o addirittura a sinistra (avvolgendolo attorno al lato destro del programma), per toglierlo di mezzo molto rapidamente. Le tecniche utili nei linguaggi monodimensionali si generalizzano anche ai linguaggi bidimensionali (ad esempio è possibile saltare il codice con;; in Befunge-98, oltre a inviare l'IP in una strana direzione), rendendolo per lo più solo un guadagno rigoroso rispetto a una soluzione unidimensionale.

Come bonus, diverse lingue bidimensionali hanno un punto di ingresso diverso da quello in alto a sinistra del programma, il che significa che non è necessario fare alcuno sforzo per separarli da altre lingue; si separeranno naturalmente dal gruppo.


20

Conosci i tuoi veri e falsi

Ogni lingua vede "vero" e "falso" in un modo leggermente diverso. Se hanno una sintassi simile, puoi sfruttarla aggiungendo una decisione che le lingue gestiranno in modo diverso.

Un esempio del thread Trick or Treat utilizza ''una stringa vuota. In Lua, questo è veritiero, ma falso in Python, quindi il seguente:

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

..stamperà una stringa diversa in ogni lingua.

Basta trovare un valore come questo. Ad esempio, potresti usare '0', che valuta falsein PHP ma truein Python.


17

Blockquotes in almeno una lingua

Ecco un esempio che funziona sia in Python che in C ++

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

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

Luis Mendo ha lanciato quella che penso sia di gran lunga la soluzione più semplice, ovvero usare i commenti.

Cerchi una lingua con commenti a blocchi e un'altra lingua in cui la sintassi regolare nella prima è la sintassi dei commenti nella seconda.

Ancora più facili sono due lingue con stili di commento a blocchi diversi che sono sintassi intercambiabile corretta, ma non potrei preoccuparmi di controllare.

Dai un'occhiata in Python 3.5 e C ++


2
La prima riga non dovrebbe avere un punto e virgola.

Vero. Buon punto
dexgecko,

15

Dividere e conquistare

Quando scrivi un poliglotta in un gran numero di lingue, non sarai necessariamente in grado di separare immediatamente tutti i flussi di controllo della lingua. Pertanto, è necessario "vero poliglotta" alcune lingue per un certo periodo di tempo, consentendo l'esecuzione dello stesso codice in ciascuna di esse. Ci sono due regole principali da tenere a mente mentre lo fai:

  • Il flusso di controllo in due lingue dovrebbe essere molto simile o molto diverso . Cercare di gestire un gran numero di flussi di controllo interfogliati è una ricetta per confondersi e rendere difficile la modifica del programma. Invece, dovresti limitare la quantità di lavoro che devi fare assicurandoti che tutti i programmi che si trovano nello stesso posto siano lì per lo stesso motivo e possano essere felicemente eseguiti in parallelo per tutto il tempo necessario. Nel frattempo, se una lingua è molto diversa dalle altre, vuoi che la sua esecuzione si sposti al più presto in una posizione molto diversa, in modo da non dover provare a rendere il tuo codice conforme a due diversi modelli sintattici contemporaneamente.

  • Cerca le opportunità per dividere una lingua o un gruppo di lingue simili l'una dall'altra. Lavora da gruppi più grandi fino a gruppi più piccoli. Una volta che hai un gruppo di lingue simili tutte a un certo punto del programma, dovrai dividerle a un certo punto. All'inizio del programma, potresti, per esempio, voler dividere le lingue che usano #come marcatore di commenti lontano dalle lingue che usano qualche altro marcatore di commento. Più tardi, forse hai un punto in cui tutte le lingue usano la f(x)sintassi per le chiamate di funzione, separano i comandi con punto e virgola e hanno similitudini sintattiche simili. A quel punto, potresti usare qualcosa di molto più specifico del linguaggio per dividerli, ad esempio il fatto che Ruby e Perl non elaborano le sequenze di escape nelle ''stringhe, ma Python e JavaScript lo fanno.

In generale, il flusso logico del tuo programma dovrebbe finire come un albero, dividendosi ripetutamente in gruppi di lingue che sono più simili tra loro. Ciò pone la maggior parte delle difficoltà nello scrivere il poliglotta all'inizio, prima della prima divisione. Man mano che il flusso di controllo si dirama sempre di più e le lingue in esecuzione in un dato punto diventano sempre più simili, l'attività diventa più semplice poiché è possibile utilizzare una sintassi più avanzata senza causare errori di sintassi nelle lingue coinvolte.

Un buon esempio è il set {JavaScript, Ruby, Perl, Python 3}; tutte queste lingue accettano chiamate di funzione tra parentesi e possono separare le istruzioni con punto e virgola. Tutti supportano anche una evaldichiarazione, che consente in modo efficace di eseguire il controllo del flusso in modo portatile. (Perl è il migliore di questi linguaggi per separarsi dal gruppo in anticipo, perché ha una sintassi diversa per le variabili dagli altri.)


13

Nasconde il codice all'interno dei valori letterali delle stringhe

Nella maggior parte delle lingue, una stringa da sola non fa nulla o fa qualcosa che può essere facilmente invertita (come spingere la stringa sulla pila). La sintassi letterale delle stringhe è anche relativamente non standardizzata, specialmente per le sintassi alternative che molte lingue usano per gestire le stringhe con newline incorporate; per esempio, Python ha """ ... """, Perl ha q( ... )e Lua ha [[ ... ]].

Esistono due usi principali di questi. Uno è quello di permetterti di intercalare sezioni destinate a lingue diverse avviando una stringa alla fine della prima sezione di una lingua e riprendendola all'inizio della seconda: dovrebbe essere abbastanza facile evitare di chiudere accidentalmente la stringa a causa della varietà di delimitatori di stringa tra lingue diverse. L'altro è che molti delimitatori di stringhe sono significativi come comando in altre lingue (spesso più dei marcatori di commenti), quindi puoi fare qualcosa del genere x = [[4] ], che è un compito innocuo in lingue che usano la notazione JSON per gli elenchi, ma che inizia una stringa in Lua (e quindi ti permette di dividere il codice Lua dal resto, dato che effettivamente "salta" al successivo ]]).


13

Terminare il programma

È possibile terminare bruscamente il programma in una lingua in modo che ignori il codice in un'altra lingua.

Quindi in pratica questo formato può essere utilizzato

code_in_language1 end_program_in_language1 code_for_language2 end_program_in_language2 ...

dove end_program_in_languageNè il comando per terminare il programma.

Ad esempio, nella mia risposta in Cosa porterai per il Ringraziamento? , Ho terminato il programma in Dip e poi ho scritto il codice per un'altra lingua, V, in modo che l'interprete Dip lo 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

Ma poi, non tutte le lingue hanno un comando che può terminare il programma in questo modo. Tuttavia, se tale linguaggio ha la caratteristica, dovrebbe essere usato saggiamente.

Come suggerito da @LuisMendo, è possibile creare un errore (se consentito) per terminare il programma se la lingua non ha già un "programma finale" incorporato.


2
Anche se la lingua non ha una funzione o un'istruzione per terminare il programma, di solito un errore farà
Luis Mendo,

1
@LuisMendo: D'accordo, sebbene si noti che molti problemi di poliglottazione vietano specificamente l'uscita attraverso lo schianto perché rende le cose troppo facili. È una buona idea sfruttarlo quando non lo fanno.

1
Dovresti probabilmente menzionare che il codice della seconda parte dovrebbe essere ancora sintatticamente corretto nella prima lingua, altrimenti la maggior parte delle lingue pratiche genererà un errore.
MilkyWay90

13

Variabile o codice all'interno dei letterali di stringa

I letterali a stringa tra virgolette sono per lo più innocui in molte lingue. Ma in alcune lingue potrebbero contenere anche codice.

In Bash, puoi usare `...`(non termina il programma):

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

In Tcl, puoi usare [...]:

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

In PHP, puoi usare ${...}(questo genera un errore in Bash, quindi deve apparire dopo il codice Bash):

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

In Ruby puoi usare #{...}:

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

Potrebbero essercene anche altri.

Queste grammatiche non sono compatibili. Ciò significa che puoi mettere tutto il codice di queste lingue in una stringa in una posizione innocua. E ignorerà semplicemente il codice non riconosciuto in altre lingue e le interpreterà come contenuto della stringa.

In molti casi, puoi anche commentare facilmente un doppio carattere di virgoletta lì e creare un poliglotta più tradizionale.


12

Aliasing variabile

Questo è probabilmente uno dei trucchi più semplici (IMO) più importanti da usare, soprattutto perché può raggiungere così tante lingue.

Esempio:

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

Questo funzionerà non solo in Javascript, ma anche in Python, Ruby, ecc. Altri esempi dopo, quando penso ad altri. Naturalmente, i suggerimenti per i commenti / le modifiche ai post sono i benvenuti.


5
Si noti che quando si fa ad esempio JS / Python, di solito è più breve di alias alerta printin Python (3 solo) a causa commento la sintassi di JS, //, può essere facilmente lavorato in un programma Python, mentre Python #non può essere lavorato in JS.
ETHproductions

11

#commenti basati

Questo suggerimento è un sottoinsieme di simboli di commenti sugli exploit e Blockquotes in almeno una lingua

Quando si creano poliglotti con molte lingue, in particolare le lingue pronte per la produzione rispetto agli esolang, può essere utile esaminare le lingue che utilizzano #in blocco o commenti a riga singola.

  • Ci sono molte lingue con cui iniziano le sintassi dei commenti a blocchi #e c'è molta varietà nei caratteri che seguono il #.
  • La maggior parte di queste lingue consente anche un singolo #come commento di linea, il che significa che qualcosa che potrebbe iniziare a bloccare un commento in una lingua è solo un normale commento in un'altra, rendendolo facile da adattare.

Ecco un breve elenco riepilogativo delle lingue che usano #in un commento a blocchi (non esaustivo):

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

Per altri esempi, vedi Rosetta Code .

Ecco un esempio semplice e veloce, come dimostrazione:

#|
###
#`[

print("Julia")
#=

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

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

]#
say "Perl 6"
#`[

...

# ]# # ### # |# ; =#

Zephyr ha #- ... -#.
DLosc,

11

Discrepanze dell'operatore aritmetico

Per lingue simili o semplici poliglotti, a volte è utile cercare differenze nel modo in cui le lingue eseguono l'aritmetica. Questo perché la maggior parte delle lingue (non esoteriche) ha aggiunto operatori aritmetici e l'aritmetica può essere un modo rapido e semplice per introdurre una differenza.

Per esempio:

  • ^ è XOR bit per bit in alcune lingue e esponenziazione in altre
  • / è la divisione intera in alcune lingue e la divisione in virgola mobile in altre
    • Per le lingue delle divisioni intere, -1/2è -1in alcune lingue (arrotondato per difetto) e 0in altre (arrotondato a zero)
  • -1%2è -1in alcune lingue e 1in altre
  • --x è vietato in alcune lingue (doppia negazione) e pre-decremento in altre
  • 1/0 dà l'infinito in alcune lingue e gli errori in altre
  • 1<<64dà 0 in alcune lingue (overflow) e 36893488147419103232in altre

3
Un semplice esempio potrebbe essere x=1;["JS","Python"][--x], che restituisce il nome della lingua in cui è eseguito (tra JS e Python).
ETHproductions

10

Usa Brainfuck

Praticamente tutte le implementazioni di BF hanno scartato i caratteri che non lo sono +-<>[].,, cosa che a caso sembra funzionare a nostro favore!

BF è probabilmente uno dei linguaggi più semplici per lavorare in un poliglotta a causa di questa funzione, purché si scriva prima la parte BF. Una volta che hai scritto il tuo codice BF, si tratta solo di modellare qualsiasi altro codice tu abbia intorno alla struttura BF.

Ecco un esempio davvero semplice:

.+[.+]

Questo praticamente incrementa e genera charcode "per sempre" (a seconda delle impostazioni di runtime). Ora, se volessi scrivere un pezzo di codice casuale, diciamo, in JS, potresti fare:

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

Notare come JS è modellato attorno al BF.

Assicurati di sapere che funziona meglio se sei davvero pronto per iniziare con BF; è piuttosto difficile iniziare con un'altra lingua e provare a incorporare BF.


6
Per i poliglotti più grandi in cui alcuni byte di risparmi derivanti dall'integrazione del BF non aiutano molto, scriverei il BF per ultimo e impaccherei l'altro codice in quanti ne sono []necessari.
Sp3000,

6
Questo vale non solo per Brainfuck ma per l'enorme numero di linguaggi simili a Brainfuck e per molti altri tarp di Turing.
0 "

2
Il primo x=>cambia la cella, che in questo caso non ha importanza, ma volevo solo dirlo
Roman Gräf,

7

Usa le lingue in cui la maggior parte dei personaggi non contano

Questa è una generalizzazione del punto di Mama Fun Roll su BF . Un esolang che ignora la maggior parte dei personaggi è molto utile nei poliglotti. Utile anche: un esolang in cui un ampio set di personaggi è intercambiabile. Qualche esempio:

  • Lo spazio bianco ignora tutto ciò che non è spazio, tabulazione o newline.
  • Brain-Flak praticamente ignora tutto oltre ()[]{}<>. (a @volte provoca un errore quando l'interprete tenta di analizzarlo come l'inizio di un flag di debug.)
  • oOo CODICE ignora tutto tranne le lettere. Inoltre, tutte le lettere minuscole sono intercambiabili, così come tutte le lettere maiuscole.
  • Wierd distingue solo tra caratteri bianchi e non bianchi.
  • In Wordy , alcuni caratteri di punteggiatura vengono ignorati e tutte le lettere sono intercambiabili.
  • Sia parenthetic e parentesi inferno ignorano tutto tranne parentesi.

Ho corretto @quell'errore.
Mago del grano,

Prova a combinare Whitespace con Python
enedil il

@enedil Non è necessario disporre di schede con Python. Puoi usareexec('''...\t\n\40''')
MilkyWay90 il

5

Fai attenzione ai commenti sul blocco nidificato

A volte più lingue useranno la stessa sintassi per i commenti di blocco, che è spesso un punto di rottura per la creazione di un poliglotta con le due lingue. Molto occasionalmente, tuttavia, una delle lingue consentirà commenti a blocchi nidificati, che possono essere abusati per creare percorsi di codice separati.

Ad esempio, considera questo poliglotta:

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

Nim e Lily usano entrambi #[e ]#per iniziare e terminare i commenti di blocco, ma solo Nim consente commenti di blocco nidificati.

Lily considera il secondo #[come parte del singolo commento di blocco e il primo ]#come termine del commento di blocco. (La #seguente dichiarazione stampata di Lily è un commento di riga che nasconde il codice di Nim.)

Nim in alternativa, vede il #[]#commento di blocco nidificato (anche se vuoto) e print("Lily")#come commento di blocco esterno.


4

Non sono sicuro se questo conta, ma ...

Usa una linea shebang per trasformare tutto in un perlprogramma valido

Secondo questa risposta e la documentazione Perl, se si passa a qualsiasi file che inizia con una riga shebang perl, viene richiamato il programma appropriato per eseguirlo. Ad esempio, questo

#!/usr/bin/python

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

viene eseguito dall'interprete Python se chiami perl filename.py.


3
Mentre il programma può essere chiamato con perl, non diventa un programma Perl.
Paŭlo Ebermann,

2
@ PaŭloEbermann Mi rendo conto che è borderline, ecco perché ho iniziato la mia risposta con "non sono sicuro se conta". :) Ma cosa definisce il vero Perl, se non "ciò che è scritto nella documentazione e restituito dall'implementazione di riferimento perl"? Sembra un buon meme filosofo ...
Federico Poloni,


4

Chiamare funzioni inesistenti, quindi uscire durante la valutazione dei loro argomenti

Molti linguaggi di programmazione sono in grado di analizzare un identificatore arbitrario seguito da una coppia di parentesi con espressioni all'interno:

identifier(1 + 1)

A volte, la forma dell'identificatore in questione potrebbe essere corretta, a causa della necessità di fornire il codice a una lingua diversa che si sta utilizzando. A prima vista ciò potrebbe causare problemi, se l'identificatore non corrisponde a una funzione effettivamente utilizzata dalla lingua.

Tuttavia, molti linguaggi di programmazione valuteranno gli argomenti di una funzione prima di verificare se la funzione stessa esiste effettivamente (ad esempio Lua), e quindi è possibile utilizzare questo tipo di costrutto comunque; tutto ciò che serve è uscire dal programma da qualche parte all'interno degli argomenti della funzione.

Ecco un esempio, un poliglotta dc / Lua:

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

c2pqè un programma in cc per stampare 2 ed uscire; Lua vede questo come il nome di una funzione, ma è possibile impedire ad Lua di sbagliare inserendo un comando exit nel suo argomento. Il grande vantaggio di questa costruzione è che, diversamente da un'assegnazione ( c2pq =), non è automaticamente incompatibile con le lingue in cui i nomi delle variabili iniziano con un sigillo; la sintassi del nome della funzione è molto più coerente tra le lingue rispetto alla sintassi del nome della variabile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.