Compiladores - JFlex. Fabio Mascarenhas

Documentos relacionados
Pratica JFlex. Prática criando o primeiro analisador léxico

Prof. Adriano Maranhão COMPILADORES

Análise Sintática - Final

Aula prática 1. Análise Léxica. José Romildo Malaquias

Computação L2. Linguagem C++ Observação: Material Baseado na Disciplina Computação Eletrônica.

Introdução ao FLEX e expressões regulares

Introdução a Programação de Jogos

Introdução à Programação

Computação 1 - Python Aula 9 - Teórica: Interferindo no fluxo de repetição: Break e Continue Laços Aninhados

JavaScript (Elementos de Programação e Programação Básica)

Simulado de Linguagem de Programação Java

Linguagem de Programação

Funções em C. Lucas Ferrari de Oliveira Professor Adjunto. Linguagem de Programação Estruturada I. Universidade Federal do Paraná

Análise Léxica. Sumário

Expressões Regulares

Computação 1 - Python Aula 7 - Teórica Estrutura de Repetição com Teste de Parada: while. João Carlos, Carla Delgado, Ana Luisa Duboc 1/ 18

Projeto de Compiladores

Java RMI. RMI Remote Method Invocation. Chamadas Remotas de Procedimentos (RPC) RPC - Implementação

Desenvolvimento de programas. Análise do problema. Análise do problema. Análise do problema. Desenvolvimento do algoritmo. Codificação do programa

Introdução a classes e objetos. Prof. Marcelo Roberto Zorzan Prof a. Rachel Reis

Paradigmas de Programação

Papel do analisador léxico: ler o arquivo fonte em busca de unidades significativas (os tokens) instanciadas por lexemas ou átomos.

Compiladores - Gramáticas

Bacharelado em Ciência e Tecnologia Processamento da Informação. Equivalência Portugol Java. Linguagem Java

Aula 11 Introdução ao Java Script

Aula 11: Desvios e Laços

Análise Sintática. Eduardo Ferreira dos Santos. Outubro, Ciência da Computação Centro Universitário de Brasília UniCEUB 1 / 18

UNIVERSIDADE DO VALE DO RIO DOS SINOS - UNISINOS CENTRO DE CIÊNCIAS EXATAS E TECNOLÓGICAS - CENTRO 06. Funções, variáveis, parâmetros formais

Conceitos de Linguagens de Programação

Construção de Compiladores. José Romildo Malaquias

Fundamentos de Programação. Linguagem C++ Introdução, identificadores, tipos de dados. Prof. Bruno E. G. Gomes IFRN

Análise Léxica. O papel do analisador léxico

IMPLEMENTAÇÃO DE UM COMPILADOR PARA UMA LINGUAGEM DE PROGRAMAÇÃO COM GERAÇÃO DE CÓDIGO LVIS E MSIL

Introdução à Programação de Computadores Parte I

LP II Estrutura de Dados

Bacharelado em Ciência e Tecnologia Processamento da Informação. Equivalência Portugol Java. Linguagem Java

Introdução à Programação Aula 03. Prof. Max Santana Rolemberg Farias Colegiado de Engenharia de Computação

Linguagem C: funções e ponteiros. Prof. Críston Algoritmos e Programação

Linguagem SQL Restrições, Triggers e Views

Introdução à Programação em C (I)

Estrutura de um Algoritmo, Variáveis, Comandos de Entrada e Saída e Expressões Aritméticas

Introdução à Programação em C (I)

Tipos Abstratos de Dados. Estrutura de Dados

Compiladores - Análise Preditiva

Programação de Computadores para GI

Funções de Entrada e Saída

Python - Variáveis e expressões

Introdução ao reconhecimento de padrões e expressões regulares; Aprendizagem dos conceitos através da realização de alguns exercícios;

Linguagem e Técnicas de Programação

Universidade Estadual de Mato Grosso do Sul Ciência da Computação Algoritmos e Estruturas de Dados I (AED-I) Prof. Nilton

POLIMORFISMO. Entender o princípio do Polimorfismo; Saber quais são os tipos existentes de Polimorfismo; Conhecer Polimorfismo de Sobrecarga.

Universidade de São Paulo São Carlos Instituto de Ciências Matemáticas e de Computação. Material preparado pela profa Silvana Maria Affonso de Lara

1.2 Uma linguagem de programação muito simples

Algoritmos e Programação. Linguagem C Procedimentos e. Eliane Pozzebon

Casamento de Padrão. Programação Funcional. Capítulo 6. José Romildo Malaquias Departamento de Computação Universidade Federal de Ouro Preto

Análise Sintática II. Eduardo Ferreira dos Santos. Outubro, Ciência da Computação Centro Universitário de Brasília UniCEUB 1 / 34

Compiladores. Prof. Bruno Moreno Aula 11 20/05/2011

Professor Leo Larback Esta apresentação pode ser baixada livremente no site

INTRODUÇÃO A LINGUAGEM C

