Você está na página 1de 6

Autômatos Teoria da Computação

aula 17: Linguagens (legais) para autômatos de pilha

1 Introdução

(. . . )

2 Linguagens legais

PARBAL é a linguagem dos parênteses balanceados.

Por exemplo, as seguintes palavras estão em PARBAL

(), (()), ()(), (()()), ...

mas essas outras não estão

)(, (((), ())), (()))(, ...

É fácil ver que as palavras de PARBAL devem ter a mesma quantidade de abre parênteses e
fecha parênteses — (essa é a parte BAL da linguagem)

Mas, isso não basta.

Imaginando que a palavra vai ser lida da esquerda para a direita

é preciso que que todo fecha parênteses corresponda a um abre parênteses que apareceu antes.

E observamos também que só pode haver um fecha parênteses em correspondência com um abre parênteses.

Quer dizer, se a gente vai contando os abre e fecha parênteses, o fecha parênteses nunca deve passar
à frente na contagem.

E é só isso — (essa é a parte PAR da linguagem)

Agora que a gente viu isso, é bem fácil construir o autômato de pilha que reconhece PARBAL

)[(,e]

e[e, ] e[ ,e]

([e,(]

Note que os sı́mbolos de abre parênteses são apenas empilhados.

E que a leitura de um fecha parênteses só pode ocorrer se existe algum abre parênteses na pilha.

1
É isso o que garante a propriedade acima.

Finalmente, o marcador de pilha vazia (⊥) inserido no inı́cio da computação, só pode ser removido
no final se a palavra tem a mesma quantidade de abre parênteses e fecha parênteses.

A seguir, nós vamos ver outros exemplos.

Exemplos

a. PAROP

Agora nós vamos complicar um pouquinho as coisas.


Quer dizer, imagine dessa vez que todo par de parênteses deve conter ao menos uma operação no
seu interior.
Essa é a linguagem PAROP.
A intuição é que as palavras de PAROP consistem em expressões aritméticas (envolvendo apenas
+ e ∗), onde os números foram todos apagados

4 + 7 ∗ 5 ⇒ +∗

2 ∗ (4 + 7 + 1) ∗ 5 ⇒ ∗(++)∗

((4 + 7) ∗ (5 + 2)) + 1 ⇒ ((+) ∗ (+))+

Mas para simplificar as coisas, nós também vamos aceitar coisas como

(+∗)(+)

Para obter um autômato de pilha que reconhece PAROP nós vamos fazer duas modificações no
autômato que foi contruı́do no exemplo anterior

- agora, os operadores + e × também vão ser empilhados

- ao encontrar um fecha parênteses, nós precisamos verificar se existe ao menos uma


operação no topo da pilha (e remover todos elas antes de remover o abre parênteses)

Abaixo nós temos o autômato de pilha que reconhece PAROP

*[e,*]
+[e,+]
([e,(]

e[e, ] e e[ ,e]

) e[(,e] e[+,e]
e[*,e]

e[+,e]
e[*,e] e[+,e]
e[*,e] 

2
b. EXPR

Essa é a linguagem das expressões aritméticas (envolvendo apenas + e ∗).


Quer dizer, a novidade é que agora os números também aparecem na palavra.
Por exemplo,
(4 + 7)

( ( 18 + 5 ) ∗ 2 )

( ( 5 + 7 ) ∗ ( 15 + 2 ) )

E isso significa que nós vamos ter que verificar uma condição adicional.
Quer dizer, as palavras de EXPR devem satisfazer o seguinte

- os parênteses devem estar balanceados


- todo par de parênteses deve ter ao menos uma operação no seu interior
- e toda operação deve ter o seu par de operandos

Na prática, duas operações podem compartilhar o mesmo operando

7 + 2 ∗ 5 + 1

Daı́ que, o que nós esperamos encontrar é uma alternação: operando, operaç~
ao, operando,
operaç~
ao, ....
E a ideia é que nós podemos empilhar tudo isso, e verificar a alternação na hora do desempil-
hamento.
Certo.
Mas ainda existe uma pequena dificuldade.
Quer dizer, às vezes o operando é uma expressão entre parênteses

3 ∗ (2 + 1 + 4) + 1

Daı́ que, se a leitura da expressão não deixar nada na pilha, então nós não vamos ver uma
alternação
3 ∗ + 1

Mas, a solução é simples: basta colocar uma variável X no lugar da expressão

3 ∗ X + 1

E agora, nós temos uma alternação outra vez.


De fato, a gente também pode substituir os números pela variável X.
E assim as coisas ficam ainda mais simples

X ∗ X + X

Abaixo nós temos o autômato de pilha que reconhece a linguagem EXPR

3
0..9

0..9 ( [e, ( ]
e[e, X] +[e, +]
∗[e, ∗]
e[e, ⊥] e[X, e] e[⊥, e]

) e[(, X] e[+, e]
e[X, e]
e[X, e] e[∗, e]

e[+, e]
e[X, e]
e[∗, e]

c. REG

Essa é a linguagem das expressões regulares.


Por exemplo,
∗
(ab ∪ ba)∗ , b(a ∪ ba)∗ (ab)∗ , a (aaa∗ b)∗ , ...

As expressões regulares são semelhantes às expressões aritméticas, com a sua estrutura de
parênteses balanceados e a operação binária de união (∪).

Por outro lado,

• não é preciso haver uma operação dentro de um par de parênteses: (baa)∗

• a estrela (∗) é um operador unário

• e a justaposição é uma operação sem operador: (ab ∪ ba)∗ baa

Para lidar com essas novidades, nós vamos utilizar a variável E (i.e., expressão), e as seguintes
regras

bba −→ E

E∗ −→ E

EE −→ E

(E ∪ . . . ∪ E) −→ E

Daı́ que, a coisa fica assim

4
a, b

a, b e[EE, E]
e[e, E] ( [e, ( ]
∗[E, E]
e[e, ⊥] e[E, e] e[⊥, e]

) e[(, E] e[∪, e] e[E, e]


e[E, e]

e[∪, e] e[E, e]

d. LOOP
A linguagem LOOP é uma linguagem de programação muito simples que possui apenas dois tipos
de instrução

X ← <expr aritmética>; Loop X


<bloco de comandos>
EndLoop

Isto é, um comando de atribuição e um laço de repetição.


O número de voltas do laço é determinado pelo valor da variável X no momento em que o programa
chega ao laço.
Modificações no valor dessa variável dentro do bloco de comandos não afetam o número de voltas
do laço.
Por exemplo, o programa abaixo troca os valores de X e Y, caso o primeiro seja menor do que o
segundo

Z <-- Y - X;
LOOP Z
X <-- X + 1;
Y <-- Y - 1;
ENDLOOP

Note que a coisa funciona porque na situação emq em Z vale zero ou um número negativo, o
programa não entra no laço.
Certo.
Agora imagine que um programa em LOOP é uma palavra — (i.e., uma sequência de caracteres)
Daı́, a gente pode definir a linguagem

ProgLOOP = palavras que correspondem a programas válidos em LOOP

5
Por exemplo, o programa acima é uma palavra de ProgLOOP.
Mas o seguinte programa não é

LOOP X
Y <-- X + * 41;
ENDLOOP
LOOP Y
ENDLOOP
LOOP Z

Quer dizer, nós temos 3 problemas aqui

- a expressão aritmética no comando de atribuição não é válida


- o segundo laço não tem um bloco de comandos
- as quantidades de termos LOOP e ENDLOOP não são iguais

A tarefa agora é

→ Construir um autômato de pilha que reconhece a linguagem ProgLOOP

Bom, é bem fácil ver que os termos LOOP e ENDLOOP fazem o papel de abre-parênteses e
fecha-parênteses.
E nós podemos reutilizar o autômato que reconhece EXPR para verificar se os comandos de
atribuição são válidos.
Daı́, a coisa fica assim


MEXPR

X, Y, ...
e[e, ⊥] e[C, e] e[⊥, e]

LOOP[e, (] e[(, C] e[C, e]


ENDLOOP
X, Y, ... e[C, e]

e[C, e]

Você também pode gostar