Files

This directory contains the code generator plugin, which is run when you do zap compile -o haskell. This document provides a high-level overview of its internals.

Like all zap code generator plugins, it accepts a zap-serialized CodeGenratorRequest (defined in schema.zap) on standard input, and emits code accordingly. For an overview on what exactly is emitted, see Zap.Tutorial.

The data read from standard input transitions through several intermediate forms before finally being written out to disk as Haskell source code. The IR/ subdirectory defines the intermediate forms themselves, while each transition is defined in a module under Trans/. Main ties ties the whole thing together.

The Pipeline

CGR ---> Stage1 ---> Flat ---> NEW ---> Haskell (IR) --> Haskell (source code)

The flow is as follows:

  • First, we read in the data from standard in, and parse it as a CodeGeneratorRequest (henceforth CGR). We use the high-level API for this.
  • Next, we translate to the IR defined in IR.Stage1. This represents essentially the same information as the CGR, but:
    • Information we won't use has been discarded.
    • It is more type safe, in that the structure of the IR cannot represent certain illegal structures that are representable in the CGR.
    • Whereas the CGR contains a list of nodes by id, and entries in the CGR reference each other by ID, in the Stage1 IR we have tied the knot, and the referenced nodes are available directly as fields of their referers.
  • Then, we translate to the Flat IR. This is still a declarative representation of the information in the schema, but it is in a form that maps more nicely to Haskell. The goal of this stage is to massage the information into a form such that later stages don't have to do any work to find out basic facts about the schema. For example:
    • We have flattened the namespace, all definitions are at top level; Haskell doesn't support nested namespaces, so this solves an impedence mismatch. The hierarchical structure is recoverable, as the names themselves encode the structure; see IR.Name.
    • Unions are now represented more cleanly as sums, rather than being mixed in with other fields.
    • Interfaces now contain a full list of their ancestors, not just immediate superclasses. This is useful when genrating instance declarations later on.
  • Next, we translate to the AbstractOp IR. This form encodes the Haskell structures to be generated, rather than what is declared in the schema -- but the it is still abstract in the sense that it only specifies what constructs should be generated. Thus, Abstract and Operational. Examples:
    • Type declaration for this zap type
    • An instance for 'Parse' for this type
    • etc
  • Then, we translate this form into a Haskell AST, defined in IR.Haskell. We use our own AST instead of an off-the-shelf library for a couple reasons:
    • We want to eventually insert comments from the schema into the code as Haddock comments, and the libraries I(zenhack) was able to find do not support comments.
    • The full Haskell syntax is quite complicated, and we don't need most of it; our AST has just the parts we need, and so is simpler to work with.
    • It is useful when generating code to be able treat certain constructs as primitive syntax which really aren't. For example:
      • Our AST does not reflect the fact that function application is curried;
      • Our expression type treats "functorial" application, e.g. f <$> x <*> y <*> z, as primitive, which is convienent for our purposes.
  • Finally, we translate the Haskell AST into text, and write it out to disk.

Misc notes

  • There are a couple modules that are shared between more than one phase and/or IR:

  • IR.Name and IR.Common are used by multiple of the IRs.

  • Trans.ToHaskellCommon used to be used by multiple modules. It should be folded into Trans.AbstractOpToHaskell.

  • The names of some of the IRs are not very good.

    • IR.Stage1 latter is not terribly descriptive.
    • IR.Flat reflects an out of date notion of what the flat IR's purpose is (it does more than just flatten the namespace).