ENG1000 Introdução à Engenharia

PROGRAMAÇÃO DE MICROPROCESSADORES 2011 / 2012

Python: Comandos Básicos. Claudio Esperança

Introdução à orientação a objetos

Existem dois tipos básicos de subrotinas: a) Procedimentos; b) Funções.

Algoritmos e Estruturas de Dados I (DCC/003) 2013/1. Estruturas Básicas. Aula Tópico 4

Módulo 04 Expressões, Estruturas de Seleção e Controle de Fluxo. Última atualização: 09/06/2010

Criando scanner para dectar BackupExec vulneráveis ao exploit do Metasploit. Inj3cti0n P4ck3t

Linguagem de Programação

C++ - Funções Virtuais (Polimorfismo) Base. Deriv0 Deriv1 Deriv2. print( ) print( ) print( ) dv0 dv1 dv2. p[0] = &dv0; p[1] = &dv1; p[2] = &dv2;

9 Classes Abstractas e Interfaces

Casamento de Padrão. Programação Funcional. Capítulo 5. José Romildo Malaquias Departamento de Computação Universidade Federal de Ouro Preto

Linguagem de Programação Pascal - Introdução

Linguagem de Programação I. Aula 10 Funções

Shell Script. Rafael Silva Guimarães

Aprendendo. Java 2. Rodrigo Mello Ramon Chiara Renato Villela. Novatec Editora Ltda.

Funções em JavaScript

Desenvolvimento de aplicações Web. Java Server Pages

WEBDESIGN. Professor: Paulo Marcos Trentin - Escola CDI de Videira

Seqüências de Caracteres

Programação Estruturada. Programação Estruturada. Idéias Básicas da Programação Estruturada


Fábio Rodrigues Jorge.

PROGRAMAÇÃO DE MICROPROCESSADORES 2009 / 2010

Compiladores. Análise léxica. Plano da aula. Vocabulário básico. Motivação para análise léxica. Estrutura de um compilador

Introdução. Surge em 1995 (Brendan Eich, programador da Netscape) com o nome de Livescript

Estrutura de Programas e Tipos de Dados Simples

Transcrição:

Compiladores - JFlex Fabio Mascarenhas 2018.1 http://www.dcc.ufrj.br/~fabiom/comp

JFlex Um gerador de analisadores léxicos que gera analisadores escritos em Java A sintaxe das especificações é inspirada na sintaxe das especificações para Lex, um gerador de analisadores léxicos para Unix Baixe em http://jflex.de/. O arquivo inclui scripts para executar ele tanto em Linux (jflex) quanto Windows (jflex.bat); use-os! $ jflex scanner_spec.jflex Reading "scanner_spec Constructing NFA : 36 states in NFA Converting NFA to DFA :... 14 states before minimization, 5 states in minimized DFA Writing code to "Scanner.java"

Especificando um Scanner Arquivo de especificação: código Java (fica fora da classe do scanner) %% opções e declarações %% regras do scanner Código Java normalmente são import de pacotes que você pretende referenciar no código do scanner Opções controlam como é o scanner gerado Regras são expressões regulares e as ações que o scanner executa quando reconhece uma delas

Opções e declarações %class Foo - Gera uma classe pro scanner com nome Foo (em um arquivo Foo.java) %line e %column - Ativa contagem automática de linhas e colunas, respectivamente (acessadas pelas variáveis yyline e yycolumn); útil para mensagens de erro %{... %} - Inclui código Java dentro da classe do scanner %init{... %init} - Inclui código Java dentro do construtor da classe do scanner nome = regexp - Define uma macro que pode ser referenciada pelas regras do scanner com {nome} %function gettoken - Define o nome do método que executa o scanner como gettoken %type Token (ou %int) - Define o tipo de retorno do método que executa o scanner como Token

Expressões regulares JFlex Expressão Significado a Caractere a foo Cadeia foo [abc] a, b ou c [a-d] a, b, c ou d [^ab] Qualquer caractere exceto a e b. Qualquer caractere exceto \n x y Expressão x ou y xy Concatenação x* Fecho de Kleene x+ Fecho positivo x? Opcional!x Negação ~x Tudo até x (inclusive)

Regras e Ações Regras têm o formato regexp { código Java } O código Java é copiado para dentro do método do scanner Para pegar o valor do lexeme usa-se o método yytext() Lembre sempre de retornar ao final do código, ou o scanner continua rodando! Regra especial <<EOF>> casa com o final do arquivo

Exemplo %% %public %class ScannerJF %implements Scanner %function token %type Token %% [ \n\r\t] { } [0-9]+ { return new Token(Token.NUM, yytext()); } print { return new Token(Token.PRINT, yytext()); } [a-za-z]+ { return new Token(Token.ID, yytext()); } [+] [-] ; [(] [)] [=] { return new Token(yytext().charAt(0), yytext()); } <<EOF>> { return new Token(Token.EOF, <<EOF>> ); }. { throw new RuntimeException("caractere inválido "+yytext()); }

