CS 5510 Homework 2

Due: Thursday, September 8th, 2011 10:45am

Part 1 – Functions that Accept Multiple Arguments

Start with the F1WAE interpreter, and extend the implementation to support multiple or zero arguments to a function, and multiple or zero arguments in a function call:

  <FunDef> = {deffun {<id> <id>*} <F1WAE>}
  <F1WAE> = <number>
          | {+ <F1WAE> <F1WAE>}
          | {- <F1WAE> <F1WAE>}
          | {with {<id> <F1WAE>} <F1WAE>}
          | <id>
          | {<id> <F1WAE>*}

Since you must change the F1WAE datatype, and since different people may change it in different ways, you must update the parse function, which accepts a quoted expression and produces an F1WAE value. Also, you must update the parse-defn function that takes one quoted deffun form and produces a FunDef value.

At run-time, a new error is now possible: function application with the wrong number of arguments. Your interp function should detect the mismatch and report an error that includes the words “wrong arity”.

Some examples:

  (test (interp (parse '{f 1 2})
                (list (parse-defn '{deffun {f x y} {+ x y}})))
        3)
  (test (interp (parse '{+ {f} {f}})
                (list (parse-defn '{deffun {f} 5})))
        10)
  (test/exn (interp (parse '{f 1})
                    (list (parse-defn '{deffun {f x y} {+ x y}})))
            "wrong arity")

A function would be ill-defined if two of its argument <id>s were the same. To prevent this problem, your parse-defn function can optionally detect this problem and reports a “bad syntax” error. For example, (parse-defn '{deffun {f x x} x}) could report a “bad syntax” error, while (parse-defn '{deffun {f x y} x}) should produce a FunDef value.

Remember that the PLAI Racket language provides the following useful function:

Part 2 – Optional Challenge: Records

This part is optional. If you define the function interp-expr, then the handin server checks your homework assiming both parts are complete, otherwise it checks only part 1.

Extend your interpreter to support the construction of records with named fields, and to support field selection from a record:

  <F1WAE> = ...
          | {record {<id> <F1WAE>}*}
          | {get <F1WAE> <id>}

Your revised parse should check that each <id> is distinct in a record expression.

Adding records means that the language now has two kinds of values: numbers and records. At run-time, an error may occur because a record is misused as a number, a number is supplied to get, or a record supplied to get does not have the named field. Your error message for the last case should include the words “no such field”, otherwise you can make up your own error messages (or just let primitive error checking handle problems, such as trying to add a record to a number).

Extend parse to support record and get expressions.

Since records are now values, the result of interp can be a record instead of a number. For homework purposes, we don’t want to nail down the representation of a record result, because there are many choices. The examples below therefore use interp-expr, which is a wrapper on interp that just a number if interp produces a number, but it returns the symbol 'record if interp produces a record value.

Examples:

  (test (interp-expr (parse '{record {a 10} {b {+ 1 2}}})
                     empty)
        'record)
  (test (interp-expr (parse '{get {record {a 10} {b {+ 1 2}}} b})
                     empty)
        3)
  (test (interp-expr (parse '{get {record {a 10}} b})
                     empty))
        "no such field")
  (test (interp-expr (parse '{g {record {a 0} {c 12} {b 7}}})
                     (list (parse-defn '{deffun {g r} {get r c}})))
        12)
  (test (interp-expr (parse '{get {record {r {record {z 0}}}} r})
                     empty)
        'record)
  (test (interp-expr (parse '{get {get {record {r {record {z 0}}}} r} z})
                     empty)
        0)

Last update: Wednesday, August 31st, 2011
mflatt@cs.utah.edu