Calculating Local Variables

We don't intend for these assignments to take too long. If you have spent five hours on any part of this assignment please stop and check in with the professor.

In this assignment, you will be working with sets. The documentation is on pyret.org/docs/latest.


1. The Paret Language

For the next couple of assignments you will be asked to create programs that either inspect or implement features of a simple functional programming language called Paret.

For the purposes of this assignment, this language is defined by the following grammar:

<expr> ::= | <num>
           | <bool>
           | (+ <expr> <expr>)
           | (num= <expr> <expr>)
           | (if <expr> <expr> <expr>)

           | <id>                       # identifier
           | (lam <id> <expr>)          # anonymous function
           | (<expr> <expr>)            # function application
           | (let (<id> <expr>) <expr>)

           | @                          # a "hole"

To make working with Paret programs easier we have provided a function parse, which takes an expression in the language's concrete syntax and returns its abstract syntax representation in the form below:

data Expr:
  | e-num(value :: Number)
  | e-bool(value :: Boolean)
  | e-op(op :: Operator, left :: Expr, right :: Expr)
  | e-if(cond :: Expr, consq :: Expr, altern :: Expr)

  | e-id(name :: String)
  | e-lam(param :: String, body :: Expr)
  | e-app(func :: Expr, arg :: Expr)
  | e-let(id :: String, value :: Expr, body :: Expr)

  | e-hole
end

data Operator:
  | op-plus
  | op-num-eq
end

For instance evaluating the expression:

parse("((lam x (+ 1 @)) 2)")

Produces an annotated representation as a Pyret datatype:

e-app(e-lam("x", e-op(op-plus, e-num(1), e-hole)), e-num(2))

2. Local Variables

In this assignment you will write code to check what is in scope at a given part of your program. This part of the program, known as a hole, is indicated by @.

You will implement a function that takes an expression with a hole in it and returns the set of identifiers that are in scope at the hole:

fun calc-locals(expr :: C.Expr, bound :: Set<String>) -> Set<String>:
  ...
end

It must return precisely the set of identifiers that are in scope at the hole. In particular, replacing a hole with any one of the identifiers returned by calc-locals must never cause an unbound identifier exception. Conversely, every identifier not in the set returned by calc-locals must be unbound at that position (though it may not result in a runtime exception, because it may never be evaluated).

For example, the following program:

parse-n-calc(```(let (x 3) 
                (let (y true)
                  (lam f (f @))))```)

Should return [set: "x", "y", "f"]. (parse-n-calc is a helper function we provide which first parses an expression, then calls calc-locals.)

Note: The purpose of calc-locals is to statically check what is in scope at a given part of your program; you do not need to evaluate the program and it should always halt.

3. Setup and Submission

To get started, you can open the code stencil and the test stencil in code.pyret.org.

We will grade your code by running our test suite against it, and grade your tests by running them against correct and incorrect solutions that we have written to see whether your tests can tell the difference. Feel free to write implementation-specific test cases in your code submission: we won't run these, but they can be helpful to you.

3.1 Writing Test Cases

Do not write test cases that include no holes or more than one hole.

For this and future assignments: Do not change the type signatures of functions in the stencils. You don't need to test parse (because we're providing it for you). You don't need to write test cases that violate the type signature of a function.

If you write helper functions in your code file (which we encourage you to do), please only test them in your code file. We will grade your test file against our own solutions, which won't have defined the same helper functions that you did.

3.2 Submission

To submit your implementation and test cases, first save them locally by clicking "More / Download this file" in the code.pyret.org editor. Your submission should consist of both "calc-vars-tests.arr" and "calc-vars-code.arr". Upload your files to the appropriate Vikingweb coursework link.