Diseño del compilador Analizador SLR(1) usando Python

Requisito previo: Analizador LR , Analizador SLR

SLR (1) gramática

SLR significa gramática LR simple. Es un ejemplo de un analizador de abajo hacia arriba. La «L» en SLR representa el escaneo que avanza de izquierda a derecha y la «R» representa construcciones de derivación en orden inverso, y el «(1)» representa el número de símbolos de entrada de anticipación.

Operaciones de cambio y reducción

El siguiente concepto importante son las operaciones SHIFT y REDUCE. Durante el análisis, podemos ingresar dos tipos de estados. Primero, podemos tener un estado donde ‘·’ está al final de la producción. Este estado se llama «Manejar». Aquí viene el papel de la operación REDUCIR. En segundo lugar, mientras analizamos podemos tener un estado en el que ‘·’ apunta a algún símbolo gramatical y todavía hay margen para avanzar. En este escenario, se realiza una operación de cambio.

Uso del Punto [ · ]

En el análisis LR, usamos el punto ‘·’ como un carácter en la regla para que sepamos el progreso del análisis en cualquier punto dado. Tenga en cuenta que ‘·’ es como un informante y no debe considerarse como un símbolo gramatical. En SLR (1), solo tenemos un símbolo de entrada como anticipación, que en última instancia nos ayuda a determinar el estado actual durante la actividad de análisis.

Análisis de decisiones

Para tomar las decisiones de análisis, necesitamos construir el autómata finito determinista. Nos ayuda a determinar el estado actual al que hemos llegado mientras estamos analizando. Cuando lo diseñamos para el analizador SLR, se lo denomina autómata LR. Los estados de este autómata son el conjunto de “Ítems” relacionados con el conjunto de reglas de producción.

Generación de la colección LR

El primer paso es crear una gramática aumentada. El proceso de aumento comienza colocando el símbolo de inicio en el lado derecho de una regla de producción. No podemos alterar la regla existente, por lo que agregamos una nueva regla en las producciones. Traer el símbolo de inicio en RHS asegura que el análisis alcance el estado de aceptación. La operación REDUCIR en esta regla recién agregada determina la aceptación de strings.  

Por ejemplo, 

SI tenemos ‘K’ como símbolo de inicio

ENTONCES L → K se agrega en producciones

(donde ‘L’ representa cualquier símbolo no preexistente en la colección)

Operaciones CLOSURE y GOTO

En el proceso de construcción de autómatas finitos deterministas, hay dos requisitos. El primero es crear “Estados” y el segundo es desarrollar “Transiciones” en el autómata.

1) CIERRE

La operación de cierre nos ayuda a formar los “Estados”. Antes de realizar la operación de cierre se deben separar todas las reglas. Luego numera todas las reglas. Esto será útil más adelante para realizar las entradas Shift y Reduce en la tabla de análisis. Sea I0 el conjunto de reglas obtenidas tras el aumento gramatical. Esto significa que también tenemos una regla recién agregada en la colección I0.  

Suposición: (considere que [L, K] no son terminales y [m, t, p] son ​​un conjunto de cero o más terminales o no terminales)

HACER REPETIR (Hasta que no se agregue ninguna regla nueva) {

SI (cualquier producción de la forma “ L → m · K t ” existe) y (tenemos producción K → · p)

ENTONCES {añadir K de producción → · p al conjunto de Cierre si no es preexistente}

}

2) IR A

La operación GOTO nos ayuda a formar las “Transiciones”. En la operación GOTO (I, X), ‘I’ puede elaborarse como el estado que estamos viendo y ‘X’ es el símbolo señalado por el Punto (·). Entonces, GOTO toma un estado con elementos y un símbolo de gramática y produce el estado nuevo o existente como salida.

El GOTO (I, X) representa la transición de estado de «I» en el símbolo de entrada «X».

Para cualquier regla de producción “ L → m · K t ” en “I”

GOTO (I, X) genera el cierre de un conjunto de todas las producciones “ L → m K · t ”

Enfoque del programa

