Resolver variables de macro SAS

13

El lenguaje de programación SAS es un lenguaje torpe y arcaico que data de 1966 y que todavía se usa en la actualidad. El compilador original fue escrito en PL / I , y de hecho gran parte de la sintaxis deriva de PL / I. SAS también tiene un lenguaje macro de preprocesador que se deriva del de PL / I también. En este desafío, interpretará algunos elementos simples del lenguaje de macros SAS.

En el lenguaje de macros SAS, las variables de macros se definen con la %letpalabra clave y la impresión en el registro se realiza con %put. Las declaraciones terminan con punto y coma. Aquí hay unos ejemplos:

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

Los nombres de variables de macro no distinguen entre mayúsculas y minúsculas y siempre coinciden con la expresión regular /[a-z_][a-z0-9_]*/i. Para los propósitos de este desafío, diremos lo siguiente:

  • Las variables macro solo pueden contener valores que consisten completamente en caracteres ASCII imprimibles , excepto , y;&%
  • No habrá espacios iniciales o finales en los valores
  • Los valores nunca tendrán más de 255 caracteres.
  • Los valores pueden estar vacíos
  • Los corchetes y las comillas en los valores pueden no coincidir
  • Puede haber cualquier cantidad de espacio antes y después del =en la %letdeclaración y este espacio debe ignorarse
  • Puede haber cualquier cantidad de espacio antes del terminal ;en la %letdeclaración y este espacio también debe ignorarse

Cuando se llama a una variable macro, decimos que "se resuelve" a su valor. Las variables macro se resuelven al anteponer &. Hay un final opcional. que denota el final del identificador. Por ejemplo,

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

escribe The value of x is 5.en el registro. Tenga en cuenta que se requieren dos períodos porque un solo período será consumido por &X.y se resolverá 5. También tenga en cuenta que aunque definimos xen minúsculas, &Xes lo mismo &xporque los nombres de las variables macro no distinguen entre mayúsculas y minúsculas.

Aquí es donde se pone difícil. Se &pueden agrupar múltiples s para resolver variables, y &s al mismo nivel de resolución de anidamiento al mismo tiempo. Por ejemplo,

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

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

Los más íntimos se &resuelven primero, y la resolución continúa hacia afuera. La coincidencia de nombres de variables se realiza con avidez. En la segunda %putdeclaración, el procesador realiza los siguientes pasos:

  1. &iresuelve 1y &se consume el liderazgo más interno , dándonos&&coolbeans1
  2. &coolbeans1resuelve broseph, dándonos&broseph
  3. &brosephresuelve a 5.

Si hay .s finales , solo .se consume una resolución, incluso si hay múltiples &s.

Tarea

Dado entre 1 y 10 %letdeclaraciones separadas por nuevas líneas y una sola %putdeclaración, imprima o devuelva el resultado de la %putdeclaración. La entrada se puede aceptar de cualquier forma estándar.

Puede suponer que la entrada siempre será válida y que las %letdeclaraciones precederán a la %putdeclaración. Las variables que se definen no se redefinirán en %letdeclaraciones posteriores .

Si realmente se ejecuta en SAS, no habrá problemas con las variables que se resuelven en variables que no existen y todo será sintácticamente correcto como se describió anteriormente.

Ejemplos

  1. Entrada:

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

    Salida:

    bEaNs.
    
  2. Entrada:

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

    Salida:

    __66__
    
  3. Entrada:

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

    Salida:

    BUNS are FUNS1!")
    
  4. Entrada:

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

    Salida:

    SAS is weird.
    
  5. Entrada:

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

    Salida:

    Hm?....
    

    Tenga en cuenta que las &&var11coincidencias var11ya que la coincidencia de nombres es codiciosa. Si hubiera habido un ., es decir &&var1.1, entonces var1coincidiría y el 1 extra no sería parte de ningún nombre.

Este es el código de golf, por lo que gana la solución más corta en bytes.

Alex A.
fuente
¿Cómo tiene un período la salida del caso de prueba 1? ¿No debería &stuff.eliminar el período?
GamrCorps
@GamrCorps Debo especificar: solo se consume un único período final en la resolución.
Alex A.
@GamrCorps Editado para especificar y lo agregó como un caso de prueba.
Alex A.
entonces, &&&&&&&&&a......................¿solo eliminaría un punto?
GamrCorps
@GamrCorps Sí.
Alex A.

Respuestas:

1

Python 3 , 354 341 336 bytes

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)

Pruébalo en línea!

editar: algo de acortamiento fácil

editar: orden inverso por -len (...) en lugar de [:: - 1] (5 bytes), ¡gracias a Jonathan Frech!

Sin golf

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
fuente
Sugeriría tomar mucho en la página de consejos de Python . Las optimizaciones triviales como la concatenación de sentencias no compuestas ( ;), la reducción de paréntesis ( if(...)-> if ...) y las operaciones de lista ( ,reverse=1-> [::-1]) pueden guardar fácilmente algunos bytes.
Jonathan Frech
¡Gracias! Lo he leído antes, pero fue hace mucho tiempo, y olvidé algunos trucos.
mmuntag
De nada. len(y[0]))[::-1]puede ser -len(y[0])).
Jonathan Frech