Разобрать двумерный синтаксис

25

Задний план

Алиса и Боб создают язык игры в гольф, чтобы выиграть каждый вызов PPCG. Алиса хочет создать двумерный язык, например> <>, но Боб предпочитает синтаксис префикса-инфикса, как в J. В качестве компромисса они решают создать двумерный язык префикса-инфикса. Парсер - это боль писать, и им нужна ваша помощь!

Синтаксическая спецификация

В языке Алисы и Боба есть переменные , которые представлены строчными буквами ASCII a-z, и функции , которые представлены прописными буквами ASCII A-Z. Функция может быть вызвана с одним или двумя аргументами. Программа представляет собой прямоугольную сетку из букв a-zA-Zи пробелов, а верхний левый угол не должен содержать пробелы. Это пример правильной программы:

F Gy
H   
R x 

Когда программа анализируется, она преобразуется в выражение языка стиля C (C, Java, Python ...), содержащее однобуквенные переменные и вызовы функций в формате <func>(<arg>)или <func>(<arg1>,<arg2>). Например, приведенная выше программа приводит к следующему выражению:

F(H(R(x)),G(x,y))

Детали процесса разбора следующие:

  • Пространства просто заполнители, поэтому они не анализируются.
  • Каждая переменная a-zвсегда анализируется как сама.
  • Каждая функция A-Zанализируется как вызов функции. Его аргументы являются ближайшими выражениями под ним и справа от него в сетке, в этом порядке. Если присутствует только один из них, он приводится в качестве единственного аргумента. Можно предположить, что все функции имеют хотя бы один аргумент в сетке.

В приведенном выше примере переменные xи yанализируются как сами по себе. Функция не Rимеет ничего ниже и xсправа от нее, поэтому она анализируется как вызов с одним аргументом R(x). Точно так же Hразбирается как H(R(x)), так как имеет Rпод ним. Функция Gрасположена xниже и yсправа от нее, поэтому она анализируется как G(x,y)и аналогично для F. Выражение, проанализированное в верхнем левом углу, является результатом процесса синтаксического анализа.

Вход и выход

Ваш ввод представляет собой непустой прямоугольный массив символов. Это всегда будет действительная программа на языке Алисы и Боба, но она может содержать выражения, которые не используются в выводе. Ваш вывод должен быть разобранным выражением, полученным в результате вышеуказанного процесса.

Правила и оценки

Вы можете написать полную программу функции. Побеждает меньшее количество байтов, и стандартные лазейки запрещены.

Контрольные примеры

Они приведены в формате grid <newline> expressionс дефисами ---между падежами. Формат SE оставляет некоторые строки пустыми, но они должны быть заполнены пробелами.

x
x
---
x y
z  
x
---
Fx
F(x)
---
Fx
y 
F(y,x)
---
ABu
A(B(u))
---
G
H
k
G(H(k))
---
ABCA
x xs
 DFk
A(x,B(D(F(k)),C(x,A(s))))
---
A  B  

C  D x
A(C(D(x)),B(D(x)))
---
RT Hq 
I xR k
R(I(x),T(H(R(k),q)))
---
A A  A a 
 S A  b  
B  C   Dx
d X  u f 
A(B(d,C(D(f,x))),A(X(u),A(u,a)))
Zgarb
источник
Подойдет ли вывод (A (B (D x)) (C (D x))), или формат фиксирован?
coredump
1
@coredump В этом вызове формат вывода строгий; Алиса и Боб хотят иметь возможность напрямую использовать проанализированное выражение.
Згарб
Где "инфиксная" часть языка? Я вижу только префикс.
Paŭlo Ebermann
6
Можете ли вы на самом деле создать язык с этим синтаксисом? :)
Мартин Эндер
4
Последующее задание: напишите мета-гольф для этого синтаксиса (с учетом дерева выражений найдите наименьшую сетку, соответствующую ему). Для дополнительной сложности различают коммутативные и некоммутативные функции.
Мартин Эндер

Ответы:

8

CJam, 67 62 60 58 57 54 байта

Спасибо Деннису за сохранение 4 байта.