La entrada para el programa es la lista de reglas que tienen un conjunto de elementos y listas que determinan los símbolos terminales y no terminales. El control comienza desde la función grammarAugmentation(), luego tenemos que calcular el estado I0, que se calcula llamando a findClosure(), ahora para la generación de nuevos estados se llama a la función generateStates() y finalmente se llama a la función createParseTable().

A) gramáticaAumentación

  • En esta función, primero creamos un símbolo único y lo usamos para crear un nuevo elemento para traer el símbolo de inicio en RHS. Luego formateamos los elementos en una lista anidada y agregamos un punto al comienzo del RHS del elemento. Además, mantenemos solo una derivación en un artículo. Por lo tanto, hemos generado una lista denominada namedRulesList.

B) encontrarCierre

  • Esta función se ejecuta de manera diferente para el estado I0. Para el estado I0, agregamos directamente a closureSet el elemento recién creado del aumento. En cualquier otro caso, closureSet se inicializa con el argumento recibido «input_state».
  • Ahora continúe con las iteraciones hasta que recibamos nuevos elementos en closureSet. Seguimos las reglas mencionadas en el título «Cierre del conjunto de elementos» anterior para buscar los siguientes elementos que se agregarán en el conjunto de cierre.

C) generar estados

  • En esta función, comenzamos con el cálculo GOTO. “statesDict” es un diccionario que almacena todos los estados y se usa como una variable global. Iteramos sobre los estadosDict hasta que se le agregan nuevos estados. Llamamos a la función compute_GOTO() en cada estado agregado solo una vez.

D) computar_GOTO

  • Ahora el control llega a compute_GOTO, esta función no tiene una lógica goto real, pero crea metadatos para llamar a la función GOTO() iterativamente. Para el estado de entrada dado, llamamos GOTO(estado,Xi), donde Xi representa un símbolo señalado por un punto. Puede haber varios Xi, por lo que aislar el procedimiento de iteración de la implementación lógica real de GOTO() reduce la complejidad.

E) IR A

  • Esta función es llamada por la función compute_GOTO(), recibimos «input_state» y «charNextToDot», realizamos la operación de cambio y generamos un nuevo estado.
  • Ahora, para cualquier elemento en un nuevo estado, el punto puede apuntar a algún símbolo para el cual podemos tener otro elemento, por lo que para incluir todos los elementos para el nuevo estado, llamamos a la función findClosure() que discutí anteriormente.
  • Para almacenar esta información de «GOTO (estado, símbolo) = nuevo estado», se crea el diccionario stateMap con una clave de tipo tupla y un valor de tipo entero. Esto ayudará a analizar la generación de tablas y la salida de impresión.

F) crearParseTable

  • En primer lugar, creamos la tabla en su estado vacío inicial. Las columnas tendrán ACCIÓN (Terminales y $) e IR A (No terminales). Las filas tendrán estados numerados (I0-In).
  • Usando stateMap complete las entradas SHIFT e GOTO. Para agregar entradas de reducción en el analizador LR, busque los «manejadores» (elemento con el punto al final) en los estados generados y coloque Rn (n es el número de elemento en la lista de reglas separadas), en Table[ stateNo ] [ Ai ], donde “stateNo” es el estado al que pertenece el elemento.
  • “Ai” es cualquier símbolo perteneciente a SEGUIR del símbolo LHS del elemento actual que estamos atravesando. La entrada REDUCIR para la nueva regla aumentada muestra «Aceptación». El analizador puede tener conflictos RR (Reducir-Reducir) y SR (Mayús-Reducir).

Python3

# SLR(1)
 
import copy
 
# perform grammar augmentation
def grammarAugmentation(rules, nonterm_userdef,
                        start_symbol):
   
    # newRules stores processed output rules
    newRules = []
 
    # create unique 'symbol' to
    # - represent new start symbol
    newChar = start_symbol + "'"
    while (newChar in nonterm_userdef):
        newChar += "'"
 
    # adding rule to bring start symbol to RHS
    newRules.append([newChar,
                     ['.', start_symbol]])
 
    # new format  => [LHS,[.RHS]],
    # can't use dictionary since
    # - duplicate keys can be there
    for rule in rules:
       
        # split LHS from RHS
        k = rule.split("->")
        lhs = k[0].strip()
        rhs = k[1].strip()
         
        # split all rule at '|'
        # keep single derivation in one rule
        multirhs = rhs.split('|')
        for rhs1 in multirhs:
            rhs1 = rhs1.strip().split()
             
            # ADD dot pointer at start of RHS
            rhs1.insert(0, '.')
            newRules.append([lhs, rhs1])
    return newRules
 
 
