0% found this document useful (0 votes)
12 views8 pages

22 2 Midterm

The document outlines the instructions and structure for a midterm exam in CS 320, Fall 2022, consisting of 12 questions across 8 pages, with specific guidelines on answering in Korean and/or English. It includes definitions of expressions, values, and semantics for various programming language constructs, as well as specific tasks related to function evaluation, expression rewriting, and desugaring in programming languages. Additionally, it provides examples and tasks that require students to analyze and manipulate expressions based on the defined syntax and semantics.

Uploaded by

vedapac571
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
12 views8 pages

22 2 Midterm

The document outlines the instructions and structure for a midterm exam in CS 320, Fall 2022, consisting of 12 questions across 8 pages, with specific guidelines on answering in Korean and/or English. It includes definitions of expressions, values, and semantics for various programming language constructs, as well as specific tasks related to function evaluation, expression rewriting, and desugaring in programming languages. Additionally, it provides examples and tasks that require students to analyze and manipulate expressions based on the defined syntax and semantics.

Uploaded by

vedapac571
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 8

Midterm Exam

CS 320, Fall 2022

Instructions

• You have 145 minutes to complete this closed-book exam.


• There are 8 pages and 12 questions.
• You should write answers in Korean and/or English.
• n ranges over integers (n ∈ Z).
• x ranges over identifiers (x ∈ Id ).
• e ranges over expressions (e ∈ Expr ).
• v ranges over values (v ∈ Val ).
• a ranges over addresses (a ∈ Addr ).
• σ ranges over environments.
• M ranges over stores.
• We use parentheses to represent expressions clearly. For example, we may write λx.(x + x) instead
of λx.x + x to make sure that we do not mean (λx.x) + x. You can use parentheses in your answers.
• We often omit the terms “evaluation” and “evaluating” for brevity. For example, “the result of
e is v” means that the result of evaluating e is v, and “e terminates” means that evaluation of e
terminates.
• If a question says “write the result of e,” then

– you should write v when e results in v.


– you should write “not terminate” when e does not terminate.
– you should write “error” when e incurs a run-time error.
• When you write expressions in your answers, you can freely choose between concrete syntax and
abstract syntax to write the expressions. For example, both { x => x } and λx.x are possible.
• The definitions of the languages defined in the lectures are as follows:

Concrete syntax
[VAE]
expr ::= num | "(" expr "+" expr ")" | "(" expr "-" expr ")" | "(" expr "*" expr ")"
| "if0" "(" expr ")" "{" expr "}" "else" "{" expr "}" | "{" expr ";" expr "}"
| id | "{" "val" id "=" expr ";" expr "}"
[FAE, LFAE]
expr ::= ... | "{" id "=>" expr "}" | expr "(" expr ")"
[RFAE]
expr ::= ... | "{" "def" id "(" id ")" "=" expr ";" expr "}"
[BFAE]
expr ::= ... | "Box" "(" expr ")" | expr "." "get" | expr "." "set" "(" expr ")"
[MFAE]
expr ::= ... | "{" id "=" expr "}"

1
Abstract syntax
Each language uses only a part of the below definition, according to the concrete syntax defined above.
e ::= n | e + e | e − e | e × e | if0 e e e | e; e | x | val x = e; e
| λx.e | e e | def x(x) = e; e | ref e | !e | e := e | x = e
sealed trait Expr
case class Num(n: Int) extends Expr
case class Add(l: Expr, r: Expr) extends Expr
case class Sub(l: Expr, r: Expr) extends Expr
case class Mul(l: Expr, r: Expr) extends Expr
case class If0(c: Expr, t: Expr, f: Expr) extends Expr
case class Seq(l: Expr, r: Expr) extends Expr
case class Id(x: String) extends Expr
case class Val(x: String, e: Expr, b: Expr) extends Expr
case class Fun(x: String, b: Expr) extends Expr
case class App(f: Expr, a: Expr) extends Expr
case class Rec(f: String, x: String, b: Expr, e: Expr) extends Expr
case class NewBox(e: Expr) extends Expr
case class OpenBox(b: Expr) extends Expr
case class SetBox(b: Expr, e: Expr) extends Expr
case class Set(x: String, e: Expr) extends Expr

