Pesquisas DSC | PETNews - UCL - Unified Compiler Language
UCL - Unified Compiler Language
Por Daniel Gondim
(danielgondimm@gmail.com)
A grande maioria das ferramentas utilizadas hoje em dia para a construção de compiladores possuem limitações quanto às estruturas disponíveis, ou as etapas do processo de compilação cobertas, ou uso de linguagens de propósito geral, além de serem de difícil compreensão. Na matéria deste mês conheça o projeto de pesquisa desenvolvido no SPLab com uma solução para lidar com tais limitações: especificação de UCL, uma linguagem de domínio específico para o desenvolvimento de compiladores, de forma independente de plataforma e unificada.



Qual o problema?

Atualmente, os frameworks existentes para a construção de compiladores são de difícil compreensão e não evidenciam ao programador várias estruturas importantes, como tabela de símbolos e árvores de derivação. Adicionalmente, são muitos os detalhes específicos de cada plataforma concebida com esse propósito. Outrossim, em sua maioria, cada framework concentra-se e provê serviços para apenas uma etapa de um compilador, como por exemplo a ferramenta JFlex (para análise léxica) e o CUP (para análise sintática).

O uso da tabela de símbolos durante o processo de desenvolvimento de compiladores é totalmente fundamental, pois ela contém uma grande quantidade de informações sobre o programa fonte. Sendo assim, as ferramentas que não permitem a manipulação desta estrutura dificultam bastante o trabalho do programador de compiladores.

Ainda, a grande maioria das ferramentas de desenvolvimento de compiladores existentes atualmente, por possuírem vários detalhes específicos, retarda o aprendizado dos programadores. Na maioria dos casos, é necessário fazer uso de uma linguagem de programação de propósito geral para o desenvolvimento total do compilador, como por exemplo no CUP, onde é necessário fazer uso da linguagem Java. Sendo assim, se faz necessário que o programador tenha conhecimento prévio também sobre a linguagem de propósito geral.

Por fim, além dos problemas já elencados, a grande maioria das ferramentas utilizadas hoje em dia, é especialista em apenas uma ou duas etapas do desenvolvimento de compiladores. Ou seja, não é possível desenvolver todo o compilador utilizando apenas uma linguagem, uma ferramenta. Esta falta de completude faz com que os programadores utilizem várias ferramentas distintas para poder construir compiladores, tornando este trabalho ainda mais complexo. Como exemplo, podemos citar a utilização conjunta do JFlex e do CUP, onde a primeira ferramenta é responsável pelo desenvolvimento da análise léxica, enquanto que a segunda é responsável pela construção das análises sintática e semântica.



Por que é importante resolve-lo?

Ao solucionar este problema estaremos facilitando a tarefa de desenvolvimento de compiladores. Com a abordagem proposta neste trabalho, permitiremos aos projetistas de compiladores desenvolver seus softwares de forma unificada e independente de plataforma. Além disso, será possível implementar nos compiladores, conceitos importantes dessa área que muitas vezes são deixados de lado pelas linguagens e ferramentas, tais como: tabela de símbolos, tratador de erros, entre outras questões de design de compiladores (tipo de scanner e tipo de parser).



Qual a solução?

Especificação de UCL, uma linguagem de domínio específico para o desenvolvimento das etapas de análise de Compiladores, de forma independente de plataforma e unificada.



Como o projeto resolve? Visão geral do projeto

UCL possui uma sintaxe declarativa, com alta legibilidade e redigibilidade. Atualmente, UCL cobre o desenvolvimento das etapas de análise (que compreende o meu trabalho de mestrado).

Na Análise Léxica:

No escopo da análise léxica foi desenvolvida inicialmente uma sintaxe abstrata para UCL, por meio da construção de metamodelos. Esses metamodelos compreendem todos os conceitos inerentes à esta fase de análise do Compilador, de forma abstrata, fazendo com que a sintaxe de UCL cubra todos os aspectos concernentes à análise léxica, de forma independente de plataforma.

Figura 1 – Visão de Pacotes do Metamodelo de UCL
Figura 2 - Subpacote RegularExpression do Metamodelo de UCL
Figura 3 - Subpacote Scanner do Metamodelo de UCL

Após a definição de toda a sintaxe abstrata concernente à etapa de análise léxica, foi desenvolvida a sintaxe concreta. Para isto, foram definidas construções textuais que permitem ao desenvolvedor de Compiladores, fazer uso dos conceitos definidos nos metamodelos já definidos.

Para desenvolver o scanner e o autômato finito, reusamos a sintaxe adotada pela ferramenta JFlex para a construção de expressões regulares e dos autômatos devido a sua simplicidade. Por exemplo, na Tabela 1, mostramos algumas expressões regulares, enquanto que na Tabela 2, mostramos todas as opções possíveis para o desenvolvedor especificar o tipo de autômato responsável por simular (e reconhecer) os padrões especificados pelas expressões regulares. Esta flexibilidade é praticamente ausente na maioria das ferramentas e linguagens.