# find closure
def findClosure(input_state, dotSymbol):
    global start_symbol, \
        separatedRulesList, \
        statesDict
 
    # closureSet stores processed output
    closureSet = []
 
    # if findClosure is called for
    # - 1st time i.e. for I0,
    # then LHS is received in "dotSymbol",
    # add all rules starting with
    # - LHS symbol to closureSet
    if dotSymbol == start_symbol:
        for rule in separatedRulesList:
            if rule[0] == dotSymbol:
                closureSet.append(rule)
    else:
        # for any higher state than I0,
        # set initial state as
        # - received input_state
        closureSet = input_state
 
    # iterate till new states are
    # - getting added in closureSet
    prevLen = -1
    while prevLen != len(closureSet):
        prevLen = len(closureSet)
 
        # "tempClosureSet" - used to eliminate
        # concurrent modification error
        tempClosureSet = []
 
        # if dot pointing at new symbol,
        # add corresponding rules to tempClosure
        for rule in closureSet:
            indexOfDot = rule[1].index('.')
            if rule[1][-1] != '.':
                dotPointsHere = rule[1][indexOfDot + 1]
                for in_rule in separatedRulesList:
                    if dotPointsHere == in_rule[0] and \
                            in_rule not in tempClosureSet:
                        tempClosureSet.append(in_rule)
 
        # add new closure rules to closureSet
        for rule in tempClosureSet:
            if rule not in closureSet:
                closureSet.append(rule)
    return closureSet
 
 
def compute_GOTO(state):
    global statesDict, stateCount
 
    # find all symbols on which we need to
    # make function call - GOTO
    generateStatesFor = []
    for rule in statesDict[state]:
        # if rule is not "Handle"
        if rule[1][-1] != '.':
            indexOfDot = rule[1].index('.')
            dotPointsHere = rule[1][indexOfDot + 1]
            if dotPointsHere not in generateStatesFor:
                generateStatesFor.append(dotPointsHere)
 
    # call GOTO iteratively on all symbols pointed by dot
    if len(generateStatesFor) != 0:
        for symbol in generateStatesFor:
            GOTO(state, symbol)
    return
 
 
def GOTO(state, charNextToDot):
    global statesDict, stateCount, stateMap
 
    # newState - stores processed new state
    newState = []
    for rule in statesDict[state]:
        indexOfDot = rule[1].index('.')
        if rule[1][-1] != '.':
            if rule[1][indexOfDot + 1] == \
                    charNextToDot:
                # swapping element with dot,
                # to perform shift operation
                shiftedRule = copy.deepcopy(rule)
                shiftedRule[1][indexOfDot] = \
                    shiftedRule[1][indexOfDot + 1]
                shiftedRule[1][indexOfDot + 1] = '.'
                newState.append(shiftedRule)
 
    # add closure rules for newState
    # call findClosure function iteratively
    # - on all existing rules in newState
 
    # addClosureRules - is used to store
    # new rules temporarily,
    # to prevent concurrent modification error
    addClosureRules = []
    for rule in newState:
        indexDot = rule[1].index('.')
        # check that rule is not "Handle"
        if rule[1][-1] != '.':
            closureRes = \
                findClosure(newState, rule[1][indexDot + 1])
            for rule in closureRes:
                if rule not in addClosureRules \
                        and rule not in newState:
                    addClosureRules.append(rule)
 
    # add closure result to newState
    for rule in addClosureRules:
        newState.append(rule)
 
    # find if newState already present
    # in Dictionary
    stateExists = -1
    for state_num in statesDict:
        if statesDict[state_num] == newState:
            stateExists = state_num
            break
 
    # stateMap is a mapping of GOTO with
    # its output states
    if stateExists == -1:
       
        # if newState is not in dictionary,
        # then create new state
        stateCount += 1
        statesDict[stateCount] = newState
        stateMap[(state, charNextToDot)] = stateCount
    else:
       
        # if state repetition found,
        # assign that previous state number
        stateMap[(state, charNextToDot)] = stateExists
    return
 
 
def generateStates(statesDict):
    prev_len = -1
    called_GOTO_on = []
 
    # run loop till new states are getting added
    while (len(statesDict) != prev_len):
        prev_len = len(statesDict)
        keys = list(statesDict.keys())
 
        # make compute_GOTO function call
        # on all states in dictionary
        for key in keys:
            if key not in called_GOTO_on:
                called_GOTO_on.append(key)
                compute_GOTO(key)
    return
 
# calculation of first
# epsilon is denoted by '#' (semi-colon)
 
# pass rule in first function
def first(rule):
    global rules, nonterm_userdef, \
        term_userdef, diction, firsts
     
    # recursion base condition
    # (for terminal or epsilon)
    if len(rule) != 0 and (rule is not None):
        if rule[0] in term_userdef:
            return rule[0]
        elif rule[0] == '#':
            return '#'
 
    # condition for Non-Terminals
    if len(rule) != 0:
        if rule[0] in list(diction.keys()):
           
            # fres temporary list of result
            fres = []
            rhs_rules = diction[rule[0]]
             
            # call first on each rule of RHS
            # fetched (& take union)
            for itr in rhs_rules:
                indivRes = first(itr)
                if type(indivRes) is list:
                    for i in indivRes:
                        fres.append(i)
                else:
                    fres.append(indivRes)
 
            # if no epsilon in result
            # - received return fres
            if '#' not in fres:
                return fres
            else:
               
                # apply epsilon
                # rule => f(ABC)=f(A)-{e} U f(BC)
                newList = []
                fres.remove('#')
                if len(rule) > 1:
                    ansNew = first(rule[1:])
                    if ansNew != None:
                        if type(ansNew) is list:
                            newList = fres + ansNew
                        else:
                            newList = fres + [ansNew]
                    else:
                        newList = fres
                    return newList
                   
                # if result is not already returned
                # - control reaches here
                # lastly if eplison still persists
                # - keep it in result of first
                fres.append('#')
                return fres
 
 
# calculation of follow
def follow(nt):
    global start_symbol, rules, nonterm_userdef, \
        term_userdef, diction, firsts, follows
     
    # for start symbol return $ (recursion base case)
    solset = set()
    if nt == start_symbol:
        # return '$'
        solset.add('$')
 
    # check all occurrences
    # solset - is result of computed 'follow' so far
 
    # For input, check in all rules
    for curNT in diction:
        rhs = diction[curNT]
         
        # go for all productions of NT
        for subrule in rhs:
            if nt in subrule:
               
                # call for all occurrences on
                # - non-terminal in subrule
                while nt in subrule:
                    index_nt = subrule.index(nt)
                    subrule = subrule[index_nt + 1:]
                     
                    # empty condition - call follow on LHS
                    if len(subrule) != 0:
                       
                        # compute first if symbols on
                        # - RHS of target Non-Terminal exists
                        res = first(subrule)
                         
                        # if epsilon in result apply rule
                        # - (A->aBX)- follow of -
                        # - follow(B)=(first(X)-{ep}) U follow(A)
                        if '#' in res:
                            newList = []
                            res.remove('#')
                            ansNew = follow(curNT)
                            if ansNew != None:
                                if type(ansNew) is list:
                                    newList = res + ansNew
                                else:
                                    newList = res + [ansNew]
                            else:
                                newList = res
                            res = newList
                    else:
                       
                        # when nothing in RHS, go circular
                        # - and take follow of LHS
                        # only if (NT in LHS)!=curNT
                        if nt != curNT:
                            res = follow(curNT)
 
                    # add follow result in set form
                    if res is not None:
                        if type(res) is list:
                            for g in res:
                                solset.add(g)
                        else:
                            solset.add(res)
    return list(solset)
 
 
