Traduci Preludio in Befunge


19

Questa è la sfida settimanale n. 2. Tema: traduzione

Scrivi un programma o una funzione che accetta il codice sorgente per un programma in Prelude e genera il codice per un programma equivalente in Befunge-93 . Perché il programma sia equivalente, dovrebbe, per ogni dato input, produrre lo stesso output del programma Prelude e arrestarsi se e solo se il programma Prelude si arresta.

Lingua di input: Preludio

Interprete Python:

Un programma Prelude è costituito da un numero di "voci" che eseguono le istruzioni contemporaneamente. Le istruzioni per ciascuna voce sono su una linea separata. Ogni voce ha uno stack separato, che viene inizializzato con una quantità infinita di zero. L'esecuzione inizia nella colonna più a sinistra e avanza di una colonna a destra ogni segno di spunta, tranne quando influenzato da )o (istruzioni. Il programma termina quando viene raggiunta l'ultima colonna.

Preludio delle specifiche per questa sfida:

Digits 0-9      Push onto the stack a number from 0 to 9. Only single-digit
                    numeric literals can be used.
^               Push onto the stack the top value of the stack of the above 
                    voice.
v               Push onto the stack the top value of the stack of the below 
                    voice.
#               Remove the top value from the stack.
+               Pop the top two integers from the stack and push their sum.
-               Pop the top two integers from the stack, subtract the topmost 
                    from the second, and push the result.
(               If the top of the stack is 0, jump to the column after the 
                    matching `)` after the current column executes.
)               If the top of the stack is not 0, jump to the column after 
                    the matching `(` after the current column executes.
?               Read an integer from STDIN.
!               Pop one value from the stack and print it to STDOUT as an
                    integer.
<space>         No-op

Appunti

  • ve ^agiscono ciclicamente, in modo che la vvoce in basso copi l'elemento stack della voce in alto e ^sulla voce in alto copierà dalla voce in basso. Corollario: entrambi ve ^duplicare la parte superiore dello stack in un programma a voce singola.
  • A (e la sua corrispondenza )possono trovarsi su linee diverse. Tuttavia , a )guarderà sempre la pila della voce in cui è (stata posizionata la corrispondente , non la pila in cui )è posizionata la stessa.
  • I valori prodotti dalle istruzioni ^e vfunzionano sui valori presenti prima del completamento di qualsiasi altra operazione nella stessa colonna.
  • ?e !operare diversamente dalle specifiche trovate su esolangs.org, quindi assicurati di provare con l'interprete leggermente modificato fornito in questo post.

L'ingresso è garantito per avere:

  • Parentesi corrispondenti
  • Non più di una parentesi in una colonna
  • Stesso numero di caratteri su ogni riga
  • Almeno una riga
  • Nessuna colonna con più di un'istruzione I / O ( !o ?)
  • Un carattere di avanzamento riga dopo le istruzioni per ciascuna voce
  • Nessun personaggio diverso da quelli sopra menzionati

Lingua di output: Befunge-93

