Capítulo 2. Linguagens formais 1. Conceitos básicos e notações Definição : Um alfabeto, ou vocabulário, geralmente representado por V, é um conjunto finito de símbolos ou caracteres. Os símbolos do alfabeto são designados por palavras. Exemplos : a) conjunto de palavras da língua portuguesa listados num dicionário; b) conjunto V = { 0, 1 } que constitui o alfabeto binário. Definição : Uma sequência finita de palavras de um dado alfabeto chama-se frase (“string”) desse alfabeto. Definição : O conjunto de todas as frases de um alfabeto V denota-se por V*. Definição : O comprimento de uma frase α é o número de palavras que a frase contém e é representado por |α|. A frase nula tem comprimento zero e é representada por ε. Logo, |ε| = 0. Definição : A concatenação da frase α com a frase β, denominada por αβ, é uma frase formada pelas palavras de α seguida pelas palavras de β. A concatenação da frase α com ela própria n vezes, αα…α, é representada por αn. A operação de concatenação goza das seguintes propriedades : • ε constitui o elemento neutro da concatenação : para qualquer frase α, α ε = ε α = α • a concatenação é associativa : para quaisquer frases α, β e δ, (α β) δ = α (β δ). Definição : Dadas duas frases quaisquer, α e β, α é prefixo da frase αβ e β é sufixo da frase αβ. Uma subfrase é formada retirando um prefixo e um sufixo de uma dada frase. Definição : Uma linguagem L sobre um alfabeto V, é um conjunto de frases, em que cada frase é composta por palavras pertencentes ao alfabeto V. Uma linguagem não admite todas as combinações possíveis de palavras do seu alfabeto; apenas certas combinações é que dão origem a frases válidas ou seja, L ⊆ V*. Normalmente, a linguagem é um conjunto infinito, o que torna a sua enumeração impossível. Definição : Denomina-se por sintaxe de uma linguagem L sobre o alfabeto V, o conjunto de regras que delimita o subconjunto V* constituinte da linguagem. 10 Gramáticas e linguagens Exemplos : • ∅ é uma linguagem vazia • { ε } é uma linguagem formada pela frase nula • { 0 } é uma linguagem formada por uma única frase, no alfabeto binário V = { 0, 1 } • { 00, 01, 10, 11 } é a linguagem formada pelas frases de comprimento 2, no alfabeto binário V = { 0, 1 } • conjunto de todos os programas em Pascal sintacticamente bem formados • conjunto de todas as frases gramaticalmente correctas da língua portuguesa A representação das frases de uma linguagem pode ser efectuada por duas formas : 1. Extensiva, em que são listadas todas as frases da linguagem 2. Predicativa, segundo uma fórmula baseada em linguagens previamente definidas. A forma predicativa tem o seguinte formato : { fórmula sobre frases genérica | Predicado }, em que o predicado restringe as frases indicadas pela fórmula declarada à esquerda. Exemplo : { an | n é par } é uma representação predicativa de uma linguagem baseada na linguagem { a }. Ou seja, de todas as frases formadas por sequências de a, apenas são admitidas aquelas que têm comprimento par. 2. Gramáticas e linguagens 2.1. Definição de gramática geradora Definição : Uma gramática geradora é todo o quádruplo ordenado G = (Σ, T, P, S), em que • Σ é um conjunto finito, não vazio, de símbolos não-terminais ou variáveis; • T é um conjunto finito, não vazio, de símbolos terminais; • S ∈ Σ é designado por símbolo inicial; • P ⊆ (Σ ∪ T)+ × (Σ ∪ T)* é um conjunto de regras sintácticas, ou de rescrita, também designadas por produções ou derivações. O conjunto V = Σ ∪ T constitui o vocabulário completo. Normalmente, cada par em P é representado na forma X → α, no qual X ∈ (Σ ∪ T)+ e α ∈ (Σ ∪ T)*, para representar que (X, σ) é um elemento de P. X e α são designados, respectivamente, por lado esquerdo e lado direito da produção, e a produção lê-se X deriva (imediatamente) em α. Duas produções com igualdade do primeiro membro do par, (Σ ∪ T)+, são frequentemente designadas por alternativas. É frequente as gramáticas serem descritas numa notação designada por BNF − Backus Naur Form − que consiste no seguinte : 1. Os símbolos terminais são escritos em letras minúsculas, por vezes a carregado. 2. Os símbolos não-terminais são iniciados por uma letra maiúscula. 3. Numa produção, os lados esquerdo e direito são separados pelo símbolo → ou pelo símbolo ::=. 4. Produções alternativas, X → α1, X → α2, …, X → αn, são representadas por X → α1|α2|…|αn . 5. Se o lado direito de uma produção X → α não contém qualquer símbolo, então escreve-se X → ε. Por vezes, as regras 1 e 2 são substituídas pela seguinte regra única : os símbolos não-terminais são delimitados pelos símbolos < e >. Linguagens formais Gramáticas e linguagens 11 2.2. Grafos de sintaxe As regras sintácticas de uma gramática podem ser representadas, graficamente, por forma a incrementar a sua legibilidade. Os grafos de sintaxe são construídos a partir da representação BNF de uma gramática com base nas cinco regras descritas a seguir. 1. Símbolos terminais α são representados por círculos : α 2. Símbolos não-terminais X são representados por caixas : X 3. Produções na forma X → α1 α2 … αn são representadas pelo grafo : X α1 α2 … αn 4. Produções na forma X → α1|α2| … |αn são representadas pelo grafo : α1 α2 X … αn 5. Produções na forma X → { α } são representadas pelo grafo : X α Por exemplo, dada a gramática, S → Sa|Bc a sua representação gráfica é a seguinte : B → ε|bB S a S c b A representação gráfica é obtida por aplicação mecânica das regras de geração do grafo. Por vezes, há necessidade de deslocar alguns elementos gráficos, por forma a evitar o cruzamento de linhas. 2.3. Derivação de frases Informalmente, uma frase pertence a uma linguagem definida por uma determinada gramática, se for possível executar, com sucesso, o seguinte algoritmo : 1. Começar pelo símbolo inicial da gramática. 2. Substituir sucessivamente os símbolos não-terminais pelo lado direito de produções que contenham no lado esquerdo o símbolo seleccionado. 3. O processo termina quando se atingir a frase a detectar. O processo de substituição de símbolos não-terminais denomina-se derivação. Podem definir-se dois processos de derivação (definições seguintes). Linguagens formais 12 Gramáticas e linguagens Definição : Dada uma gramática G = (Σ, T, P, S) e dois símbolos gramaticais, X, Y ∈ (Σ ∪ T)+, diz-se que Y deriva de X por uma derivação de um só passo e escreve-se X ⇒ Y G se existirem símbolos gramaticais p1 e p2 em (Σ ∪ T)* e uma produção P → Q tal que X = p1 P p2 e Y = p1 Q p2 Definição : Se p1, p2, …, pm ∈ (Σ ∪ T)*, m ≥ 1, verificarem p 1 ⇒ p 2 , p 2 ⇒ p 3 , ..., p m − 1 ⇒ p m G escreve-se G G ∗ p1 ⇒ pm G e diz-se que pm deriva de p1 na gramática G. 2.4. Linguagem gerada por uma gramática Definição : A linguagem gerada pela gramática G contém todas as frases derivadas a partir do símbolo inicial S que contenham somente símbolos terminais (caracteres), ∗ ∗ G G L(G) = { p : S ⇒ p com p ∈ T* } = { p : S ⇒ p } ∩ T* Os símbolos não-terminais são auxiliares nas derivações dos elementos de uma linguagem. Repare que pela definição da relação em P, para a existência de uma produção é necessário que o elemento da esquerda contenha um símbolo não-terminal. Uma derivação colapsa se existindo no elemento da esquerda símbolos não-terminais não existem produções que possam ser aplicadas. Logo, uma frase está na linguagem L(G) se e só se (i) for constituída somente por elementos terminais; (ii) for obtida por derivação a partir de S. Exemplos : 1. Consideremos a gramática G = (Σ, T, P, S) onde Σ={S} T = { a, b } P={S→aSb, S→ab} isto é, S (símbolo inicial) é o único símbolo não-terminal, a e b são símbolos terminais, existindo apenas as duas produções apresentadas. O nosso objectivo é determinar a linguagem gerada pela gramática G, L(G). (i) Ao aplicar-se a primeira produção (n−1)-vezes seguida pela aplicação uma vez da 2ª produção, tem-se : S ⇒ a S b ⇒ a a S b b ⇒ a3 S b3 ⇒ … ⇒ an-1 S bn-1 ⇒ an bn . (ii) Repare-se agora que sempre que se faz uso da primeira produção, o número de S’s mantém-se. Na utilização da segunda produção, o número de S’s decresce de uma unidade. Logo, a utilização da segunda produção elimina os S’s na sequência resultante. Como ambas as produções têm um único S à esquerda, a ordem pela qual as produções podem ser aplicadas é S → a b, isto é, (i). Logo, L(G) = { an bn : n ≥ 1 } NOTA : Neste exemplo foi muito simples determinar que frases foram derivadas de S, assim como o único tipo de frases derivadas de S. No entanto, geralmente, é difícil determinar a linguagem gerada por uma dada gramática. Linguagens formais Gramáticas e linguagens 13 2. Consideremos a gramática geradora G = (Σ, T, P, S) onde Σ = { S, A, B } T = { a, b } contendo as seguintes produções S → aB S → bA A → bAA A → a A → aS B → b B → bS B → aBB Prova-se por indução que L(G) é o conjunto de frases em T* contendo um número igual de a’s e de b’s : L(G) = { σ ∈ T + : na (σ) = nb (σ) } onde na (σ) representa o número de a’s que aparecem em σ ∈ T*. 3. Consideremos a gramática geradora G = (Σ, T, P, S) onde Σ = { S, X, Y } T = { a, b, c } contendo as seguintes produções S → abc Xc → Ybcc aY → aaX S → aXbc Xb → bX bY → Yb Prova-se que L(G) = { an bn cn : n = 1, 2, 3, … } aY → aa Definição : Uma derivação diz-se efectuada pela esquerda (“leftmost”), se em cada passo é substituído o símbolo não-terminal situado mais à esquerda. Definição : Uma derivação diz-se efectuada pela direita (“rightmost”), se em cada passo é substituído o símbolo não-terminal situado mais à direita. 2.5. Derivação de formas sentenciais O processo de derivação inicia-se no símbolo inicial da gramática e termina na frase. No meio do processo, são obtidas sequências de símbolos terminais e não-terminais, as quais são designadas por formas sentenciais. Definição : Dada uma gramática G = (Σ, T, P, S), uma forma sentencial é uma sequência de símbolos ∗ ∗ terminais e não-terminais, α, tal que existe uma frase s ∈ L(G) e existem 2 derivações S ⇒ α ⇒ s 2.6. Reconhecimento de uma frase A operação inversa da derivação é o reconhecimento. Informalmente, um reconhecimento verifica se uma sequência de palavras (frase) faz parte, ou não, da linguagem. Definição : Seja L ⊆ T* uma linguagem. Um reconhecimento RL é um algoritmo que aceita ⇐ s ∈ L R L (s) = rejeita ⇐ s ∉ L O reconhecimento não tem por objectivo apresentar a sequência de derivação da frase s, mas apenas indicar se ela existe ou não. 2.7. Símbolos anuláveis e símbolos inúteis Os símbolos não-terminais podem ser desnecessários em todas as derivações de frases da linguagem, isto é, a partir do símbolo consegue-se derivar a frase nula. Neste caso, diz-se que os símbolos são anuláveis. Linguagens formais 14 Gramáticas e linguagens Definição : Um símbolo não terminal A ∈ Σ diz-se anulável se existir uma produção A → ε. Definição : Um símbolo não-terminal diz-se inútil ou não-necessário se : a) não existir uma derivação de uma frase a partir dela; b) quando não apareça em qualquer forma sentencial derivada com inicio no símbolo inicial. No primeiro caso diz-se que a variável é inactiva ou morta. No segundo caso diz-se que é uma variável inatingível. Definição : Uma variável numa gramática G diz-se activa se pelo menos uma frase terminal for originada a partir dela. Uma variável diz-se atingível se ocorrer em, pelo menos, uma forma setencial derivada a partir do símbolo inicial da gramática. 2.8. Árvores de derivação Uma árvore de derivação não é mais do que uma representação gráfica, em forma de árvore, da sequência de derivação. Numa árvore de derivação, os nós interiores estão associados a símbolos nãoterminais e as folhas associadas a símbolos terminais. Definição : Seja N um conjunto não-vazio. Chamaremos vértices aos elementos de N. O conjunto de arestas, E, é todo o subconjunto E ⊆ N×N. Para cada aresta e = (n1, n2) ∈ E, s(e) = n1, t(e) = n2 dizem-se, respectivamente, o vértice inicial e o vértice terminal de e. Uma sucessão de arestas e0, e1, …, ek diz-se um caminho orientado de comprimento k de s(e0) a t(ek) se s(ei+1) = t(ei) para i = 0, 1, …, k-1. Um par ordenado (N, E) diz-se uma árvore orientada se existir um vértice r ∈ N tal que 1) nenhum dos vértices terminais em E coincide com r; 2) existe um único caminho orientado a partir de r para qualquer outro vértice em N. O vértice r diz-se a raiz da árvore. Pela própria definição, cada árvore admite uma única raiz. Cada vértice da árvore é a raiz de uma sub-árvore. Vértices terminais que não sejam vértices iniciais de uma aresta dizem-se folhas. 2.9. Recursividade Definição : Uma gramática G = (Σ, T, P, S) diz-se recursiva à esquerda (à direita) se existirem A ∈ Σ ∗ ∗ e a ∈ L(G), tal que existe uma derivação A ⇒ A a ( A ⇒ a A ) 2.10. Hierarquia de Chomsky 2.10.1. Classificação de linguagens segundo Chomsky Cada gramática gera uma única linguagem, mas a mesma linguagem pode ser gerada por várias gramáticas. Definição : Duas gramáticas dizem-se equivalentes se gerarem a mesma linguagem. Apresenta-se em seguida uma classificação das linguagens de acordo com o tipo de produções que contenham, devido a Chomsky. Linguagens formais Gramáticas e linguagens 15 Definição : Uma gramática geradora diz-se do tipo i se satisfazer as seguintes restrições : i=0 se nenhuma restrição for imposta. i=1 se toda a produção tiver a forma Q1 A Q2 → Q1 α Q2 para Q1, Q2, α ∈ V*, A ∈ Σ, onde α i=2 i=3 não pode ser a frase vazia, Λ, excepto no caso em que o símbolo inicial não apareça no lado direito das regras, caso esse em que se permite a presença da produção S → Λ. toda a produção é da forma A → α para A ∈ Σ e α ∈ V*. toda a regra em G é da forma A → αB e A → X, com A, B ∈ Σ, α ∈ T*. Uma linguagem diz-se do tipo i se for gerada por uma gramática do tipo i. Representa-se por Li a classe das linguagens do tipo i. Gramáticas do tipo 0 dizem-se gramáticas com estrutura de frase. Gramáticas do tipo 1 são chamadas gramáticas sensíveis ao contexto, porque as respectivas regras só permitem a substituição da ocorrência de um símbolo não-terminal no contexto Q1, Q2. As regras do tipo 2 dizem-se livres do contexto, uma vez que permitem a substituição de A por P em qualquer contexto. Assim, as linguagens L2 dizem-se linguagens livres do contexto. As gramáticas do tipo 3 constituem as gramáticas regulares ou gramáticas de estados finitos, e estão relacionadas com os autómatos finitos. Como consequências imediatas das definições, tem-se : (a) L3 ⊆ L2 ⊆ L0 (b) L1 ⊆ L0 e também L3 ⊂ L2 ⊂ L1 ⊂ L0 2.10.2. Operações com linguagens Sendo as linguagens conjuntos, é legítimo estabelecerem-se as seguintes definições (com L1 e L2 designarem linguagens arbitrárias) : • L1 ∩ L2 = { P : P ∈ L1 e P ∈ L2 } • L1 ∪ L2 = { P : P ∈ L1 ou P ∈ L2 } • L1 − L2 = { P : P ∈ L1 e P ∉ L2 } = L 1 ∩ L 2 O complemento de L relativamente a T* representa-se por L e é, por definição, L = T* − L • A justaposição dos elementos de T* permite estabelecer-se L1L2 = { P1P2 : P1 ∈ L1 e P2 ∈ L2 } Admitindo como generalização para cada inteiro i, • Li pondo, para i = 0, L0 = { ε }. Toma-se ainda L* = U Li . i ≥0 Teorema : Para cada gramática G = (Σ, T, P, S) existe uma gramática equivalente do mesmo tipo, na qual as partes esquerdas das regras não contêm símbolos terminais. Definição : As operações de união, justaposição e geração de linguagens (representada por *) chamam-se operações regulares de linguagem. Teorema : Cada classe de linguagem Li, i = 0, 1, 2, 3 é fechada para as operações regulares. Linguagens formais 16 Linguagens independentes do contexto Exemplo : Sejam T1 e T2, respectivamente, o alfabeto da língua portuguesa e o conjunto dos dez algarismos decimais. Pretende-se construir uma gramática do tipo 3 geradora de uma linguagem, L, das frases que contenham só letras ou só dígitos, ou que começando por letras continuem com dígitos. Se quisermos incluir a frase vazia, ε ∈ L, então ter-se-á L = T1* ∪ T2* ∪ T1* T2* = T1* T2* Consideremos dois símbolos S1, S2 que não pertençam a T1 e a T2, e consideremos as gramáticas G1 = ( { S1 }, T1 , P1 , S1 ) G2 = ( { S2 }, T2 , P2 , S2 ) onde P1 contém S1 → ε ; e P2 contém S1 → a S1 ; … ; S1 → z S1 S2 → ε ; S2 → 0 S2 ; … ; S2 → 9 S2 É imediato que sendo estas gramáticas do tipo 3 se tem L(G1) = T1* e que L(G2) = T2* . Então, a gramática G’ = ( { S1, S2 }, T1 ∪ T2 , P1‘ ∪ P2 , S1 ) com P1’ contendo S1 → S2 ; S1 → a S1 ; … ; S1 → z S1 é do tipo 3 e gera T1* T2* 3. Linguagens independentes do contexto 3.1. Introdução Uma das classes de gramáticas mais usadas, nomeadamente na definição de linguagens de programação, são as independentes (livres) do contexto do tipo 1. Teorema : Para toda a gramática livre do contexto, G, é possível determinar uma gramática livre do contexto, G’, onde os lados direitos das regras são todos diferentes de Λ, excepto quando Λ ∈ L(G) caso em que é permitida a regra S’ → Λ, sempre que S’ não apareça no lado direito das regras. Corolário : L2 ⊆ L1. Definição : Uma gramática G diz-se ε−livre se nenhum dos lados direitos das regras de G contiver o símbolo ε. 3.2. Forma normal de Chomsky Definição : Uma gramática livre do contexto diz-se que está na forma normal de Chomsky, se as respectivas regras forem dos dois tipos seguintes : 1) X → a , X ∈ Σ, a ∈ T 2) X → Y Z , X, Y, Z ∈ Σ Teorema : Para cada gramática do tipo 2 ε−livre, G = (Σ, T, P, S), é possível determinar uma gramática equivalente, G’ = (Σ, T, P’, S’), na forma normal de Chomsky. Linguagens formais Linguagens independentes do contexto 17 3.3. Árvores de derivação É possível associar uma árvore a cada derivação de uma frase numa linguagem independente do contexto, chamada árvore de derivação. Para tal, à raiz da árvore associaremos o símbolo inicial S. A qualquer outro símbolo do vocabulário V = Σ ∪ T corresponde um vértice, que é rotulado por ele, sendo necessariamente os símbolos terminais associados a folhas na árvore. Se todas as folhas da árvore forem rotuladas por símbolos terminais, a árvore (de derivação) representa a derivação de uma frase da linguagem. Exemplo : Considere-se a gramática G = ( { S, A, B }, { a, b, c }, P, S) onde o conjunto P é constituído pelas produções seguintes : (1) S → A B c (2) A → A B (3) A → B c (4) B → a A c (5) B → b c A seguinte derivação em G S ⇒ Abc ⇒ AaAcc ⇒ BcaAcc ⇒ BcaaBcc ⇒ bccaaBcc ⇒ bccaabccc pode ser representada pela seguinte árvore de derivação S A B b B c c a c A a c B b c NOTA : Uma árvore de derivação não especifica a ordem pela qual as produções são utilizadas. No exemplo anterior a regra A → Bc foi aplicada antes da regra B → bc. No entanto, é indiferente a ordem de aplicação das regras B → aAc e A → Bc. Para evitar as escolhas possíveis considera-se uma derivação à esquerda numa árvore de derivação, convencionando que em cada passo se substitui, em primeiro lugar, o símbolo não-terminal mais à esquerda. Duas derivações serão equivalente se diferirem somente pela ordem de aplicação das regras, isto é, se originarem a mesma árvore de derivação. 3.4. First, Follow e Lookahead 3.4.1. First Se α é uma forma sentencial, isto é, α ∈ (Σ ∪ T)*, então First (α) é o conjunto dos símbolos terminais que começam as cadeias de derivação a partir de α : ∗ First (α) = { a ∈ T : α ⇒ a β , β ∈ (Σ ∪ T)* }. ∗ Se α ⇒ ε então ε ∈ First (α). Linguagens formais 18 Linguagens independentes do contexto Para se calcular o First (X) para todos os símbolos gramaticais (terminais e não terminais) da gramática G, aplique-se as seguintes regras até que nenhum terminal ou ε possa ser adicionado a qualquer conjunto First : 1. Se X for um terminal (X ∈ T), então First (X) = { X }. 2. Se X → ε for uma produção de G, adicionar ε a First (X) ε ∈ First (X). 3. Se X for um não terminal (X ∈ Σ) e X → Y1Y2 … Yn uma produção, então a ∈ First (X) se, para ∗ algum i, a ∈ First (Yi) e Y1Y2 … Yi−1 ⇒ ε (isto é, ε ∈ First (Yk), ∀ k = 1, 2, …, i−1). ∗ Se Y1Y2 … Yn ⇒ ε, então ε ∈ First (X). 3.4.2. Follow Seja A um símbolo não terminal (A ∈ Σ). Define-se Follow (A) como sendo o conjunto de símbolos terminais a que podem figurar imediatamente à direita de A em qualquer forma sentencial : ∗ Follow (A) = { a ∈ T : S ⇒ α A a β , α, β ∈ (Σ ∪ T)* } Note-se que podem ter existido, nalgum instante da derivação, símbolos entre A e a, mas, se assim o foi, os mesmos derivaram ε e desapareceram. Se A puder ser o símbolo mais à direita nalguma forma sentencial, então $ (marcador de fim de entrada à direita) está em Follow (A). Para calcular Follow (A) para todos os símbolos não terminais da gramática G, aplique-se as seguintes regras até que nada mais possa ser adicionado a qualquer conjunto Follow : 1. Colocar $ em Follow (S), onde S é o símbolo inicial de G : $ ∈ Follow (S). 2. Se existir uma produção A → α B β, então qualquer a ∈ First (β) (a ≠ ε), a ∈ Follow (B). 3. Se existir uma produção A → α B ou uma produção A → α B β onde First (β) contém ε, então qualquer a ∈ Follow (A), a ∈ Follow (B). 3.4.3. Lookahead (símbolos directores) Por vezes o conjunto Lookahead não é apresentado nalguns livros sobre compiladores, porque este conjunto é definido com base nos conjuntos First e Follow. Desta forma, define-se conjunto L (Lookahead) na gramática G, da seguinte forma : 1. Dado A ∈ Σ, ∗ Follow (A ) se A ⇒ ε L (A) = First (A) ∪ = First (First (A) Follow (A) ) ∅ caso contrário 2. Para qualquer produção A → α Follow (A ) L (A) = First (α) ∪ ∅ ∗ se α ⇒ ε = First (First (α) Follow (A) ) caso contrário 3.5. Gramáticas redundantes Definição : Uma gramática independente do contexto diz-se redundante se contiver símbolos nãoterminais não-necessários. NOTA : É evidente que a eliminação das variáveis inactivas e inatingíveis numa gramática independente do contexto não altera a linguagem por ela gerada. Assim, é possível determinar uma gramática equivalente não-redundante para cada gramática independente do contexto dada. É, no entanto, mais fácil determinar as variáveis activas e as variáveis atingíveis. Linguagens formais Linguagens independentes do contexto 19 Definição : Uma gramática independente do contexto é não-redundante ou reduzida se toda a variável for activa e atingível. Teorema : Para cada gramática independente do contexto é possível determinar uma gramática equivalente não-redundante. Método para determinar uma gramática reduzida de uma dada gramática G : 1. Eliminação dos símbolos inactivos : Relativamente à gramática dada definam-se, por recursão, os seguintes conjuntos : A1 = { X ∈ Σ : X → P, para algum P ∈ T* } A2 = A1 ∪ { X ∈ Σ : X → W, para algum W ∈ (T ∪ A1)* } … ou ainda Ai+1 = Ai ∪ { X ∈ Σ : X → W, para algum P ∈ (T ∪ Ai)* } Os conjuntos Ak constituem uma sequência não decrescente de subconjuntos de Σ, conjunto finito, A1 ⊆ A2 ⊆ … ⊆ Ai ⊆ Ai+1 ⊆ … Logo, existe um k tal que Ak = Ak+j , para j = 1, 2, 3, … . Pela própria definição, Ak é o conjunto das variáveis activas da gramática. Por último, eliminam-se todos os símbolos que não pertençam a Ak, assim como todas as produções onde estes ocorrem. 2. Eliminação dos símbolos inatingíveis : Da mesma maneira define-se, também recursivamente e depois de eliminar os símbolos inactivos, o conjunto das variáveis atingíveis, do modo seguinte : R1 = { S } R2 = R1 ∪ { Y ∈ Σ : S → U Y W, para U, W ∈ (Σ ∪ T)* } … ou ainda Ri+1 = Ri ∪ { Y ∈ Σ : X → U Y W, para algum X ∈ Ri , e para U, W ∈ (Σ ∪ T)* } Existe então um inteiro m tal que Rm+j = Rm , para j = 1, 2, 3, … . Tal conjunto, Rm , constitui o conjunto das variáveis atingíveis. Ao eliminar-se todos os símbolos que não pertençam a Rm, assim como todas as produções onde estes ocorrem, obtém-se uma gramática equivalente (gramática reduzida) à gramática G. NOTA : Pode-se aplicar este teorema para se verificar se uma linguagem gerada por uma gramática, L(G), é vazia, ou não : L(G) é vazia se e só se o símbolo inicial S for inactivo. 3.6. Gramáticas do tipo LL(1) O significado de LL(1) é o seguinte : L “Left-scan”, leitura da frase de entrada da esquerda para a direita L “Leftmost” derivation”, derivação da esquerda para a direita (1) O uso de um (1) único símbolo de entrada como lookahead a cada passo para tomar decisões. As gramáticas LL(1) possuem várias propriedades distintas. Nenhuma gramática ambígua ou recursiva à esquerda pode ser LL(1). Pode também ser mostrado que uma gramática independente do Linguagens formais 20 Linguagens independentes do contexto contexto G é LL(1) se e só se para cada par de produções A → α | β distintas de G, vigorarem as seguintes condições : 1. α e β não derivem, ao mesmo tempo, cadeias que começam pelo mesmo símbolo terminal a, qualquer que seja a. 2. No máximo um dos dois, α e β, derive a cadeia vazia (ε). ∗ 3. Se β ⇒ ε , então α não deriva qualquer cadeia que comece por um terminal pertencente a Follow (A). Por outras palavras, e utilizando a definição do conjunto Lookahead, uma gramática independente do contexto G é LL(1) se e só se para cada par de produções A → α | β distintas de G L(A → α) ∩ L(A → β) = ∅. 3.7. Gramáticas do tipo LL(k) Uma gramática do LL(k), é definida segundo dois parâmetros : 1. O conjunto dos primeiros k símbolos, da seguinte forma : First (k, t) = { t }, para t ∈ T ; First (k, ε) = { ε } α se α ≤k First (k, α) = se α = βδ : β = k β First (k, A B … X) = First ( k, First (k, A) First (k, B) … First (k, X) ) First (k, A) = U First (k , α ) A→ α 2. O conjunto dos k símbolos seguidores, da seguinte forma : ∀p : B → αAδ Follow (k, A) = U First ( k , First (k , δ) Follow(k , B)) δ,B ∀p : B → αSδ Follow (k, S) = { $ } ∪ U First ( k , First (k , δ) Follow(k , B)) δ,B 3. O conjunto dos k símbolos directores, da seguinte forma : ∗ L(k, A) = First (k, A) ∪ { Follow (k, A), se A ⇒ ε } = First (k, First (k, A) Follow (k, A)) ∗ L(k, A → α) = First (k, α) ∪ { Follow (k, A), se A ⇒ ε } = First (k, First (k, α) Follow (k, A)) Definição : Uma gramática independente do contexto é do tipo LL(k) se e só se : L(k, A → α) ∩ L(k, A → β) = ∅ para qualquer par de produções A → α | β. Exemplo : Estudar a gramática com as seguintes produções : S → bRS | RcSa | ε R→ acR | b Prova-se que a gramática é do tipo LL(2). Linguagens formais Linguagens independentes do contexto 21 3.8. Gramáticas de atributos Uma gramática de atributos não é mais do que uma gramática independente de contexto, na qual as produções são enriquecidas por regras semânticas por forma a que os símbolos (terminais ou não terminais) recebam valores. As gramáticas de atributos têm sido usadas na definição de compiladores : a gramática independente de contexto descreve a linguagem formal e as regras semânticas permitem a translação da entrada para uma linguagem objecto, tipicamente uma representação intermédia. 3.8.1. Definição Definição : Uma gramática de atributos AG consiste em : • uma gramática independente de contexto G = (Σ, T, P, S); • para cada símbolo gramatical X ∈ (Σ ∪ T), um conjunto finito de atributos sintetizados, S(X), e um conjunto de atributos herdados, I(X). Os conjuntos S(X) e I(X) possuem as seguintes restrições : − S(X) e I(X) são conjuntos disjuntos, isto é, S(X) ∩ I(X) = ∅ − I(S) = ∅, isto é, o símbolo inicial da gramática não possui atributos herdados − ∀ x ∈ Σ, S(x) = ∅, isto é, os símbolos terminais não possuem atributos sintetizados. A(X) = S(X) ∪ I(X) representa a união dos conjuntos S(X) e I(X). A cada atributo a é atribuído um domínio Da, ou seja, o conjunto de valores possíveis. • a cada produção P na forma X0 → X1 . . . Xn Para todos os atributos a ∈ S(X0) e para todos os atributos a ∈ I(Xi), 1 ≤ i ≤ n, existe um conjunto de regras semânticas ΓP na forma : A(Xi) = f(b1(Xi ), . . ., bk(Xi )) 1 k nas quais são verificadas as seguintes restrições : − 0 ≤ i ≤ n e i1, . . ., ik ≤ n ; − a, b1, ..., bk são atributos ; f é uma função Db × . . . × Db → Da ; 1 k − a ∈ A(Xi), b1 ∈ A(Xi ), . . ., bk ∈ A(Xi )) ; − 1 k se a é um atributo sintetizado, então i = 0 ; − se a é um atributo herdado, então 1 ≤ i ≤ n ; − Cada atributo tem um identificador e um domínio. Normalmente, o identificador do atributo possui a forma símbolo . id Nas funções semânticas apenas podem ser usados : • literais ; • valores de atributos em ocorrências de símbolos na produção ; • funções puras, isto é, sem efeitos colaterais. Definição : Seja uma gramática de atributos AG, α uma frase de LAG e t uma árvore de derivação + S ⇒ α. Num nó n de t, etiquetado por X ∈ (Σ ∪ T), a diz-se ocorrência (ou instância) de um atributo em t se e só se a ∈ A(X). O atributo a possui um valor de Da. 3.8.2. Grafos de dependência local As dependências nas regras semânticas entre os atributos de uma gramática podem ser representados graficamente por grafos de dependência local. Linguagens formais 22 Linguagens e expressões regulares As representações gráficas de cada elemento dos grafos de dependência local são descritos na seguinte tabela : Elemento Representado por Atributo Círculo Símbolo gramatical Identificador Dependência entre atributos Arco entre círculos Os atributos e os símbolos gramaticais são inscritos dentro de um rectângulo. Os identificadores dos atributos são colocados ao lado do símbolo gramatical respectivo. O lado do rectângulo onde se posicionam os atributos depende do lado onde o símbolo gramatical se encontra na produção. Posição na produção Lado de rectângulo Esquerdo Superior Direito Inferior O fluxo de cálculo de atributos sintetizados é representado graficamente por setas ascendentes, ou por setas horizontais, e o fluxo de cálculo de atributos herdados é representado graficamente por setas descendentes. 4. Linguagens e expressões regulares As linguagens regulares constituem uma classe bastante importante das linguagens. Elas são usadas, por exemplo, para definir os elementos léxicos de uma linguagem de programação a partir do alfabeto formado pelos caracteres latinos. Definição : Uma linguagem diz-se regular se puder ser obtida aplicando um número finito de operações sobre as seguintes linguagens : • linguagem vazia; • linguagem cuja única frase é a frase nula; • linguagens com uma única frase. Os operadores aplicáveis a quaisquer duas linguagens L1 e L2 são : 1. Concatenação : L1 L2 = { u v : u ∈ L1 ∧ v ∈ L2 } 2. Exponenciação : L0 = { ε } e Li = L Li-1 3. União : L1 ∪ L2 = { u : u ∈ L1 ∨ u ∈ L2 } 4. Fecho de Kleene (zero ou mais concatenações de L) : L* = U ∞i =0 L i Exemplo : Seja T = { a, b }. Como exemplos de linguagens regulares tem-se : • ({ a } ∪ { b })* { a } são utilizados o operador de fecho de Kleene e o operador de concatenação sobre a linguagem base La = { a }. A linguagem resultante é, portanto, L*T L a e descreve o conjunto de frases terminadas em a (com comprimento superior ou igual a 1). • { a }{ a }* resulta na linguagem L a L*a e descreve o conjunto de frases iniciadas em a. Atendendo a que um conjunto finito de frases pode ser considerado como uma linguagem regular (onde o conjunto das regras será S → P, para os elementos P desse conjunto), é possível a partir de um conjunto finito de frases construir uma linguagem regular por aplicação das operações regulares nesse conjunto. Linguagens formais Gramáticas lineares e linguagens regulares 23 As expressões regulares permitem representar, de modo sucinto, as linguagens regulares. Uma das vantagens das expressões regulares reside na possibilidade da sua utilização como linguagem de especificação de analisadores léxicos. Definição : Uma expressão regular sobre um alfabeto T define uma linguagem L, segundo o seguinte conjunto de regras : • Λ é uma expressão regular que define a linguagem vazia; • ε é uma expressão regular que define uma linguagem que consiste na frase nula { ε }; • dado a ∈ T, a é uma expressão regular que representa a linguagem La, tal que La = { a }; • dadas 2 expressões regulares, p e q, que representam, respectivamente, as linguagens Lp e Lq − p + q é uma expressão regular que representa a linguagem Lp ∪ Lq − pq é uma expressão regular que representa a linguagem Lp Lq − p* é uma expressão regular que representa a linguagem L*p − (p) é uma expressão regular que representa a linguagem Lp Os operadores que aparecem nas expressões regulares têm a seguinte prioridade (da maior para a menor) : fecho de Kleene, concatenação e união. Todos os operadores são associativos à esquerda. Para facilitar a definição de expressões regulares, acrescentaram-se os seguintes operadores : • fecho transitivo “+” corresponde a uma ou mais ocorrências. Por exemplo, p+ é equivalente a pp* e p* é equivalente a p+ + ε • parêntesis rectos “[ ]” servem para substituir classes de símbolos de V. Por exemplo, [pq] corresponde a p + q. Caso os símbolos de V sejam ordenados, podem ser definidos intervalos como [p−q]. Notações : Considere-se o alfabeto T = { a, b }. Então pode-se representar por a* = (a)* a linguagem { a }* a linguagem { a, b }* (a ∪ b)* a* b a linguagem { a }* { b } b ∪ a b* a linguagem { b } ∪ { a } { b }* a linguagem { a, b } { a }* (a ∪ b) a* Definição : Duas expressões regulares α e β dizem-se equivalentes, α ≡ β, se e só se definirem a mesma linguagem (isto é, Lα = Lβ) Teorema : Toda a expressão regular representa uma linguagem regular e toda a linguagem regular pode ser representada por uma expressão regular. 5. Gramáticas lineares e linguagens regulares Definição : Uma gramática independente do contexto diz-se linear se as respectivas produções forem dos dois tipos seguintes : (1) A → P A ∈ Σ, P ∈ T* (2) A → Q1 B Q2 A, B ∈ Σ, Q1, Q2 ∈ T* A gramática diz-se linear à esquerda (linear à direita) se em cada regra do tipo (2), Q1 (Q2) for a frase vazia (ε). NOTA : Pela própria definição, as gramáticas lineares à direita são as gramáticas regulares (do tipo 3). Teorema : Toda a gramática linear à esquerda gera uma linguagem regular. Corolário : Toda a linguagem regular pode ser gerada por uma gramática linear à esquerda. Linguagens formais 24 Linguagens sensíveis ao contexto e linguagens com estrutura de frase Teorema : Toda a gramática regular é equivalente a uma gramática contendo unicamente regras do tipo seguinte : (1) X → aY X, Y ∈ Σ , a ∈ T (2) X → ε X∈Σ Teorema : Sendo L ∈ L2 e L’ ∈ L3 então L ∩ L’ ∩ L2 , isto é, a linguagem intersecção de uma linguagem livre do contexto com uma linguagem regular é uma linguagem livre do contexto. 6. Linguagens sensíveis ao contexto e linguagens com estrutura de frase Definição : Uma gramática com estrutura de frase (do tipo 0), G, diz-se de comprimento crescente (ou mais propriamente, de comprimento não decrescente) se todas as produções de G, P → Q, verificarem |P| ≤ |Q|. Teorema : Toda a gramática de comprimento crescente gera uma linguagem sensível ao contexto. Corolário : A classe das linguagens livres do contexto está propriamente contida na classe das linguagens sensíveis ao contexto, isto é, L2 ⊂ L1 . Definição : Uma gramática de comprimento crescente diz-se na forma normal de Kuroda se as respectivas produções forem do tipo : (1) A → a (2) A → B (3) A → B C (4) A B → C D Teorema : Toda a gramática de comprimento crescente é equivalente a uma gramática na forma normal de Kuroda. Corolário : Toda a linguagem sensível ao contexto ε-livre é gerada por uma gramática na forma normal de Kuroda. Teorema : Para toda a gramática com estrutura de frase, G, existe uma gramática equivalente, G’, em que cada regra é de um dos tipos seguintes : (1) S → A (4) A → B C (2) A → a (5) A B → A C (3) A → B (6) A B → B e onde o símbolo inicial S só pode ocorrer no lado esquerdo das regras. Linguagens formais