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