Values
[VAE]
v ::= n
sealed trait Value
case class NumV(n: Int) extends Value
[FAE, RFAE]
v ::= · · · | hλx.e, σi
case class CloV(x: String, b: Expr, var env: Env) extends Value
[BFAE, MFAE]
v ::= · · · | a
case class AddrV(a: Addr) extends Value
[LFAE]
v ::= · · · | (e, σ)
case class ExprV(e: Expr, env: Env) extends Value
Semantics
[VAE, FAE, RFAE] (VAE and FAE use only a part of the below rules.)
fin
σ ∈ Id →7 Val
type Env = Map[String, Value]
σ ` e1 ⇒ n1 σ ` e2 ⇒ n2 σ ` e1 ⇒ n1 σ ` e2 ⇒ n2 σ ` e1 ⇒ n1 σ ` e2 ⇒ n2
σ`n⇒n
σ ` e1 + e2 ⇒ n1 + n2 σ ` e1 − e2 ⇒ n1 − n2 σ ` e1 × e2 ⇒ n1 × n2

σ ` e1 ⇒ 0 σ ` e 2 ⇒ v2 σ ` e 1 ⇒ v1 v1 6= 0 σ ` e 3 ⇒ v3 σ ` e 1 ⇒ v1 σ ` e 2 ⇒ v2
σ ` if0 e1 e2 e3 ⇒ v2 σ ` if0 e1 e2 e3 ⇒ v3 σ ` e 1 ; e 2 ⇒ v2

x ∈ Domain(σ) σ ` e 1 ⇒ v1 σ[x 7→ v1 ] ` e2 ⇒ v2
σ ` λx.e ⇒ hλx.e, σi
σ ` x ⇒ σ(x) σ ` val x = e1 ; e2 ⇒ v2

σ ` e1 ⇒ hλx.e, σ 0 i σ ` e 2 ⇒ v2 σ 0 [x 7→ v2 ] ` e ⇒ v σ 0 = σ[x1 7→ hλx2 .e1 , σ 0 i] σ 0 ` e2 ⇒ v2


σ ` e1 e2 ⇒ v σ ` def x1 (x2 ) = e1 ; e2 ⇒ v2

2
[BFAE] (Showing only representative rules)
fin
σ ∈ Id →7 Val
fin
M ∈ Addr →7 Val
type Addr = Int
type Env = Map[String, Value]
type Sto = Map[Addr, Value]
σ, M ` e1 ⇒ n1 , M1 σ, M1 ` e2 ⇒ n2 , M2
σ, M ` n ⇒ n, M
σ, M ` e1 + e2 ⇒ n1 + n2 , M2

σ, M ` e1 ⇒ hλx.e, σ 0 i, M1 σ, M1 ` e2 ⇒ v2 , M2 σ 0 [x 7→ v2 ], M2 ` e ⇒ v, M3
σ, M ` e1 e2 ⇒ v, M3

σ, M ` e ⇒ v, M1 a 6∈ Domain(M1 ) σ, M ` e ⇒ a, M1 a ∈ Domain(M1 )
σ, M ` ref e ⇒ a, M1 [a 7→ v] σ, M `!e ⇒ M1 (a), M1

σ, M ` e1 ⇒ a, M1 σ, M1 ` e2 ⇒ v, M2
σ, M ` e1 := e2 ⇒ v, M2 [a 7→ v]

[MFAE] (Showing only representative rules)


fin
σ ∈ Id →7 Addr
fin
M ∈ Addr →7 Val
type Addr = Int
type Env = Map[String, Addr]
type Sto = Map[Addr, Value]
x ∈ Domain(σ) σ(x) ∈ Domain(M ) σ, M ` e ⇒ v, M1 x ∈ Domain(σ)
σ, M ` x ⇒ M (σ(x)), M σ, M ` x = e ⇒ v, M1 [σ(x) 7→ v]

σ, M ` e1 ⇒ hλx.e, σ 0 i, M1
0
σ, M1 ` e2 ⇒ v , M2 a 6∈ Domain(M2 ) σ 0 [x 7→ a], M2 [a 7→ v 0 ] ` e ⇒ v, M3
σ, M ` e1 e2 ⇒ v, M3

