https://github.com/akkartik/mu/blob/master/subx/029transforms.cc

     1 //: Ordering transforms is a well-known hard problem when building compilers.
     2 //: In our case we also have the additional notion of layers. The ordering of
     3 //: layers can have nothing in common with the ordering of transforms when
     4 //: SubX is tangled and run. This can be confusing for readers, particularly
     5 //: if later layers start inserting transforms at arbitrary points between
     6 //: transforms introduced earlier. Over time adding transforms can get harder
     7 //: and harder, having to meet the constraints of everything that's come
     8 //: before. It's worth thinking about organization up-front so the ordering is
     9 //: easy to hold in our heads, and it's obvious where to add a new transform.
    10 //: Some constraints:
    11 //:
    12 //:   1. Layers force us to build SubX bottom-up; since we want to be able to
    13 //:   build and run SubX after stopping loading at any layer, the overall
    14 //:   organization has to be to introduce primitives before we start using
    15 //:   them.
    16 //:
    17 //:   2. Transforms usually need to be run top-down, converting high-level
    18 //:   representations to low-level ones so that low-level layers can be
    19 //:   oblivious to them.
    20 //:
    21 //:   3. When running we'd often like new representations to be checked before
    22 //:   they are transformed away. The whole reason for new representations is
    23 //:   often to add new kinds of automatic checking for our machine code
    24 //:   programs.
    25 //:
    26 //: Putting these constraints together, we'll use the following broad
    27 //: organization:
    28 //:
    29 //:   a) We'll divide up our transforms into "levels", each level consisting
    30 //:   of multiple transforms, and dealing in some new set of representational
    31 //:   ideas. Levels will be added in reverse order to the one their transforms
    32 //:   will be run in.
    33 //:
    34 //:     To run all transforms:
    35 //:       Load transforms for level n
    36 //:       Load transforms for level n-1
    37 //:       ...
    38 //:       Load transforms for level 2
    39 //:       Run code at level 1
    40 //:
    41 //:   b) *Within* a level we'll usually introduce transforms in the order
    42 //:   they're run in.
    43 //:
    44 //:     To run transforms for level n:
    45 //:       Perform transform of layer l
    46 //:       Perform transform of layer l+1
    47 //:       ...
    48 //:
    49 //:   c) Within a level it's often most natural to introduce a new
    50 //:   representation by showing how it's transformed to the level below. To
    51 //:   make such exceptions more obvious checks usually won't be first-class
    52 //:   transforms; instead code that keeps the program unmodified will run
    53 //:   within transforms before they mutate the program. As an example:
    54 //:
    55 //:     Layer l introduces a transform
    56 //:     Layer l+1 adds precondition checks for the transform
    57 //:
    58 //: This may all seem abstract, but will hopefully make sense over time. The
    59 //: goals are basically to always have a working program after any layer, to
    60 //: have the order of layers make narrative sense, and to order transforms
    61 //: correctly at runtime.
    62
    63 :(before "End One-time Setup")
    64 // Begin Transforms
    65 // End Transforms