Compilador Diseño LL(1) Analizador en Python

Requisito previo: construcción de la tabla de análisis LL(1) , clasificación de analizadores de arriba hacia abajo, conjunto PRIMERO , conjunto SEGUIR 

En este artículo, vamos a ver cómo diseñar el compilador LL(1) Parser usando Python.

LL(1) gramática

La primera ‘L’ en LL(1) significa escanear la entrada de izquierda a derecha, la segunda ‘L’ significa producir una derivación más a la izquierda y el ‘1’ para usar un símbolo de entrada de anticipación en cada paso para realizar el análisis decisiones de acción. La gramática LL(1) sigue el método de análisis de arriba hacia abajo. Para una clase de gramáticas llamada LL(1) podemos construir un analizador predictivo de gramáticas. Eso funciona con el concepto de analizador de descenso recursivo que no requiere retroceso.

Eliminación de recursividad izquierda

Una gramática se deja recursiva si tiene un no terminal A tal que hay una derivación A → A α | β. Los métodos de análisis de arriba hacia abajo no pueden manejar gramáticas recursivas a la izquierda, por lo que se necesita una transformación para eliminar la recursividad a la izquierda. La recursión a la izquierda se puede eliminar modificando la regla de la siguiente manera: (A’ es un nuevo no terminal y ε representa a épsilon).

A → β A’
A’ → α A’ | ε

Factoraje a la izquierda

Es una transformación gramatical que es útil para producir una gramática adecuada para el análisis predictivo o de arriba hacia abajo. Cuando la elección entre dos producciones alternativas no está clara, reescribimos las producciones para diferir la decisión de hacer la elección correcta.  

For example, if we have grammar rule A → α β1 | α β2
A → α A’
A’ → β1 | β2

Concepto de primero y seguimiento

La construcción de un analizador de arriba hacia abajo es asistida por las funciones PRIMERO y SEGUIR, que están asociadas con una gramática G. Durante el análisis de arriba hacia abajo, PRIMERO y SEGUIR nos permiten elegir qué producción aplicar, según el siguiente símbolo de entrada.

Reglas para el primer cómputo:

1) Si x es terminal, entonces PRIMERO(x)={x}

2) Si X→ ε es producción, entonces agregue ε a FIRST(X)

3) Si X no es terminal y X → PQR entonces PRIMERO(X)=PRIMERO(P)

   Si PRIMERO(P) contiene ε, entonces  

        PRIMERO(X) = (PRIMERO(P) – {ε}) U PRIMERO(QR)

Reglas para el cálculo de seguimiento:

Nota: Epsilon (ε) nunca puede estar presente en SEGUIMIENTO de ningún símbolo que no sea terminal.

1) Para el símbolo de inicio, coloque $en SEGUIR(S)

2) Si A→ α B, entonces SIGUE(B) = SIGUE(A)

3) Si A→ α B β, entonces

  Si ε no está en PRIMERO(β),

       SEGUIR (B) = PRIMERO (β)

  más hacer,

       SEGUIR(B) = (PRIMERO(β)-{ε}) U SEGUIR(A)

tabla de análisis

Después de la construcción de la tabla de análisis, si para cualquier símbolo no terminal en la tabla tenemos más de una regla de producción para cualquier símbolo terminal en la columna de la tabla, la gramática no es LL(1). De lo contrario, la gramática se considera como LL(1).  

Reglas para la construcción de la tabla de análisis:

Paso 1: Para cada producción A → α , de la gramática dada, realice el Paso 2 y el Paso 3.  

Paso 2: Para cada símbolo de terminal ‘a’ en FIRST(α), AGREGAR A → α en la tabla T[A,a], donde ‘A’ determina la fila y ‘a’ determina la columna.

Paso 3:  Si ε está presente en PRIMERO(α), busque SEGUIR(A), AGREGAR A → ε, en todas las columnas ‘b’, donde ‘b’ es SEGUIR(A). (Pestaña])

Paso 4: Si ε está en PRIMERO(α) y $es el SIGUIENTE(A), AGREGAR A → α a T[A,$].

La suposición hecha en el código:  

a) El símbolo LHS de la Primera regla se considera como símbolo de inicio.

b) ‘#’ representa el símbolo épsilon.

