You will implement the function `calc-locals`

twice -- first for a
language in which every `with`

and `lam`

is annotated with the type of its
argument, and second where they have no annotations.

A summary of the grammar and abstract syntax for both parts is at the bottom of the page. The definitions files can be found here: Part I, Part II.

`(with rec body)`

first evaluates `rec`

to a record value,
and then evaluates `body`

with all of the record's fields added as bindings to
the current environment.

For example,

` (with (record (a 1) (b 2)) (Record (a Num) (b Num)) (+ a b)) # in the typed version`

` (with (record (a 1) (b 2)) (+ a b)) # in the untyped version`

would both evaluate to 3.

In Part I, `with`

and `lam`

expressions have type annotations.
**Please note that an expression can be a subtype of the given type
annotation.** For example, in `(with <rec-expr> <rec-type> <expr>)`

,
`<rec-expr>`

can be any subtype of `<rec-type>`

.

For example, the following program:

```
parse-n-calc(```(with (record (a (+ 1 2)) (b 3)) (Record (a Num) (b Num))
(+ a @))```)
```

should return `[set: "a", "b"]`

.

You may assume all input to `calc-locals`

is well-formed. By this we mean that the type annotation on a `with`

expression
will always be a record and there are no type-errors in the input.

For the second part of the assignment, your task is exactly the same as
before, except that now the language doesn't have types (or type annotations on
`with`

or `lam`

).

For example, the following program:

```
parse-n-calc(```(with (record (a (+ 1 2)) (b 3))
(+ a @))```)
```

would return `[set: "a", "b"]`

.

The grammar is almost the same for both parts, so here is a combined presentation:

```
<expr> ::= <num>
| <bool>
| (+ <expr> <expr>)
| (num= <expr> <expr>)
| (if <expr> <expr> <expr>)
| <id>
| (<expr> <expr>)
| (let (<id> <expr>) <expr>)
| (record (<id> <expr>) ...)
| (lookup <expr> <id>)
| (extend <expr> <id> <expr>)
| @ # a "hole"
# In the "typed" language, for Part I
| (with <expr> <type> <expr>)
| (lam (<id> : <type>) <expr>)
# In the "untyped" language, for Part II
| (with <expr> <expr>)
| (lam <id> <expr>)
<type> ::= Num
| Bool
| (<type> -> <type>) # function type
| (Record (<id> <type>) ...) # record type
```

Notice that the syntax for `with`

and `lam`

is different in each part, and the untyped part doesn't use the type syntax. The abstract syntax is also mostly the same, so we show them together. Again, the only difference between parts is `e-with`

and `e-lam`

, and the lack of types in the untyped version.

```
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-app(func :: Expr, arg :: Expr)
| e-let(name :: String, expr :: Expr, body :: Expr)
| e-rec(fields :: StringDict<Expr>)
| e-lookup(record :: Expr, field-name :: String)
| e-extend(record :: Expr, field-name :: String, new-value :: Expr)
| e-hole
# In the "typed" language, for Part I
| e-with(record :: Expr, rec-type :: Type, body :: Expr)
| e-lam(param :: String, arg-type :: Type, body :: Expr)
# In the "untyped" language, for Part II
| e-with(record :: Expr, body :: Expr)
| e-lam(param :: String, body :: Expr)
end
data Operator:
| op-plus
| op-num-eq
end
data Type:
| t-num
| t-bool
| t-fun(arg-type :: Type, return-type :: Type)
| t-rec(field-types :: StringDict<Type>)
end
```

To get started on Part I, here is:

Here are the stencils for Part II:

Please read the test guidelines

Do not write test cases that include no holes or more than one hole. For Part I, do
not write test cases where the argument to `with`

is not a subtype of the type annotation.