Befunge è un linguaggio basato su stack il cui contatore del programma (PC; un puntatore all'istruzione corrente) si muove liberamente su una griglia bidimensionale. Inizia nell'angolo in alto a sinistra, spostandosi a destra. Il campo di gioco è toroidale, ovvero il movimento del PC avvolge entrambi i bordi. Befunge ha anche uno stack inizializzato su un numero infinito di zero. Befunge ha le seguenti operazioni:

È possibile assumere le seguenti caratteristiche del compilatore / interprete Befunge-93:

  • I numeri interi sono di precisione illimitata.
  • Permette reti di qualsiasi dimensione.
  • Le coordinate della griglia (per ge p) sono basate su 0.

punteggio

Al fine di prevenire invii che producono semplicemente un interprete Prelude in Befunge e codificano il sorgente Prelude in esso, l'obiettivo sarà quello di ridurre al minimo le dimensioni del codice sorgente Befunge risultante.

Di seguito sono forniti alcuni programmi Prelude. Il tuo traduttore verrà eseguito su tutti questi. Il tuo punteggio è la somma delle dimensioni dei programmi Befunge, a condizione che siano tutti validi.

Il tuo traduttore non dovrebbe essere ottimizzato in modo specifico per questi casi di test (ad es. Programmando a mano i programmi Befunge scritti a mano). Se sospetto che ci sia una risposta, mi riservo il diritto di modificare gli input o crearne di nuovi.

Ingressi campione

Stampa n-1fino a 0:

?(1-^!)

E logico:

?  (0)  
 ?(0  ) 
    1  !

O logico:

 ?   (0) 
? (0)    
   1  1 !

Verificare la parità di input (ovvero modulo 2) di numero non negativo:

?(1-)   
 ^  v   
 v1-^^-!

Square l'input:

 ^    
  ^+ !
?(1-) 

Stampa il n -esimo numero di Fibonacci, in cui n = 0corrisponde a 0 e n = 1corrisponde a 1:

0 v+v!
1   ^ 
?(1-) 

Signum:

  1) v #  - !
 vv (##^v^+) 
?(#   ^   ## 

Divisione per input non negativi:

1 (#  1) v #  - 1+)   
     vv (##^v^+)      
?  v-(0 # ^   #       
 ?                    
   1+              1-!

Ovviamente, il tuo programma deve mostrare lo stesso comportamento per tutti i casi, anche se il comportamento del programma di esempio per i numeri negativi non è specificato.

Infine, il tuo traduttore non dovrebbe essere irragionevolmente lungo:

  • Deve essere contenuto in un post di Stack Exchange
  • Dovrebbe elaborare gli input di esempio in meno di 10 minuti su un tipico computer desktop.

Si noti che un input numerico per Prelude o Befunge viene fornito come segno meno facoltativo seguito da una o più cifre decimali, seguite da una nuova riga. L'altro input è un comportamento indefinito.

Puoi scrivere il tuo traduttore in qualsiasi lingua. Vince il codice Befunge tradotto più breve.

Classifica

  • Sp3000 : 16430 byte

Non capisco: "Spingi nello stack il valore più alto nello stack della voce sopra." Non deve essere: "Spingi nello stack il valore più alto dello stack della voce sopra."
Def

Dice che preludio esegue le voci contemporaneamente, significa che sono realmente eseguite su un thread separato o posso semplicemente eseguire i primi comandi su tutte le voci (dall'alto verso il basso), quindi i secondi comandi e così via.
Def

@Deformyer L'ho cambiato da "on" a "of", ma ho pensato che "il massimo valore nello stack" non era neanche sbagliato. Per quanto riguarda la simultaneità, no non è necessario interpretarli effettivamente in parallelo. L'importante è che agiscano tutti sullo stato precedente degli stack e nessuna operazione in una determinata colonna può influire su qualsiasi altra operazione in quella colonna.
Martin Ender,

Molti casi di test non violano "Nessuna colonna con più di un'istruzione I / O (! O?)?"
Fuwjax

@proudhaskeller 1È all'interno di un loop, quindi potrebbe non essere premuto. Uno 0 può derivare dalla quantità infinita di 0 che iniziano sugli stack.
feersum

Risposte:


10

Python 3, segnerà più tardi

from collections import defaultdict
from functools import lru_cache
import sys

NUMERIC_OUTPUT = True

@lru_cache(maxsize=1024)
def to_befunge_num(n):
    # Convert number to Befunge number, using base 9 encoding (non-optimal,
    # but something simple is good for now)

    assert isinstance(n, int) and n >= 0

    if n == 0:
        return "0"

    digits = []

    while n:
        digits.append(n%9)
        n //= 9

    output = [str(digits.pop())]

    while digits:
        output.append("9*")
        d = digits.pop()

        if d:
            output.append(str(d))
            output.append("+")

    output = "".join(output)

    if output.startswith("19*"):
        return "9" + output[3:]

    return output

def translate(program_str):
    if program_str.count("(") != program_str.count(")"):
        exit("Error: number of opening and closing parentheses do not match")

    program = program_str.splitlines()
    row_len = max(len(row) for row in program)
    program = [row.ljust(row_len) for row in program]
    num_stacks = len(program)


    loop_offset = 3
    stack_len_offset = program_str.count("(")*2 + loop_offset
    stack_offset = stack_len_offset + 1
    output = [[1, ["v"]], [1, [">"]]] # (len, [strings]) for each row
    max_len = 1 # Maximum row length so far

    HEADER_ROW = 0
    MAIN_ROW = 1
    FOOTER_ROW = 2
    # Then stack lengths, then loop rows, then stacks

    # Match closing parens with opening parens
    loop_map = {} # {column: (loop num, stack number to check, is_start)}
    loop_stack = []
    loop_num = 0

    for col in range(row_len):
        col_str = "".join(program[stack][col] for stack in range(num_stacks))

        if col_str.count("(") + col_str.count(")") >= 2:
            exit("Error: more than one parenthesis in a column")

        if "(" in col_str:
            stack_num = col_str.index("(")

            loop_map[col] = (loop_num, stack_num, True)
            loop_stack.append((loop_num, stack_num, False))
            loop_num += 1

        elif ")" in col_str:
            if loop_stack:
                loop_map[col] = loop_stack.pop()

            else:
                exit("Error: mismatched parentheses")


    def pad_max(row):
        nonlocal max_len, output

        while len(output) - 1 < row:
            output.append([0, []])

        if output[row][0] < max_len:
            output[row][1].append(" "*(max_len - output[row][0]))
            output[row][0] = max_len


    def write(string, row):
        nonlocal max_len, output

        output[row][1].append(string)
        output[row][0] += len(string)

        max_len = max(output[row][0], max_len)


    def stack_len(stack, put=False):
        return (to_befunge_num(stack) + # x
                str(stack_len_offset) + # y
                "gp"[put])


    def get(stack, offset=0):
        assert offset in [0, 1] # 1 needed for 2-arity ops

        # Check stack length
        write(stack_len(stack) + "1-"*(offset == 1) + ":0`", MAIN_ROW)

        pad_max(HEADER_ROW)
        pad_max(MAIN_ROW)
        pad_max(FOOTER_ROW)

        write(">" + to_befunge_num(stack + stack_offset) + "g", HEADER_ROW)
        write("|", MAIN_ROW)
        write(">$0", FOOTER_ROW)

        pad_max(HEADER_ROW)
        pad_max(MAIN_ROW)
        pad_max(FOOTER_ROW)

        write("v", HEADER_ROW)
        write(">", MAIN_ROW)
        write("^", FOOTER_ROW)


    def put(stack, value=""):
        put_inst = (value +
                    stack_len(stack) +
                    to_befunge_num(stack + stack_offset) +
                    "p")

        post_insts.append(put_inst)


    def pop(stack):
        put(stack, "0")


    def inc_stack_len(stack):
        post_insts.append(stack_len(stack) + "1+")
        post_insts.append(stack_len(stack, put=True))


    def dec_stack_len(stack):
        post_insts.append(stack_len(stack) + ":0`-") # Ensure nonnegativity
        post_insts.append(stack_len(stack, put=True))


    # Technically not necessary to initialise stack lengths per spec, but it makes it
    # more portable and easier to test against other Befunge interpreters

    for stack in range(num_stacks):
        write("0" + stack_len(stack, put=True), MAIN_ROW)

    for col in range(row_len):
        post_insts_all = []

        loop_start = False
        loop_end = False

        if col in loop_map:
            if loop_map[col][2]:
                loop_start = True
            else:
                loop_end = True

        if loop_start:
            loop_row = loop_offset + 2*loop_map[col][0]
            get(loop_map[col][1])

        elif loop_end:
            get(loop_map[col][1])
            write("!", MAIN_ROW)


        for stack in range(num_stacks-1, -1, -1):
            char = program[stack][col]
            post_insts = [] # Executed after the gets in reverse order, i.e. last added first

            if char in " ()":
                continue

            # Pre-inc, post-dec
            elif char.isdigit():
                inc_stack_len(stack)
                put(stack, char)

            elif char == "?":
                inc_stack_len(stack)
                put(stack, "&")

            elif char == "!":
                get(stack)
                post_insts.append(".91+," if NUMERIC_OUTPUT else ",")
                pop(stack)
                dec_stack_len(stack)

            elif char == "#":
                pop(stack)
                dec_stack_len(stack)

            elif char in "+-":
                get(stack, 1)
                get(stack)
                post_insts.append(char)
                pop(stack) # This one first in case of ! or 1!
                post_insts.append(stack_len(stack) + ":1`-:1\\`+") # Ensure >= 1
                post_insts.append(stack_len(stack, put=True))
                put(stack)                

            elif char in "^v":
                offset = -1 if char == "^" else 1

                get((stack + offset) % num_stacks)
                inc_stack_len(stack)
                put(stack)

            else:
                exit("Error: invalid character " + char)

            post_insts_all.append(post_insts)


        while post_insts_all:
            write("".join(post_insts_all.pop()), MAIN_ROW)

        if loop_start or loop_end:
            loop_row = loop_offset + 2*loop_map[col][0]

            pad_max(HEADER_ROW)
            pad_max(MAIN_ROW)
            pad_max(loop_row)
            pad_max(loop_row + 1)

            write(">v", HEADER_ROW)
            write("|>", MAIN_ROW)

            if loop_start:
                write(" ^", loop_row)
                write(">", loop_row + 1)

            else:
                write("<", loop_row)
                write(" ^", loop_row + 1)


    write("@", MAIN_ROW)
    return "\n".join("".join(row) for row_len, row in output)

if __name__ == '__main__':
    if len(sys.argv) < 3:
        exit("Usage: py -3 prefunge.py <input filename> <output filename>")

    with open(sys.argv[1]) as infile:
        with open(sys.argv[2], "w") as outfile:
            outfile.write(translate(infile.read()))

Corri come py -3 prefunge.py <input filename> <output filename>.

È stata una settimana lenta per me, quindi ero finalmente abbastanza annoiato da affrontare questa domanda di sei mesi. Chiederei perché nessun altro ci abbia provato, ma sento ancora il dolore per il debug (e probabilmente ci sono ancora bug per tutto quello che so).

La domanda non fornisce un interprete Befunge-93, quindi ho usato questo , che è leggermente diverso dalle specifiche. Le due differenze principali sono:

  • Se un carattere non esiste in una determinata riga del programma, non è possibile scrivere su quella riga. Ciò significa che dovrai premere Invio più volte per introdurre abbastanza nuove righe alla fine . Se vedi NaNs nell'output, questa è la causa più probabile.

  • Le celle della griglia non sono preinizializzate a zero - per comodità ho incluso un po 'di preinizializzazione nelle uscite di Befunge, ma poiché non è necessario potrei toglierlo quando inizio il punteggio.

Il layout principale dei programmi di output è questo:

v [header row]
> [main row]
  [footer row]
  ---
   |
   | rows for loops (2 per loop)
   |
  ---
  [stack length row]
  ---
   |
   | rows for stack space (1 per voice)
   |
  ---

Lo spazio dello stack è esterno al programma, quindi il nuovo commento Enter-spamming di prima.

L'idea principale è assegnare ad ogni voce una riga che funge da pila. Per mantenere queste pile, abbiamo anche una riga di lunghezza pila speciale in cui la lunghezza di ciascuna pila è registrata in una cella lungo la fila. Il programma è quindi un sacco di gets e puts, ad esempio per stampare il processo è:

  • Prendi la cella a y = stack_row[stack], x = stack_length[stack]
  • Esegui .91+,, ovvero stampa come numero intero, quindi stampa una nuova riga
  • Sostituisci la cella con le corde sopra con 0 (per simulare lo scoppiettio)
  • diminuzione stack_length[stack]

Per eseguire la valutazione simultanea di una colonna, vengono lette tutte le celle necessarie e i loro valori vengono mantenuti in pila prima di scrivere qualsiasi cella (ad esempio, per l'esempio di stampa, potrebbero esserci più istruzioni tra il primo e il secondo passaggio).

`, che è maggiore di, viene utilizzato per assicurarsi che le lunghezze dello stack non diventino mai negative e per spingere 0s quando lo stack è vuoto. Questo è da dove proviene la ramificazione chiaramente visibile, ma ho un'idea che rimuoverà la ramificazione, che dovrebbe rimuovere una grande quantità di spazio bianco dalla prima e dalla terza riga.

Per i loop, poiché i loop Prelude possono saltare in entrambi i modi, utilizziamo due righe per loop in una configurazione come questa:

       >v                     >v
(cond) |>  (program)  (cond) !|>

        ^                     <
       >                       ^

Questi loop attualmente costituiscono la maggior parte dei byte, ma possono essere facilmente eliminati inserendoli nella codebox con p , cosa che intendo fare dopo che sono contento che il traduttore correttamente.

Ecco alcuni esempi di output per ?(1-^!), ad es. Stampa n-1fino a 0:

v                        >6gv>v                      >6gv      >6gv                                 >6gv                   >6gv                           >6gv >v
>005p05g1+05p&05g6p05g:0`|  >|>05g1+05p105g6p05g1-:0`|  >05g:0`|  >-005g6p05g:1`-:1\`+05p05g6p05g:0`|  >05g1+05p05g6p05g:0`|  >.91+,005g6p05g:0`-05p05g:0`|  >!|>@
                         >$0^                        >$0^      >$0^                                 >$0^                   >$0^                           >$0^
                              ^                                                                                                                                <
                             >                                                                                                                                  ^

Square-la-di ingresso:

v                                >8gv      >8gv             >v      >6gv                                   >8gv      >8gv        >7gv      >7gv                                                            >8gv >v      >7gv
>005p015p025p25g1+25p&25g8p25g:0`|  >25g:0`|  >05g1+05p05g6p|>05g:0`|  >15g1+15p15g7p25g1+25p125g8p25g1-:0`|  >25g:0`|  >15g1-:0`|  >15g:0`|  >+015g7p15g:1`-:1\`+15p15g7p-025g8p25g:1`-:1\`+25p25g8p25g:0`|  >!|>15g:0`|  >.91+,015g7p15g:0`-15p@
                                 >$0^      >$0^                     >$0^                                   >$0^      >$0^        >$0^      >$0^                                                            >$0^         >$0^
                                                             ^                                                                                                                                                  <
                                                            >                                                                                                                                                    ^

Divisione (sono consigliati piccoli input):

v                                                                          >91+gv>v      >94+gv                                                         >95+gv      >95+gv        >93+gv      >93+gv                                                                    >93+gv      >93+gv               >v      >93+gv                                                     >93+gv >v      >92+gv                  >v      >92+gv                                       >92+gv                                       >91+gv                                       >93+gv                     >91+gv                       >92+gv      >92+gv        >91+gv      >91+gv                                                                                      >92+gv >v                        >91+gv      >91+gv                                     >91+gv >v                        >95+gv      >95+gv                                     >95+gv
>009p019p029p039p049p09g1+09p109g91+p29g1+29p&29g93+p39g1+39p&39g94+p09g:0`|    >|>39g:0`|    >009g91+p09g:0`-09p29g1+29p29g93+p49g1+49p149g95+p49g1-:0`|    >49g:0`|    >29g1-:0`|    >29g:0`|    >-029g93+p29g:1`-:1\`+29p29g93+p+049g95+p49g:1`-:1\`+49p49g95+p29g:0`|    >29g:0`|    >19g1+19p19g92+p|>29g:0`|    >09g1+09p109g91+p19g1+19p19g92+p29g1+29p029g93+p29g:0`|    >!|>19g:0`|    >029g93+p29g:0`-29p|>19g:0`|    >09g1+09p09g91+p019g92+p19g:0`-19p19g:0`|    >019g92+p19g:0`-19p29g1+29p29g93+p09g:0`|    >009g91+p09g:0`-09p19g1+19p19g92+p29g:0`|    >19g1+19p19g92+p09g:0`|    >19g1+19p19g92+p19g1-:0`|    >19g:0`|    >09g1-:0`|    >09g:0`|    >-009g91+p09g:1`-:1\`+09p09g91+p+019g92+p19g:1`-:1\`+19p19g92+p029g93+p29g:0`-29p19g:0`|    >!|>09g1+09p109g91+p09g1-:0`|    >09g:0`|    >+009g91+p09g:1`-:1\`+09p09g91+p09g:0`|    >!|>49g1+49p149g95+p49g1-:0`|    >49g:0`|    >-049g95+p49g:1`-:1\`+49p49g95+p49g:0`|    >.91+,049g95+p49g:0`-49p@
                                                                           >$0  ^        >$0  ^                                                         >$0  ^      >$0  ^        >$0  ^      >$0  ^                                                                    >$0  ^      >$0  ^                       >$0  ^                                                     >$0  ^         >$0  ^                          >$0  ^                                       >$0  ^                                       >$0  ^                                       >$0  ^                     >$0  ^                       >$0  ^      >$0  ^        >$0  ^      >$0  ^                                                                                      >$0  ^                           >$0  ^      >$0  ^                                     >$0  ^                           >$0  ^      >$0  ^                                     >$0  ^
                                                                                  ^                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        <
                                                                                 >                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          ^
                                                                                                                                                                                                                                                                                                          ^                                                                        <
                                                                                                                                                                                                                                                                                                         >                                                                          ^
                                                                                                                                                                                                                                                                                                                                                                                                                    ^                                                                                                                                                                                                                                                                                                                                              <
                                                                                                                                                                                                                                                                                                                                                                                                                   >                                                                                                                                                                                                                                                                                                                                                ^

C'è anche un sacco di altre ottimizzazioni minori che vengono in mente, come la sostituzione 07p07gcon :07p, ma sto prendendo un passo alla volta :)


Così. Tanto. Gratuito. Tempo.
Ottimizzatore

1
Will score later2 anni e oltre! :)
HyperNeutrino,
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.