Due: Wednesday, October 4th, 2017 11:59pm
Implement an interpreter with lazy evaluation and the following grammar:
<Expr> = <Num> | <Sym> | {+ <Expr> <Expr>} | {* <Expr> <Expr>} | {lambda {<id>} <Expr>} | {<Expr> <Expr>} | {let {[<id> <Expr>]} <Expr>} | {if0 <Expr> <Expr> <Expr>} | {pair <Expr> <Expr>} | {fst <Expr>} | {snd <Expr>}
That is, a language with single-argument functions and application, an if-zero conditional, and pair, fst, and snd operations. (The language does not include recursive bindings or records.) Unlike cons, the pair operation does not require its second argument to be a list (and we do not have an empty-list value, anyway).
Implement your interpreter with the plai-typed language, not a lazy language.
Evaluation of the interpreted langauge must be lazy. In particular, if a function never uses the value of an argument, then the argument expression should not be evaluated. Similarly, if the first or second part of a pair is never needed, then the first or second expression should not be evaluated.
Start with more-lazy.rkt. Expand the parse function to support the new forms: if0, pair, fst, and snd. Also, as in HW 4, provide an interp-expr function; the interp-expr wrapper for interp should take an expression and return either a number S-expression, `function for a function result, or `pair for a pair result. (Meanwhile, the interp function should never return the symbol `pair, just like the starting interp function never returns the symbol `function.)
(test (interp-expr (parse '10)) '10) (test (interp-expr (parse '{+ 10 17})) '27) (test (interp-expr (parse '{* 10 7})) '70) (test (interp-expr (parse '{{lambda {x} {+ x 12}} {+ 1 17}})) '30) (test (interp-expr (parse '{let {[x 0]} {let {[f {lambda {y} {+ x y}}]} {+ {f 1} {let {[x 3]} {f 2}}}}})) '3) (test (interp-expr (parse '{if0 0 1 2})) '1) (test (interp-expr (parse '{if0 1 1 2})) '2) (test (interp-expr (parse '{pair 1 2})) `pair) (test (interp-expr (parse '{fst {pair 1 2}})) '1) (test (interp-expr (parse '{snd {pair 1 2}})) '2) ;; Lazy evaluation: (test (interp-expr (parse '{{lambda {x} 0} {+ 1 {lambda {y} y}}})) '0) (test (interp-expr (parse '{let {[x {+ 1 {lambda {y} y}}]} 0})) '0) (test (interp-expr (parse '{fst {pair 3 {+ 1 {lambda {y} y}}}})) '3) (test (interp-expr (parse '{snd {pair {+ 1 {lambda {y} y}} 4}})) '4) (test (interp-expr (parse '{fst {pair 5 ;; Infinite loop: {{lambda {x} {x x}} {lambda {x} {x x}}}}})) '5) (test (interp-expr (parse '{let {[mkrec ;; This is call-by-name mkrec ;; (simpler than call-by-value): {lambda {body-proc} {let {[fX {lambda {fX} {body-proc {fX fX}}}]} {fX fX}}}]} {let {[fib {mkrec {lambda {fib} ;; Fib: {lambda {n} {if0 n 1 {if0 {+ n -1} 1 {+ {fib {+ n -1}} {fib {+ n -2}}}}}}}}]} ;; Call fib on 4: {fib 4}}})) '5) (test (interp-expr (parse '{let {[mkrec ;; This is call-by-name mkrec ;; (simpler than call-by-value): {lambda {body-proc} {let {[fX {lambda {fX} {body-proc {fX fX}}}]} {fX fX}}}]} {let {[nats-from {mkrec {lambda {nats-from} ;; nats-from: {lambda {n} {pair n {nats-from {+ n 1}}}}}}]} {let {[list-ref {mkrec {lambda {list-ref} ;; list-ref: {lambda {n} {lambda {l} {if0 n {fst l} {{list-ref {+ n -1}} {snd l}}}}}}}]} ;; Call list-ref on infinite list: {{list-ref 4} {nats-from 2}}}}})) '6)
Last update: Friday, September 29th, 2017mflatt@cs.utah.edu |