EECS 395 Programming Languages: Homework 3

Due: Friday, January 28th, 2011, noon

Part 1 – Functions that Accept Multiple Arguments

Start with the F1WAE interpreter, and extend the implementation to support any number of arguments to a function (including zero), and any number of arguments (including zero) in a function application:

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

Since you must change the F1WAE datatype, and since different people may change it in different ways, you must provide a parse function this time, which accepts a quoted expression and produces an FnWAE value. For parsing, assume that any symbol other than '+, '-, or 'with can be a function name for a function call. Also, you must provide a 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}})))
  (test (interp (parse '{+ {f} {f}})
                (list (parse-defn '{deffun {f} 5})))
  (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 should detect this problem and reports a "bad syntax" error. For example, (parse-defn '{deffun {f x x} x}) should report a "bad syntax" error, while (parse-defn '{deffun {f x y} x}) should produce a FunDef value.

Remember that the PLAI language provides the following useful function:

Part 2 – Records

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

  <FnWAE> = ...
          | {rec {<id> <FnWAE>}*}
          | {get <FnWAE> <id>}

Your revised parse should check that each <id> is distinct in a rec expression. If there are duplicates, the error messages should include the words "duplicate fields"

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 rec 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 returns a number if interp produces a number, but it returns the symbol 'record if interp produces a record value.

Note that the ids in get and rec expressions are not bound by with or function parameters. They are more like the "x" in "o.x" expressions in C and Java.

Hint: change the result type of interp so that it retuns a new define-type that represents values, with two alternatives (numV n) for some number n or a record expression (however you chose to represent them). Note that this also affects your subst function's input (since its input is interp's output in the example code (of course, your rewritten one may not do that).


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

The final program you handin should have a definition of interp-expr that supports both records and multi-arity functions.

Last update: Sunday, January 23rd, 2011