def createParseTable(statesDict, stateMap, T, NT):
    global separatedRulesList, diction
 
    # create rows and cols
    rows = list(statesDict.keys())
    cols = T+['$']+NT
 
    # create empty table
    Table = []
    tempRow = []
    for y in range(len(cols)):
        tempRow.append('')
    for x in range(len(rows)):
        Table.append(copy.deepcopy(tempRow))
 
    # make shift and GOTO entries in table
    for entry in stateMap:
        state = entry[0]
        symbol = entry[1]
        # get index
        a = rows.index(state)
        b = cols.index(symbol)
        if symbol in NT:
            Table[a][b] = Table[a][b]\
                + f"{stateMap[entry]} "
        elif symbol in T:
            Table[a][b] = Table[a][b]\
                + f"S{stateMap[entry]} "
 
    # start REDUCE procedure
 
    # number the separated rules
    numbered = {}
    key_count = 0
    for rule in separatedRulesList:
        tempRule = copy.deepcopy(rule)
        tempRule[1].remove('.')
        numbered[key_count] = tempRule
        key_count += 1
 
    # start REDUCE procedure
    # format for follow computation
    addedR = f"{seperatedRulesList[0][0]} -> " \
        f"{seperatedRulesList[0][1][1]}"
    rules.insert(0, addedR)
    for rule in rules:
        k = rule.split("->")
         
        # remove un-necessary spaces
        k[0] = k[0].strip()
        k[1] = k[1].strip()
        rhs = k[1]
        multirhs = rhs.split('|')
         
        # remove un-necessary spaces
        for i in range(len(multirhs)):
            multirhs[i] = multirhs[i].strip()
            multirhs[i] = multirhs[i].split()
        diction[k[0]] = multirhs
 
    # find 'handle' items and calculate follow.
    for stateno in statesDict:
        for rule in statesDict[stateno]:
            if rule[1][-1] == '.':
               
                # match the item
                temp2 = copy.deepcopy(rule)
                temp2[1].remove('.')
                for key in numbered:
                    if numbered[key] == temp2:
                       
                        # put Rn in those ACTION symbol columns,
                        # who are in the follow of
                        # LHS of current Item.
                        follow_result = follow(rule[0])
                        for col in follow_result:
                            index = cols.index(col)
                            if key == 0:
                                Table[stateno][index] = "Accept"
                            else:
                                Table[stateno][index] =\
                                    Table[stateno][index]+f"R{key} "
 
    # printing table
    print("\nSLR(1) parsing table:\n")
    frmt = "{:>8}" * len(cols)
    print("  ", frmt.format(*cols), "\n")
    ptr = 0
    j = 0
    for y in Table:
        frmt1 = "{:>8}" * len(y)
        print(f"{{:>3}} {frmt1.format(*y)}"
              .format('I'+str(j)))
        j += 1
 
def printResult(rules):
    for rule in rules:
        print(f"{rule[0]} ->"
              f" {' '.join(rule[1])}")
 
def printAllGOTO(diction):
    for itr in diction:
        print(f"GOTO ( I{itr[0]} ,"
              f" {itr[1]} ) = I{stateMap[itr]}")
 
# *** MAIN *** - Driver Code
 
# uncomment any rules set to test code
# follow given format to add -
# user defined grammar rule set
# rules section - *START*
 
# example sample set 01
rules = ["E -> E + T | T",
         "T -> T * F | F",
         "F -> ( E ) | id"
         ]
nonterm_userdef = ['E', 'T', 'F']
term_userdef = ['id', '+', '*', '(', ')']
start_symbol = nonterm_userdef[0]
 
# example sample set 02
# rules = ["S -> a X d | b Y d | a Y e | b X e",
#          "X -> c",
#          "Y -> c"
#          ]
# nonterm_userdef = ['S','X','Y']
# term_userdef = ['a','b','c','d','e']
# start_symbol = nonterm_userdef[0]
 