Acercarse

  • Hay siete funciones y el código del controlador, juntos ejecutan los cálculos. El código toma reglas gramaticales como entrada. El usuario debe determinar qué símbolos son terminales (lista: term_userdef) y cuáles no son terminales (lista: nonterm_userdef).
  • El código se ejecuta en el conjunto de muestra 5 con fines de demostración, como se muestra en el código, esta gramática ha abandonado la recursividad, se llama a la función computeAllFirsts(). Esta función es responsable de llamar a las funciones removeLeftRecursion() y LeftFactoring(). Estas funciones funcionan según las reglas mencionadas anteriormente, respectivamente. Ahora, se llama a la función first() para cada no terminal. La lógica recursiva implementada se basa únicamente en las reglas mencionadas de FIRST cálculo solamente.
  • La condición base de first() es si está presente un símbolo épsilon o terminal, para cada código no terminal ingresa una lógica recursiva y si el PRIMERO tiene múltiples símbolos, se devuelve una lista; de lo contrario, se devuelve una string. Por lo tanto, entre líneas de código solo se hace que el programa tenga seguridad de tipo.
  • El cálculo FIRST es el requisito previo para el cálculo FOLLOW ya que la función follow() tiene varias llamadas a la función first(). Un símbolo_de_inicio es el símbolo LHS de la Primera regla dada en la lista de ‘reglas’. (Esto se puede modificar), para el cálculo de SEGUIMIENTO se llama a la función computeAllFollows(). Esto conduce a la llamada de la función follow() en todos los no terminales. La función follow() tiene una lógica recursiva con una condición base, la función invocada en start_symbol que devolverá el símbolo $.
  • Resto, todas las condiciones se manejan según las reglas mencionadas anteriormente en el cálculo de SEGUIMIENTO. Para el objetivo no terminal, se recorren todas las reglas y se calculan los primeros requeridos y los siguientes circulares. Todos los resultados intermedios se acumulan durante el recorrido y la lista final de seguimiento calculado se devuelve al final del recorrido. Aquí también, el caso base devuelve una string, pero si hay varios símbolos en el resultado, se devuelve una lista. Por lo tanto, entre líneas adicionales de código se agregan para seguridad de tipo.
  • Después de calcular FIRST y FOLLOW, se llama a la función createPaseTable(). Aquí, los primeros y los siguientes se emiten de forma formateada. Luego, para la tabla de análisis, se prepara una lista 2D llamada ‘mat’, los no terminales forman filas y los terminales forman columnas, con ‘$’ como una columna adicional. Las reglas mencionadas anteriormente para la construcción de la tabla de análisis forman la base de la lógica implementada.

Enfoque para la validación de strings

  • Después de generar la tabla de análisis, debemos validar la string de entrada para la gramática dada. Hacemos uso de la pila y el búfer para este propósito. Si la gramática no es LL(1), no se puede realizar la validación de strings. Ingrese lo siguiente del SÍMBOLO DE INICIO, ‘$’ tanto en la pila como en el búfer. En primer lugar, ingrese la string de entrada en orden inverso en el búfer. Luego agregue el SÍMBOLO DE INICIO en la parte superior de la pila.
  • Luego, recorremos iterativamente el estado actual de la pila y el búfer y hacemos coincidir las entradas de Table[x][y], donde ‘x’ es un símbolo en la parte superior de la pila e ‘y’ es un símbolo en la parte posterior del búfer. . Tomamos la regla correspondiente de la tabla y expandimos No terminal en TOS en la siguiente iteración.
  • Si x e y ambos símbolos son los mismos terminales, entonces los sacamos de la pila y del búfer y continuamos con el cálculo. Si la pila y el búfer permanecen solo con el símbolo ‘$’, esto muestra que todos los símbolos de string de entrada se han emparejado y la string pertenece a la gramática dada. Si Rule no está presente en la tabla de análisis o x e y son símbolos terminales desiguales, entonces la string no pertenece a la gramática dada.

A continuación se muestra la implementación completa:

Python3

# LL(1) parser code in python
 