{:Gsc_32&{"()"[GGz2{1m<_{S#}#>W<\}*z]{},{J}%',**+}|}:J

Это определяет именованный блок (функцию) Jи оставляет его в стеке. Сама функция ожидает массив строк в стеке, который представляет входную сетку, и оставляет желаемую строку вместо нее.

Проверьте это здесь.

Я собирался заняться этим с момента его публикации, но, видимо, мне нужно было вознаграждение и слишком длинное решение Pyth, чтобы мотивировать себя достаточно.

объяснение

Решение, конечно, рекурсивное и постепенно создает строку.

{
  :G       e#  Store the current grid in G.
  sc       e#  Convert the grid to a string, flattening it, then to a character, which
           e#  discards everything but the first character. This is a golfed 0=0=.
           e#  This character will either be an upper-case letter (function) or a lower-
           e#  case letter (variable).
  _32&     e#  Make a copy of the character and take bitwise AND with 32. This gives a
           e#  NULL character for functions and a space for variables.
  {        e#  If the result is falsy... (i.e. NULL, i.e. we have a function)
    "()"   e#   Push parentheses for later use.
    [      e#   Remember the current stack depth.
    GGz    e#   Push an array which contains the grid and its transpose.
    2{     e#   Run this block twice... (it will be applied once to each grid)
      1m<  e#    Rotate the rows. This has two effects: a) It removes the first row
           e#    from the front such that we can go looking for the next non-space
           e#    character down the first column from the start. b) It places a row
           e#    at the end which we know doesn't start with a space, which acts as
           e#    a guard in case there are no further letters below the current one.
      _    e#    Duplicate this grid.
      {    e#    Find the first index where this block yields something truthy...
        S# e#     Find the index of the first space in the current row. If the row
           e#     starts with a space, this is 0, i.e. falsy and the search continues.
           e#     If there is a space in any later position, that will be positive and
           e#     truthy, so this index gets returned. If there is no space at all,
           e#     the result is -1 which is also truthy and ends the search.
      }#        
      >    e#     Discard all lines up to (but not including) the found index.
      W<   e#     Discard the guard row at the end.
      \    e#     Swap the grids so that the next pass processes the other grid.
    }*       
    z      e#    Transpose the second grid back to its original orientation.
    ]      e#    Wrap both processed grids in an array.
    {},    e#    Remove a grid if it's empty.
    {J}/   e#    For each remaining grid, call J recursively.
    ',*    e#    Join the grids with commas if there are still two of them.
    *      e#    Wrap this string in the parentheses below on the stack.
    +      e#    Prepend the function name.
  }|
}:J
Мартин Эндер
источник
5

Python 2, 227 223 192 182 179 177 байт

def r(i,y=0,x=0):
 c=i[y][x];z=[]
 for t in"pq":
    p=q=0
    try:
     while" "==i[y+p][x+q]or 1>p+q:exec t+"+=1"
     z+=[r(i,y+p,x+q)]
    except:1
 return c+"(%s)"%",".join(z)*c.isupper()

(Четыре пробела фактически являются вкладками)

Принимает 2-й список символов в качестве первого аргумента для r.

синий
источник
5

Pyth, 97 байт

D:TkdI}p@@QTkGR)=Z0p\(V2JK0W|q\ @@Q=b+TJ=H+kK!+JK=J+NJ=K+!NK)I!|gblQgHl@Q0p*Z\,:bH+=Z1d))p\);:000

Боже мой, что заняло много времени (около 5/6 часов?). Пиф действительно не был предназначен для этого ...

Попробуй это здесь .

Попытка объяснения, а также эквивалент Python

Q = literal_eval(input())

def at_slice(T,k,d):
  if Pprint(Q[T][k]) in "abcdefghijklmnopqrstuvwxyz": return 
  Z = 0
  Pprint("(")
  for N in range(2):
    J=K=0
    while " "==Q[assign('b',T+J)][assign('H',k+K)] or not J+K:
      J+=N
      K+=not N
    if not (b>=len(Q) or H>=len(Q[0])):
      Pprint(Z*",")
      at_slice(b,H,assign('Z',1)+d)
   Pprint(")")
at_slice(0,0,0)

Где функции Pprintи assignвернуть то, что им дают.

синий
источник
Много краткости. Такое вау.
Эддисон Крамп
5

Haskell, 124 122 120 119 байтов

r@((c:_):_)#g|c>'_'=[c]|c<'!'=g%r|1<2=c:'(':(tail%r)!(map tail%r)++")"
_#_=""
g%r=g r#g
a!b=a++[','|a>"",b>""]++b
(#id)

Пример использования: (#id) ["RT Hq ","I xR k"]-> "R(I(x),T(H(R(k),q)))".

Как это работает: помимо входной сетки r, функция# принимает gв качестве аргумента еще одну функцию, которая применяется rвсякий раз, когда верхний левый символ является пробелом. Если это строчный символ, верните его. В противном случае это должен быть заглавный символ, и он #вызывается рекурсивно, один раз tailдля перехода вниз и один раз map tailдля перехода вправо. !объединяет результаты рекурсивных вызовов с ,, если необходимо. Все начинается с входной сетки и функции идентификации.

Ними
источник
0

Python 3, 187 байт

Все еще ищу способы сыграть в эту игру, но я просто рад, что мне удалось превратить ее в однострочник.

lambda g,r=0,c=0:g[r][c]+'(%s)'%','.join([p(g,R,c)for R in range(r+1,len(g))if c<len(g[R])and' '!=g[R][c]][:1]+[p(g,r,C)for C in range(c+1,len(g[r]))if' '!=g[r][C]][:1])*g[r][c].isupper()
Тим Педерик
источник