Résoudre les variables de macro SAS

13

Le langage de programmation SAS est un langage maladroit et archaïque datant de 1966 qui est toujours utilisé aujourd'hui. Le compilateur d'origine a été écrit en PL / I , et en effet une grande partie de la syntaxe dérive de PL / I. SAS a également un langage macro préprocesseur qui de dérive de celle de PL / I ainsi. Dans ce défi, vous allez interpréter quelques éléments simples du langage macro SAS.

Dans le langage de macro SAS, les variables de macro sont définies à l'aide du %letmot - clé et l'impression dans le journal se fait avec %put. Les déclarations se terminent par des points-virgules. Voici quelques exemples:

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

Les noms de variables de macro ne respectent pas la casse et correspondent toujours à l'expression régulière /[a-z_][a-z0-9_]*/i. Aux fins de ce défi, nous dirons ce qui suit:

  • Les variables de macro ne peuvent contenir des valeurs composées uniquement de caractères ASCII imprimables sauf ; , &et%
  • Il n'y aura pas d'espaces de début ou de fin dans les valeurs
  • Les valeurs ne dépasseront jamais 255 caractères
  • Les valeurs peuvent être vides
  • Les crochets et les guillemets dans les valeurs peuvent être inégalés
  • Il peut y avoir n'importe quelle quantité d'espace avant et après le =dans l' %letinstruction et cet espace doit être ignoré
  • Il peut y avoir n'importe quelle quantité d'espace avant le terminal ;dans l' %letinstruction et cet espace doit également être ignoré

Lorsqu'une macro variable est appelée, nous disons qu'elle "résout" sa valeur. Les variables de macro sont résolues par préfixation &. Il y a une fin facultative. qui indique la fin de l'identifiant. Par exemple,

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

écrit The value of x is 5.dans le journal. Notez que deux périodes sont nécessaires car une seule période sera consommée par &X.et résolu 5. Notez également que même si nous avons défini xen minuscules, &Xc'est la même chose que &xparce que les noms de variables de macro ne respectent pas la casse.

Voici où cela devient difficile. Plusieurs &s peuvent être liés ensemble pour résoudre des variables, et &s au même niveau de résolution d'imbrication en même temps. Par exemple,

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

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

Les plus intimes se &résolvent en premier et la résolution se poursuit vers l'extérieur. La correspondance des noms de variables se fait avec avidité. Dans la deuxième %putinstruction, le processeur effectue les étapes suivantes:

  1. &ise résout à 1, et le plus intime leader &est consommé, nous donnant&&coolbeans1
  2. &coolbeans1décide de brosephnous donner&broseph
  3. &brosephdécide de 5.

S'il y a des .s finaux , un seul .est consommé en résolution, même s'il y a plusieurs &s.

Tâche

Étant donné entre 1 et 10 %letinstructions séparées par des retours à la ligne et une seule %putinstruction, imprimez ou renvoyez le résultat de l' %putinstruction. L'entrée peut être acceptée de n'importe quelle manière standard.

Vous pouvez supposer que l'entrée sera toujours valide et que les %letinstructions précéderont l' %putinstruction. Les variables définies ne seront pas redéfinies dans les %letinstructions ultérieures .

S'ils s'exécutent réellement dans SAS, il n'y aurait aucun problème avec la résolution des variables en variables qui n'existent pas et tout sera syntaxiquement correct comme décrit ci-dessus.

Exemples

  1. Contribution:

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

    Production:

    bEaNs.
    
  2. Contribution:

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

    Production:

    __66__
    
  3. Contribution:

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

    Production:

    BUNS are FUNS1!")
    
  4. Contribution:

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

    Production:

    SAS is weird.
    
  5. Contribution:

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

    Production:

    Hm?....
    

    Notez que &&var11correspond var11car l'appariement de nom est gourmand. S'il y avait eu un ., c'est-à-dire &&var1.1, alors il y var1aurait correspondance et le 1 supplémentaire ne ferait partie d'aucun nom.

C'est le golf de code, donc la solution la plus courte en octets gagne!

Alex A.
la source
Comment la sortie du scénario de test 1 a-t-elle une période? Ne devrait pas &stuff.supprimer la période?
GamrCorps
@GamrCorps Je dois préciser: une seule période de fin est consommée en résolution.
Alex A.
@GamrCorps Modifié pour le spécifier et l'ajouter comme cas de test.
Alex A.
donc &&&&&&&&&a......................serait encore seulement enlever une période?
GamrCorps
@GamrCorps Oui.
Alex A.

Réponses:

1

Python 3 , 354 341 336 octets

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)

Essayez-le en ligne!

modifier: un raccourcissement facile

edit: tri inversé par -len (...) au lieu de [:: - 1] (5 octets), merci à Jonathan Frech!

Non 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
la source
Je suggérerais d'en prendre beaucoup sur la page des conseils Python . Les optimisations triviales telles que la concaténation des instructions non composées ( ;), la réduction des parenthèses ( if(...)-> if ...) et les opérations de liste ( ,reverse=1-> [::-1]) peuvent facilement enregistrer certains octets.
Jonathan Frech
Merci! Je l'ai déjà lu, mais c'était il y a longtemps et j'ai oublié quelques trucs.
mmuntag
Je vous en prie. len(y[0]))[::-1]peut être -len(y[0])).
Jonathan Frech