terça-feira, 18 de maio de 2010

Proposta de Parser

Segue minha proposta de parser para nosso trabalho, eu creio que seja a melhor solução mas aguardo para ver se conseguem encontrar algum erro.
Minha proposta é o de utilizar uma tabela para parser top-down determinístico utilizando os conjuntos FIRST-FOLLOW para nossa gramática ambígua. Eu sei que não se pode fazer isto, pois a gramática é ambígua, mas para nosso caso isto é ótimo.

O problema de utilizar este tipo de parser no caso de uma gramática ambígua é que o método não se torna determinístico, pois em algum momento teremos duas produções distintas a seguir e que casam com a cadeia estudada. Entretanto, como em nosso trabalho todos os casamentos possíveis devem ser encontradas, logo este método se torna adequado porque além de derivar somente em produções que casam com a cadeia, encontra todas as formas de casamento possíveis. A seguir vamos exemplificar o método com uma regra de substituição simples:

a{b*}{b*} → /1a/2

Primeira convertemos para gramática com o método proposto no paper, teremos:

S → aG1G2
G1 → bG1 | ε
G2 → bG2 | ε

Para simplificar a derivação podemos escrever:

S → aA
A → G1G2
G1 → bG1
G1 → ε
G2 → bG2
G2 → ε

Em seguida encontramos os conjuntos First e Follow de cada uma das variáveis: S, A, G1, G2.

O conjunto FIRST de uma variável x indica quais são os símbolos mais a esquerda que aparecem na derivação a partir de uma variável x.

Uma maneira fácil de se encontrar o conjunto First(x) de uma variável x é analisando todas as produções x → α da gramática. Considerando a como um terminal e A como uma variável, temos a regra:

α = ε ˅ a,                                          First(ε ou a) = {α} = {ε} ou {a}
α = aβ,                                              First(aβ) = {a}
α = A ˄ A → β1 | β2 | ...,                  First(A) U= First(β1) U First(β2) U First(β3) U ...
α = Aβ ˄ A não deriva ε                   First(Aβ) U= First(A)
α = Aβ ˄ A deriva ε                          First(Aβ) U= (First(A) - {ε}) U First(β)

Vamos calcular os conjuntos FIRST das variáveis de nosso exemplo. Começando com a primeira produção: S → aA, teremos:

FISRT(S), (S → aA) = FISRT(aA) = a

Continuando teremos:

FIRST(A), (A → G1G2) = FIRST(G1G2) = {FIRST(G1) - {ε}} U FIRST(G2) = {b} U {b, ε} = {b, ε}
FIRST(G1), (G1 → bG1) = FIRST(bG1) = {b}
FIRST(G1), (G1 → ε) = FIRST(ε) = {ε}
FIRST(G2), (G2 → bG2) = FIRST(bG2) = {b}
FIRST(G2), (G2 → ε) = FIRST(ε) = {ε}

Resumindo:

FIRST(S) = a
FIRST(A) = {b, ε}
FIRST(G1) = {b, ε}
FIRST(G2) = {b, ε}

Com os conjuntos FIRST, partindo da variável inicial S podemos encontrar a árvore de derivação de uma cadeia, caso a gramática não seja ambigua e não possua produções que derive ε. Não vou dar exemplos pois existem vários na Internet sobre isto, vou seguir para o FOLLOW.

Um problema ocorre quando dentre as produções da gramática exista alguma que derive ε, neste caso não podemos definir qual é o símbolo mais a esquerda produzido pela variável. Por exemplo, tome como base a sentença AB, sendo que A deriva ε, o primeiro símbolo mais a esquerda desta sentença poderá ser também os símbolos que estão mais a esquerda de B, quando A for ε. O objetivo de FOLLOW é justamente resolver este problema.

O conjunto FOLLOW é encontrado através das regras:

A → α ˄ A é o símbolo inicial,               FOLLOW(A) U= {#}
B → αAY,                                             FOLLOW(A) U= FIRST(Y) - {ε}
B → αAY ˄ ε ϵ FIRST(Y),                    FOLLOW(A) U= FOLLOW(B)

Para nosso exemplo teremos:

FOLLOW(S) = {#}
FOLLOW(A) = FOLLOW(S → aA) = FOLLOW(S → aAε) = FOLLOW(S) = {#}
FOLLOW(G1) = FOLLOW(A → G1G2) = FIRST(G2)-{ε} U FOLLOW(A) = {b} U {#}
FOLLOW(G1) = FOLLOW(G1 → bG1) = FOLLOW(G1)

FOLLOW(G2) = FOLLOW(A → G1G2) = FOLLOW(A) = {#}
FOLLOW(G2) = FOLLOW(G2 → bG2) = FOLLOW(G2)

Resumindo:


FOLLOW(S) = {#}
FOLLOW(A) = {#}
FOLLOW(G1) = {b, #}
FOLLOW(G2) = {#}

Com os conjuntos FIRST e FOLLOW conseguimos construir nossa tabela de parser para geração das cadeias substituídas. A tabela de parser é uma tabela com pares (A, a), onde para cada produção A → α adicionamos α em (A, a) para todos os terminais a em FIRST(α FOLLOW(A)). Em nosso caso, teremos:

S → aA
FIRST(aA FOLLOW(S)) = a, logo, (S, a) terá S → aA,

A → G1G2
FIRST(G1G2 FOLLOW(A)) = {b, ε} {#}, logo, (A, b) e (A, #) terão A → G1G2,

G1 → bG1
FIRST(bG1 FOLLOW(G1)) = b, logo (G1, b) terá G1 → bG1,

G1 → ε
FIRST(ε FOLLOW(G1)) = {b, #}, logo (G1, b) e (G1, #) terão G1 → ε,

G2 → bG2
FIRST(bG2 FOLLOW(G2)) = b, logo (G2, b) terá G2 → bG2,

G2 → ε
FIRST(ε FOLLOW(G2)) = {#}, logo (G2, #) terá G2 → ε,

A tabela fica então:


Voltando agora ao nosso exemplo de regra de substituição inicial: a{b*}{b*} → /1a/2, vamos realizar uma simulação com a tabela de parser e a cadeia abb.
Antes de iniciar adicionamos na cadeia o símbolo de final de cadeia #: abb#. Segue a tabela de parser realizado:

No passo 3, quando temos G1 no topo da pilha e o símbolo b mais a esquerda para casar, temos duas opções na tabela de parser, portanto, neste caso teremos que ramificar nestas duas opções e realizá-las separadamente. Primeiro vamos expandir a ramificação 3.1:


Aqui encontramos outra ambiguidade, seguimos expandindo a ramificação 3.1.1.1:


Agora encontramos o símbolo de final de cadeia e portanto, encontramos um caminho de realizar o match. Percorrendo novamente os estados podemos associar os símbolos da cadeia inicial com seus respectivos grupos G1 e G2.
G1 apareceu primeiro em 3.1.0 e produzir um b, logo em seguida produziu outro b em 3.1.1.1.0 e G2 produziu ε em 3.1.1.1.3. Logo para esta árvore de derivação temos:

G1 = bb
G2 = ε

Voltando e ramificando os que faltam teremos:



Analisando os grupos temos os seguintes casamentos:

G1 = bb, G2 = ε
G1 = b, G2 = b
G1 = ε, G2 = bb

Finalmente, com os grupos definidos, podemos criar as cadeias substituídas da regra de substituição: a{b*}{b*} → /1a/2 que serão:

bba
bab
abb

Caso não tenham entendido alguma coisa me digam.

Um comentário: