Material de Apoio ao Desenvolvimento do Projecto

O compilador, escrito em C++, realiza as seguintes etapas de análise para implementar a linguagem:

  • análise lexical (gerador de analisadores lexicais GNU Flex 2.5.35);
  • sintáctica (gerador de analisadores LALR(1) byacc-1.9);
  • análise semântica e geração de código (CDK8, RTS2 e extensões); e
  • compilação de assembly (ferramenta yasm -- formato de teste: linux/elf32).
  

As bibliotecas CDK e RTS de apoio ao desenvolvimento do projecto são de uso obrigatório:

O compilador Compact exemplifica a utilização destas bibliotecas:

  

As ferramentas Flex e byacc estão disponíveis para a maioria dos sistemas actuais. No entanto, apesar de o número de versão poder ser o mesmo, o código gerado pode variar e não ser compatível com o ambiente oficial de teste. Recomenda-se apenas a utilização do material fornecido pela disciplina.

O compilador pf2asm permite compilar directamente código Postfix. É também um exemplo (tal como o Compact) de uso do restante material de apoio.Este compilador é de utilização opcional (é independente da realização do projecto), mas pode ser útil para a realização de testes de protótipos de geração de código.

Tanto o manterial de apoio, como os compiladores fornecidos, estão disponíveis para instalação directa através do repositório (usar instalador favorito) http://download.opensuse.org/repositories/home:/d4vid:/co13/ (escolher distribuição ou usar openSUSE 12.2, a oficial).

Está disponível uma máquina virtual (baseada em openSUSE 12.2) para download, que contém todo o material acima.

Material de Apoio ao Desenvolvimento do Projecto

O compilador, escrito em C++, realiza as seguintes etapas de análise para implementar a linguagem:

  • análise lexical (gerador de analisadores lexicais GNU Flex 2.5.35);
  • sintáctica (gerador de analisadores LALR(1) byacc-1.9);
  • análise semântica e geração de código (CDK8, RTS2 e extensões); e
  • compilação de assembly (ferramenta yasm -- formato de teste: linux/elf32).
  

As bibliotecas CDK e RTS de apoio ao desenvolvimento do projecto são de uso obrigatório:

O compilador Compact exemplifica a utilização destas bibliotecas:

  

As ferramentas Flex e byacc estão disponíveis para a maioria dos sistemas actuais. No entanto, apesar de o número de versão poder ser o mesmo, o código gerado pode variar e não ser compatível com o ambiente oficial de teste. Recomenda-se apenas a utilização do material fornecido pela disciplina.

O compilador pf2asm permite compilar directamente código Postfix. É também um exemplo (tal como o Compact) de uso do restante material de apoio.Este compilador é de utilização opcional (é independente da realização do projecto), mas pode ser útil para a realização de testes de protótipos de geração de código.

Tanto o manterial de apoio, como os compiladores fornecidos, estão disponíveis para instalação directa através do repositório (usar instalador favorito) http://download.opensuse.org/repositories/home:/d4vid:/co13/ (escolher distribuição ou usar openSUSE 12.2, a oficial).

Está disponível uma máquina virtual (baseada em openSUSE 12.2) para download, que contém todo o material acima.

Pacotes de Testes

Pacotes de testes para a linguagem do projecto:

Os resultados da execução dos vários testes estão disponíveis nos seguintes locais:

Fases da Análise da Linguagem

O desenvolvimento do novo compilador deve ser incremental. O desenvolvimento dos novos processos de análise e geração deve ser horizontal. Existe um exemplo prático no wiki da disciplina (note-se que muitos dos passos ali descritos já foram realizados sobre a versão existente no repositório CVS).

O desenvolvimento do compilador, em especial na análise sintática e geração de código, pode evoluir da seguinte forma (partindo da sintaxe/semântica do compilador exemplo):

  1. migrar os tipos de dados básicos (inteiros, etc.) para a linguagem a implementar;
  2. migrar a instrução de impressão para a semântica a implementar;
  3. migrar a instrução de atribuição Compact para a nova expressão de atribuição;
  4. migrar/criar expressões aritméticas e lógicas inteiras;
  5. migrar/criar instruções condicionais e de ciclo;
  6. implementar invocação de funções;
  7. implementar funções;
  8. outras partes da linguagem.

Embora as fases de análise genéricas (lexical, sintáctica, semântica) estejam indicadas sequencialmente, devem ser atacadas horizontalmente e em simultâneo para cada parte da linguagem. É natural que alguns tópicos da teoria não estejam cobertos em momentos do desenvolvimento. Nestes casos, devem ser criados esqueletos de código para definir a localização do material em falta.