def removeLeftRecursion(rulesDiction):
    # for rule: A->Aa|b
    # result: A->bA',A'->aA'|#
 
    # 'store' has new rules to be added
    store = {}
    # traverse over rules
    for lhs in rulesDiction:
        # alphaRules stores subrules with left-recursion
        # betaRules stores subrules without left-recursion
        alphaRules = []
        betaRules = []
        # get rhs for current lhs
        allrhs = rulesDiction[lhs]
        for subrhs in allrhs:
            if subrhs[0] == lhs:
                alphaRules.append(subrhs[1:])
            else:
                betaRules.append(subrhs)
        # alpha and beta containing subrules are separated
        # now form two new rules
        if len(alphaRules) != 0:
            # to generate new unique symbol
            # add ' till unique not generated
            lhs_ = lhs + "'"
            while (lhs_ in rulesDiction.keys()) \
                    or (lhs_ in store.keys()):
                lhs_ += "'"
            # make beta rule
            for b in range(0, len(betaRules)):
                betaRules[b].append(lhs_)
            rulesDiction[lhs] = betaRules
            # make alpha rule
            for a in range(0, len(alphaRules)):
                alphaRules[a].append(lhs_)
            alphaRules.append(['#'])
            # store in temp dict, append to
            # - rulesDiction at end of traversal
            store[lhs_] = alphaRules
    # add newly generated rules generated
    # - after removing left recursion
    for left in store:
        rulesDiction[left] = store[left]
    return rulesDiction
 
 
def LeftFactoring(rulesDiction):
    # for rule: A->aDF|aCV|k
    # result: A->aA'|k, A'->DF|CV
 
    # newDict stores newly generated
    # - rules after left factoring
    newDict = {}
    # iterate over all rules of dictionary
    for lhs in rulesDiction:
        # get rhs for given lhs
        allrhs = rulesDiction[lhs]
        # temp dictionary helps detect left factoring
        temp = dict()
        for subrhs in allrhs:
            if subrhs[0] not in list(temp.keys()):
                temp[subrhs[0]] = [subrhs]
            else:
                temp[subrhs[0]].append(subrhs)
        # if value list count for any key in temp is > 1,
        # - it has left factoring
        # new_rule stores new subrules for current LHS symbol
        new_rule = []
        # temp_dict stores new subrules for left factoring
        tempo_dict = {}
        for term_key in temp:
            # get value from temp for term_key
            allStartingWithTermKey = temp[term_key]
            if len(allStartingWithTermKey) > 1:
                # left factoring required
                # to generate new unique symbol
                # - add ' till unique not generated
                lhs_ = lhs + "'"
                while (lhs_ in rulesDiction.keys()) \
                        or (lhs_ in tempo_dict.keys()):
                    lhs_ += "'"
                # append the left factored result
                new_rule.append([term_key, lhs_])
                # add expanded rules to tempo_dict
                ex_rules = []
                for g in temp[term_key]:
                    ex_rules.append(g[1:])
                tempo_dict[lhs_] = ex_rules
            else:
                # no left factoring required
                new_rule.append(allStartingWithTermKey[0])
        # add original rule
        newDict[lhs] = new_rule
        # add newly generated rules after left factoring
        for key in tempo_dict:
            newDict[key] = tempo_dict[key]
    return newDict
 
 
# 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
# use 'rules' list, and 'diction' dict from above
 
# follow function input is the split result on
# - Non-Terminal whose Follow we want to compute
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 computeAllFirsts():
    global rules, nonterm_userdef, \
        term_userdef, diction, firsts
    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
 
    print(f"\nRules: \n")
    for y in diction:
        print(f"{y}->{diction[y]}")
    print(f"\nAfter elimination of left recursion:\n")
 
    diction = removeLeftRecursion(diction)
    for y in diction:
        print(f"{y}->{diction[y]}")
    print("\nAfter left factoring:\n")
 
    diction = LeftFactoring(diction)
    for y in diction:
        print(f"{y}->{diction[y]}")
 
    # calculate first for each rule
    # - (call first() on all RHS)
    for y in list(diction.keys()):
        t = set()
        for sub in diction.get(y):
            res = first(sub)
            if res != None:
                if type(res) is list:
                    for u in res:
                        t.add(u)
                else:
                    t.add(res)
 
        # save result in 'firsts' list
        firsts[y] = t
 
    print("\nCalculated firsts: ")
    key_list = list(firsts.keys())
    index = 0
    for gg in firsts:
        print(f"first({key_list[index]}) "
              f"=> {firsts.get(gg)}")
        index += 1
 
 
