Difference between revisions of "MUPL Assignment"
Line 42: | Line 42: | ||
=Warning= | =Warning= | ||
What makes this assignment challenging is that you have to understand mupl well and debugging an interpreter is an acquired skill. | What makes this assignment challenging is that you have to understand mupl well and debugging an interpreter is an acquired skill. | ||
+ | |||
+ | =Problems= | ||
+ | ==1. Warm-Up== | ||
+ | ===(a)=== | ||
+ | Write a Racket function racketlist->mupllist that takes a Racket list (presumably of mupl values but that will not affect your solution) and produces an analogous mupl list with the same elements in the same order. | ||
+ | ===(b)=== | ||
+ | Write a Racket function mupllist->racketlist that takes a mupl list (presumably of mupl values but that will not affect your solution) and produces an analogous Racket list (of mupl values) with the same elements in the same order. | ||
+ | |||
+ | ==2. Implementing the mupl Language== | ||
+ | |||
+ | Write a mupl interpreter, i.e., a Racket function eval-exp that takes a mupl expression e and either returns the mupl value that e evaluates to under the empty environment or calls Racket’s error if evaluation encounters a run-time mupl type error or unbound mupl variable. | ||
+ | |||
+ | A mupl expression is evaluated under an environment (for evaluating variables, as usual). In your interpreter, use a Racket list of Racket pairs to represent this environment which is initially empty) so that you can use without modification the provided envlookup function. Here is a description of the semantics of mupl expressions: | ||
+ | |||
+ | * All values (including closures) evaluate to themselves. For example, (eval-exp (int 17)) would return (int 17), not 17. | ||
+ | * A variable evaluates to the value associated with it in the environment. | ||
+ | * An addition evaluates its subexpressions and assuming they both produce integers, produces the integer that is their sum. (Note this case is done for you to get you pointed in the right direction.) | ||
+ | * Functions are lexically scoped: A function evaluates to a closure holding the function and the current environment. | ||
+ | * An ifgreater evaluates its first two subexpressions to values v1 and v2 respectively. If both values are integers, it evaluates its third subexpression if v1 is a strictly greater integer than v2 else it evaluates its fourth subexpression. | ||
+ | * An mlet expression evaluates its first expression to a value v. Then it evaluates the second expression to a value, in an environment extended to map the name in the mlet expression to v. | ||
+ | * A call evaluates its first and second subexpressions to values. If the first is not a closure, it is an error. Else, it evaluates the closure’s function’s body in the closure’s environment extended to map the function’s name to the closure (unless the name field is #f) and the function’s argument-name (i.e., the parameter name) to the result of the second subexpression. | ||
+ | * A pair expression evaluates its two subexpressions and produces a (new) pair holding the results. | ||
+ | * A fst expression evaluates its subexpression. If the result for the subexpression is a pair, then the result for the fst expression is the e1 field in the pair. | ||
+ | * A snd expression evaluates its subexpression. If the result for the subexpression is a pair, then the result for the snd expression is the e2 field in the pair. | ||
+ | * An isaunit expression evaluates its subexpression. If the result is an aunit expression, then the result for the isaunit expression is the mupl value (int 1), else the result is the mupl value (int 0). | ||
+ | |||
+ | Hint: The call case is the most complicated. In the sample solution, no case is more than 12 lines | ||
+ | and several are 1 line. |
Revision as of 06:27, 5 November 2020
Road To Victory
- UW MUPL Assignment Part 1 Warm Up
- UW MUPL Assignment Part 2 Implementing the MUPL Language
- UW MUPL Assignment Part 3 Expanding the Language
- MUPL Programs Studio
- UW MUPL Assignment Part 4 Using The Language mupl-map, mupl-mapAddN
- UW MUPL Assignment Part 5 Challenge Problem
Contents
Set-up
For this assignment, edit a copy of hw5.rkt. In particular, replace occurrences of "CHANGE" to complete the problems. Do not use any mutation (set!, set-mcar!, etc.) anywhere in the assignment.
Overview
This homework has to do with mupl (a Made Up Programming Language). mupl programs are written directly in Racket by using the constructors defined by the structs defined at the beginning of hw5.rkt. This is the definition of mupl’s syntax:
- If s is a Racket string, then (var s) is a mupl expression (a variable use).
- If n is a Racket integer, then (int n) is a mupl expression (a constant).
- If e1 and e2 are mupl expressions, then (add e1 e2) is a mupl expression (an addition).
- If s1 and s2 are Racket strings and e is a mupl expression, then (fun s1 s2 e) is a mupl expression (a function). In e, s1 is bound to the function itself (for recursion) and s2 is bound to the (one) argument. Also, (fun #f s2 e) is allowed for anonymous nonrecursive functions.
- If e1, e2, and e3, and e4 are mupl expressions, then (ifgreater e1 e2 e3 e4) is a mupl expression. It is a conditional where the result is e3 if e1 is strictly greater than e2 else the result is e4. Only one of e3 and e4 is evaluated.
- If e1 and e2 are mupl expressions, then (call e1 e2) is a mupl expression (a function call).
- If s is a Racket string and e1 and e2 are mupl expressions, then (mlet s e1 e2) is a mupl expression (a let expression where the value resulting e1 is bound to s in the evaluation of e2).
- If e1 and e2 are mupl expressions, then (apair e1 e2) is a mupl expression (a pair-creator).
- If e1 is a mupl expression, then (fst e1) is a mupl expression (getting the first part of a pair).
- If e1 is a mupl expression, then (snd e1) is a mupl expression (getting the second part of a pair).
- (aunit) is a mupl expression (holding no data, much like () in ML or null in Racket). Notice (aunit) is a mupl expression, but aunit is not.
- If e1 is a mupl expression, then (isaunit e1) is a mupl expression (testing for (aunit)).
- (closure env f) is a mupl value where f is mupl function (an expression made from fun) and env is an environment mapping variables to values. Closures do not appear in source programs; they result from evaluating functions.
A mupl value is a mupl integer constant, a mupl closure, a mupl aunit, or a mupl pair of mupl values. Similar to Racket, we can build list values out of nested pair values that end with a mupl aunit. Such a mupl value is called a mupl list.
You should assume mupl programs are syntactically correct (e.g., do not worry about wrong things like (int "hi") or (int (int 37)). But do not assume mupl programs are free of type errors like (add (aunit) (int 7)) or (fst (int 7)).
Warning
What makes this assignment challenging is that you have to understand mupl well and debugging an interpreter is an acquired skill.
Problems
1. Warm-Up
(a)
Write a Racket function racketlist->mupllist that takes a Racket list (presumably of mupl values but that will not affect your solution) and produces an analogous mupl list with the same elements in the same order.
(b)
Write a Racket function mupllist->racketlist that takes a mupl list (presumably of mupl values but that will not affect your solution) and produces an analogous Racket list (of mupl values) with the same elements in the same order.
2. Implementing the mupl Language
Write a mupl interpreter, i.e., a Racket function eval-exp that takes a mupl expression e and either returns the mupl value that e evaluates to under the empty environment or calls Racket’s error if evaluation encounters a run-time mupl type error or unbound mupl variable.
A mupl expression is evaluated under an environment (for evaluating variables, as usual). In your interpreter, use a Racket list of Racket pairs to represent this environment which is initially empty) so that you can use without modification the provided envlookup function. Here is a description of the semantics of mupl expressions:
- All values (including closures) evaluate to themselves. For example, (eval-exp (int 17)) would return (int 17), not 17.
- A variable evaluates to the value associated with it in the environment.
- An addition evaluates its subexpressions and assuming they both produce integers, produces the integer that is their sum. (Note this case is done for you to get you pointed in the right direction.)
- Functions are lexically scoped: A function evaluates to a closure holding the function and the current environment.
- An ifgreater evaluates its first two subexpressions to values v1 and v2 respectively. If both values are integers, it evaluates its third subexpression if v1 is a strictly greater integer than v2 else it evaluates its fourth subexpression.
- An mlet expression evaluates its first expression to a value v. Then it evaluates the second expression to a value, in an environment extended to map the name in the mlet expression to v.
- A call evaluates its first and second subexpressions to values. If the first is not a closure, it is an error. Else, it evaluates the closure’s function’s body in the closure’s environment extended to map the function’s name to the closure (unless the name field is #f) and the function’s argument-name (i.e., the parameter name) to the result of the second subexpression.
- A pair expression evaluates its two subexpressions and produces a (new) pair holding the results.
- A fst expression evaluates its subexpression. If the result for the subexpression is a pair, then the result for the fst expression is the e1 field in the pair.
- A snd expression evaluates its subexpression. If the result for the subexpression is a pair, then the result for the snd expression is the e2 field in the pair.
- An isaunit expression evaluates its subexpression. If the result is an aunit expression, then the result for the isaunit expression is the mupl value (int 1), else the result is the mupl value (int 0).
Hint: The call case is the most complicated. In the sample solution, no case is more than 12 lines and several are 1 line.