Due: Monday, November 16th, 2009 9:40am
Start with kcfae.ss and add two new arithmetic operators: neg and avg. The neg form takes a single number and returns its nagation; the avg form takes three numbers and returns the average of the numbers.
<KCFAE> = <number> | {+ <KCFAE> <KCFAE>} | {- <KCFAE> <KCFAE>} | {neg <KCFAE>} | {avg <KCFAE> <KCFAE> <KCFAE>} | <id> | {if0 <KCFAE> <KCFAE> <KCFAE>} | {fun {<id>} <KCFAE>} | {<KCFAE> <KCFAE>} | {withcc <id> <KCFAE>
As in recent previous homeworks, provide a parse function that accepts a quoted expression and produces an KCFAE value. Also, provide interp-expr, which takes an expression, interprets it with an empty substitution, and produces either a number or 'function; return 'function when the result is a continuation.
(test (interp-expr (parse '{neg 2})) -2) (test (interp-expr (parse '{avg 0 6 6})) 4) (test (interp-expr (parse '{withcc k {neg {k 3}}})) 3)
Change your interpreter to support multiple or zero arguments to a function, and multiple or zero arguments in a function call:
<KCFAE> = <number> | {+ <KCFAE> <KCFAE>} | {- <KCFAE> <KCFAE>} | {neg <KCFAE>} | {avg <KCFAE> <KCFAE> <KCFAE>} | <id> | {if0 <KCFAE> <KCFAE> <KCFAE>} | {fun {<id>*} <KCFAE>} | {<KCFAE> <KCFAE>*} | {withcc <id> <KCFAE>
Assume that each argument <id> is distinct for a fun expression. All continuations still accept a single argument.
(test (interp-expr (parse '{{fun {x y} {- y x}} 10 12})) 2) (test (interp-expr (parse '{fun {} 12})) 'function) (test (interp-expr (parse '{fun {x} {fun {} x}})) 'function) (test (interp-expr (parse '{{{fun {x} {fun {} x}} 13}})) 13) (test (interp-expr (parse '{withcc esc {{fun {x y} x} 1 {esc 3}}})) 3) (test (interp-expr (parse '{{withcc esc {{fun {x y} {fun {z} {+ z y}}} 1 {withcc k {esc k}}}} 10})) 20)
This exercise is optional, for extra credit.
Extend your interpreter to support catching and throwing exceptions:
<KCFAE> = ... | {try <KCFAE> catch <KCFAE>} | {throw}
The try ... catch form evaluates its first <KCFAE> and returns its value --- unless evaluating the first <KCFAE> throws an exception by evaluating{throw}, in which case the result of the try expression is the result of the second <KCFAE>. Of course, catching the exception (and starting to evaluate the second <KCFAE>) means that the throw is not seen by any enclosing try. The result is undefined if throw is used without a dynamically enclosing catch.
(test (interp-expr (parse '{try 7 catch 8})) 7) (test (interp-expr (parse '{try {throw} catch 8})) 8) (test (interp-expr (parse '{try {+ 1 {throw}} catch 8})) 8) (test (interp-expr (parse '{{fun {f} {try {f} catch 8}} {fun {} {throw}}})) 8) (test (interp-expr (parse '{try {try {throw} catch 8} catch 9})) 8) (test (interp-expr (parse '{try {try {throw} catch {throw}} catch 9})) 9) (test (interp-expr (parse '{try {try 7 catch {throw}} catch 9})) 7) (test (interp-expr (parse '{{withcc esc {try {{withcc k {esc k}}} catch {fun {x} 8}}} {fun {} {throw}}})) 8) (test (interp-expr (parse '{{withcc esc {try {{withcc k {try {esc k} catch {fun {} {fun {y} 9}}}}} catch {fun {x} 8}}} {fun {} {throw}}})) 8)
Hint: The currently active try ... catch is much like a dynamic binding.
Last update: Thursday, December 3rd, 2009mflatt@cs.utah.edu |