Rozwiąż makrozmienne SAS

13

Język programowania SAS to niezgrabny, archaiczny język z 1966 roku, który jest nadal używany. Oryginalny kompilator został napisany w języku PL / I i rzeczywiście duża część składni pochodzi z języka PL / I. SAS posiada również makro preprocesora języka, który wywodzi się , że z PL / I , jak również. W tym wyzwaniu będziesz interpretować kilka prostych elementów języka makr SAS.

W języku makr SAS zmienne makr są definiowane za pomocą %letsłowa kluczowego i drukowanie w dzienniku jest wykonywane %put. Instrukcje kończą się średnikami. Oto kilka przykładów:

%let x = 5;
%let cool_beans =Cool beans;
%let what123=46.lel"{)-++;

Nazwy makrozmiennych nie uwzględniają wielkości liter i zawsze pasują do wyrażenia regularnego /[a-z_][a-z0-9_]*/i. Na potrzeby tego wyzwania powiemy:

  • Zmienne makro może posiadać tylko wartości składających się w całości z znaków ASCII z wyjątkiem ; , &i%
  • W wartościach nie będzie spacji wiodących ani końcowych
  • Wartości nigdy nie będą miały więcej niż 255 znaków
  • Wartości mogą być puste
  • Nawiasy i cytaty w wartościach mogą być niedopasowane
  • Nie może być dowolna ilość miejsca przed i po =w %letsprawozdaniu, a przestrzeń ta powinna być ignorowane
  • Przed terminalem ;w %letinstrukcji może znajdować się dowolna ilość miejsca, która również powinna zostać zignorowana

Kiedy wywoływana jest makrozmienna, mówimy, że „rozwiązuje” swoją wartość. Makrozmienne są rozwiązywane przez dodawanie &. Istnieje opcjonalny znak końcowy .oznaczający koniec identyfikatora. Na przykład,

%put The value of x is &X..;

zapisuje The value of x is 5.w dzienniku. Należy pamiętać, że wymagane są dwa okresy, ponieważ jeden okres zostanie wykorzystany &X.i zostanie rozwiązany 5. Zauważ również, że chociaż zdefiniowaliśmy xmałe litery, &Xto jest tak samo, &xponieważ nazwy makrozmiennych nie rozróżniają wielkości liter.

Tutaj jest trudniej. Wiele &s może być łączonych razem, aby rozstrzygać zmienne, i &s na tym samym poziomie rozpoznawania zagnieżdżenia w tym samym czasie. Na przykład,

%let i = 1;
%let coolbeans1 = broseph;
%let broseph = 5;

%put &&coolbeans&i;  /* Prints broseph */
%put &&&coolbeans&i; /* Prints 5 */

Najbardziej wewnętrzni &rozpatrują najpierw, a rozwiązywanie kontynuuje na zewnątrz. Dopasowywanie nazw zmiennych odbywa się zachłannie. W drugiej %putinstrukcji procesor wykonuje następujące kroki:

  1. &ipostanawia 1, a najgłębsze prowadzenie &zostaje zużyte, dając nam&&coolbeans1
  2. &coolbeans1postanawia broseph, dając nam&broseph
  3. &brosephrozwiązuje do 5.

Jeśli są końcowe ., tylko jeden .zużywa się w rozdzielczości, nawet jeśli jest ich wiele &.

Zadanie

Biorąc pod uwagę od 1 do 10 %letinstrukcji oddzielonych znakiem nowej linii i pojedynczą %putinstrukcją, wydrukuj lub zwróć wynik %putinstrukcji. Dane wejściowe można zaakceptować w dowolny standardowy sposób.

Możesz założyć, że dane wejściowe zawsze będą prawidłowe i że %letinstrukcje będą poprzedzać %putinstrukcje. Zdefiniowane zmienne nie zostaną ponownie zdefiniowane w późniejszych %letinstrukcjach.

Gdyby faktycznie działał w SAS, nie byłoby problemów z rozdzielaniem zmiennych na zmienne, które nie istnieją i wszystko będzie poprawne składniowo, jak opisano powyżej.

