440 likes | 529 Vues
Learn about functional programming in ML, abstract syntax, and creating an IMP interpreter in ML. Includes examples and explanations for defining functions, interactive environment, and type declarations.
E N D
Prototipação de um Interpretador para IMP com ML Juliana Vizzotto Júlio Machado Fevereiro 2002
Roteiro • Programação funcional em ML • Sintaxe abstrata • Implementação de um interpretador para IMP em ML • Conclusões • Questões abertas
Roteiro • Programação funcional em ML • Sintaxe abstrata • Implementação de um interpretador para IMP em ML • Conclusões • Questões abertas
Programação Funcional em ML • ML é uma linguagem funcional desenvolvida na Universidade de Edinburgh • SML/NJ (Standard ML of New Jersey) é de distribuição livre • SML inclui: • suporte para listas, árvores, estruturas de dados infinitas e funções de alta ordem • biblioteca de funções • compilador, com ambiente interativo
Para programar em linguagem funcional: • definir funções • funções de alta ordem são funções que trabalham sobre outras funções • definir novos tipos • construir expressões utilizando aplicação de funções • avaliar expressões (não comandos!)
Ambiente interativo: • Maioria das linguagens funcionais são interativas • Entra-se com uma expressão e o ambiente a avalia e retorna o valor resultante • Prompt do SML/NJ “-” • Nova linha indicada por “=“ • Término de uma expressão indicado por “;” • Exemplos: • avaliar 2 + 3 • 2 + 3; • avaliar 2 + 3 * 5 • 2 + 3 * 5;
Declarações: • Uma declaração dá um nome a alguma coisa • val x = e; • avalia a expressão e e liga o valor resultante com o nome x • val x = 2 + 3; • val y = 1 + x; • let d in e end; • a declaração d é local à avaliação da expressão e • val x = 0 and y = 1; • let val x = 2 in x * y end; • x;
Definição de funções: • fun f x = e; • define uma função f com parâmetro formal x e corpo e • fun dobro x = 2 * x; • f e; • aplicação da função f ao parâmetro atual e • dobro 4; • Qual o tipo da função fun soma a b = a+b; ? • no SML/NJ, “+” é uma operação sobre inteiros, logo a função soma trabalha sobre inteiros • cuidado! SML tenta “adivinhar” os tipos dos parâmetros e da função • fun soma_r (a:real) (b:real) = a+b;
fn x => 2 * x; • uma função sem nome associado • é equivalente à função dobro definida anteriormente • fun negativo x = x < 0; • esta função retorna verdadeiro se o número for negativo • negativo 3; • SML suporta funções polimórficas • fun umpar x = (x,x); • Essa função constrói um par de qualquer tipo • umpar 1; resulta no par (1,1) de inteiros • umpar 1.0; resulta no par (1.0,1.0) de reais
Alguns tipos úteis: • Inteiros int • 123 • Reais real • 123.45 • Booleanos bool • true • false • Pares (e1, e2) • (3,true) é um par composto por um inteiro e um booleano • (1,(2,3)) é um par cujo primeiro elemento é um inteiro e o segundo é um par de inteiros
Tuplas (e1, e2, e3, ...) • (1,2,3) • Listas [e1, e2, e3, ...] list • [1,2,3] é uma lista de inteiros e não deve ser confundida com a tupla do item acima • [] ou nil é a lista vazia • 1::[2,3] adiciona um elemento ao início da lista • [1,2]@[3,4] concatena duas listas • Caracteres char • #”a” é o caracter que representa a letra a • Strings string • “abc” é uma string de tamanho três • cuidado! no SML/NJ “a” é diferente de #”a”
Definição de tipos: • Utiliza-se dois tipos de declarações: • type para indicar um sinônimo • datatype para construir um novo tipo • type • type inteiro = int; • o nome inteiro é um sinônimo para o tipo int do SML e são equivalentes • type dupla_int = int * int; • o símbolo * é um construtor de tuplas
datatype • Uma declaração de um novo tipo inclui um nome seguido de construtores para esse tipo • É muito utilizado na construção de árvores • datatype nome_tipo = construtor | construtor | ...;
type Loc = string; type Nval = int; type Bval = bool; datatype ExpArit = n of Nval | l of Loc | plus of ExpArit * ExpArit | times of ExpArit * ExpArit; • plus(l “x”, n 1); representa a expressão aritmética x+1 da linguagem IMP
Pattern matching: • Funções podem ser definidas usando pattern matching, o qual é a forma resumida da construção case em SML • fun f p1 = e1 | f p2 = e2 | ...; • uma expressão f e é avaliada pela comparação do valor de e com os padrões p1, p2, ... (seguindo a ordem na definição) até atingir um sucesso na comparação, retornando então o valor ei correspondente
fun EvalE (n x) m = x |EvalE (l x) m = get_val m x |EvalE (plus(e1,e2)) m = let val v1 = (EvalE e1 m) val v2 = (EvalE e2 m) in (v1 + v2) end |EvalE (times(e1,e2)) m = let val v1 =(EvalE e1 m) val v2 =(EvalE e2 m) in (v1 * v2) end;
Roteiro • Programação funcional em ML • Sintaxe abstrata • Implementação de um interpretador para IMP em ML • Conclusões • Questões abertas
Sintaxe Abstrata • Sintaxe Abstrata de uma linguagem • descreve uma árvore finita rotulada: • nodos folha: R B Loc {skip} • nodos não folhas: Iop Bop {:=, ;, if_then_else, while_do_ } • Gramáticas do ponto de vista da semântica formal!!
Exemplo: Linguagem IMP if x>0 then skip else (skip; x:=0) If_then_else_ ; > skip skip assign x 0 x 0
Exemplo: | greater of ExpArit * ExpArit | assign of Loc * ExpArit assign greater Loc ExpArit ExpArit ExpArit
type Loc = string; type Nval = int; type Bval = bool; datatype ExpArit = n of Nval | l of Loc | plus of ExpArit * ExpArit | times of ExpArit * ExpArit; datatype ExpBool = b of Bval | equal of ExpArit * ExpArit | smaller of ExpArit * ExpArit;
datatype Cmd = skip | assign of Loc * ExpArit | comp of Cmd * Cmd; datatype Program = command of Cmd | expressiona of ExpArit | expressionb of ExpBool;
Exemplo para linguagem ML if (greater(l “x”,n 0),skip, comp(skip,assign(x, n 0))) if greater comp skip assign skip l n n x x 0 0
IMP X ML : codificamos a sintaxe abstrata de IMP em ML. • OBS: os programas para o interpretador implementado em ML são digitados na linguagem ML. • O que precisamos para trabalhar com os programas direto na linguagem IMP??
Parser if If_then_else_ comp greater skip ; > skip assign skip n l assign skip x 0 x n x 0 x 0 0
Em compiladores: • Após análise léxica obtemos uma lista de “tokens” a partir de um texto do programa • A lista de “tokens” é, na verdade, a árvore (linear) única gerada pela gramática (BNF) não-ambígua da linguagem • Assim, o parser é um mapeamento entre árvores isomorfas (árvore linear e a árvore a ser avaliada pelo interpretador)
Roteiro • Programação funcional em ML • Sintaxe abstrata • Implementação de um interpretador para IMP em ML • Conclusões • Questões abertas
Um Interpretador para IMP • O que faz um parser? • Na análise léxica, seqüências de caracteres são convertidas em seqüências tokens (números, identificadores, símbolos especiais, etc) • A seguir, o parser construirá uma árvore a partir dos tokens identificados • O que faz um interpretador? • Avalia a árvore gerada, dessa forma executando um programa.
Implementação: • Codificação da sintaxe abstrata do IMP para a notação SML • uma definição de datatype para cada entidade da sintaxe abstrata • resulta em uma árvore • expressões aritméticas, expressões booleanas, comandos, programas
type Loc = string; type Nval = int; type Bval = bool; datatype ExpArit = n of Nval | l of Loc | plus of ExpArit * ExpArit | times of ExpArit * ExpArit; datatype ExpBool = b of Bval | equal of ExpArit * ExpArit | smaller of ExpArit * ExpArit;
datatype Cmd = skip | assign of Loc * ExpArit | comp of Cmd * Cmd; datatype Program = command of Cmd | expressiona of ExpArit | expressionb of ExpBool;
Codificação da estrutura de memória • memória é uma função de posições de memória (para nós são variáveis) em valores inteiros • type Mem = Loc -> Nval; • é importante salientar que em SML, podemos definir a memória como realmente uma função fun m1 "x" = 0 |m1 "y" = 1 |m1 "z" = 0; • m1 “x”; irá avaliar em 0
Codificação das regras do SOS • cada regra de inferência do SOS pode ser implementada de forma intuitiva e direta em SML • foram implementadas funções de avaliação para expressões aritméticas, expressões booleanas e comandos • utiliza-se funções com pattern matching para cada regra de inferência do SOS
fun EvalE (n x) m = x |EvalE (l x) m = get_val m x |EvalE (plus(e1,e2)) m = let val v1 = (EvalE e1 m) val v2 = (EvalE e2 m) in (v1 + v2) end |EvalE (times(e1,e2)) m = let val v1 =(EvalE e1 m) val v2 =(EvalE e2 m) in (v1 * v2) end;
fun EvalC ((assign(l1,e1)):Cmd,m:Mem) = let val v1 = (EvalE e1 m) in (skip, (update m l1 v1)) end |EvalC ((comp(c1,c2)):Cmd,m:Mem) = let val (ci1, m1) = (EvalC (c1, m)) in if (is_final(ci1,m1)) then (c2, m1) else ((comp(ci1,c2)), m1) end |EvalC (skip,m:Mem) = (skip,m);
Codificação das transições do SOS • a aplicação das regras do SOS resultam em um sistema de transições entre configurações • foi implementada uma função que gera o sistema de transição pela aplicação das funções de avaliação a partir de uma configuração inicial • Codificação de funções de impressão • foi necessário implementar funções que convertem um termo na forma de árvore abstrata em SML para uma representação em string
fun Deriv ((command c,m:Mem), listloc:Loc list) = let val (ci1, mi1) = (EvalC (c,m)) in if (is_final(c,m)) then print ("(" ^ (DumpCommand c) ^ ", [" ^ (DumpMem m listloc) ^ "])\n") else (print ("(" ^ (DumpCommand c) ^ ", [" ^ (DumpMem m listloc) ^ "])\n->\n"); (Deriv ((command ci1, mi1),listloc))) end ...
Como utilizar o interpretador? • Inicie o SML/NJ • Carregue o programa do interpretador • use “meu_diretório/imp.sml”; • Carregue o arquivo contendo a memória e os programas de teste • use “meu_diretório/exemplos”; • Inicie o interpretador para um programa • é necessário informar a configuração incial (um par programa e memória) e uma lista de variáveis que se deseja visualizar o conteúdo • Deriv ((programa, memória), [“var1”, “var2”,...]); • Deriv ((p1,m1),[“x”,”y”,”z”]);
Roteiro • Programação funcional em ML • Sintaxe abstrata • Implementação de um interpretador para IMP em ML • Conclusões • Questões abertas
Conclusões • Prototipação X Implementação de linguagens • durante o desenvolvimento de uma linguagem, o custo de implementação é proibitivo • Linguagens funcionais como base para prototipação de linguagens • notação mais próxima da notação utilizada na definição da semântica dos comandos facilita a prototipação • sintaxe abstrata -> datatypes • regras da SOS -> funções de avaliação
Interpretador implementado em ML nos permite avaliar (automaticamente) os programas em IMP. • Automatização do processo de avaliação das expressões da linguagem IMP • Acompanhamento visual do sistema de transição resultante • Parser é um mapeamento entre árvores • árvore IMP para árvore ML • esta noção geral inclui a definição vista em compiladores, uma vez que uma lista de “tokens” é uma árvore (degenerada)
Roteiro • Programação funcional em ML • Sintaxe abstrata • Implementação de um interpretador para IMP em ML • Conclusões • Questões abertas
Questões Abertas • Existe a necessidade de provar, “automaticamente”, propriedades sobre os programas? • Precisamos de um framework que possibilite executar provas de propriedades? • ML fornece alguma ferramenta para isso? • Não foi encontrada • Vale a pena implementar uma ferramenta ou reutilizar?
SOS RWL • Maude = “ferramenta” • RWL = “Metatool” • Permite “expressar” regras e também as propriedades.