def computeAllFollows():
    global start_symbol, rules, nonterm_userdef,\
        term_userdef, diction, firsts, follows
    for NT in diction:
        solset = set()
        sol = follow(NT)
        if sol is not None:
            for g in sol:
                solset.add(g)
        follows[NT] = solset
 
    print("\nCalculated follows: ")
    key_list = list(follows.keys())
    index = 0
    for gg in follows:
        print(f"follow({key_list[index]})"
              f" => {follows[gg]}")
        index += 1
 
 
# create parse table
def createParseTable():
    import copy
    global diction, firsts, follows, term_userdef
    print("\nFirsts and Follow Result table\n")
 
    # find space size
    mx_len_first = 0
    mx_len_fol = 0
    for u in diction:
        k1 = len(str(firsts[u]))
        k2 = len(str(follows[u]))
        if k1 > mx_len_first:
            mx_len_first = k1
        if k2 > mx_len_fol:
            mx_len_fol = k2
 
    print(f"{{:<{10}}} "
          f"{{:<{mx_len_first + 5}}} "
          f"{{:<{mx_len_fol + 5}}}"
          .format("Non-T", "FIRST", "FOLLOW"))
    for u in diction:
        print(f"{{:<{10}}} "
              f"{{:<{mx_len_first + 5}}} "
              f"{{:<{mx_len_fol + 5}}}"
              .format(u, str(firsts[u]), str(follows[u])))
 
    # create matrix of row(NT) x [col(T) + 1($)]
    # create list of non-terminals
    ntlist = list(diction.keys())
    terminals = copy.deepcopy(term_userdef)
    terminals.append('$')
 
    # create the initial empty state of ,matrix
    mat = []
    for x in diction:
        row = []
        for y in terminals:
            row.append('')
        # of $ append one more col
        mat.append(row)
 
    # Classifying grammar as LL(1) or not LL(1)
    grammar_is_LL = True
 
    # rules implementation
    for lhs in diction:
        rhs = diction[lhs]
        for y in rhs:
            res = first(y)
            # epsilon is present,
            # - take union with follow
            if '#' in res:
                if type(res) == str:
                    firstFollow = []
                    fol_op = follows[lhs]
                    if fol_op is str:
                        firstFollow.append(fol_op)
                    else:
                        for u in fol_op:
                            firstFollow.append(u)
                    res = firstFollow
                else:
                    res.remove('#')
                    res = list(res) +\
                          list(follows[lhs])
            # add rules to table
            ttemp = []
            if type(res) is str:
                ttemp.append(res)
                res = copy.deepcopy(ttemp)
            for c in res:
                xnt = ntlist.index(lhs)
                yt = terminals.index(c)
                if mat[xnt][yt] == '':
                    mat[xnt][yt] = mat[xnt][yt] \
                                   + f"{lhs}->{' '.join(y)}"
                else:
                    # if rule already present
                    if f"{lhs}->{y}" in mat[xnt][yt]:
                        continue
                    else:
                        grammar_is_LL = False
                        mat[xnt][yt] = mat[xnt][yt] \
                                       + f",{lhs}->{' '.join(y)}"
 
    # final state of parse table
    print("\nGenerated parsing table:\n")
    frmt = "{:>12}" * len(terminals)
    print(frmt.format(*terminals))
 
    j = 0
    for y in mat:
        frmt1 = "{:>12}" * len(y)
        print(f"{ntlist[j]} {frmt1.format(*y)}")
        j += 1
 
    return (mat, grammar_is_LL, terminals)
 
 
