1. Resposta:
case Let(n, e1, e2) => Ap(Fun(n, e2.desugar), e1.desugar)
2. Resposta:
case Let(ns, es, ec) => Ap(Fun(ns, ec.desugar), es.map(e => e.desugar))
3. Os primeiro bug é o uso de env
ao invés de fenv
como ambiente base para
a avaliação do corpo da função, e o segundo é o uso de fenv
ao invés de env
como ambiente de avaliação do argumento. O primeiro bug faz as funções terem
escopo dinâmico, enquanto o segundo faz os argumentos da chamada de uma função
serem avaliados no que deveria ser o escopo léxico da função. Resumindo, os dois
bugs trocam os escopos de avaliação do argumento com o do corpo da função. O
trecho de código abaixo exercita ambos os bugs:
let a = 2 in
let f = fun (x)
x - a
end in
let a = 5 in
(f)(a)
end
end
end
O resultado correto para o código acima é 3 (5-2), mas os bugs fazem ele
ser -3 (2-5), pois o a
no argumento da chamada (f)(a)
é avaliado em um
ambiente em que a
é 2, enquanto o a
no corpo de f
é avaliado em um
ambiente que que a
é 5.
A correção do bug é simples:
case Ap(fun, arg) => fun.eval(env) match {
case FunV(fenv, param, corpo) =>
corpo.eval(fenv + (param -> arg.eval(env))
case _ => sys.error("não é uma função")
}
4. Aqui o bug consiste em “esquecer” os efeitos colaterais do lado
esquerdo da soma, usando novamente mem
na execução do lado direito. O
trecho abaixo exercita o bug:
let a = 1 in
(a = 2) + a
end
O resultado correto do programa é 4, pois o lado esquerdo muda o valor de
a
, mas com o bug o resultado será 3. A correção é simples:
case Soma(e1, e2) => mem => {
val (v1, mem1) = e1.eval(funs)(env)(mem)
val (v2, mem2) = e2.eval(funs)(env)(mem1)
(v1, v2) match {
case (NumV(n1), NumV(n2)) => (NumV(n1 + n2), mem2)
case _ => sys.error("soma precisa de dois números")
}
}