Especificações heterogêneas O analisador léxico trabalha sem nenhuma noção da estrutura do programa, e se o próximo token que ele leu faz sentido naquela parte do programa ou não Um analisador léxico para Java interpretaria 123+-/4if como um número, seguido de +, seguido de -, seguido de /, seguido de outro número, seguido de if Mas o nível léxico da linguagem pode não ser uniforme Em HTML, por exemplo, as regras léxicas no interior de uma tag (entre os tokens < e > ou />) são diferentes das regras fora de uma tag

Estados Podemos tratar uma linguagem com regras léxicas heterogêneas como várias linguagens misturadas Cada uma com sua especificação léxica homogênea Basta haver um mecanismo de separar as diferentes especificações, e chavear entre elas No JFlex (e todos os analisadores léxicos derivados do Lex original) isso é feito através de estados

Estados Um estado é um jeito de isolar partes da especificação léxica <ESTADO> {... regras... } As regras dentro do bloco só serão válidas se o o estado atual do analisador léxico for ESTADO Uma regra fora de um bloco vale em qualquer estado Há sempre um estado inicial YYINITIAL

Declarando e mudando estados Estados são declarados na seção de declarações com a diretiva %state Para mudar de um estado para outro usa-se a função yybegin dentro da ação de algum token, passando o estado para o qual se quer ir Não existe uma função yyend! Para voltar a o estado inicial se usa yybegin(yyinitial); %state TAG %% <YYINITIAL> { [<] { yybegin(tag); return new Token( < ); } [^< ]+ { return new Token(PALAVRA, yytext()); } } <TAG> { [>] { yybegin(yyinitial); return new Token( > );... outras regras... }

Outros usos de estados Usar estados pode ser útil mesmo que a linguagem tenha uma especificação léxica homogênea Podemos tratar partes tradicionalmente espinhosas de muitas linguagens, como literais string e comentários, com estados próprios para isso Regras mais simples para o que é permitido no interior de uma string ou um comentário, sem afetar o resto da especificação Podemos até fazer coisas que só com expressões regulares não é possível!

Comentários aninhados Um comentário Java é qualquer texto entre /* e */ Ou seja, o primeiro */ que aparece dentro de um comentário acaba ele! Em /* foo /* bar */ baz */ o comentário termina no primeiro */, e baz */ vai ser tokenizado normalmente Mas existem linguagens que permitem aninhamento de comentários, onde o que está acima seria um único comentário Não podemos expressar a linguagem dos comentários aninhados com uma expressão regular (lema do bombeamento), mas podemos simular isso com estados e ações em JFlex

Comentários aninhados A ideia é ter um estado só para comentários, e as outras regras do scanner ficam associadas apenas ao estado YYINITIAL Quando entramos no estado de comentários, inicializamos um contador de nível de aninhamento em 1 Dentro do estado de comentários, cada /* encontrado aumenta nosso nível de aninhamento em 1 Cada */ encontrado diminui o nível em 1, e quando o nível chega a 0 voltamos a YYINITIAL Qualquer outro caractere, incluindo quebras de linha, é ignorado

Mudando o estado em outras partes A mudança de um estado para outro pode não ser controlada pela análise léxica, mas por outras partes do compilador Em Java, List<List<Integer>> foo é uma sequência ID < ID < ID > > ID se isso for uma declaração de uma variável, enquanto é ID < ID < ID RSHIFT ID se isso for uma expressão O analisador sintático pode mudar o estado do analisador léxico a depender de qual parte do programa ele está analisando

Outros truques com JFlex - Indentação A linguagem Python não tem tokens especiais para delimitar blocos no programa Ela usa indentação para sinalizar um bloco, aproveitando que é bastante comum agrupar todos os comandos de um bloco em um mesmo nível de indentação def fatorial(n): res = 1 while n > 1: res = res * n n = n - 1 return res print fatorial(5)

Indentação A ideia é manter uma pilha de níveis de indentação, onde cada nível é o número de espaços daquele nível Então associamos uma expressão regular que casa espaços no início de cada linha a uma regra que: Empilha um novo nível de indentação caso o número de espaços seja maior que o topo da pilha, e gera um token BEGIN Não faz nada se o número de espaços seja igual ao topo da pilha Desempilha e gera um token END enquanto o número de espaços é menor que o topo da pilha

Indentação Entrada deve começar com \n Linhas em branco são ignoradas \n[ ]*. { int nivel = yytext().length() - 2; int atual = niveis.peek(); yypushback(1); // volta o. if(nivel > atual) { // indenta niveis.push(nivel); return new Token(BEGIN); } else if(nivel < atual) { niveis.pop(); // vai casar de novo para // gerar todos os ENDs yypushback(nivel + 1); return new Token(END); } else { // mesmo nível, não faz nada! } } <<EOF>> { if(niveis.peek() > 0) { levels.pop(); return new Token(END); } else { return new Token(Token.EOF, ""); } }