def validateStringUsingStackBuffer(parsing_table, grammarll1,
                                   table_term_list, input_string,
                                   term_userdef,start_symbol):
 
    print(f"\nValidate String => {input_string}\n")
 
    # for more than one entries
    # - in one cell of parsing table
    if grammarll1 == False:
        return f"\nInput String = " \
               f"\"{input_string}\"\n" \
               f"Grammar is not LL(1)"
 
    # implementing stack buffer
 
    stack = [start_symbol, '$']
    buffer = []
 
    # reverse input string store in buffer
    input_string = input_string.split()
    input_string.reverse()
    buffer = ['$'] + input_string
 
    print("{:>20} {:>20} {:>20}".
          format("Buffer", "Stack","Action"))
 
    while True:
        # end loop if all symbols matched
        if stack == ['$'] and buffer == ['$']:
            print("{:>20} {:>20} {:>20}"
                  .format(' '.join(buffer),
                          ' '.join(stack),
                          "Valid"))
            return "\nValid String!"
        elif stack[0] not in term_userdef:
            # take font of buffer (y) and tos (x)
            x = list(diction.keys()).index(stack[0])
            y = table_term_list.index(buffer[-1])
            if parsing_table[x][y] != '':
                # format table entry received
                entry = parsing_table[x][y]
                print("{:>20} {:>20} {:>25}".
                      format(' '.join(buffer),
                             ' '.join(stack),
                             f"T[{stack[0]}][{buffer[-1]}] = {entry}"))
                lhs_rhs = entry.split("->")
                lhs_rhs[1] = lhs_rhs[1].replace('#', '').strip()
                entryrhs = lhs_rhs[1].split()
                stack = entryrhs + stack[1:]
            else:
                return f"\nInvalid String! No rule at " \
                       f"Table[{stack[0]}][{buffer[-1]}]."
        else:
            # stack top is Terminal
            if stack[0] == buffer[-1]:
                print("{:>20} {:>20} {:>20}"
                      .format(' '.join(buffer),
                              ' '.join(stack),
                              f"Matched:{stack[0]}"))
                buffer = buffer[:-1]
                stack = stack[1:]
            else:
                return "\nInvalid String! " \
                       "Unmatched terminal symbols"
 
 
# DRIVER CODE - MAIN
 
# NOTE: To test any of the sample sets, uncomment ->
# 'rules' list, 'nonterm_userdef' list, 'term_userdef' list
# and for any String validation uncomment following line with
# 'sample_input_String' variable.
 
sample_input_string = None
 
# sample set 1 (Result: Not LL(1))
# rules=["A -> S B | B",
#        "S -> a | B c | #",
#        "B -> b | d"]
# nonterm_userdef=['A','S','B']
# term_userdef=['a','c','b','d']
# sample_input_string="b c b"
 
# sample set 2 (Result: LL(1))
# rules=["S -> A | B C",
#        "A -> a | b",
#        "B -> p | #",
#        "C -> c"]
# nonterm_userdef=['A','S','B','C']
# term_userdef=['a','c','b','p']
# sample_input_string="p c"
 
# sample set 3 (Result: LL(1))
# rules=["S -> A B | C",
#        "A -> a | b | #",
#        "B-> p | #",
#        "C -> c"]
# nonterm_userdef=['A','S','B','C']
# term_userdef=['a','c','b','p']
# sample_input_string="a c b"
 
# sample set 4 (Result: Not LL(1))
# rules = ["S -> A B C | C",
#          "A -> a | b B | #",
#          "B -> p | #",
#         "C -> c"]
# nonterm_userdef=['A','S','B','C']
# term_userdef=['a','c','b','p']
# sample_input_string="b p p c"
 
# sample set 5 (With left recursion)
# rules=["A -> B C c | g D B",
#        "B -> b C D E | #",
#        "C -> D a B | c a",
#        "D -> # | d D",
#        "E -> E a f | c"
#       ]
# nonterm_userdef=['A','B','C','D','E']
# term_userdef=["a","b","c","d","f","g"]
# sample_input_string="b a c a c"
 
# sample set 6
# rules=["E -> T E'",
#        "E' -> + T E' | #",
#        "T -> F T'",
#        "T' -> * F T' | #",
#        "F -> ( E ) | id"
# ]
# nonterm_userdef=['E','E\'','F','T','T\'']
# term_userdef=['id','+','*','(',')']
# sample_input_string="id * * id"
# example string 1
# sample_input_string="( id * id )"
# example string 2
# sample_input_string="( id ) * id + id"
 
# sample set 7 (left factoring & recursion present)
rules=["S -> A k O",
       "A -> A d | a B | a C",
       "C -> c",
       "B -> b B C | r"]
 
nonterm_userdef=['A','B','C']
term_userdef=['k','O','d','a','c','b','r']
sample_input_string="a r k O"
 
