FCT/UNL Universidade Nova de Lisboa Faculdade de Ciências e Tecnologia

Linguagens de Programação II (2005/2006)

Página Principal
Objectivos
Sumários
Bibliografia
Docentes
Alunos
Horários
Trabalhos Práticos
Avaliação
Material de Apoio

Trabalhos Práticos

links rápidos: [t1][t2][t3][t4][final]

Enunciado do Trabalho Prático 1: (slides [pdf])

Objectivo: Construir um interpretador de expressões aritméticas utilizando o gerador de analisadores sintácticos JavaCC.

Fase 1: Complete a gramática JavaCC definida no ficheiro G1.jj para aceitar a linguagem CALC cuja sintaxe concreta é a seguinte:

 exp  ::= term ( "+" | "-" ) exp
 term ::= factor ( "*" | "/" ) term
 factor ::= num | "(" exp ")"

Utilize a classe Calc, definida no ficheiro Calc1.java que lê uma expressão do Objecto System.in e faz uso da classe Parser produzida pelo JavaCC para validar as expressões. Teste a sua gramática com diferentes expressões.

Fase 2: Utilize o resultado da fase 1 e complete a gramática JavaCC definida no ficheiro G2.jj de modo a construir a árvore de sintaxe abstracta da linguagem CALC que se define da seguinte forma:

          num: integer -> CALC
                                                                                
          add: CALC x CALC -> CALC
                                                                                
          sub: CALC x CALC -> CALC
                                                                                
          mul: CALC x CALC -> CALC
                                                                                
          div: CALC x CALC -> CALC

Utilize para o efeito as seguintes classes e interface de exemplo:

Implemente as restantes classes necessárias para completar o exercício de construir a AST. Note que as classes devem implementar o interface IASTNode.

Fase 3: Finalmente, utilize o método evaluate, que as classes AST implementam, para avaliar as expressões. Modifique o método main do parser para que este receba a AST construída, avalie a expressão e apresente o resultado.

Fase 4: Gere o código CIL correspondente à avaliação das expressões.

Utilize para o efeito as seguintes classes e interface de exemplo:

Implemente as restantes classes e modifique a classe geradora de código CIL de acordo com as necessidades encontradas.

Dicas úteis:

  • Utilize o ficheiro contendo todos os ficheiros [t1.zip]
  • Utilize diferentes directorias para as diferentes fases do trabalho.
  • Utilize uma directoria diferente para ficheiros de código e ficheiros binários (de modo a facilmente poder apagar os binários)

Enunciado do Trabalho Prático 2 (slides [pdf]):

Objectivo: Estender a linguagem CALC com uma expressão de declaração local de identificadores.

Fase 1: Considere a linguagem CALCI, uma extensão da linguagem CALC com identificadores e o operador de declaração

 decl x = exp1 in exp2 end

tal como foi descrito nas aulas teóricas da disciplina. Estenda a gramática que desenvolveu no trabalho 1 com este operador.
Implemente as classes necessárias para construir a árvore de sintaxe abstracta da linguagem segundo a especificação:

        num: integer -> CALCI
                                                                                
        id: string -> CALCI
                                                                                
        add: CALCI x CALCI -> CALCI
                                                                                
        sub: CALCI x CALCI -> CALCI
                                                                                
        mul: CALCI x CALCI -> CALCI
                                                                                
        div: CALCI x CALCI -> CALCI
                                                                                
        decl: string x CALCI x CALCI -> CALCI                                                                                
Implemente de seguida um interpretador para CALCI, usando ambientes seguindo a técnica apresentada nas aulas teóricas.

Fase 2: Generalize a construção decl de modo a que esta suporte declarações multiplas na forma

        decl id1 = exp1 id2 = exp2 ... idn = expn in E end
Fase 3: Gere o código CIL de modo a implementar a declaração de identificadores na máquina virtual mono.

Para tal utilize a directiva

.locals init (int32 var0)
para a declaração de variáveis no inicio do ficheiro.

Enunciado do Trabalho Prático 3 (slides [pdf]):

Objectivo: Estender a linguagem CALCI com operacões imperativas.

Fase 1: Considere a linguagem CALCSTATE, uma extensão da linguagem CALCI com as seguintes expressões e comandos

 
E ::=
            E = E
            E > E 
            E & E
            not E
            var(E)
            !E