# rules section - *END*
print("\nOriginal grammar input:\n")
for y in rules:
    print(y)
 
# print processed rules
print("\nGrammar after Augmentation: \n")
seperatedRulesList = \
    grammarAugmentation(rules,
                        nonterm_userdef,
                        start_symbol)
printResult(seperatedRulesList)
 
# find closure
start_symbol = seperatedRulesList[0][0]
print("\nCalculated closure: I0\n")
I0 = findClosure(0, start_symbol)
printResult(I0)
 
# use statesDict to store the states
# use stateMap to store GOTOs
statesDict = {}
stateMap = {}
 
# add first state to statesDict
# and maintain stateCount
# - for newState generation
statesDict[0] = I0
stateCount = 0
 
# computing states by GOTO
generateStates(statesDict)
 
# print goto states
print("\nStates Generated: \n")
for st in statesDict:
    print(f"State = I{st}")
    printResult(statesDict[st])
    print()
 
print("Result of GOTO computation:\n")
printAllGOTO(stateMap)
 
# "follow computation" for making REDUCE entries
diction = {}
 
# call createParseTable function
createParseTable(statesDict, stateMap,
                 term_userdef,
                 nonterm_userdef)

Producción:

Original grammar input:

E -> E + T | T
T -> T * F | F
F -> ( E ) | id

Grammar after Augmentation: 

E' -> . E
E -> . E + T
E -> . T
T -> . T * F
T -> . F
F -> . ( E )
F -> . id

Calculated closure: I0

E' -> . E
E -> . E + T
E -> . T
T -> . T * F
T -> . F
F -> . ( E )
F -> . id

States Generated: 

State = I0
E' -> . E
E -> . E + T
E -> . T
T -> . T * F
T -> . F
F -> . ( E )
F -> . id

State = I1
E' -> E .
E -> E . + T

State = I2
E -> T .
T -> T . * F

State = I3
T -> F .

State = I4
F -> ( . E )
E -> . E + T
E -> . T
T -> . T * F
T -> . F
F -> . ( E )
F -> . id

State = I5
F -> id .

State = I6
E -> E + . T
T -> . T * F
T -> . F
F -> . ( E )
F -> . id

State = I7
T -> T * . F
F -> . ( E )
F -> . id

State = I8
F -> ( E . )
E -> E . + T

State = I9
E -> E + T .
T -> T . * F

State = I10
T -> T * F .

State = I11
F -> ( E ) .

Result of GOTO computation:

GOTO ( I0 , E ) = I1
GOTO ( I0 , T ) = I2
GOTO ( I0 , F ) = I3
GOTO ( I0 , ( ) = I4
GOTO ( I0 , id ) = I5
GOTO ( I1 , + ) = I6
GOTO ( I2 , * ) = I7
GOTO ( I4 , E ) = I8
GOTO ( I4 , T ) = I2
GOTO ( I4 , F ) = I3
GOTO ( I4 , ( ) = I4
GOTO ( I4 , id ) = I5
GOTO ( I6 , T ) = I9
GOTO ( I6 , F ) = I3
GOTO ( I6 , ( ) = I4
GOTO ( I6 , id ) = I5
GOTO ( I7 , F ) = I10
GOTO ( I7 , ( ) = I4
GOTO ( I7 , id ) = I5
GOTO ( I8 , ) ) = I11
GOTO ( I8 , + ) = I6
GOTO ( I9 , * ) = I7

SLR(1) parsing table:

         id       +       *       (       )       $       E       T       F 

 I0      S5                      S4                       1       2       3 
 I1              S6                           Accept                        
 I2              R2      S7              R2      R2                         
 I3              R4      R4              R4      R4                         
 I4      S5                      S4                       8       2       3 
 I5              R6      R6              R6      R6                         
 I6      S5                      S4                               9       3 
 I7      S5                      S4                                      10 
 I8              S6                     S11                                 
 I9              R1      S7              R1      R1                         
I10              R3      R3              R3      R3                         
I11              R5      R5              R5      R5    

Publicación traducida automáticamente

Artículo escrito por tanmaybisen2001 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *