Перекласти прелюдію на функціонування


19

Це тижневий виклик №2. Тема: Переклад

Напишіть програму або функцію, яка приймає вихідний код для програми в Prelude і виводить код для еквівалентної програми в Befunge-93 . Щоб програма була еквівалентною, вона повинна при будь-якому даному введенні виробляти той самий вихід, що і програма Prelude, і зупинятися, якщо і лише тоді, коли програма Prelude зупиняється.

Мова введення: Прелюдія

Інтерпретатор Python:

Програма Prelude складається з декількох "голосів", які виконують інструкції одночасно. Інструкції для кожного голосу знаходяться в окремому рядку. Кожен голос має окремий стек, який ініціалізується з нескінченною кількістю нулів. Виконання починається в крайньому лівому стовпчику і просуває по одному стовпчику праворуч кожну галочку, за винятком випадків, коли під впливом )чи (інструкціями. Програма завершується, коли досягнуто останнього стовпця.

Прелюдія для цього виклику:

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

Примітки

  • vі ^діють циклічно, тому vголос внизу копіюватиме елемент стека верхнього голосу, а ^верхній - копіювати з нижнього. Слідство: Як vі ^дублювати вершини стека в одній голосової програмі.
  • А (та його відповідність )можуть розташовуватися на різних лініях. Тим НЕ менше , )завжди буде дивитися на стосі голосу , де відповідного (містився, а НЕ стек , де )сам поміщається.
  • Значення, отримані вказівками ^та vінструкціями, діють на значення, наявні до завершення будь-яких інших операцій у цьому ж стовпці.
  • ?і !діють по-іншому, ніж специфікація, знайдена на esolangs.org, тому не забудьте протестувати з трохи модифікованим перекладачем, наданим у цій публікації.

Гарантоване введення:

  • Узгодження дужок
  • У колоні не більше однієї дужки
  • Однакова кількість символів у кожному рядку
  • Принаймні один рядок
  • Немає стовпця з більш ніж однією інструкцією I / O ( !або ?)
  • Один символ передачі ліній після інструкцій для кожного голосу
  • Немає жодних символів, крім зазначених вище

Мова виводу: Befunge-93

Befunge - мова на основі стека, лічильник програми (ПК; вказівник на поточну інструкцію) вільно рухається по двовимірній сітці. Він починається у верхньому лівому куті, рухаючись праворуч. Ігрове поле тороїдальне, тобто рух ПК обертається навколо обох країв. Befunge також має стек, який ініціалізується на нескінченну кількість нулів. Befunge виконує такі операції:

Ви можете припустити наступні характеристики компілятора / інтерпретатора Befunge-93:

  • Цілі ліміти - необмежена точність.
  • Він дозволяє сітки будь-якого розміру.
  • Координати сітки (для gі p) базуються на 0.

Оцінка балів

З метою запобігання поданням, які просто створюють інтерпретатор Prelude в Befunge та жорстко кодують джерело Prelude до нього, метою буде мінімізувати розмір отриманого вихідного коду Befunge.

Нижче наведено ряд програм Prelude. Ваш перекладач буде працювати над усім цим. Ваш бал - це сума розмірів програм Befunge, за умови, що всі вони дійсні.

Ваш перекладач не повинен бути оптимізований спеціально для цих тестових випадків (наприклад, шляхом жорсткого кодування рукописних програм Befunge для них). Якщо я підозрюю будь-яку відповідь на це, я залишаю за собою право змінювати дані або створювати додаткові.

Приклади введення

Друкувати n-1вниз до 0:

?(1-^!)

Логічний І:

?  (0)  
 ?(0  ) 
    1  !

Логічний АБО:

 ?   (0) 
? (0)    
   1  1 !

Перевірте парність вхідного сигналу (тобто модуля 2) негативного числа:

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

Квадрат вводу:

 ^    
  ^+ !
?(1-) 

Надрукуйте n- е число Фібоначчі, де n = 0відповідає 0 і n = 1відповідає 1:

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

Вхід:

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

Відділ для негативних входів:

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

Звичайно, ваша програма повинна проявляти однакову поведінку у всіх випадках, навіть якщо поведінка зразка програми для негативних чисел не вказана.

Нарешті, ваш перекладач не повинен бути необґрунтовано довгим:

  • Він повинен міститись у публікації Stack Exchange
  • На типовому настільному комп’ютері він повинен обробляти зразки входів за 10 хвилин.

Зауважте, що числовий вхід для Prelude або Befunge подається як необов'язковий знак мінус з наступною однією або кількома десятковими цифрами, а за ними новий рядок. Інший внесок - це невизначена поведінка.