Tabela 1 - Expressões Regulares em UCL
Tabela 2 - Sintaxe UCL para definição do tipo do Autômato

UCL também fornece um construtor capaz de facilitar o desenvolvimento do scanner, permitindo que os desenvolvedores definam as palavras-chave da linguagem fonte, evitando, assim, um futuro conflito com expressões regulares que descrevem possíveis identificadores na mesma linguagem. Podemos ver um exemplo de trecho de código em Código 1 que mostra essa construção.

Código 1 – Definindo palavras-chave em UCL
  • Na Análise Sintática:

Seguindo a metodologia de primeiramente desenvolver a sintaxe abstrata, para posteriormente partir para o desenvolvimento da sintaxe concreta, definimos para a etapa de análise sintática dois subpacotes que estão inseridos no pacote "SyntaxAnalysis", como pudemos observar na Figura 1.

Figura 4 - Subpacote Grammar do Metamodelo de UCL
Figura 5 - Subpacote Parser do Metamodelo de UCL

A sintaxe concreta proposta para especificar a gramática é bem parecida com a sintaxe BNF [1], como podemos observar no Código 2.

Código 2– Sintaxe concreta da gramática

Em relação ao módulo do parser, UCL permite ao programador escolher o tipo de análise que será utilizada, e até mesmo o tratamento de possíveis conflitos inerentes à gramática, como podemos observar no Código 3 e no Código 4, respectivamente

Código 3 – Sintaxe concreta para escolha de análise sintática
Código 4 – Sintaxe concreta para tratamento de conflitos
  • Na Análise Semântica:

Diferentemente dos outros dois módulos de análise, a léxica e a sintática, a sintaxe do analisador semântico é dado de forma direta através de uma API, onde é fornecido um conjunto de operações. Devido à complexidade de abstrair os conceitos sobre a análise semântica, uma vez que cada linguagem de programação e seu paradigma tem as suas especificidades, não foi fornecida uma sintaxe abstrata para esta fase de análise. Por outro lado, foi fornecido um conjunto de serviços de alto e baixo nível, necessários para a construção do analisador semântico. Por falta de espaço, apenas serão ilustrados alguns desses construtores.

  • TypeHierarchy.createType(String type):Type

Cria um determinado tipo, representado por um objeto da classe Type. Também é criado um identificador para o tipo criado, esse identificador leva o próprio nome do tipo, que é passado como parâmetro (type).

  • TypeHierarchy.createType(String type, Type superType):Type

Cria um tipo e já o adiciona na hierarquia, abaixo do seu supertipo. O segundo parâmetro indica seu supertipo na hierarquia. Também é criado um identificador para o tipo criado, esse identificador leva o próprio nome do tipo, que é passado como parâmetro (type).

  • TypeSystem.setPolymorphism(String kind):void

Define que a linguagem permite polimorfismo, e especifica o tipo. Existem dois tipos: independente ou dependente de contexto (representadas pelas strings "Parametric" e "Inclusion").

  • Attribute.createAttribute(String name, AttributeKind kind, Type type):Attribute

Cria um atributo. Recebe como parâmetros o nome do atributo, se o mesmo é sintetizado ou herdado (representados pela enumeração AttributeKind, que possui dois valores: Synthesized e Inherited) e o seu tipo.

  • Terminal.getAllAttributes():OrderedSet(Attribute)

Recupera todos os atributos de um determinado símbolo terminal.

Por fim, UCL também fornece alguns construtores para a manipulação da Tabela de Sìmbolos. A tabela de símbolos é uma das principais estruturas de dados presente no desenvolvimento de compiladores. Ela armazena informações dos tokens do programa fonte. A tabela é utilizada em todas os módulos de construção de compiladores, desde a etapa de análise até a etapa de geração de código.

Na Tabela 3 pode-se observar alguns desses construtores fornecidos por UCL.



Quais os integrantes do projeto?

Pesquisador:

  • Daniel Gondim

Professores Orientadores:

  • Prof. Franklin Ramalho
  • Prof. Adalberto Cajueiro

Alunos de Graduação que ajudaram no projeto:

  • Demontiê Júnior
  • Lucas Cavalcante
  • Acácio Leal



Qual o laboratório?

Software Practices Laboratory (SPLab)

Artigos publicados e prêmios:

  • Artigo:

GONDIM, D. ; FARIAS, A. ; RAMALHO, F. . UCL Uma Linguagem Unificada para Construção de Compiladores. In: Congresso Brasileiro de Software, 2012, Natal. Anais do II Workshop de Teses e Dissertações do CBSoft, 2012., 2012.

  • Prêmio:

Melhor Trabalho de Mestrado no XII Workshop da Pós-Graduação em Informática, Universidade Federal de Campina Grande (WDCOPIN - 2013)

Editado por: Maria Letícia Leôncio Barbosa


Jornal PETNews - Edição: Rafael Rêgo e Abner Araújo - Revisão: Lívia Sampaio e Gleyser Guimarães
Grupo PET Computação UFCG, 2013. All rights reserved.