17/03/2014 Análise Sintática Introdução Renato Ferreira Linguagens e Automatas • Linguagens formais são importantes em Computação – Especialmente em linguagens de programação • Linguagens regulares – A linguagem formal mais fraca – Adotada na prática – Muitas aplicações • Linguagens Livres de Contexto – Várias outras definições em Linguagens Formais 1 17/03/2014 Linguagens Regulares • Linguagens que necessitam de contagem módulo um inteiro fixo • Intuição: Um autômato finito que roda por tempo suficiente tem que repetir estados • Automatos finitos não se lembram quantas vezes um estado particular foi visitado Além das Linguagens Regulares • Muitas linguagens são não regulares • Strings com parentesis balanceados: { (i)i | i ≥ 0} 2 17/03/2014 A funcionalidade de em Parser • Entrada: sequencia de tokens do analisador léxico • Saída: uma árvore de reconhecimento do programa – Alguns parsers não produzem a árvore… Exemplo • COOL if x = y then 1 else 2 fi • Entrada do parser IF ID = ID THEN INT ELSE INT FI • Saída do parser IF‐THEN‐ELSE = I D INT INT I D 3 17/03/2014 Comparação com Análise Léxica Fase Entrada Saída Analisador léxico Strings de Caracteres String de Tokens Analisador sintático (Parser) String de Tokens Árvore de Reconhecimento (Parse tree) O Papel do Analisador Sintático • Nem todos os strings de tokens são programas… – O parser tem que distinguir entre os strings válidos e os inválidos • Demandas – Uma linguagem pra descrever strings de tokens – Um método para distinguir strings válidos de inválidos 4 17/03/2014 Gramáticas Livres de Contexto • Construções em Linguagens de Programação tem estrutura recursiva • Uma EXPR é: – if EXPR then EXPR else EXPR fi – while EXPR loop EXPR pool –… • Gramáticas livres de contexto constituem uma notação natural para essa estrutura recursiva Definição • Uma gramática livre de contexto consiste de: – Um conjunto de símbolos terminais T – Um conjunto de símbolos não‐terminais N – Um símbolo inicial S (S N) – Um conjunto de produções: X Y1Y2 ...Yn • onde XN Yi T N 5 17/03/2014 Exemplos de Gramáticas Livres de Contexto (GLC) • Um fragmento de COOL: EXPR if EXPRthen EXPR else EXPR fi | while EXPRloop EXPR pool | id Mais Exemplos de GLCs • Expressões aritméticas E E*E | EE | (E) | id 6 17/03/2014 A linguagem de uma GLC • As produções são vistas como regras: X Y1...Yn • Significa que X pode ser substituido por Y1…Yn • Idéia Chave 1. Inicie com um string que consiste do Símbolo Inicial “S” 2. Substitua qualquer X não terminal na string pelo lado direito de uma produção qualquer X Y1LYn 3. Repita (2) até que não hajam mais símbolos não terminais na string 7 17/03/2014 Formalizando • Seja G uma GLC com símbolo inicial S. • A linguagem definida por G é: {a1…an | S *a1…an onde todo ai é terminal} Terminais • Terminais são assim chamados pois não existem regras para substituí‐los • Uma vez gerados, os terminais são permanentes • Os terminais são os tokens da linguagem 8 17/03/2014 Exemplos • Seja L(G) a linguagem da GLC G – Strings com parentesis balanceados {(i)i|i≥0} S (S) S ε Exemplos em COOL EXPR if EXPRthen EXPR else EXPR fi | while EXPRloop EXPR pool | id id if id then id elseid fi whileid loop id pool if whileid loop id pool then id elseid if if id then id elseid fi then id elseid fi 9 17/03/2014 Exemplos de Expressões Aritméticas E E * E | E E | (E) | id id id id (id) (id) * id id * id id * (id) Observações • Grande avanço com as GLC, porém: – Precisamos de mais do que só determinar a validade • árvore de reconhecimento – Erros precisam ser tratados – Precisamos de uma implementação de GLC (bison) 10 17/03/2014 Mais Observações • Forma da gramática é importante – Muitas gramáticas produzem a mesma linguagem – Ferramentas obedecem a gramática – Ferramentas de expressão regular (flex) são também sensíveis, porém raramente produzem problemas na prática Derivações e Árvores de Reconhecimento • Um derivação é uma sequencia de produções • A derivação pode ser representada como uma árvore – O símbolo inicial é a raíz da árvore – Cada produção acrescenta os filhos ao símbolo à esquerda 11 17/03/2014 Observações sobre Derivações • Uma parse tree tem: – Terminais nas folhas – Não‐terminais nos nós interiores • Caminhamento inorder das folhas produz o string original • A árvore representa associatividade, o string não. Sumarizando • Não estamos interessados apenas em se a string pertence a L(G) – A parse tree é necessária • Uma derivação define uma árvore de reconhecimento – Mas uma árvore pode ter mais de uma derivações • Derivações a esquerda e a direita são importantes na implementação do parser. 12 17/03/2014 Ambiguidade • Uma gramática é ambigua permite mais de uma árvore de reconhecimento para uma mesma string • Problema grave – Deixa a semântica dos programas mal‐definida Como lidar com ambiguidade • Várias estratégias são possíveis – Não há forma automática • A mais direta consiste em re‐escrever a gramática de forma inambígua 13 17/03/2014 O problema do ELSE • Caso clássico em compiladores: – Linguagens que permitem if com else e sem else. – Como decidir a qual if o else se refere? • O else se associa ao then mais próximo. – Pode se descrever isso na gramática Ambiguidade • Não há tecnicas gerais para tratar ambiguidade • Impossível conversão automática – Problema indecindível (Problema da Correspondência de Post) • Usada com cuidado, simplifica a gramática – Permite definições mais naturais – Mecanismos de desambiguação são necesários 14 17/03/2014 Declarações de Precedência e Associatividade • Ao invés de re‐escrever a gramática – Use a mesma gramática (mais natural) – Com algumas declarações de desambiguação • A maioria das ferramenta possui declarações de precedência e de associativade para permitir a desambiguação das gramáticas. 15