[LFAE] (Showing only representative rules)


fin
σ ∈ Id →7 Val
type Env = Map[String, Value]
σ ` e ⇒ v1 v1 ⇓ v 2 σ ` e 1 ⇒ v1 v1 ⇓ n1 σ ` e 2 ⇒ v2 v2 ⇓ n 2
(e, σ) ⇓ v2 σ ` e1 + e2 ⇒ n1 + n2

σ ` e1 ⇒ v 1 v1 ⇓ hλx.e, σ 0 i σ 0 [x 7→ (e2 , σ)] ` e ⇒ v


σ ` e1 e2 ⇒ v

1) (5pts) The following paragraph explains functions in programming languages:

A(n) (a) is a function that is not a value, and a(n) (b) is a function that is a value.
If a language uses (c) , the body of a function is evaluated under the environment when
the function is defined, and if it uses (d) , the body is evaluated under the environment
when the function is called. To implement (c) , a(n) (e) , which captures the
environment when the function is defined, is necessary.

Fill the blanks with some of the following terms.

static scope concrete scope pure function first-class function store


dynamic scope closure abstract scope recursive function first-order function

3
2) (10pts) Consider a language with the following concrete syntax:
expr ::= num | "(" expr "*" expr ")" | "(" expr "^" expr ")" | "set" id "=" expr
where
• num consists of one or more numeric characters, and
• id is a nonempty string that does not contain numeric characters and white spaces.
a) (8pts) For each of the following, describe whether a given string is expr or not. You do not
need to explain the reason.
A) (2 * (3 ^ x))
B) (set x = (2 * 3))
C) (set x = (2 * 3) ^ set x = (4 * 5))
D) (2 * 3) ^ (4 * 5)
You will get 2 × max(0, (number of correct answers) − (number of wrong answers)) points. If you
do not write anything, it will be considered neither correct nor wrong.
b) (2pts) We define the abstract syntax of the language as follows:
e ::= n | e × e | eˆe | set x = e
Complete the following implementation:
sealed trait Expr
case class Num(n: Int) extends Expr
case class Mul(l: Expr, r: Expr) extends Expr
case class Exp( (A) ) extends Expr
case class Set( (B) ) extends Expr
We represent x with a string.

3) (10pts) Consider the following FAE expression:


{
val f = { x => (x + f) };
1 2 3 4
{
val g = { y => f(y) };
5 6 7 8
{
val f = 1;
9
g(2)
10
}
}
}
(The numbers below the identifiers are not parts of the expression. They are for convenience of
writing answers. Use the numbers to denote identifiers in your answers.)

a) (2pts) Write every bound occurrence and its binding occurrence. (If an identifier numbered n
is a bound occurrence whose binding occurrence is an identifier numbered m, write n → m.)
b) (2pts) Write every free occurrence. (If an identifier numbered n is a free occurrence, write n.)
c) (2pts) Write every shadowing occurrence and its shadowed occurrence. (If an identifier numbered
n shadows an identifier numbered m, write n → m.)
d) (2pts) Write the result of evaluating the expression under static scope.
e) (2pts) Write the result of evaluating the expression under dynamic scope.

4
4) (10pts) Draw the evaluation derivation of each of the following FAE expressions:
a) (5pts) (λf.(f (1 + 2))) (λy.(5 − y))
b) (5pts) ((λx.(λy.(x + y))) 2) 4

5) (10pts) Rewrite each of the following BFAE expressions by replacing val and sequencing (;) with
functions and applications. Do not evaluate the expression.
a) (5pts)
{
val x = { Box(3); x.get };
(x + x)
}
b) (5pts)
{
val b = Box(5);
{ b.set((b.get + 3)); (b.get + 1) }
}

6) (10pts) Consider BoolFAE, which extends FAE with booleans:


case object True extends Expr
case object False extends Expr
case class If(c: Expr, t: Expr, f: Expr) extends Expr
True is true; False is false; If(e1 , e2 , e3 ) first evaluates e1 to v and then evaluates e2 if v is true
and e3 if v is false. If v is neither true nor false, an error happens.
This question asks you to implement the desugar function, which takes a BoolFAE expression and re-
turns an expression that lacks True, False, and If but has the same behavior as the given expression.
Precisely speaking, desugar satisfies the following:
• If e evaluates to an integer, desugar(e) evaluates to the same integer.
• If e evaluates to a function, desugar(e) evaluates to a function.
• If e evaluates to a boolean, desugar(e) evaluates to a function.
• If e does not terminate, desugar(e) does not terminate.
• If e incurs a run-time error, desugar(e) can have any behavior.
def desugar(e: Expr): Expr = e match {
case Num(n) => Num(n)
case Id(x) => Id(x)
case Fun(x, b) => Fun(x, desugar(b))
case App(f, a) => App(desugar(f), desugar(a))
case True => Fun("x", Fun("y", Id("x")))
case False => Fun("x", Fun("y", Id("y")))
case If(c, t, f) => ??? }
Complete the implementation by filling ???. Note that FAE of this question does not have if0. You
may use the following helper functions in your answer without defining them:
// returns the set of every binding identifier in e
def binding(e: Expr): Set[String]
// returns the set of every bound identifier in e
def bound(e: Expr): Set[String]
// returns the set of every free identifier in e
def free(e: Expr): Set[String]
// returns a new identifier that does not belong to xs
def fresh(xs: Set[String]): String

5
7) (10pts) Consider the following interp for BFAE that prints given arguments each time it is called:

def malloc(sto: Sto): Addr = (sto.keySet + 0).max + 1

def interp(e: Expr, env: Env, sto: Sto): (Value, Sto) = {


println(s"$e, $env, $sto")
e match {
case Num(n) => (NumV(n), sto)
case Id(x) => (env.getOrElse(x, error()), sto)
case Fun(x, b) => (CloV(x, b, env), sto)
case Val(x, e, b) =>
val (v, vs) = interp(e, env, sto)
interp(b, env + (x -> v), vs)
case App(f, a) =>
val (fv, fs) = interp(f, env, sto)
val (av, as) = interp(a, env, fs)
fv match {
case CloV(x, b, fenv) => interp(b, fenv + (x -> av), as)
case _ => error() }
case Seq(l, r) =>
val (lv, ls) = interp(l, env, sto)
interp(r, env, ls)
case NewBox(e) =>
val (v, vs) = interp(e, env, sto)
val a = malloc(vs)
(AddrV(a), vs + (a -> v))
case OpenBox(b) =>
val (bv, bs) = interp(b, env, sto)
bv match {
case AddrV(a) => (bs.getOrElse(a, error()), bs)
case _ => error() }
case SetBox(b, e) =>
val (bv, bs) = interp(b, env, sto)
val (v, vs) = interp(e, env, bs)
bv match {
case AddrV(a) => (v, vs + (a -> v))
case _ => error() }}

What does interp(e, Map(), Map()) print for each of the following definitions of e?

a) (5pts) Val("b", NewBox(Num(0)), Seq(SetBox(Id("b"), Num(1)), OpenBox(Id("b")))),


which is
{
val b = Box(0);
{ b.set(1); b.get }
}
b) (5pts) App(App(Fun("x", Fun("y", OpenBox(Id("x")))), NewBox(Num(0))), NewBox(Num(1))),
which is
{ x => { y => x.get } }(Box(0))(Box(1))
You can use any notation you prefer in your answer. If you think your notation is confusing, describe
what your notation means in the answer.

6
8) (5pts) Consider the following FAE expression, which evaluates to a function:
1 {
2 val fX = { fY => {
3 val f = { n => fY(fY) };
4 { n => if0 (n) { 0 } else { (n + f((n - 1))) } }
5 }};
6 fX(fX)
7 }
Does the function compute the sum from 0 to n, where n ∈ N? If not, fix the expression (use line
numbers in your answer). If your answer is “yes,” you do not need to explain the reason.

9) (5pts) Consider the App case of interp for MFAE:


case App(f, a) =>
val (fv, fs) = interp(f, env, sto)
fv match {
case CloV(x, b, fenv) => ???
case _ => error() }
Two different implementations for ??? are possible.
(1) val (av, as) = interp(a, env, fs)
val addr = malloc(as)
interp(b, fenv + (x -> addr), as + (addr -> av))
(2) a match {
case Id(x) =>
val addr = env.getOrElse(x, error())
interp(b, fenv + (x -> addr), fs)
case _ =>
val (av, as) = interp(a, env, fs)
val addr = malloc(as)
interp(b, fenv + (x -> addr), as + (addr -> av)) }
a) (1pt) Write which semantics each of (1) and (2) is.
Consider the following MFAE expression:
{ val x = 1; { { x => { x = 2 } }(x); x } }
b) (2pts) Write the result of the expression evaluated with (1).
c) (2pts) Write the result of the expression evaluated with (2).

10) (5pts) Suppose a garbage-collected interepreter uses the following kinds of record:
• Tag 1: a record containing one integer
• Tag 2: a record containing one integer and one pointer
• Tag 3: a record containing two pointers
• Tag 99: forwarding pointer (to to-space)
The interpreter has one register, which always contains a pointer, and a memory pool of size 34. The
allocator/collector is a two-space copying collector, so each space is of size 17. Records are allocated
consecutively in from-space, starting from the first memory location, 0.
The following is the snapshot of the memory just before garbage collection, where all the memory
has been allocated:
• Register: 6
• From space: 2 0 0 2 3 15 3 15 9 3 0 15 2 2 0 1 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

• To space: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

What are the values in the register, the from-space, and the to-space after garbage collection? Draw
your answer just like the above figure.

7
11) (5pts) Consider the following interp for LFAE that prints given arguments each time it is called:
def strict(v: Value): Value = v match {
case ExprV(e, env) => strict(interp(e, env))
case _ => v }
def interp(e: Expr, env: Env): Value = {
println(s"$e, $env")
e match {
case Num(n) => NumV(n)
case Add(l, r) => (strict(interp(l, env)), strict(interp(r, env))) match {
case (NumV(x), NumV(y)) => NumV(x + y)
case _ => error() }
case Id(x) => env.getOrElse(x, error())
case Fun(x, b) => CloV(x, b, env)
case App(f, a) => strict(interp(f, env)) match {
case CloV(x, b, fenv) => interp(b, fenv + (x -> ExprV(a, env)))
case _ => error() }}}
What does interp(e, Map()) print for the following definition of e?
App(Fun("x", App(Fun("y", Add(Id("y"), Id("y"))), Id("x"))), Num(1)), which is
{ x => { y => (y + y) }(x) }(1)
You can use any notation you prefer in your answer. If you think your notation is confusing, describe
what your notation means in the answer.

12) (15pts) This question extends BFAE with pipe (.), which feeds a value to a function.
e ::= · · · | e . e
e1 . e2 first evaluates e1 to v1 and then evaluates e2 to v2 . v2 must be a closure. v2 is applied to v1
using the usual function application semantics.
a) (3pts) Write the operational semantics of e1 . e2 of the form σ, M ` e ⇒ v, M .
b) (3pts) Complete the following interpreter implementation by filling ???.
case class Pipe(a: Expr, f: Expr) extends Expr
def interp(e: Expr, env: Env, sto: Sto): (Value, Sto) = e match {
...
case Pipe(a, f) => ??? }
c) (9pts) Complete the desugar function, which takes a BFAE expression and returns an expression
that lacks Pipe but produces the same result as the given expression, by filling ???.
def desugar(e: Expr): Expr = e match {
case Num(n) => Num(n)
case Id(x) => Id(x)
case Val(x, e, b) => Val(x, desugar(e), desugar(b))
case Fun(x, b) => Fun(x, desugar(b))
case App(f, a) => App(desugar(f), desugar(a))
case NewBox(e) => NewBox(desugar(e))
case OpenBox(b) => OpenBox(desugar(b))
case SetBox(b, e) => SetBox(desugar(b), desugar(e))
case Pipe(a, f) => ??? }
You may use the helper functions for Q6 (binding, bound, free, and fresh) in your answer.

—End of Exam—

You might also like