I implemented the top-level variables and primitive functions described in 3imp 4.7.

Top-level Variables

Two implementation approaches were mentioned.

  • The first method is to put everything into the display closure when creating the closure, like free variables so far. It’s supposed to be simple, but I didn’t feel like doing it, so I rejected it. If we do this, naturally all display closures will bloat, and we’ll get into the story of boxes being referenced from various places…

  • The second method is to manage top-level variables separately from free variables. Distinguish between top-level variables, free variables, and bound variables. With this, the display closure stays small and no boxes are needed. I adopted this approach.

The changes are in the following areas:

  • find-free

    • A function that searches for free variables contained in expression x. Added judgment for whether it’s top-level.
  • compile-lookup

    • A function that receives variable x and (list of local variables . list of free variables) and checks which of the three types the variable x belongs to (then calls the appropriate function). Search in the order: local variables -> free variables -> top-level.
  • collect-free

    • A function that collects free variable values to put into the display closure when creating a closure. The values that should be put into the display closure should be bound variables or free variables of the caller.

Primitive Functions

Since primitive functions are also functions, we place their closure objects at the top level in advance. When calling them, like other closures, we pass arguments, create a call frame, and so on. Even for single-argument cases, we’re deliberately pushing to the stack, which feels wasteful, but for two-argument cases, it would be confusing to have one in a register and the other on the stack, so I’ll go with this approach for now.

Compound Statements

By the way, I made it possible to write compound statements in lambda expressions.