Consider the following typed language that includes numbers, booleans, conditionals, functions, and numeric lists. The concrete syntax for the language is given by the following BNF grammars:
<expr> ::= <num>
| true
| false
| {+ <expr> <expr>}
| {iszero <expr>}
| {bif <expr> <expr> <expr>}
| <id>
| {let {<id> : <type> <expr>} <expr>}
| {fun {<id> : <type>} : <type> <expr>}
| {rec {<id> : <type> <expr>} <expr>}
| {<expr> <expr>}
| nempty
| {ncons <expr> <expr>}
| {nempty? <expr>}
| {nfirst <expr>}
| {nrest <expr>}
<type> ::= number
| boolean
| nlist
| (<type> -> <type>)
In the syntax for types, base types are represented by symbols, and
the arrow type by a Scheme list of three elements: the type of the
argument, the symbol ->, and the type of the result.
This language includes some primitives and constructs beyond those discussed in the lecture on types:
The file typecheck-init.scm contains the datatype definitions and a parser for this language. Start with that file for the following exercises.
Write the type judgments for the five numeric list constructs: nempty, ncons, nempty?, nfirst, and nrest. (You can write these by hand -- they do not need to be included in the file you submit via turnin).
Implement the function type-of, which consumes a TFunIfRecExp (the output of the parser) and an escape continuation that accepts a string. If the program has no type errors, type-of returns the type of the program, using the external representation of types given above. If the program does have a type error, type-of invokes the continuation with a string containing an error message. For example:
(let/cc esc (type-of (parse '{+ 1 2}) esc))
should produce number, while:
(let/cc esc (type-of (parse '{3 4}) esc))
might produce "cannot apply a number".
(This use of continuations is a primitive form of exceptions.)
This interface will make it easier for me to test your program
than if you used error.
As always, I will examine your test cases when grading.