# sample set 8 (Multiple char symbols T & NT)
# rules = ["S -> NP VP",
#          "NP -> P | PN | D N",
#          "VP -> V NP",
#          "N -> championship | ball | toss",
#          "V -> is | want | won | played",
#          "P -> me | I | you",
#          "PN -> India | Australia | Steve | John",
#          "D -> the | a | an"]
#
# nonterm_userdef = ['S', 'NP', 'VP', 'N', 'V', 'P', 'PN', 'D']
# term_userdef = ["championship", "ball", "toss", "is", "want",
#                 "won", "played", "me", "I", "you", "India",
#                 "Australia","Steve", "John", "the", "a", "an"]
# sample_input_string = "India won the championship"
 
# diction - store rules inputed
# firsts - store computed firsts
diction = {}
firsts = {}
follows = {}
 
# computes all FIRSTs for all non terminals
computeAllFirsts()
# assuming first rule has start_symbol
# start symbol can be modified in below line of code
start_symbol = list(diction.keys())[0]
# computes all FOLLOWs for all occurrences
computeAllFollows()
# generate formatted first and follow table
# then generate parse table
 
(parsing_table, result, tabTerm) = createParseTable()
 
# validate string input using stack-buffer concept
if sample_input_string != None:
    validity = validateStringUsingStackBuffer(parsing_table, result,
                                              tabTerm, sample_input_string,
                                              term_userdef,start_symbol)
    print(validity)
else:
    print("\nNo input String detected")
 
# Author: Tanmay P. Bisen
Producción

Rules: 

S->[['A', 'k', 'O']]
A->[['A', 'd'], ['a', 'B'], ['a', 'C']]
C->[['c']]
B->[['b', 'B', 'C'], ['r']]

After elimination of left recursion:

S->[['A', 'k', 'O']]
A->[['a', 'B', "A'"], ['a', 'C', "A'"]]
C->[['c']]
B->[['b', 'B', 'C'], ['r']]
A'->[['d', "A'"], ['#']]

After left factoring:

S->[['A', 'k', 'O']]
A->[['a', "A''"]]
A''->[['B', "A'"], ['C', "A'"]]
C->[['c']]
B->[['b', 'B', 'C'], ['r']]
A'->[['d', "A'"], ['#']]

Calculated firsts: 
first(S) => {'a'}
first(A) => {'a'}
first(A'') => {'c', 'r', 'b'}
first(C) => {'c'}
first(B) => {'r', 'b'}
first(A') => {'d', '#'}

Calculated follows: 
follow(S) => {'$'}
follow(A) => {'k'}
follow(A'') => {'k'}
follow(C) => {'d', 'c', 'k'}
follow(B) => {'d', 'c', 'k'}
follow(A') => {'k'}

Firsts and Follow Result table

Non-T      FIRST                FOLLOW              
S          {'a'}                {'$'}               
A          {'a'}                {'k'}               
A''        {'c', 'r', 'b'}      {'k'}               
C          {'c'}                {'d', 'c', 'k'}     
B          {'r', 'b'}           {'d', 'c', 'k'}     
A'         {'d', '#'}           {'k'}               

Generated parsing table:

           k           O           d           a           c           b           r           $
S                                         S->A k O                                                
A                                         A->a A''                                                
A''                                                    A''->C A'   A''->B A'   A''->B A'            
C                                                         C->c                                    
B                                                                 B->b B C        B->r            
A'        A'->#                A'->d A'                                                            

Validate String => a r k O

              Buffer                Stack               Action
           $O k r a                  S $       T[S][a] = S->A k O
           $O k r a              A k O $       T[A][a] = A->a A''
           $O k r a          a A'' k O $           Matched:a
             $O k r            A'' k O $    T[A''][r] = A''->B A'
             $O k r           B A' k O $           T[B][r] = B->r
             $O k r           r A' k O $           Matched:r
               $O k             A' k O $         T[A'][k] = A'->#
               $O k                k O $           Matched:k
                 $O                  O $           Matched:O
                   $                   $               Valid

Valid String!

Inferencia

El conjunto de muestras 7 se utiliza para representar la salida del código y cubre todos los aspectos del análisis LL(1). Se imprime la gramática después de la eliminación de la recursividad por la izquierda y después de la factorización por la izquierda. Después de eso, también tenemos los primeros y siguientes resultados de cálculo. Luego generamos la tabla de análisis, si no hay entradas múltiples en ninguna posición (Tabla[NT][T]) en la tabla, decimos que la gramática es LL(1). Finalmente, la string de entrada de muestra se valida mediante la validación del búfer de pila.

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 *