Ви можете написати перекладача будь-якою мовою. Виграє найкоротший перекладений код Befunge.

Таблиця лідерів

  • Sp3000 : 16430 байт

Я не розумію: "Натисніть на стек верхнє значення на стеку вищезгаданого голосу." Хіба це не повинно бути: «Натисніть на стек верхнє значення з стека зазначеного вище голосу.»
Деф

У ньому сказано, що прелюдія виконує голоси одночасно, чи це означає, що вони справді виконуються в окремому потоці, чи я можу просто виконати перші команди на всіх голосах (зверху вниз), потім другі команди тощо.
Деф

@Deformyer Я змінив його з "на" на "з", але я подумав, що "найвище значення в стеці" теж не помилився. Що стосується одночасності, то вам не потрібно насправді їх інтерпретувати паралельно. Важливо те, що всі вони діють на попередній стан стеків, і жодна операція в заданому стовпці не може впливати на будь-яку іншу операцію в цьому стовпці.
Мартін Ендер

Чи кілька тестових випадків не порушують "Немає стовпця з більш ніж однією інструкцією вводу / виводу (! Чи?)?"
Fuwjax

@proudhaskeller 1Внутрішній цикл, тому його не можна натискати. 0 може виходити з нескінченної кількості 0, яка починається на стеках.
feersum

Відповіді:


10

Python 3, буде оцінено пізніше

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()))

Бігай, як py -3 prefunge.py <input filename> <output filename>.

Для мене пройшов повільний тиждень, тож я нарешті набриднув, щоб вирішити це піврічне питання. Я б запитав, чому ніхто інший не намагався, але я все ще відчуваю біль від налагодження (і, мабуть, все ще залишаються помилки, наскільки я знаю).

Питання не містить перекладача Befunge-93, тому я використав цей , який трохи відрізняється від специфікації. Дві ключові відмінності:

  • Якщо значок у вказаному рядку програми не існує, ви не можете записувати до цього рядка. Це означає, що вам потрібно буде натиснути Enter кілька разів, щоб ввести достатньо нових рядків наприкінці . Якщо ви бачите NaNs у виході, це найвірогідніша причина.

  • Осередки сітки не мають попереднього рівня до нуля - для зручності я включив деяку преініціалізацію у виходи Befunge, але оскільки це не потрібно, я можу зняти її, коли я починаю оцінювати.

Основний макет вихідних програм такий:

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

Простір стеків знаходиться поза програмою, звідси новий коментар Enter-спам-коментар від попереднього.

Основна ідея полягає у призначенні кожному голосу рядку, який служить його стеком. Для підтримки цих стеків ми також маємо спеціальний ряд довжиною стека, де довжина кожної стеки записується в комірку уздовж ряду. Програма тоді багато gет і pут, наприклад для друку процес:

  • Отримайте осередок у y = stack_row[stack], x = stack_length[stack]
  • Виконати .91+,, тобто надрукувати як ціле число, а потім надрукувати новий рядок
  • Замініть комірку вищевказаних координат на 0 (щоб імітувати вискакування)
  • Поважність stack_length[stack]

Для одночасного оцінювання стовпця всі необхідні комірки зчитуються і їх значення зберігаються на стеку перед тим, як записувати будь-які комірки (наприклад, для прикладу друку може бути більше інструкцій між першим та другим кроками).

`, що більше, ніж використовується, щоб переконатися, що довжина стека ніколи не стає негативною, і для натискання 0s, коли стек порожній. Звідси походить чітко видиме розгалуження, але у мене з’явилася ідея, що видалити розгалуження, яке повинно видалити велику кількість пробілів з першого та третього рядків.

Для циклів, оскільки петлі Prelude можуть стрибати обома способами, ми використовуємо два ряди на цикл у такій конфігурації:

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

        ^                     <
       >                       ^

Наразі ці петлі складають більшість байтів, але їх можна легко переграти, помістивши їх у кодове поле p, що я планую зробити після того, як буду щасливий, що перекладач працює правильно.

Ось декілька прикладів виводу ?(1-^!), тобто надрукуйте n-1на 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^
                              ^                                                                                                                                <
                             >                                                                                                                                  ^

Квадратний вхід:

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

Відділ (рекомендуються невеликі введення):

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

Там же купа інших дрібних оптимізацій , які приходять на розум, як заміна 07p07gз :07p, але я беру це один крок за один раз :)


Так. Багато чого. Безкоштовно. Час.
Оптимізатор

1
Will score later2 роки і рахую! :)
HyperNeutrino
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.