Przykłady

  1. Wejście:

    %let dude=stuff;
    %let stuff=bEaNs;
    %put &&dude..;
    

    Wynik:

    bEaNs.
    
  2. Wejście:

    %let __6 = 6__;
    %put __6&__6;
    

    Wynik:

    __66__
    
  3. Wejście:

    %let i=1;
    %let hOt1Dog = BUNS;
    %put &&HoT&i.Dog are FUNS&i!");
    

    Wynik:

    BUNS are FUNS1!")
    
  4. Wejście:

    %let x = {*':TT7d;
    %put SAS is weird.;
    

    Wynik:

    SAS is weird.
    
  5. Wejście:

    %let var1   =  Hm?;
    %let var11 = var1;
    %let UNUSED = ;
    %put &&var11.....;
    

    Wynik:

    Hm?....
    

    Zauważ, że &&var11pasuje, var11ponieważ dopasowywanie nazw jest zachłanne. Gdyby istniał ., to znaczy &&var1.1, var1byłby dopasowany, a dodatkowy 1 nie byłby częścią żadnej nazwy.

To jest golf golfowy, więc wygrywa najkrótsze rozwiązanie w bajtach!

Alex A.
źródło
W jaki sposób dane wyjściowe z przypadku testowego 1 mają kropkę? Nie należy &stuff.usuwać okresu?
GamrCorps
@GamrCorps Powinienem określić: Tylko jeden końcowy okres jest zużywany w rozdzielczości.
Alex A.,
@GamrCorps Edytowano, aby określić i dodać go jako przypadek testowy.
Alex A.
więc &&&&&&&&&a......................nadal usunąłby tylko jeden okres?
GamrCorps
@GamrCorps Tak.
Alex A.

Odpowiedzi:

1

Python 3 , 354 341 336 bajtów

import re
S=re.sub
def f(x):
	r=x.splitlines();C=r[-1].strip('%put ');D=0
	while D!=C:
		D=C
		for a in sorted([l.strip('%let ').replace(" ","").split(';')[0].split('=')for l in r[:-1]],key=lambda y:-len(y[0])):
			s=1
			while s:C,s=re.subn('&'+a[0]+'(\.?)',a[1]+'😍\\1',S('😍+\.([^\.])','\\1',C),0,re.I)
	return S('😍+\.?','',C)

Wypróbuj online!

edycja: proste skrócenie

edit: odwróć sortowanie według -len (...) zamiast [:: - 1] (5 bajtów), dzięki Jonathan Frech!

Nie golfił

import re
S=re.sub # new name for the function re.sub()
def f(x):
    r=x.splitlines() # input string to list of rows
    C=r[-1].strip('%put ') # get the string to put (from the last row)
    D=0
    while(D!=C): # iterate until the result does not change
        D=C
        for a in                                                                                                                    : # iterate over the list of variables
                 sorted(                                                                          ,key=lambda y:len(y[0]),reverse=1) # sort list for greediness by decreasing var.name lengths
                        [l.strip('%let ') # cut the 'let' keyword
                                         .replace(" ","") # erase spaces
                                                         .split(';')[0] # cut parts after ';'
                                                                       .split('=') # create [variable_name,value] list
                                                                                  for l in r[:-1]] # for each row but last
            s=1
            while(s): # iterate until the result does not change
                C,s=re.subn( # substitute
                            '&'+a[0]+'(\.?)', # &varname. or &varname
                                                 a[1]+'😍\\1', # to value😍. or value😍
                                                              S('😍+\.([^\.])','\\1',C), # in the string we can get from C erasing (😍's)(.) sequences if the next char is not .
                                                                                        0,re.I) # substituting is case insensitive
    return S('😍+\.?','',C) # erase smileys and one .
mmuntag
źródło
Sugerowałbym, aby wziąć dużo na stronie ze wskazówkami w Pythonie . Trywialne optymalizacje, takie jak konkatenacja instrukcji nieskomplikowanych ( ;), redukcja nawiasów ( if(...)-> if ...) i operacje na listach ( ,reverse=1-> [::-1]) mogą z łatwością zaoszczędzić niektóre bajty.
Jonathan Frech,
Dzięki! Przeczytałem go wcześniej, ale to było dawno temu i zapomniałem kilku sztuczek.
mmuntag,
Zapraszamy. len(y[0]))[::-1]może być -len(y[0])).
Jonathan Frech,