A análise lexical da linguagem é realizada pela ferramenta GNU Flex 2.5.35 (em modo C++), que deve operar (sem erros) sobre o ficheiro MFScanner.l (especificação lexical). Durante a análise lexical, deverá ser possível remover comentários e espaços brancos, identificar literais (valores constantes), identificadores (nomes de variáveis e funções), palavras chave, etc. Notar que a análise lexical não garante que estes elementos se encontrem pela ordem correcta. As sequências de escape nas cadeias de caracteres deverão ser substituídas pelos respectivos caracteres (ver manual de referência), o espaço necessário para o texto dos identificadores e cadeias de caracteres deverá ser reservado antes de devolvido.

A avaliação da análise lexical considera as expressões regulares utilizadas, bem como a robustez, clareza, simplicidade e extensibilidade da solução.

A análise sintáctica é realizada pela ferramenta byacc, que opera sobre o ficheiro MFParser.y (especificação sintáctica), sem gerar qualquer tipo de conflitos (shift-reduce ou reduce-reduce) nem avisos ou erros. Com a análise sintáctica, é possível garantir a correcta sequência dos símbolos, embora não se verifique se as variáveis utilizadas nas expressões estão declaradas, nem se as operações suportam os tipos de dados utilizados, etc. Simultaneamente, são associadas acções que permitam construir uma árvore de análise sintáctica abstracta do programa a ser processado. Para tal, são utilizadas subclassess de cdk::node::Node (existentes na CDK ou definidas por derivação, no contexto do novo compilador).

Eventuais erros em programas, encontrados durante a análise sintáctica, devem ser identificados e descritos ao utilizador (o processamento não deve ser interrompido, devendo o ficheiro deve ser processado até ao fim, procurando outros erros).

A avaliação considera a gramática do ponto de vista das verificações de coerência tratadas sintacticamente, das regras escolhidas e símbolos não terminais escolhidos, bem como a robustez, clareza, simplicidade e extensibilidade da solução proposta.
A análise semântica deverá garantir que um programa se encontra correctamente escrito e que pode ser executado.

A análise semântica identifica todos os erros estáticos (detectáveis no processo de compilação) e produzidas mensagens de erro descritivas. Caso surjam erros semânticos (estáticos), o compilador deverá terminar com um código de erro 2 (dois). Na sequência da análise semântica, e caso não surjam erros sintácticos ou semânticos (estáticos), é gerado o código final através da CDK (subclasses de cdk::generator::Postfix).

Utilizando a árvore sintáctica abstracta (composta por instâncias de subclasses de cdk::node::Node), pode-se efectuar a análise semântica e a geração de código numa só passagem em profundidade sobre a árvore sintáctica gerada.

Embora a análise semântica possa decorrer em paralelo com a geração de código, não deve ser gerado código se houver erros em alguma das fases de análise. Recorda-se que a execução de código pelo processador realiza poucas verificações, podendo produzir resultados indesejados. Aconselha-se nunca executar os programas gerados como utilizador previlegiado (e.g., em Linux, como "root" (uid=0) ou equivalente) ou num sistema sem protecção.

A geração de código final é realizada através das subsclasses de cdk::generator::Postfix fornecidas. Estas classes utilizam o processador como uma máquina de pilha para gerar código final. Por exemplo, uma soma de duas constantes inteiras pode ser efectuada por:

  Postfix *gen = new ix86(out); // exemplo para gerador ix86 (ferramenta yasm/nasm)
  gen->INT(2);                  // coloca o primeiro operando na pilha
  gen->INT(2);                  // coloca o segundo operando na pilha
  gen->ADD();                   // realiza a soma e deixa o resultado na pilha

O operador retira os operandos da pilha e deixa lá o resultado da operação. A impressão de um inteiro no topo da pilha pode ser realizada através das seguintes instruções:

  gen->CALL("printi");   // chama a rotina de impressão
  gen->TRASH(4);         // remove o operando da pilha

A remoção do resultado da soma da pilha é necessária, depois da impressão, já que na convenção de chamada (C) é o chamador quem deve retirar os argumentos. A documentação da classe cdk::generator::Postfix está disponível na CDK.

O resultado da "execução" das instruções Postfix é um ficheiro assembly, que deverá ser compilado com a ferramenta yasm, por forma a produzir um binário ELF. Assim, o comando yasm -felf32 file.asm permite gerar o ficheiro file.o a partir do ficheiro file.asm (assumindo que não são encontrados erros). A geração do executável é efecutada através da ligação do ficheiro objecto com a biblioteca de run-time da linguagem librts.a (disponível acima) com o comando ld -m elf_i386 -o executavel file.o -lrts (podem ser incluídos outros ficheiros objecto ou bibliotecas, caso se utilize compilação separada).