S ::=
            E := E
            while E do S end
            if E then S else S end
            S ; S
            decl id1 = E1 id2 = E2 ... idn = En in S end
            print(E)

Estenda e adapte a gramática que desenvolveu no trabalho 2 com estes novos operadores. Note que agora a linguagem tem DUAS categorias sintacticas diferentes, para expressões e comandos (EXP e COM). Um programa é um fragmento fechado da categoria sintáctica COM. Note tambem que a declaração é agora um comando e não uma expressão.
Implemente as classes Java necessárias para construir as árvore de sintaxe abstracta da linguagem segundo a especificação:

        num: integer -> EXP
                                                                                
        id: string -> EXP
                                                                                
        add: EXP x EXP -> EXP 
                                                                                
        sub: EXP x EXP -> EXP 
                                                                                
        mul: EXP x EXP -> EXP 
                                                                                
        div: EXP x EXP -> EXP

        and: EXP x EXP -> EXP                        (* E & E *)

        not: EXP -> EXP			

        eq: EXP x EXP -> EXP                         (* E = E *)

        gt: EXP x EXP -> EXP                         (* E > E *)

        var: EXP -> EXP

        deref: EXP -> EXP                            (* !E *)

        assign: EXP  x EXP -> COM 
                                                                                
        decl: string x EXP x COM ->  COM

        while: EXP x COM -> COM 

        if: EXP x COM  x COM -> COM 

        seq: COM x COM -> COM                        (* S ; S *)

        print: EXP -> COM
                                                                                
Implemente também classes Java para representar os vários tipos de valores manipulados pelo interpretador, da forma requerida pela semântica da linguagem: inteiros, booleanos e referências.

Implemente de seguida um interpretador para CALCSTATE, adaptando o modelo de ambiente e memória apresentado nas aulas teóricas e a função de interpretação semântica respectiva da forma que achar mais eficiente.

Sugestão: Uma célula de memória pode ser representada por um objecto com métodos set e get.

Não implemente nenhum mecanismo de detecção de erros de execução, pelo que um programa pode abortar miseravelmente com uma excepção não tratada!

Por exemplo, o programa

        decl
          x = var(2)
          y = 3
        in
          print (x+y)
        end 	
"estoira" na operação de soma, pois o identificador x não denota um inteiro. Por outro lado, o programa
        decl 
          x = var(0)
        in
          while (100 > !x) do 
            x := !x + 1;
            print ( !x )
          end
        end
imprime os números de 1 a 100.

Fase 2: Implemente o compilador para esta linguagem.

Enunciado do Trabalho Prático 4:

Objectivo: Estender a linguagem do trabalho 3 com as operações de abstracção funcional e procedimental.

Serão introduzidas expressões sintácticas diferentes para funções (fun) e procedimentos (proc).

A sintaxe concreta das mesmas será a seguinte:
E ::=	...
        (fun x1,x2,...,xn => E) 
        (proc x1,x2,...,xn => C)
        E(E1,E2,...,En)
        do C return E end
        decl x1=E1, ...,xn=En in E end 
		
C ::=	... 
        P(E1,E2,...,En)
        decl x1=E1, ...,xn=En in C end
A expressão do C return E end tem a seguinte semântica: executar o comando C e depois avaliar a expressão E, a qual produz o valor de do C return E end.

A expressão decl em que o corpo é uma expressão tem o mesmo significado que na linguagem do exercício 2. Ou seja, voltamos a poder considerar expressões com declarações locais, alem de comandos com declarações locais. Pretende-se também que as declarações possam ser recursivas.

Defina os construtores da sintaxe abstracta convenientes e defina as funções de interpretação necessárias. A técnica de interpretação deverá usar fechos (closures), tal como explicado nas aulas teóricas.

Eis um exemplo de programa (para ilustrar as construções):

decl
  Acum = (proc  r,v => r := !r + v)
  inc  = (proc j => Acum (j, 1))
  looper = (fun x,f =>  
             decl 
               i = var(0)
               s = var(0)
             in
               do
                 while (!i < x) do
                   Acum(s,f(!i)) ;
                   inc(i)
                 end 
               return (!s) end
             end)
  google = (fun x => x * x)
in
  print (looper(10,google))
end

Enunciado do trabalho final :

versão inicial (1.0), 22 de Novembro 2005: [pdf]
versão (1.1), 25 de Novembro: [pdf].
versão (1.2), 6 de Dezembro: [pdf].